focalboard/webapp/src/mutator.ts

635 lines
22 KiB
TypeScript
Raw Normal View History

2020-10-20 21:50:53 +02:00
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
2020-10-22 04:54:21 +02:00
import {IBlock, MutableBlock} from './blocks/block'
2020-10-21 03:28:55 +02:00
import {Board, IPropertyOption, IPropertyTemplate, MutableBoard, PropertyType} from './blocks/board'
import {BoardView, ISortOption, MutableBoardView} from './blocks/boardView'
import {Card, MutableCard} from './blocks/card'
import {MutableImageBlock} from './blocks/imageBlock'
import {IOrderedBlock, MutableOrderedBlock} from './blocks/orderedBlock'
2020-10-21 03:54:39 +02:00
import {BoardTree} from './viewModel/boardTree'
2020-10-20 21:52:56 +02:00
import {FilterGroup} from './filterGroup'
import octoClient from './octoClient'
import undoManager from './undomanager'
import {Utils} from './utils'
2020-11-12 19:16:59 +01:00
import {OctoUtils} from './octoUtils'
2020-10-08 18:21:27 +02:00
//
// The Mutator is used to make all changes to server state
// It also ensures that the Undo-manager is called for each action
//
class Mutator {
2020-10-28 21:57:53 +01:00
private undoGroupId?: string
2020-11-12 20:18:19 +01:00
private beginUndoGroup(): string | undefined {
if (this.undoGroupId) {
Utils.assertFailure('UndoManager does not support nested groups')
2020-11-12 23:55:55 +01:00
return undefined
}
2020-10-28 21:57:53 +01:00
this.undoGroupId = Utils.createGuid()
return this.undoGroupId
2020-10-28 21:57:53 +01:00
}
private endUndoGroup(groupId: string) {
if (this.undoGroupId !== groupId) {
Utils.assertFailure('Mismatched groupId. UndoManager does not support nested groups')
return
}
2020-10-28 21:57:53 +01:00
this.undoGroupId = undefined
}
async performAsUndoGroup(actions: () => Promise<void>): Promise<void> {
const groupId = this.beginUndoGroup()
try {
await actions()
2020-11-03 00:47:45 +01:00
} catch (err) {
Utils.assertFailure(`ERROR: ${err?.toString?.()}`)
}
2020-11-12 20:18:19 +01:00
if (groupId) {
this.endUndoGroup(groupId)
}
}
2020-10-28 18:46:36 +01:00
async updateBlock(newBlock: IBlock, oldBlock: IBlock, description: string): Promise<void> {
2020-10-21 03:47:02 +02:00
await undoManager.perform(
async () => {
await octoClient.updateBlock(newBlock)
},
async () => {
await octoClient.updateBlock(oldBlock)
},
description,
2020-11-03 00:47:45 +01:00
this.undoGroupId,
2020-10-21 03:47:02 +02:00
)
}
private async updateBlocks(newBlocks: IBlock[], oldBlocks: IBlock[], description: string): Promise<void> {
await undoManager.perform(
async () => {
await octoClient.updateBlocks(newBlocks)
},
async () => {
await octoClient.updateBlocks(oldBlocks)
},
description,
2020-11-03 00:47:45 +01:00
this.undoGroupId,
2020-10-21 03:47:02 +02:00
)
}
2020-10-20 21:50:53 +02:00
async insertBlock(block: IBlock, description = 'add', afterRedo?: () => Promise<void>, beforeUndo?: () => Promise<void>) {
await undoManager.perform(
async () => {
await octoClient.insertBlock(block)
await afterRedo?.()
},
async () => {
await beforeUndo?.()
await octoClient.deleteBlock(block.id)
},
description,
2020-11-03 00:47:45 +01:00
this.undoGroupId,
2020-10-20 21:50:53 +02:00
)
}
async insertBlocks(blocks: IBlock[], description = 'add', afterRedo?: () => Promise<void>, beforeUndo?: () => Promise<void>) {
await undoManager.perform(
async () => {
await octoClient.insertBlocks(blocks)
await afterRedo?.()
},
async () => {
await beforeUndo?.()
2020-11-13 00:48:31 +01:00
const awaits = []
2020-10-20 21:50:53 +02:00
for (const block of blocks) {
2020-11-13 00:48:31 +01:00
awaits.push(octoClient.deleteBlock(block.id))
2020-10-20 21:50:53 +02:00
}
2020-11-13 00:48:31 +01:00
await Promise.all(awaits)
2020-10-20 21:50:53 +02:00
},
description,
2020-11-03 00:47:45 +01:00
this.undoGroupId,
2020-10-20 21:50:53 +02:00
)
}
async deleteBlock(block: IBlock, description?: string, beforeRedo?: () => Promise<void>, afterUndo?: () => Promise<void>) {
2020-11-12 23:55:55 +01:00
const actualDescription = description || `delete ${block.type}`
2020-10-20 21:50:53 +02:00
await undoManager.perform(
async () => {
await beforeRedo?.()
await octoClient.deleteBlock(block.id)
},
async () => {
await octoClient.insertBlock(block)
await afterUndo?.()
},
2020-11-12 23:55:55 +01:00
actualDescription,
2020-11-03 00:47:45 +01:00
this.undoGroupId,
2020-10-20 21:50:53 +02:00
)
}
async changeTitle(block: IBlock, title: string, description = 'change title') {
2020-10-21 03:28:55 +02:00
const newBlock = new MutableBlock(block)
newBlock.title = title
2020-10-21 03:47:02 +02:00
await this.updateBlock(newBlock, block, description)
2020-10-20 21:50:53 +02:00
}
async changeIcon(block: Card | Board, icon: string, description = 'change icon') {
let newBlock: IBlock
2020-10-21 03:28:55 +02:00
switch (block.type) {
case 'card': {
const card = new MutableCard(block)
card.icon = icon
newBlock = card
break
}
case 'board': {
const board = new MutableBoard(block)
board.icon = icon
newBlock = board
break
}
2020-11-12 20:18:19 +01:00
default: {
Utils.assertFailure(`changeIcon: Invalid block type: ${block.type}`)
return
}
2020-10-21 03:28:55 +02:00
}
2020-10-21 03:47:02 +02:00
await this.updateBlock(newBlock, block, description)
2020-10-20 21:50:53 +02:00
}
2020-11-18 20:11:51 +01:00
async changeDescription(block: IBlock, boardDescription: string, description = 'change description') {
const newBoard = new MutableBoard(block)
newBoard.description = boardDescription
await this.updateBlock(newBoard, block, description)
}
async showDescription(board: Board, showDescription = true, description?: string) {
const newBoard = new MutableBoard(board)
newBoard.showDescription = showDescription
let actionDescription = description
if (!actionDescription) {
actionDescription = showDescription ? 'show description' : 'hide description'
}
await this.updateBlock(newBoard, board, actionDescription)
}
2020-10-20 21:50:53 +02:00
async changeOrder(block: IOrderedBlock, order: number, description = 'change order') {
2020-10-21 03:28:55 +02:00
const newBlock = new MutableOrderedBlock(block)
newBlock.order = order
2020-10-21 03:47:02 +02:00
await this.updateBlock(newBlock, block, description)
2020-10-20 21:50:53 +02:00
}
// Property Templates
async insertPropertyTemplate(boardTree: BoardTree, index = -1, template?: IPropertyTemplate) {
const {board, activeView} = boardTree
2020-11-12 20:18:19 +01:00
if (!activeView) {
Utils.assertFailure('insertPropertyTemplate: no activeView')
return
}
2020-10-20 21:50:53 +02:00
2020-11-13 01:25:14 +01:00
const newTemplate = template || {
id: Utils.createGuid(),
name: 'New Property',
type: 'text',
options: [],
2020-10-20 21:50:53 +02:00
}
2020-10-21 03:28:55 +02:00
const oldBlocks: IBlock[] = [board]
2020-10-20 21:50:53 +02:00
2020-10-21 03:28:55 +02:00
const newBoard = new MutableBoard(board)
2020-11-13 01:25:14 +01:00
const startIndex = (index >= 0) ? index : board.cardProperties.length
newBoard.cardProperties.splice(startIndex, 0, newTemplate)
2020-10-21 03:28:55 +02:00
const changedBlocks: IBlock[] = [newBoard]
2020-10-20 21:50:53 +02:00
2020-10-20 21:52:56 +02:00
let description = 'add property'
2020-10-20 21:50:53 +02:00
if (activeView.viewType === 'table') {
2020-10-21 03:28:55 +02:00
oldBlocks.push(activeView)
const newActiveView = new MutableBoardView(activeView)
2020-11-13 01:25:14 +01:00
newActiveView.visiblePropertyIds.push(newTemplate.id)
2020-10-21 03:28:55 +02:00
changedBlocks.push(newActiveView)
2020-10-20 21:50:53 +02:00
2020-10-20 21:52:56 +02:00
description = 'add column'
2020-10-20 21:50:53 +02:00
}
2020-10-21 03:47:02 +02:00
await this.updateBlocks(changedBlocks, oldBlocks, description)
2020-10-20 21:50:53 +02:00
}
2020-10-22 04:54:21 +02:00
async duplicatePropertyTemplate(boardTree: BoardTree, propertyId: string) {
2020-10-20 21:50:53 +02:00
const {board, activeView} = boardTree
2020-11-12 20:18:19 +01:00
if (!activeView) {
Utils.assertFailure('duplicatePropertyTemplate: no activeView')
return
}
2020-10-20 21:50:53 +02:00
2020-10-21 03:28:55 +02:00
const oldBlocks: IBlock[] = [board]
2020-10-20 21:50:53 +02:00
2020-10-21 03:28:55 +02:00
const newBoard = new MutableBoard(board)
const changedBlocks: IBlock[] = [newBoard]
const index = newBoard.cardProperties.findIndex((o) => o.id === propertyId)
2020-10-20 21:50:53 +02:00
if (index === -1) {
2020-10-21 03:28:55 +02:00
Utils.assertFailure(`Cannot find template with id: ${propertyId}`)
return
2020-10-20 21:50:53 +02:00
}
2020-10-21 03:28:55 +02:00
const srcTemplate = newBoard.cardProperties[index]
2020-10-20 21:50:53 +02:00
const newTemplate: IPropertyTemplate = {
id: Utils.createGuid(),
name: `Copy of ${srcTemplate.name}`,
type: srcTemplate.type,
options: srcTemplate.options.slice(),
}
2020-10-21 03:28:55 +02:00
newBoard.cardProperties.splice(index + 1, 0, newTemplate)
2020-10-20 21:50:53 +02:00
2020-10-20 21:52:56 +02:00
let description = 'duplicate property'
2020-10-20 21:50:53 +02:00
if (activeView.viewType === 'table') {
2020-10-21 03:28:55 +02:00
oldBlocks.push(activeView)
const newActiveView = new MutableBoardView(activeView)
newActiveView.visiblePropertyIds.push(newTemplate.id)
changedBlocks.push(newActiveView)
2020-10-20 21:50:53 +02:00
2020-10-20 21:52:56 +02:00
description = 'duplicate column'
2020-10-20 21:50:53 +02:00
}
2020-10-21 03:47:02 +02:00
await this.updateBlocks(changedBlocks, oldBlocks, description)
2020-10-20 21:50:53 +02:00
}
async changePropertyTemplateOrder(board: Board, template: IPropertyTemplate, destIndex: number) {
const templates = board.cardProperties
const newValue = templates.slice()
const srcIndex = templates.indexOf(template)
Utils.log(`srcIndex: ${srcIndex}, destIndex: ${destIndex}`)
newValue.splice(destIndex, 0, newValue.splice(srcIndex, 1)[0])
2020-10-21 03:28:55 +02:00
const newBoard = new MutableBoard(board)
newBoard.cardProperties = newValue
2020-10-21 03:47:02 +02:00
await this.updateBlock(newBoard, board, 'reorder properties')
2020-10-20 21:50:53 +02:00
}
async deleteProperty(boardTree: BoardTree, propertyId: string) {
const {board, views, cards} = boardTree
2020-10-21 03:28:55 +02:00
const oldBlocks: IBlock[] = [board]
2020-10-20 21:50:53 +02:00
2020-10-21 03:28:55 +02:00
const newBoard = new MutableBoard(board)
const changedBlocks: IBlock[] = [newBoard]
newBoard.cardProperties = board.cardProperties.filter((o) => o.id !== propertyId)
2020-10-20 21:50:53 +02:00
views.forEach((view) => {
if (view.visiblePropertyIds.includes(propertyId)) {
2020-10-21 03:28:55 +02:00
oldBlocks.push(view)
const newView = new MutableBoardView(view)
newView.visiblePropertyIds = view.visiblePropertyIds.filter((o) => o !== propertyId)
changedBlocks.push(newView)
2020-10-20 21:50:53 +02:00
}
})
cards.forEach((card) => {
if (card.properties[propertyId]) {
2020-10-21 03:28:55 +02:00
oldBlocks.push(card)
const newCard = new MutableCard(card)
delete newCard.properties[propertyId]
changedBlocks.push(newCard)
2020-10-20 21:50:53 +02:00
}
})
2020-10-21 03:47:02 +02:00
await this.updateBlocks(changedBlocks, oldBlocks, 'delete property')
2020-10-20 21:50:53 +02:00
}
async renameProperty(board: Board, propertyId: string, name: string) {
2020-10-21 03:28:55 +02:00
const newBoard = new MutableBoard(board)
2020-10-20 21:50:53 +02:00
2020-10-21 03:28:55 +02:00
const template = newBoard.cardProperties.find((o) => o.id === propertyId)
2020-10-20 21:50:53 +02:00
if (!template) {
2020-10-21 03:28:55 +02:00
Utils.assertFailure(`Can't find property template with Id: ${propertyId}`)
return
2020-10-20 21:50:53 +02:00
}
Utils.log(`renameProperty from ${template.name} to ${name}`)
template.name = name
2020-10-21 03:47:02 +02:00
await this.updateBlock(newBoard, board, 'rename property')
2020-10-20 21:50:53 +02:00
}
// Properties
async insertPropertyOption(boardTree: BoardTree, template: IPropertyTemplate, option: IPropertyOption, description = 'add option') {
const {board} = boardTree
Utils.assert(board.cardProperties.includes(template))
2020-10-21 03:28:55 +02:00
const newBoard = new MutableBoard(board)
2020-11-12 20:18:19 +01:00
const newTemplate = newBoard.cardProperties.find((o) => o.id === template.id)!
2020-10-21 03:28:55 +02:00
newTemplate.options.push(option)
2020-10-20 21:50:53 +02:00
2020-10-21 03:47:02 +02:00
await this.updateBlock(newBoard, board, description)
2020-10-20 21:50:53 +02:00
}
async deletePropertyOption(boardTree: BoardTree, template: IPropertyTemplate, option: IPropertyOption) {
const {board} = boardTree
2020-10-21 03:28:55 +02:00
const newBoard = new MutableBoard(board)
2020-11-12 20:18:19 +01:00
const newTemplate = newBoard.cardProperties.find((o) => o.id === template.id)!
2020-10-23 21:59:09 +02:00
newTemplate.options = newTemplate.options.filter((o) => o.id !== option.id)
2020-10-20 21:50:53 +02:00
2020-10-21 03:47:02 +02:00
await this.updateBlock(newBoard, board, 'delete option')
2020-10-20 21:50:53 +02:00
}
async changePropertyOptionOrder(board: Board, template: IPropertyTemplate, option: IPropertyOption, destIndex: number) {
2020-10-21 03:28:55 +02:00
const srcIndex = template.options.indexOf(option)
2020-10-20 21:50:53 +02:00
Utils.log(`srcIndex: ${srcIndex}, destIndex: ${destIndex}`)
2020-10-21 03:28:55 +02:00
const newBoard = new MutableBoard(board)
2020-11-12 20:18:19 +01:00
const newTemplate = newBoard.cardProperties.find((o) => o.id === template.id)!
2020-10-21 03:28:55 +02:00
newTemplate.options.splice(destIndex, 0, newTemplate.options.splice(srcIndex, 1)[0])
2020-10-20 21:50:53 +02:00
2020-10-21 03:47:02 +02:00
await this.updateBlock(newBoard, board, 'reorder options')
2020-10-20 21:50:53 +02:00
}
async changePropertyOptionValue(boardTree: BoardTree, propertyTemplate: IPropertyTemplate, option: IPropertyOption, value: string) {
2020-11-13 01:25:14 +01:00
const {board} = boardTree
2020-10-20 21:50:53 +02:00
2020-10-21 03:28:55 +02:00
const oldBlocks: IBlock[] = [board]
2020-10-20 21:50:53 +02:00
2020-10-21 03:28:55 +02:00
const newBoard = new MutableBoard(board)
2020-11-12 20:18:19 +01:00
const newTemplate = newBoard.cardProperties.find((o) => o.id === propertyTemplate.id)!
const newOption = newTemplate.options.find((o) => o.id === option.id)!
2020-10-21 03:28:55 +02:00
newOption.value = value
const changedBlocks: IBlock[] = [newBoard]
2020-10-20 21:50:53 +02:00
2020-10-21 03:47:02 +02:00
await this.updateBlocks(changedBlocks, oldBlocks, 'rename option')
2020-10-20 21:50:53 +02:00
return changedBlocks
}
2020-10-21 03:28:55 +02:00
async changePropertyOptionColor(board: Board, template: IPropertyTemplate, option: IPropertyOption, color: string) {
const newBoard = new MutableBoard(board)
2020-11-12 20:18:19 +01:00
const newTemplate = newBoard.cardProperties.find((o) => o.id === template.id)!
const newOption = newTemplate.options.find((o) => o.id === option.id)!
2020-10-21 03:28:55 +02:00
newOption.color = color
2020-10-21 03:47:02 +02:00
await this.updateBlock(newBoard, board, 'change option color')
2020-10-20 21:50:53 +02:00
}
async changePropertyValue(card: Card, propertyId: string, value?: string, description = 'change property') {
2020-10-21 03:28:55 +02:00
const newCard = new MutableCard(card)
2020-11-12 20:18:19 +01:00
if (value) {
newCard.properties[propertyId] = value
} else {
delete newCard.properties[propertyId]
}
2020-10-21 03:47:02 +02:00
await this.updateBlock(newCard, card, description)
2020-10-20 21:50:53 +02:00
}
async changePropertyType(boardTree: BoardTree, propertyTemplate: IPropertyTemplate, type: PropertyType) {
2020-10-27 22:00:15 +01:00
const {board} = boardTree
2020-10-21 03:28:55 +02:00
const newBoard = new MutableBoard(board)
2020-11-12 20:18:19 +01:00
const newTemplate = newBoard.cardProperties.find((o) => o.id === propertyTemplate.id)!
2020-10-21 03:28:55 +02:00
newTemplate.type = type
const oldBlocks: IBlock[] = [board]
const newBlocks: IBlock[] = [newBoard]
if (propertyTemplate.type === 'select') {
// Map select to their values
for (const card of boardTree.allCards) {
const oldValue = card.properties[propertyTemplate.id]
if (oldValue) {
2020-10-27 22:00:15 +01:00
const newValue = propertyTemplate.options.find((o) => o.id === oldValue)?.value
const newCard = new MutableCard(card)
2020-11-12 20:18:19 +01:00
if (newValue) {
newCard.properties[propertyTemplate.id] = newValue
} else {
delete newCard.properties[propertyTemplate.id]
}
newBlocks.push(newCard)
oldBlocks.push(card)
}
}
} else if (type === 'select') {
// Map values to template option IDs
for (const card of boardTree.allCards) {
const oldValue = card.properties[propertyTemplate.id]
if (oldValue) {
2020-10-27 22:00:15 +01:00
const newValue = propertyTemplate.options.find((o) => o.value === oldValue)?.id
const newCard = new MutableCard(card)
2020-11-12 20:18:19 +01:00
if (newValue) {
newCard.properties[propertyTemplate.id] = newValue
} else {
delete newCard.properties[propertyTemplate.id]
}
newBlocks.push(newCard)
oldBlocks.push(card)
}
}
}
await this.updateBlocks(newBlocks, oldBlocks, 'change property type')
2020-10-20 21:50:53 +02:00
}
// Views
2020-10-23 21:59:09 +02:00
async changeViewSortOptions(view: BoardView, sortOptions: ISortOption[]): Promise<void> {
2020-10-21 03:28:55 +02:00
const newView = new MutableBoardView(view)
newView.sortOptions = sortOptions
2020-10-21 03:47:02 +02:00
await this.updateBlock(newView, view, 'sort')
2020-10-20 21:50:53 +02:00
}
2020-11-13 02:24:24 +01:00
async changeViewFilter(view: BoardView, filter: FilterGroup): Promise<void> {
2020-10-21 03:28:55 +02:00
const newView = new MutableBoardView(view)
newView.filter = filter
2020-10-21 03:47:02 +02:00
await this.updateBlock(newView, view, 'filter')
2020-10-20 21:50:53 +02:00
}
async changeViewGroupById(view: BoardView, groupById: string): Promise<void> {
const newView = new MutableBoardView(view)
newView.groupById = groupById
await this.updateBlock(newView, view, 'group by')
}
2020-10-23 21:59:09 +02:00
async changeViewVisibleProperties(view: BoardView, visiblePropertyIds: string[], description = 'show / hide property'): Promise<void> {
2020-10-21 03:28:55 +02:00
const newView = new MutableBoardView(view)
newView.visiblePropertyIds = visiblePropertyIds
2020-10-21 03:47:02 +02:00
await this.updateBlock(newView, view, description)
2020-10-20 21:50:53 +02:00
}
async changeViewVisibleOptionIds(view: BoardView, visibleOptionIds: string[], description = 'reorder'): Promise<void> {
2020-10-21 03:28:55 +02:00
const newView = new MutableBoardView(view)
newView.visibleOptionIds = visibleOptionIds
await this.updateBlock(newView, view, description)
}
async changeViewHiddenOptionIds(view: BoardView, hiddenOptionIds: string[], description = 'reorder'): Promise<void> {
const newView = new MutableBoardView(view)
newView.hiddenOptionIds = hiddenOptionIds
await this.updateBlock(newView, view, description)
2020-10-20 21:50:53 +02:00
}
2020-10-23 21:59:09 +02:00
async hideViewColumn(view: BoardView, columnOptionId: string): Promise<void> {
2020-10-26 22:35:45 +01:00
if (view.hiddenOptionIds.includes(columnOptionId)) {
2020-10-23 21:59:09 +02:00
return
}
const newView = new MutableBoardView(view)
2020-10-27 22:00:15 +01:00
newView.visibleOptionIds = newView.visibleOptionIds.filter((o) => o !== columnOptionId)
2020-10-26 22:35:45 +01:00
newView.hiddenOptionIds.push(columnOptionId)
2020-10-23 21:59:09 +02:00
await this.updateBlock(newView, view, 'hide column')
}
async unhideViewColumn(view: BoardView, columnOptionId: string): Promise<void> {
2020-10-26 22:35:45 +01:00
if (!view.hiddenOptionIds.includes(columnOptionId)) {
2020-10-23 21:59:09 +02:00
return
}
const newView = new MutableBoardView(view)
2020-10-26 22:35:45 +01:00
newView.hiddenOptionIds = newView.hiddenOptionIds.filter((o) => o !== columnOptionId)
2020-10-27 22:00:15 +01:00
// Put the column at the end of the visible list
2020-10-27 22:00:15 +01:00
newView.visibleOptionIds = newView.visibleOptionIds.filter((o) => o !== columnOptionId)
newView.visibleOptionIds.push(columnOptionId)
2020-10-23 21:59:09 +02:00
await this.updateBlock(newView, view, 'show column')
}
2020-10-28 18:46:36 +01:00
async changeViewCardOrder(view: BoardView, cardOrder: string[], description = 'reorder'): Promise<void> {
const newView = new MutableBoardView(view)
newView.cardOrder = cardOrder
await this.updateBlock(newView, view, description)
}
2020-11-12 19:16:59 +01:00
// Duplicate
async duplicateCard(
cardId: string,
description = 'duplicate card',
asTemplate = false,
afterRedo?: (newCardId: string) => Promise<void>,
beforeUndo?: () => Promise<void>,
): Promise<[IBlock[], string]> {
2020-11-12 19:16:59 +01:00
const blocks = await octoClient.getSubtree(cardId, 2)
const [newBlocks1, newCard] = OctoUtils.duplicateBlockTree(blocks, cardId) as [IBlock[], MutableCard, Record<string, string>]
2020-11-13 01:25:14 +01:00
const newBlocks = newBlocks1.filter((o) => o.type !== 'comment')
2020-11-12 19:16:59 +01:00
Utils.log(`duplicateCard: duplicating ${newBlocks.length} blocks`)
if (asTemplate === newCard.isTemplate) {
newCard.title = `Copy of ${newCard.title}`
} else if (asTemplate) {
// Template from card
newCard.title = 'New card template'
} else {
// Card from template
newCard.title = ''
}
newCard.isTemplate = asTemplate
2020-11-12 19:16:59 +01:00
await this.insertBlocks(
newBlocks,
description,
async () => {
2020-11-17 23:11:04 +01:00
await afterRedo?.(newCard.id)
2020-11-12 19:16:59 +01:00
},
beforeUndo,
)
2020-11-17 23:11:04 +01:00
return [newBlocks, newCard.id]
2020-11-12 19:16:59 +01:00
}
async duplicateBoard(
boardId: string,
description = 'duplicate board',
asTemplate = false,
afterRedo?: (newBoardId: string) => Promise<void>,
beforeUndo?: () => Promise<void>,
): Promise<[IBlock[], string]> {
2020-11-12 19:16:59 +01:00
const blocks = await octoClient.getSubtree(boardId, 3)
const [newBlocks1, newBoard] = OctoUtils.duplicateBlockTree(blocks, boardId) as [IBlock[], MutableBoard, Record<string, string>]
2020-11-13 01:25:14 +01:00
const newBlocks = newBlocks1.filter((o) => o.type !== 'comment')
2020-11-12 19:16:59 +01:00
Utils.log(`duplicateBoard: duplicating ${newBlocks.length} blocks`)
if (asTemplate === newBoard.isTemplate) {
newBoard.title = `Copy of ${newBoard.title}`
} else if (asTemplate) {
// Template from board
newBoard.title = 'New board template'
} else {
// Board from template
newBoard.title = ''
}
newBoard.isTemplate = asTemplate
2020-11-12 19:16:59 +01:00
await this.insertBlocks(
newBlocks,
description,
async () => {
2020-11-17 23:11:04 +01:00
await afterRedo?.(newBoard.id)
2020-11-12 19:16:59 +01:00
},
beforeUndo,
)
2020-11-17 23:11:04 +01:00
return [newBlocks, newBoard.id]
2020-11-12 19:16:59 +01:00
}
2020-10-28 18:46:36 +01:00
// Other methods
2020-10-20 21:50:53 +02:00
// Not a mutator, but convenient to put here since Mutator wraps OctoClient
2020-10-23 21:59:09 +02:00
async exportFullArchive(): Promise<IBlock[]> {
2020-10-20 21:50:53 +02:00
return octoClient.exportFullArchive()
}
// Not a mutator, but convenient to put here since Mutator wraps OctoClient
async importFullArchive(blocks: readonly IBlock[]): Promise<Response> {
2020-10-20 21:50:53 +02:00
return octoClient.importFullArchive(blocks)
}
async createImageBlock(parent: IBlock, file: File, order = 1000): Promise<IBlock | undefined> {
2020-10-20 21:50:53 +02:00
const url = await octoClient.uploadFile(file)
if (!url) {
return undefined
}
2020-10-21 03:28:55 +02:00
const block = new MutableImageBlock()
block.parentId = parent.id
block.rootId = parent.rootId
2020-10-20 21:50:53 +02:00
block.order = order
block.url = url
await undoManager.perform(
async () => {
await octoClient.insertBlock(block)
},
async () => {
await octoClient.deleteBlock(block.id)
},
2020-10-21 03:47:02 +02:00
'add image',
2020-11-03 00:47:45 +01:00
this.undoGroupId,
2020-10-20 21:50:53 +02:00
)
return block
}
2020-10-24 20:41:23 +02:00
get canUndo(): boolean {
return undoManager.canUndo
2020-10-20 21:50:53 +02:00
}
2020-10-24 20:41:23 +02:00
get canRedo(): boolean {
return undoManager.canRedo
2020-10-20 21:50:53 +02:00
}
2020-10-24 20:41:23 +02:00
get undoDescription(): string | undefined {
return undoManager.undoDescription
2020-10-20 21:50:53 +02:00
}
2020-10-24 20:41:23 +02:00
get redoDescription(): string | undefined {
2020-10-20 21:50:53 +02:00
return undoManager.redoDescription
}
2020-10-24 20:41:23 +02:00
async undo() {
await undoManager.undo()
}
async redo() {
await undoManager.redo()
}
2020-10-08 18:21:27 +02:00
}
const mutator = new Mutator()
export default mutator
2020-10-20 21:50:53 +02:00
export {mutator}