1
0
mirror of https://github.com/mattermost/focalboard.git synced 2024-11-24 08:22:29 +02:00

Undo grouping

This commit is contained in:
Chen-I Lim 2020-10-28 13:57:53 -07:00
parent 25a1ebeda4
commit b7ea5e677b
4 changed files with 54 additions and 10 deletions

View File

@ -545,6 +545,7 @@ class BoardComponent extends React.Component<Props, State> {
Utils.assertValue(boardTree) Utils.assertValue(boardTree)
if (draggedCards.length > 0) { if (draggedCards.length > 0) {
mutator.beginUndoGroup()
for (const draggedCard of draggedCards) { for (const draggedCard of draggedCards) {
Utils.log(`ondrop. Card: ${draggedCard.title}, column: ${optionId}`) Utils.log(`ondrop. Card: ${draggedCard.title}, column: ${optionId}`)
const oldValue = draggedCard.properties[boardTree.groupByProperty.id] const oldValue = draggedCard.properties[boardTree.groupByProperty.id]
@ -552,6 +553,7 @@ class BoardComponent extends React.Component<Props, State> {
await mutator.changePropertyValue(draggedCard, boardTree.groupByProperty.id, optionId, 'drag card') await mutator.changePropertyValue(draggedCard, boardTree.groupByProperty.id, optionId, 'drag card')
} }
} }
mutator.endUndoGroup()
} else if (draggedHeaderOption) { } else if (draggedHeaderOption) {
Utils.log(`ondrop. Header option: ${draggedHeaderOption.value}, column: ${option?.value}`) Utils.log(`ondrop. Header option: ${draggedHeaderOption.value}, column: ${option?.value}`)
Utils.assertValue(boardTree.groupByProperty) Utils.assertValue(boardTree.groupByProperty)
@ -573,13 +575,15 @@ class BoardComponent extends React.Component<Props, State> {
Utils.log(`onDropToCard: ${card.title}`) Utils.log(`onDropToCard: ${card.title}`)
const {boardTree} = this.props const {boardTree} = this.props
const {activeView} = boardTree const {activeView} = boardTree
const {draggedCards, draggedHeaderOption} = this const {draggedCards} = this
const optionId = card.properties[activeView.groupById] const optionId = card.properties[activeView.groupById]
if (draggedCards.length < 1) { if (draggedCards.length < 1) {
return return
} }
mutator.beginUndoGroup()
const cardOrder = boardTree.orderedCards().map((o) => o.id) const cardOrder = boardTree.orderedCards().map((o) => o.id)
for (const draggedCard of draggedCards) { for (const draggedCard of draggedCards) {
if (draggedCard.id === card.id) { if (draggedCard.id === card.id) {
@ -604,6 +608,7 @@ class BoardComponent extends React.Component<Props, State> {
} }
await mutator.changeViewCardOrder(activeView, cardOrder) await mutator.changeViewCardOrder(activeView, cardOrder)
mutator.endUndoGroup()
} }
} }

View File

@ -87,28 +87,33 @@ class ViewHeader extends React.Component<Props, State> {
const startCount = boardTree?.cards?.length const startCount = boardTree?.cards?.length
let optionIndex = 0 let optionIndex = 0
mutator.beginUndoGroup()
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
const card = new MutableCard() const card = new MutableCard()
card.parentId = boardTree.board.id card.parentId = boardTree.board.id
card.properties = CardFilter.propertiesThatMeetFilterGroup(activeView.filter, board.cardProperties) card.properties = CardFilter.propertiesThatMeetFilterGroup(activeView.filter, board.cardProperties)
card.title = `Test Card ${startCount + i + 1}`
card.icon = BlockIcons.shared.randomIcon()
if (boardTree.groupByProperty && boardTree.groupByProperty.options.length > 0) { if (boardTree.groupByProperty && boardTree.groupByProperty.options.length > 0) {
// Cycle through options // Cycle through options
const option = boardTree.groupByProperty.options[optionIndex] const option = boardTree.groupByProperty.options[optionIndex]
optionIndex = (optionIndex + 1) % boardTree.groupByProperty.options.length optionIndex = (optionIndex + 1) % boardTree.groupByProperty.options.length
card.properties[boardTree.groupByProperty.id] = option.id card.properties[boardTree.groupByProperty.id] = option.id
card.title = `Test Card ${startCount + i + 1}`
card.icon = BlockIcons.shared.randomIcon()
} }
await mutator.insertBlock(card, 'test add card') await mutator.insertBlock(card, 'test add card')
} }
mutator.endUndoGroup()
} }
private async testRandomizeIcons() { private async testRandomizeIcons() {
const {boardTree} = this.props const {boardTree} = this.props
mutator.beginUndoGroup()
for (const card of boardTree.cards) { for (const card of boardTree.cards) {
mutator.changeIcon(card, BlockIcons.shared.randomIcon(), 'randomize icon') mutator.changeIcon(card, BlockIcons.shared.randomIcon(), 'randomize icon')
} }
mutator.endUndoGroup()
} }
render(): JSX.Element { render(): JSX.Element {

View File

@ -17,6 +17,16 @@ import {Utils} from './utils'
// It also ensures that the Undo-manager is called for each action // It also ensures that the Undo-manager is called for each action
// //
class Mutator { class Mutator {
private undoGroupId?: string
beginUndoGroup() {
this.undoGroupId = Utils.createGuid()
}
endUndoGroup() {
this.undoGroupId = undefined
}
async updateBlock(newBlock: IBlock, oldBlock: IBlock, description: string): Promise<void> { async updateBlock(newBlock: IBlock, oldBlock: IBlock, description: string): Promise<void> {
await undoManager.perform( await undoManager.perform(
async () => { async () => {
@ -26,6 +36,7 @@ class Mutator {
await octoClient.updateBlock(oldBlock) await octoClient.updateBlock(oldBlock)
}, },
description, description,
this.undoGroupId
) )
} }
@ -38,6 +49,7 @@ class Mutator {
await octoClient.updateBlocks(oldBlocks) await octoClient.updateBlocks(oldBlocks)
}, },
description, description,
this.undoGroupId
) )
} }
@ -52,6 +64,7 @@ class Mutator {
await octoClient.deleteBlock(block.id) await octoClient.deleteBlock(block.id)
}, },
description, description,
this.undoGroupId
) )
} }
@ -68,6 +81,7 @@ class Mutator {
} }
}, },
description, description,
this.undoGroupId
) )
} }
@ -86,6 +100,7 @@ class Mutator {
await afterUndo?.() await afterUndo?.()
}, },
description, description,
this.undoGroupId
) )
} }
@ -457,6 +472,7 @@ class Mutator {
await octoClient.deleteBlock(block.id) await octoClient.deleteBlock(block.id)
}, },
'add image', 'add image',
this.undoGroupId
) )
return block return block

