mirror of
https://github.com/suitenumerique/docs.git
synced 2026-05-08 16:12:26 +02:00
🐛(frontend) fix loading comments transaction
When we load the comments we have to notify the subscribers of the DocsThreadStore. This generates a Yjs transaction that is currently treated as a user-initiated content change that will trigger a patch request when the doc will try to save. We now update the transaction origin when we notify the subscribers so that we can reliably identify and ignore those transactions in the useSaveDoc hook.
This commit is contained in:
@@ -12,6 +12,7 @@ and this project adheres to
|
||||
|
||||
### Fixed
|
||||
|
||||
- 🐛(frontend) fix loading comments transaction #2273
|
||||
- 💬(frontend) add missing link in onboarding description #2233
|
||||
- 🐛(frontend) sanitize pasted and dropped content in document title #2210
|
||||
- 🐛(frontend) Emoji menu doesn't display above comment box #2229
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CommentBody, ThreadStore } from '@blocknote/core/comments';
|
||||
import type { Awareness } from 'y-protocols/awareness';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import { APIError, errorCauses, fetchAPI } from '@/api';
|
||||
import { Doc } from '@/features/docs/doc-management';
|
||||
@@ -17,6 +18,13 @@ import {
|
||||
|
||||
type ServerThreadListResponse = ServerThread[];
|
||||
|
||||
/**
|
||||
* notifySubscribers generate a transaction, to distinguish
|
||||
* the origin of the update, we use a specific origin "commentMarkUpdate"
|
||||
* for the updates coming from the comment mark changes.
|
||||
*/
|
||||
export const COMMENT_UPDATE_ORIGIN = 'commentMarkUpdate';
|
||||
|
||||
export class DocsThreadStore extends ThreadStore {
|
||||
protected static COMMENTS_PING = 'commentsPing';
|
||||
protected threads: Map<string, ClientThreadData> = new Map();
|
||||
@@ -24,6 +32,7 @@ export class DocsThreadStore extends ThreadStore {
|
||||
(threads: Map<string, ClientThreadData>) => void
|
||||
>();
|
||||
private awareness?: Awareness;
|
||||
private yDoc?: Y.Doc;
|
||||
private lastPingAt = 0;
|
||||
private pingTimer?: ReturnType<typeof setTimeout>;
|
||||
|
||||
@@ -31,11 +40,13 @@ export class DocsThreadStore extends ThreadStore {
|
||||
protected docId: Doc['id'],
|
||||
awareness: Awareness | undefined,
|
||||
protected docAuth: DocsThreadStoreAuth,
|
||||
yDoc?: Y.Doc,
|
||||
) {
|
||||
super(docAuth);
|
||||
|
||||
if (docAuth.canSee) {
|
||||
this.awareness = awareness;
|
||||
this.yDoc = yDoc;
|
||||
this.awareness?.on('update', this.onAwarenessUpdate);
|
||||
|
||||
this.refreshThreads();
|
||||
@@ -134,18 +145,30 @@ export class DocsThreadStore extends ThreadStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all subscribers about the current thread state
|
||||
* Notifies all subscribers about the current thread state.
|
||||
* We trigger the transaction with a specific origin so we will be able
|
||||
* to flag that the update comes from a comment update.
|
||||
* The inner ydoc.transact calls from y-prosemirror will see there's already
|
||||
* an active transaction and reuse it.
|
||||
*/
|
||||
private notifySubscribers() {
|
||||
// Always emit a new Map reference to help consumers detect changes
|
||||
const threads = new Map(this.threads);
|
||||
this.subscribers.forEach((cb) => {
|
||||
try {
|
||||
cb(threads);
|
||||
} catch (e) {
|
||||
console.warn('DocsThreadStore subscriber threw', e);
|
||||
}
|
||||
});
|
||||
const notify = () => {
|
||||
this.subscribers.forEach((cb) => {
|
||||
try {
|
||||
cb(threads);
|
||||
} catch (e) {
|
||||
console.warn('DocsThreadStore subscriber threw', e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (this.yDoc) {
|
||||
this.yDoc.transact(notify, COMMENT_UPDATE_ORIGIN);
|
||||
} else {
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
private upsertClientThreadData(thread: ClientThreadData) {
|
||||
|
||||
@@ -27,8 +27,15 @@ export function useComments(
|
||||
encodeURIComponent(user?.full_name || ''),
|
||||
canComment,
|
||||
),
|
||||
provider?.document,
|
||||
);
|
||||
}, [docId, canComment, provider?.awareness, user?.full_name]);
|
||||
}, [
|
||||
docId,
|
||||
canComment,
|
||||
provider?.awareness,
|
||||
provider?.document,
|
||||
user?.full_name,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (canComment) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useRouter } from 'next/router';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import { COMMENT_UPDATE_ORIGIN } from '@/docs/doc-editor/components/comments/DocsThreadStore';
|
||||
import { useDocContentUpdate } from '@/docs/doc-management/api/useDocContentUpdate';
|
||||
import { useProviderStore } from '@/docs/doc-management/stores/useProviderStore';
|
||||
import { KEY_LIST_DOC_VERSIONS } from '@/docs/doc-versioning/api/useDocVersions';
|
||||
@@ -65,6 +66,16 @@ export const useSaveDoc = (docId: string, yDoc: Y.Doc) => {
|
||||
const isAIChange =
|
||||
!transaction.local && transactionOrigin !== PROVIDER_ORIGIN_CONSTRUCTOR;
|
||||
|
||||
/**
|
||||
* notifySubscribers generate a transaction that can be
|
||||
* interpreted as a local change.
|
||||
* We intercept the update with this origin to
|
||||
* avoid marking the change as local.
|
||||
*/
|
||||
if (transaction.origin === COMMENT_UPDATE_ORIGIN) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLocalChange(transaction.local || isAIChange);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user