Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/actions/toggleNote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ export default toggleNote

// Register this action's metadata
registerActionMetadata('toggleNote', {
undoable: true,
undoable: false,
isNavigation: true,
})
86 changes: 86 additions & 0 deletions src/commands/__tests__/undo-redo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { indentActionCreator as indent } from '../../actions/indent'
import { moveThoughtDownActionCreator as moveThoughtDown } from '../../actions/moveThoughtDown'
import { newThoughtActionCreator as newThought } from '../../actions/newThought'
import { redoActionCreator as redo } from '../../actions/redo'
import { toggleNoteActionCreator as toggleNote } from '../../actions/toggleNote'
import { undoActionCreator as undo } from '../../actions/undo'
import { executeCommandWithMulticursor } from '../../commands'
import moveThoughtDownCommand from '../../commands/moveThoughtDown'
Expand Down Expand Up @@ -562,6 +563,91 @@ describe('grouping', () => {
expect(exported).toEqual(expectedOutput)
})

it('contiguous note additions should undo and redo in one step', () => {
store.dispatch([
importText({
text: `
- note-add
- =note
- x`,
}),
editThought(['note-add', '=note', 'x'], 'xy'),
editThought(['note-add', '=note', 'xy'], 'xyz'),
undo(),
])

expect(exportContext(store.getState(), ['note-add'], 'text/plain')).toEqual(`- note-add
- =note
- x`)

store.dispatch(redo())

expect(exportContext(store.getState(), ['note-add'], 'text/plain')).toEqual(`- note-add
- =note
- xyz`)
})

it('contiguous note deletes should undo in one step', () => {
store.dispatch([
importText({
text: `
- note-delete
- =note
- xyz`,
}),
editThought(['note-delete', '=note', 'xyz'], 'xy'),
editThought(['note-delete', '=note', 'xy'], 'x'),
undo(),
])

expect(exportContext(store.getState(), ['note-delete'], 'text/plain')).toEqual(`- note-delete
- =note
- xyz`)
})

it('note replacements should stay as separate undo steps', () => {
store.dispatch([
importText({
text: `
- note-replace
- =note
- cat`,
}),
editThought(['note-replace', '=note', 'cat'], 'bat'),
editThought(['note-replace', '=note', 'bat'], 'bit'),
undo(),
])

expect(exportContext(store.getState(), ['note-replace'], 'text/plain')).toEqual(`- note-replace
- =note
- bat`)

store.dispatch(undo())

expect(exportContext(store.getState(), ['note-replace'], 'text/plain')).toEqual(`- note-replace
- =note
- cat`)
})

it('creating an empty note should not add an undo patch', () => {
store.dispatch([
importText({
text: `
- note-new`,
}),
setCursor(['note-new']),
])

const undoPatchesBefore = store.getState().undoPatches.length

store.dispatch(toggleNote())

expect(store.getState().undoPatches.length).toBe(undoPatchesBefore)
expect(exportContext(store.getState(), ['note-new'], 'text/plain')).toEqual(`- note-new
- =note
- `)
})

it('contiguous edit additions should should not be grouped with deletions', () => {
store.dispatch([
importText({
Expand Down
30 changes: 23 additions & 7 deletions src/components/Note.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import { useDispatch, useSelector } from 'react-redux'
import { css, cx } from '../../styled-system/css'
import { textNoteRecipe } from '../../styled-system/recipes'
import Path from '../@types/Path'
import SimplePath from '../@types/SimplePath'
import { cursorDownActionCreator as cursorDown } from '../actions/cursorDown'
import { deleteThoughtActionCreator as deleteThought } from '../actions/deleteThought'
import { editThoughtActionCreator as editThought } from '../actions/editThought'
import { keyboardOpenActionCreator as keyboardOpen } from '../actions/keyboardOpen'
import { setCursorActionCreator as setCursor } from '../actions/setCursor'
import { setDescendantActionCreator as setDescendant } from '../actions/setDescendant'
Expand All @@ -15,11 +17,13 @@ import { isTouch } from '../browser'
import preventAutoscroll, { preventAutoscrollEnd } from '../device/preventAutoscroll'
import * as selection from '../device/selection'
import useFreshCallback from '../hooks/useFreshCallback'
import { firstVisibleChild } from '../selectors/getChildren'
import getThoughtById from '../selectors/getThoughtById'
import noteValue from '../selectors/noteValue'
import resolveNotePath from '../selectors/resolveNotePath'
import store from '../stores/app'
import batchEditingStore from '../stores/batchEditing'
import appendToPath from '../util/appendToPath'
import equalPathHead from '../util/equalPathHead'
import head from '../util/head'
import strip from '../util/strip'
Expand Down Expand Up @@ -124,14 +128,26 @@ const Note = React.memo(
const state = getState()

const targetPath = resolveNotePath(state, path) ?? path
const noteThought = firstVisibleChild(state, head(targetPath))

dispatch(
setDescendant({
path: targetPath,
values: [value],
mergePrev: batchEditingStore.getState(), // If batch editing is in progress, merge this edit with the previous one in the undo stack.
}),
)
if (noteThought) {
dispatch(
editThought({
path: appendToPath(targetPath, noteThought.id) as SimplePath,
oldValue: noteThought.value,
newValue: value,
mergePrev: batchEditingStore.getState(), // If batch editing is in progress, merge this edit with the previous one in the undo stack.
}),
)
} else {
dispatch(
setDescendant({
path: targetPath,
values: [value],
mergePrev: batchEditingStore.getState(), // If batch editing is in progress, merge this edit with the previous one in the undo stack.
}),
)
}
})
},
[dispatch, path, justPasted],
Expand Down
Loading