Skip to content

Commit 4051e72

Browse files
committed
🐛(frontend) fix lost content during sync
The tests e2e highlighted a problem where content was lost during synchronization. This bug started to occurs after upgrading Blocknote to 0.41.1 version. It seems to happen only when the initial document is empty and 2 users are collaborating, so before the first minute. We now initialize the editor only when the y-doc has attempted to sync. This should ensure that all updates are applied before the editor is initialized.
1 parent 03f360d commit 4051e72

File tree

4 files changed

+20
-15
lines changed

4 files changed

+20
-15
lines changed

src/frontend/apps/e2e/__tests__/app-impress/doc-visibility.spec.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
keyCloakSignIn,
88
verifyDocName,
99
} from './utils-common';
10+
import { writeInEditor } from './utils-editor';
1011
import { addNewMember, connectOtherUserToDoc } from './utils-share';
1112
import { createRootSubPage } from './utils-sub-pages';
1213

@@ -151,18 +152,15 @@ test.describe('Doc Visibility: Restricted', () => {
151152

152153
await verifyDocName(page, docTitle);
153154

154-
await page
155-
.locator('.ProseMirror')
156-
.locator('.bn-block-outer')
157-
.last()
158-
.fill('Hello World');
155+
await writeInEditor({ page, text: 'Hello World' });
159156

160157
const docUrl = page.url();
161158

162-
const { otherBrowserName, otherPage } = await connectOtherUserToDoc({
163-
browserName,
164-
docUrl,
165-
});
159+
const { otherBrowserName, otherPage, cleanup } =
160+
await connectOtherUserToDoc({
161+
browserName,
162+
docUrl,
163+
});
166164

167165
await expect(
168166
otherPage.getByText('Insufficient access rights to view the document.'),
@@ -178,6 +176,8 @@ test.describe('Doc Visibility: Restricted', () => {
178176
await expect(otherPage.getByText('Hello World')).toBeVisible({
179177
timeout: 10000,
180178
});
179+
180+
await cleanup();
181181
});
182182
});
183183

src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,9 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
8383
const { user } = useAuth();
8484
const { setEditor } = useEditorStore();
8585
const { t } = useTranslation();
86-
const { isSynced } = useProviderStore();
86+
const { isSynced: isConnectedToCollabServer } = useProviderStore();
8787

8888
const { isEditable, isLoading } = useIsCollaborativeEditable(doc);
89-
const isConnectedToCollabServer = isSynced;
9089
const readOnly = !doc.abilities.partial_update || !isEditable || isLoading;
9190
const isDeletedDoc = !!doc.deleted_at;
9291

src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ interface DocEditorProps {
2525
export const DocEditor = ({ doc, versionId }: DocEditorProps) => {
2626
const { isDesktop } = useResponsiveStore();
2727
const isVersion = !!versionId && typeof versionId === 'string';
28-
const { provider } = useProviderStore();
28+
const { provider, isReady } = useProviderStore();
2929

3030
// TODO: Use skeleton instead of loading
31-
if (!provider) {
31+
if (!provider || !isReady) {
3232
return <Loading />;
3333
}
3434

src/frontend/apps/impress/src/features/docs/doc-management/stores/useProviderStore.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface UseCollaborationStore {
1414
destroyProvider: () => void;
1515
provider: HocuspocusProvider | undefined;
1616
isConnected: boolean;
17+
isReady: boolean;
1718
isSynced: boolean;
1819
hasLostConnection: boolean;
1920
resetLostConnection: () => void;
@@ -22,6 +23,7 @@ export interface UseCollaborationStore {
2223
const defaultValues = {
2324
provider: undefined,
2425
isConnected: false,
26+
isReady: false,
2527
isSynced: false,
2628
hasLostConnection: false,
2729
};
@@ -46,14 +48,18 @@ export const useProviderStore = create<UseCollaborationStore>((set, get) => ({
4648
onDisconnect(data) {
4749
// Attempt to reconnect if the disconnection was clean (initiated by the client or server)
4850
if ((data.event as ExtendedCloseEvent).wasClean) {
49-
provider.connect();
51+
void provider.connect();
5052
}
5153
},
54+
onAuthenticationFailed() {
55+
set({ isReady: true });
56+
},
5257
onStatus: ({ status }) => {
5358
set((state) => {
5459
const nextConnected = status === WebSocketStatus.Connected;
5560
return {
5661
isConnected: nextConnected,
62+
isReady: state.isReady || status === WebSocketStatus.Disconnected,
5763
hasLostConnection:
5864
state.isConnected && !nextConnected
5965
? true
@@ -62,7 +68,7 @@ export const useProviderStore = create<UseCollaborationStore>((set, get) => ({
6268
});
6369
},
6470
onSynced: ({ state }) => {
65-
set({ isSynced: state });
71+
set({ isSynced: state, isReady: true });
6672
},
6773
onClose(data) {
6874
/**

0 commit comments

Comments
 (0)