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}