Modify undo manager to return a value and use it on undo (#1616)

* Modify undo manager to return a value and use it on undo

* Storing the last redo value internally to correctly run undo

* Fix types

* Improve test ensuring redo didn't add original block

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
Miguel de la Cruz 2021-10-21 18:50:19 +02:00 committed by GitHub
parent 9f74287802
commit d22fe7fbc0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 8 deletions

View file

@ -47,6 +47,59 @@ test('Basic undo/redo', async () => {
expect(undoManager.redoDescription).toBe(undefined) expect(undoManager.redoDescription).toBe(undefined)
}) })
test('Basic undo/redo response dependant', async () => {
expect(undoManager.canUndo).toBe(false)
expect(undoManager.canRedo).toBe(false)
expect(undoManager.currentCheckpoint).toBe(0)
const blockIds = [2, 1]
const blocks: Record<string, any> = {}
const newBlock = await undoManager.perform(
async () => {
const responseId = blockIds.pop() // every time we run the action a new ID is obtained
const block: Record<string, any> = {id: responseId, title: 'Sample'}
blocks[block.id] = block
return block
},
async (block: Record<string, any>) => {
delete blocks[block.id]
},
'test',
)
// should insert the block and return the new block for its use
expect(undoManager.canUndo).toBe(true)
expect(undoManager.canRedo).toBe(false)
expect(blocks).toHaveProperty('1')
expect(blocks[1]).toEqual(newBlock)
// should correctly remove the block based on the info gathered in
// the redo function
await undoManager.undo()
expect(undoManager.canUndo).toBe(false)
expect(undoManager.canRedo).toBe(true)
expect(blocks).not.toHaveProperty('1')
// when redoing, as the function has side effects the new id will
// be different
await undoManager.redo()
expect(undoManager.canUndo).toBe(true)
expect(undoManager.canRedo).toBe(false)
expect(blocks).not.toHaveProperty('1')
expect(blocks).toHaveProperty('2')
expect(blocks[2].id).toEqual(2)
// when undoing, the undo manager has saved the new id internally
// and it removes the right block
await undoManager.undo()
expect(undoManager.canUndo).toBe(false)
expect(undoManager.canRedo).toBe(true)
expect(blocks).not.toHaveProperty('2')
await undoManager.clear()
})
test('Grouped undo/redo', async () => { test('Grouped undo/redo', async () => {
expect(undoManager.canUndo).toBe(false) expect(undoManager.canUndo).toBe(false)
expect(undoManager.canRedo).toBe(false) expect(undoManager.canRedo).toBe(false)

View file

@ -2,10 +2,11 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
interface UndoCommand { interface UndoCommand {
checkpoint: number checkpoint: number
undo: () => Promise<void> undo: (value?: any) => Promise<void>
redo: () => Promise<void> redo: () => Promise<void>
description?: string description?: string
groupId?: string groupId?: string
value?: any
} }
// //
@ -50,30 +51,36 @@ class UndoManager {
} }
this.isExecuting = true this.isExecuting = true
await command[action]() if (action === 'redo') {
command.value = await command[action]()
} else {
await command[action](command.value)
}
this.isExecuting = false this.isExecuting = false
return this return this
} }
async perform( async perform(
redo: () => Promise<void>, redo: () => Promise<any>,
undo: () => Promise<void>, undo: (value?: any) => Promise<void>,
description?: string, description?: string,
groupId?: string, groupId?: string,
isDiscardable = false, isDiscardable = false,
): Promise<UndoManager> { ): Promise<any> {
await redo() const value = await redo()
return this.registerUndo({undo, redo}, description, groupId, isDiscardable) this.registerUndo({undo, redo}, description, groupId, value, isDiscardable)
return value
} }
registerUndo( registerUndo(
command: { command: {
undo: () => Promise<void>, undo: (value?: any) => Promise<void>,
redo: () => Promise<void> redo: () => Promise<void>
}, },
description?: string, description?: string,
groupId?: string, groupId?: string,
value?: any,
isDiscardable = false, isDiscardable = false,
): UndoManager { ): UndoManager {
if (this.isExecuting) { if (this.isExecuting) {
@ -97,6 +104,7 @@ class UndoManager {
redo: command.redo, redo: command.redo,
description, description,
groupId, groupId,
value,
} }
this.commands.push(internalCommand) this.commands.push(internalCommand)