View File

@ -5,6 +5,7 @@ interface UndoCommand {
undo: () => Promise<void> undo: () => Promise<void>
redo: () => Promise<void> redo: () => Promise<void>
description?: string description?: string
groupId?: string
} }
// //
@ -59,10 +60,11 @@ class UndoManager {
redo: () => Promise<void>, redo: () => Promise<void>,
undo: () => Promise<void>, undo: () => Promise<void>,
description?: string, description?: string,
groupId?: string,
isDiscardable = false, isDiscardable = false,
): Promise<UndoManager> { ): Promise<UndoManager> {
await redo() await redo()
return this.registerUndo({undo, redo}, description, isDiscardable) return this.registerUndo({undo, redo}, description, groupId, isDiscardable)
} }
registerUndo( registerUndo(
@ -71,6 +73,7 @@ class UndoManager {
redo: () => Promise<void> redo: () => Promise<void>
}, },
description?: string, description?: string,
groupId?: string,
isDiscardable = false, isDiscardable = false,
): UndoManager { ): UndoManager {
if (this.isExecuting) { if (this.isExecuting) {
@ -93,6 +96,7 @@ class UndoManager {
undo: command.undo, undo: command.undo,
redo: command.redo, redo: command.redo,
description, description,
groupId,
} }
this.commands.push(internalCommand) this.commands.push(internalCommand)
@ -115,13 +119,20 @@ class UndoManager {
} }
async undo() { async undo() {
const command = this.commands[this.index] if (this.isExecuting) {
return this
}
let command = this.commands[this.index]
if (!command) { if (!command) {
return this return this
} }
await this.execute(command, 'undo') const currentGroupId = command.groupId
this.index -= 1 do {
await this.execute(command, 'undo')
this.index -= 1
command = this.commands[this.index]
} while (this.index >= 0 && currentGroupId && currentGroupId === command.groupId)
if (this.onStateDidChange) { if (this.onStateDidChange) {
this.onStateDidChange() this.onStateDidChange()
@ -131,13 +142,20 @@ class UndoManager {
} }
async redo() { async redo() {
const command = this.commands[this.index + 1] if (this.isExecuting) {
return this
}
let command = this.commands[this.index + 1]
if (!command) { if (!command) {
return this return this
} }
await this.execute(command, 'redo') const currentGroupId = command.groupId
this.index += 1 do {
await this.execute(command, 'redo')
this.index += 1
command = this.commands[this.index + 1]
} while (this.index < this.commands.length && currentGroupId && currentGroupId === command.groupId)
if (this.onStateDidChange) { if (this.onStateDidChange) {
this.onStateDidChange() this.onStateDidChange()