Skip to content

Commit be5dc6c

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 4ce699a commit be5dc6c

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
@@ -13,6 +13,7 @@ export interface UseCollaborationStore {
1313
destroyProvider: () => void;
1414
provider: HocuspocusProvider | undefined;
1515
isConnected: boolean;
16+
isReady: boolean;
1617
isSynced: boolean;
1718
hasLostConnection: boolean;
1819
resetLostConnection: () => void;
@@ -21,6 +22,7 @@ export interface UseCollaborationStore {
2122
const defaultValues = {
2223
provider: undefined,
2324
isConnected: false,
25+
isReady: false,
2426
isSynced: false,
2527
hasLostConnection: false,
2628
};
@@ -45,14 +47,18 @@ export const useProviderStore = create<UseCollaborationStore>((set, get) => ({
4547
onDisconnect(data) {
4648
// Attempt to reconnect if the disconnection was clean (initiated by the client or server)
4749
if ((data.event as ExtendedCloseEvent).wasClean) {
48-
provider.connect();
50+
void provider.connect();
4951
}
5052
},
53+
onAuthenticationFailed() {
54+
set({ isReady: true });
55+
},
5156
onStatus: ({ status }) => {
5257
set((state) => {
5358
const nextConnected = status === WebSocketStatus.Connected;
5459
return {
5560
isConnected: nextConnected,
61+
isReady: state.isReady || status === WebSocketStatus.Disconnected,
5662
hasLostConnection:
5763
state.isConnected && !nextConnected
5864
? true
@@ -61,7 +67,7 @@ export const useProviderStore = create<UseCollaborationStore>((set, get) => ({
6167
});
6268
},
6369
onSynced: ({ state }) => {
64-
set({ isSynced: state });
70+
set({ isSynced: state, isReady: true });
6571
},
6672
onClose(data) {
6773
/**

0 commit comments

Comments
 (0)