diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json index 844f6858b..f2547846b 100644 --- a/webapp/i18n/en.json +++ b/webapp/i18n/en.json @@ -185,6 +185,7 @@ "Sidebar.add-board": "+ Add board", "Sidebar.changePassword": "Change password", "Sidebar.delete-board": "Delete board", + "Sidebar.duplicate-board": "Duplicate board", "Sidebar.export-archive": "Export archive", "Sidebar.import": "Import", "Sidebar.import-archive": "Import archive", diff --git a/webapp/src/components/sidebar/sidebarBoardItem.tsx b/webapp/src/components/sidebar/sidebarBoardItem.tsx index 3ef59d9ea..d2ad07792 100644 --- a/webapp/src/components/sidebar/sidebarBoardItem.tsx +++ b/webapp/src/components/sidebar/sidebarBoardItem.tsx @@ -1,6 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useState} from 'react' +import React, {useCallback, useMemo, useState} from 'react' import {useIntl} from 'react-intl' import {Board} from '../../blocks/board' @@ -27,6 +27,10 @@ import CalendarIcon from '../../widgets/icons/calendar' import {getCurrentTeam} from '../../store/teams' import {Permission} from '../../constants' +import DuplicateIcon from "../../widgets/icons/duplicate" +import {Utils} from "../../utils" + +import {useHistory, useRouteMatch} from "react-router-dom" const iconForViewType = (viewType: IViewType): JSX.Element => { switch (viewType) { @@ -58,6 +62,9 @@ const SidebarBoardItem = (props: Props) => { const currentViewId = useAppSelector(getCurrentViewId) const teamID = team?.id || '' + const match = useRouteMatch<{boardId: string, viewId?: string, cardId?: string, teamId?: string}>() + const history = useHistory() + const generateMoveToCategoryOptions = (blockID: string) => { return props.allCategories.map((category) => ( { } const board = props.board + + const handleDuplicateBoard = useCallback(() => { + return mutator.duplicateBoard( + board.id, + undefined, + board.isTemplate, + undefined, + () => { + Utils.showBoard(board.id, match, history) + return Promise.resolve() + } + ) + }, [board.id]) + const title = board.title || intl.formatMessage({id: 'Sidebar.untitled-board', defaultMessage: '(Untitled Board)'}) return ( <> @@ -126,6 +147,12 @@ const SidebarBoardItem = (props: Props) => { > {generateMoveToCategoryOptions(board.id)} + } + onClick={handleDuplicateBoard} + /> diff --git a/webapp/src/components/sidebar/sidebarCategory.tsx b/webapp/src/components/sidebar/sidebarCategory.tsx index cb068f361..2c3fd3d73 100644 --- a/webapp/src/components/sidebar/sidebarCategory.tsx +++ b/webapp/src/components/sidebar/sidebarCategory.tsx @@ -60,15 +60,16 @@ const SidebarCategory = (props: Props) => { const teamID = team?.id || '' const showBoard = useCallback((boardId) => { - // if the same board, reuse the match params - // otherwise remove viewId and cardId, results in first view being selected - const params = {...match.params, boardId: boardId || ''} - if (boardId !== match.params.boardId) { - params.viewId = undefined - params.cardId = undefined - } - const newPath = generatePath(match.path, params) - history.push(newPath) + // // if the same board, reuse the match params + // // otherwise remove viewId and cardId, results in first view being selected + // const params = {...match.params, boardId: boardId || ''} + // if (boardId !== match.params.boardId) { + // params.viewId = undefined + // params.cardId = undefined + // } + // const newPath = generatePath(match.path, params) + // history.push(newPath) + Utils.showBoard(boardId, match, history) props.hideSidebar() }, [match, history]) diff --git a/webapp/src/utils.ts b/webapp/src/utils.ts index 015c47ee7..3830d5987 100644 --- a/webapp/src/utils.ts +++ b/webapp/src/utils.ts @@ -4,6 +4,10 @@ import {marked} from 'marked' import {IntlShape} from 'react-intl' import moment from 'moment' +import {generatePath, match as routerMatch} from "react-router-dom" + +import {History} from "history" + import {Block} from './blocks/block' import {Board as BoardType, BoardMember, createBoard} from './blocks/board' import {createBoardView} from './blocks/boardView' @@ -703,6 +707,22 @@ class Utils { } return (Utils.isMac() && e.metaKey) || (!Utils.isMac() && e.ctrlKey && !e.altKey) } + + static showBoard( + boardId: string, + match: routerMatch<{boardId: string, viewId?: string, cardId?: string, teamId?: string}>, + history: History, + ) { + // if the same board, reuse the match params + // otherwise remove viewId and cardId, results in first view being selected + const params = {...match.params, boardId: boardId || ''} + if (boardId !== match.params.boardId) { + params.viewId = undefined + params.cardId = undefined + } + const newPath = generatePath(match.path, params) + history.push(newPath) + } } export {Utils, IDType}