From f628e6c4b94b57a5869085f293837cfc41d40a30 Mon Sep 17 00:00:00 2001 From: LeeJongBeom <52884648+devleejb@users.noreply.github.com> Date: Tue, 30 Jan 2024 16:46:56 +0900 Subject: [PATCH] Synchronization Issue when Multiple Users are Editing the Document in the Editor (#121) * Change document local update logic * Change yorkie updating logic * Add auto soft line break --- frontend/src/components/editor/Preview.tsx | 15 ++++- frontend/src/utils/yorkie/yorkieSync.ts | 65 +++++++++++----------- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/frontend/src/components/editor/Preview.tsx b/frontend/src/components/editor/Preview.tsx index 1003ab5b..fb3d8340 100644 --- a/frontend/src/components/editor/Preview.tsx +++ b/frontend/src/components/editor/Preview.tsx @@ -14,10 +14,21 @@ function Preview() { useEffect(() => { if (!editorStore.doc) return; - setContent(editorStore.doc?.getRoot().content?.toString() || ""); + const updatePreviewContent = () => { + const editorText = editorStore.doc?.getRoot().content?.toString() || ""; + // Add soft line break + setContent( + editorText + .split("\n") + .map((line) => line + " ") + .join("\n") + ); + }; + + updatePreviewContent(); const unsubsribe = editorStore.doc.subscribe("$.content", () => { - setContent(editorStore.doc?.getRoot().content.toString() as string); + updatePreviewContent(); }); return () => { diff --git a/frontend/src/utils/yorkie/yorkieSync.ts b/frontend/src/utils/yorkie/yorkieSync.ts index 97e010eb..addb0bbe 100644 --- a/frontend/src/utils/yorkie/yorkieSync.ts +++ b/frontend/src/utils/yorkie/yorkieSync.ts @@ -41,14 +41,24 @@ class YorkieSyncPluginValue implements cmView.PluginValue { view: cmView.EditorView; conf: YorkieSyncConfig; _doc: yorkie.Document; - _observer: yorkie.NextFn>; - _unsubscribe: yorkie.Unsubscribe; constructor(view: cmView.EditorView) { this.view = view; this.conf = view.state.facet(yorkieSyncFacet); + this._doc = this.conf.doc; + + this._doc.subscribe((event) => { + if (event.type !== "snapshot") return; + + // The text is replaced to snapshot and must be re-synced. + const text = this._doc.getRoot().content; + view.dispatch({ + changes: { from: 0, to: view.state.doc.length, insert: text.toString() }, + annotations: [cmState.Transaction.remote.of(true)], + }); + }); - this._observer = (event) => { + this._doc.subscribe("$.content", (event) => { if (event.type !== "remote-change") return; const { operations } = event.value; @@ -65,44 +75,33 @@ class YorkieSyncPluginValue implements cmView.PluginValue { view.dispatch({ changes, - annotations: [yorkieSyncAnnotation.of(this.conf)], + annotations: [cmState.Transaction.remote.of(true)], }); } }); - }; - this._doc = this.conf.doc; - this._unsubscribe = this._doc.subscribe("$.content", this._observer); + }); } update(update: cmView.ViewUpdate) { - if ( - !update.docChanged || - (update.transactions.length > 0 && - update.transactions[0].annotation(yorkieSyncAnnotation) === this.conf) - ) { - return; - } - - let adj = 0; - this._doc.update((root, presence) => { - update.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => { - if (!root.content) { - root.content = new yorkie.Text(); + if (update.docChanged) { + for (const tr of update.transactions) { + const events = ["select", "input", "delete", "move", "undo", "redo"]; + if (!events.some((event) => tr.isUserEvent(event))) { + continue; } - const insertText = insert.sliceString(0, insert.length, "\n"); - const updatedIndexRange = root.content.edit(fromA + adj, toA + adj, insertText); - adj += insertText.length - (toA - fromA); - if (updatedIndexRange) { - presence.set({ - selection: root.content.indexRangeToPosRange(updatedIndexRange), - } as unknown as YorkieCodeMirrorPresenceType); + if (tr.annotation(cmState.Transaction.remote)) { + continue; } - }); - }); - } - - destroy() { - this._unsubscribe(); + let adj = 0; + tr.changes.iterChanges((fromA, toA, _, __, inserted) => { + const insertText = inserted.toJSON().join("\n"); + this._doc.update((root) => { + root.content.edit(fromA + adj, toA + adj, insertText); + }); + adj += insertText.length - (toA - fromA); + }); + } + } } }