-
Notifications
You must be signed in to change notification settings - Fork 151
fix(collaboration): sync comments to second collab client on export #3593
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,25 @@ import { actorIdentitiesMatch } from '@superdoc/common'; | |
|
|
||
| import { addYComment, updateYComment, deleteYComment } from './collaboration-comments'; | ||
|
|
||
| /** | ||
| * Queue for comment events that arrive before collaboration is ready. | ||
| * Flushed when provider syncs via flushPendingCommentEvents(). | ||
| */ | ||
| let pendingCommentEvents = []; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This module-level queue is shared by every SuperDoc instance loaded in the page/process. If one non-collaborative or not-yet-collaborative editor emits a comment event, then another editor later reaches provider sync, Useful? React with 👍 / 👎. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Module-level Prompt for AI agents |
||
|
|
||
| /** | ||
| * Flush any queued comment events to Y.Array. | ||
| * Called when collaboration becomes ready (provider synced). | ||
| * | ||
| * @param {Object} superdoc The SuperDoc instance | ||
| */ | ||
| const flushPendingCommentEvents = (superdoc) => { | ||
| if (!pendingCommentEvents.length) return; | ||
| const events = pendingCommentEvents; | ||
| pendingCommentEvents = []; | ||
| events.forEach((event) => syncCommentsToClients(superdoc, event)); | ||
| }; | ||
|
|
||
| /** | ||
| * Load comments from the ydoc into the comments store. | ||
| * | ||
|
|
@@ -68,6 +87,12 @@ export const initCollaborationComments = (superdoc) => { | |
| const updateCommentsStore = () => loadCommentsFromYdoc(superdoc); | ||
|
|
||
| const onSuperDocYdocSynced = () => { | ||
| // Flush queued comment events to Y.Array BEFORE loading. | ||
| // When comments are imported before collaboration is fully wired | ||
| // (isCollaborative is false), they get queued. Flushing now ensures | ||
| // the Y.Array is seeded so other clients receive them. | ||
| flushPendingCommentEvents(superdoc); | ||
|
|
||
| if (!updateCommentsStore()) { | ||
| setTimeout(updateCommentsStore, 0); | ||
| } | ||
|
|
@@ -86,6 +111,10 @@ export const initCollaborationComments = (superdoc) => { | |
| superdoc.provider.on('synced', onSuperDocYdocSynced); | ||
|
|
||
| // Load any existing comments immediately (in case provider synced before we subscribed) | ||
| // If provider is already synced, flush queued events first | ||
| if (superdoc.provider?.synced) { | ||
| flushPendingCommentEvents(superdoc); | ||
| } | ||
| if (!updateCommentsStore()) { | ||
| setTimeout(updateCommentsStore, 0); | ||
| } | ||
|
|
@@ -169,7 +198,13 @@ export const makeDocumentsCollaborative = (superdoc) => { | |
| * @returns {void} | ||
| */ | ||
| export const syncCommentsToClients = (superdoc, event) => { | ||
| if (!superdoc.isCollaborative || !superdoc.config.modules.comments) return; | ||
| if (!superdoc.config.modules.comments) return; | ||
|
|
||
| // Queue events until collaboration is ready | ||
| if (!superdoc.isCollaborative) { | ||
| pendingCommentEvents.push(event); | ||
| return; | ||
| } | ||
|
|
||
| const yArray = superdoc.ydoc.getArray('comments'); | ||
| const user = superdoc.config.user; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
decision.action === 'seed'and a document path is provided,docForEditoris the same truthydoc, so this now passesisNewFile: falsefor the normal seed-from-file path. The editor's collaboration bootstrap only inserts the parsed document into the shared Yjs fragment whenoptions.isNewFileis true (initializeCollaborationData()returns early otherwise), so an empty room seeded from a real DOCX can be marked bootstrapped without actually publishing the document content to other clients.Useful? React with 👍 / 👎.