Skip to content

Commit 95f8b2e

Browse files
authored
chore: create redo/undo bridge (#2760)
* chore: create redo/undo bridge * chore: update test * chore: review update * chore: review update * chore: react redo/undo * chore: review update * chore: add test * chore: review update * chore: generate document id * chore: update undo/redo * chore: update cargo lock
1 parent 27dd719 commit 95f8b2e

File tree

31 files changed

+981
-407
lines changed

31 files changed

+981
-407
lines changed

frontend/appflowy_tauri/src-tauri/Cargo.lock

Lines changed: 284 additions & 309 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/appflowy_tauri/src/appflowy_app/components/document/Overlay/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import BlockSlash from '$app/components/document/BlockSlash';
66
import { useCopy } from '$app/components/document/_shared/CopyPasteHooks/useCopy';
77
import { usePaste } from '$app/components/document/_shared/CopyPasteHooks/usePaste';
88
import LinkEditPopover from '$app/components/document/_shared/TextLink/LinkEditPopover';
9+
import { useUndoRedo } from '$app/components/document/_shared/UndoHooks/useUndoRedo';
910

1011
export default function Overlay({ container }: { container: HTMLDivElement }) {
1112
useCopy(container);
1213
usePaste(container);
14+
useUndoRedo(container);
1315
return (
1416
<>
1517
<BlockSideToolbar container={container} />

frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/useKeyDown.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,11 @@ export function useKeyDown(id: string) {
8888

8989
const onKeyDown = useCallback(
9090
(e: React.KeyboardEvent<HTMLDivElement>) => {
91-
e.stopPropagation();
9291
const filteredEvents = interceptEvents.filter((event) => event.canHandle(e));
93-
filteredEvents.forEach((event) => event.handler(e));
92+
filteredEvents.forEach((event) => {
93+
e.stopPropagation();
94+
event.handler(e);
95+
});
9496
},
9597
[interceptEvents]
9698
);

frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/useSlateYjs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export function useSlateYjs({ delta }: { delta?: Delta }) {
1919
// eslint-disable-next-line react-hooks/exhaustive-deps
2020
}, []);
2121

22-
const editor = useMemo(() => withYjs(withReact(createEditor()), sharedType), []);
22+
const editor = useMemo(() => withReact(withYjs(createEditor(), sharedType)), []);
2323

2424
// Connect editor in useEffect to comply with concurrent mode requirements.
2525
useEffect(() => {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { useCallback, useContext, useEffect } from 'react';
2+
import { DocumentControllerContext } from '$app/stores/effects/document/document_controller';
3+
import isHotkey from 'is-hotkey';
4+
import { Keyboard } from '@/appflowy_app/constants/document/keyboard';
5+
6+
export function useUndoRedo(container: HTMLDivElement) {
7+
const controller = useContext(DocumentControllerContext);
8+
9+
const onUndo = useCallback(() => {
10+
if (!controller) return;
11+
controller.undo();
12+
}, [controller]);
13+
14+
const onRedo = useCallback(() => {
15+
if (!controller) return;
16+
controller.redo();
17+
}, [controller]);
18+
19+
const handleKeyDownCapture = useCallback(
20+
(e: KeyboardEvent) => {
21+
if (isHotkey(Keyboard.keys.UNDO, e)) {
22+
e.stopPropagation();
23+
onUndo();
24+
}
25+
if (isHotkey(Keyboard.keys.REDO, e)) {
26+
e.stopPropagation();
27+
onRedo();
28+
}
29+
},
30+
[onRedo, onUndo]
31+
);
32+
33+
useEffect(() => {
34+
container.addEventListener('keydown', handleKeyDownCapture, true);
35+
return () => {
36+
container.removeEventListener('keydown', handleKeyDownCapture, true);
37+
};
38+
}, [container, handleKeyDownCapture]);
39+
}

frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/FolderItem.hooks.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import { AppObserver } from '$app/stores/effects/folder/app/app_observer';
1010
import { useNavigate } from 'react-router-dom';
1111
import { INITIAL_FOLDER_HEIGHT, PAGE_ITEM_HEIGHT } from '../../_shared/constants';
1212

13-
import { DocumentController } from '$app/stores/effects/document/document_controller';
14-
1513
export const useFolderEvents = (folder: IFolder, pages: IPage[]) => {
1614
const appDispatch = useAppDispatch();
1715
const workspace = useAppSelector((state) => state.workspace);
@@ -118,9 +116,6 @@ export const useFolderEvents = (folder: IFolder, pages: IPage[]) => {
118116
layoutType: ViewLayoutPB.Document,
119117
});
120118
try {
121-
const c = new DocumentController(newView.id);
122-
await c.create();
123-
await c.dispose();
124119
appDispatch(
125120
pagesActions.addPage({
126121
folderId: folder.id,

frontend/appflowy_tauri/src/appflowy_app/constants/document/keyboard.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,7 @@ export const Keyboard = {
3838
COPY: 'Mod+c',
3939
CUT: 'Mod+x',
4040
PASTE: 'Mod+v',
41+
REDO: 'Mod+Shift+z',
42+
UNDO: 'Mod+z',
4143
},
4244
};

frontend/appflowy_tauri/src/appflowy_app/stores/effects/document/document_bd_svc.ts

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,23 @@ import {
66
ApplyActionPayloadPB,
77
BlockActionPB,
88
CloseDocumentPayloadPB,
9+
DocumentRedoUndoPayloadPB,
10+
DocumentRedoUndoResponsePB,
911
} from '@/services/backend';
1012
import { Result } from 'ts-results';
1113
import {
1214
DocumentEventApplyAction,
1315
DocumentEventCloseDocument,
1416
DocumentEventOpenDocument,
1517
DocumentEventCreateDocument,
18+
DocumentEventCanUndoRedo,
19+
DocumentEventRedo,
20+
DocumentEventUndo,
1621
} from '@/services/backend/events/flowy-document2';
1722

1823
export class DocumentBackendService {
1924
constructor(public readonly viewId: string) {}
2025

21-
create = (): Promise<Result<void, FlowyError>> => {
22-
const payload = CreateDocumentPayloadPB.fromObject({
23-
document_id: this.viewId,
24-
});
25-
return DocumentEventCreateDocument(payload);
26-
};
27-
2826
open = (): Promise<Result<DocumentDataPB, FlowyError>> => {
2927
const payload = OpenDocumentPayloadPB.fromObject({
3028
document_id: this.viewId,
@@ -46,4 +44,25 @@ export class DocumentBackendService {
4644
});
4745
return DocumentEventCloseDocument(payload);
4846
};
47+
48+
canUndoRedo = (): Promise<Result<DocumentRedoUndoResponsePB, FlowyError>> => {
49+
const payload = DocumentRedoUndoPayloadPB.fromObject({
50+
document_id: this.viewId,
51+
});
52+
return DocumentEventCanUndoRedo(payload);
53+
};
54+
55+
undo = (): Promise<Result<DocumentRedoUndoResponsePB, FlowyError>> => {
56+
const payload = DocumentRedoUndoPayloadPB.fromObject({
57+
document_id: this.viewId,
58+
});
59+
return DocumentEventUndo(payload);
60+
};
61+
62+
redo = (): Promise<Result<DocumentRedoUndoResponsePB, FlowyError>> => {
63+
const payload = DocumentRedoUndoPayloadPB.fromObject({
64+
document_id: this.viewId,
65+
});
66+
return DocumentEventRedo(payload);
67+
};
4968
}

frontend/appflowy_tauri/src/appflowy_app/stores/effects/document/document_controller.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,6 @@ export class DocumentController {
3131
this.observer = new DocumentObserver(documentId);
3232
}
3333

34-
create = async (): Promise<FlowyError | void> => {
35-
const result = await this.backendService.create();
36-
if (result.ok) {
37-
return;
38-
}
39-
return result.val;
40-
};
4134
open = async (): Promise<DocumentData> => {
4235
await this.observer.subscribe({
4336
didReceiveUpdate: this.updated,
@@ -114,6 +107,26 @@ export class DocumentController {
114107
};
115108
};
116109

110+
canUndo = async () => {
111+
const result = await this.backendService.canUndoRedo();
112+
return result.ok && result.val.can_undo;
113+
};
114+
115+
canRedo = async () => {
116+
const result = await this.backendService.canUndoRedo();
117+
return result.ok && result.val.can_redo;
118+
};
119+
120+
undo = async () => {
121+
const result = await this.backendService.undo();
122+
return result.ok && result.val.is_success;
123+
};
124+
125+
redo = async () => {
126+
const result = await this.backendService.redo();
127+
return result.ok && result.val.is_success;
128+
};
129+
117130
dispose = async () => {
118131
this.onDocChange = undefined;
119132
await this.backendService.close();

frontend/rust-lib/Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)