diff --git a/webapp/src/blocks/boardView.ts b/webapp/src/blocks/boardView.ts index 07dcdb7ac..c90e3b773 100644 --- a/webapp/src/blocks/boardView.ts +++ b/webapp/src/blocks/boardView.ts @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import {IBlock} from '../blocks/block' import {FilterGroup} from '../filterGroup' +import {Utils} from '../utils' import {MutableBlock} from './block' @@ -18,6 +19,8 @@ interface BoardView extends IBlock { readonly filter: FilterGroup readonly cardOrder: readonly string[] readonly columnWidths: Readonly> + + duplicate(): MutableBoardView } class MutableBoardView extends MutableBlock implements BoardView { @@ -101,6 +104,12 @@ class MutableBoardView extends MutableBlock implements BoardView { this.viewType = 'board' } } + + duplicate(): MutableBoardView { + const view = new MutableBoardView(this) + view.id = Utils.createGuid() + return view + } } export {BoardView, MutableBoardView, IViewType, ISortOption} diff --git a/webapp/src/components/viewMenu.tsx b/webapp/src/components/viewMenu.tsx index 4906cde1e..9d1c6f811 100644 --- a/webapp/src/components/viewMenu.tsx +++ b/webapp/src/components/viewMenu.tsx @@ -12,6 +12,7 @@ import {BoardTree} from '../viewModel/boardTree' import AddIcon from '../widgets/icons/add' import BoardIcon from '../widgets/icons/board' import DeleteIcon from '../widgets/icons/delete' +import DuplicateIcon from '../widgets/icons/duplicate' import TableIcon from '../widgets/icons/table' import Menu from '../widgets/menu' @@ -24,6 +25,27 @@ type Props = { } export class ViewMenu extends React.PureComponent { + private handleDuplicateView = async () => { + const {boardTree, showView} = this.props + Utils.log('duplicateView') + const currentViewId = boardTree.activeView.id + const newView = boardTree.activeView.duplicate() + newView.title = `Copy of ${boardTree.activeView.title}` + await mutator.insertBlock( + newView, + 'duplicate view', + async () => { + // This delay is needed because OctoListener has a default 100 ms notification delay before updates + setTimeout(() => { + showView(newView.id) + }, 120) + }, + async () => { + showView(currentViewId) + }, + ) + } + private handleDeleteView = async () => { const {boardTree, showView} = this.props Utils.log('deleteView') @@ -113,6 +135,14 @@ export class ViewMenu extends React.PureComponent { onClick={this.handleViewClick} />))} + {!this.props.readonly && + } + onClick={this.handleDuplicateView} + /> + } {!this.props.readonly && boardTree.views.length > 1 &&