🐛(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.
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
|||||||
keyCloakSignIn,
|
keyCloakSignIn,
|
||||||
verifyDocName,
|
verifyDocName,
|
||||||
} from './utils-common';
|
} from './utils-common';
|
||||||
|
import { writeInEditor } from './utils-editor';
|
||||||
import { addNewMember, connectOtherUserToDoc } from './utils-share';
|
import { addNewMember, connectOtherUserToDoc } from './utils-share';
|
||||||
import { createRootSubPage } from './utils-sub-pages';
|
import { createRootSubPage } from './utils-sub-pages';
|
||||||
|
|
||||||
@@ -151,18 +152,15 @@ test.describe('Doc Visibility: Restricted', () => {
|
|||||||
|
|
||||||
await verifyDocName(page, docTitle);
|
await verifyDocName(page, docTitle);
|
||||||
|
|
||||||
await page
|
await writeInEditor({ page, text: 'Hello World' });
|
||||||
.locator('.ProseMirror')
|
|
||||||
.locator('.bn-block-outer')
|
|
||||||
.last()
|
|
||||||
.fill('Hello World');
|
|
||||||
|
|
||||||
const docUrl = page.url();
|
const docUrl = page.url();
|
||||||
|
|
||||||
const { otherBrowserName, otherPage } = await connectOtherUserToDoc({
|
const { otherBrowserName, otherPage, cleanup } =
|
||||||
browserName,
|
await connectOtherUserToDoc({
|
||||||
docUrl,
|
browserName,
|
||||||
});
|
docUrl,
|
||||||
|
});
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
otherPage.getByText('Insufficient access rights to view the document.'),
|
otherPage.getByText('Insufficient access rights to view the document.'),
|
||||||
@@ -178,6 +176,8 @@ test.describe('Doc Visibility: Restricted', () => {
|
|||||||
await expect(otherPage.getByText('Hello World')).toBeVisible({
|
await expect(otherPage.getByText('Hello World')).toBeVisible({
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await cleanup();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -83,10 +83,9 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
|||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { setEditor } = useEditorStore();
|
const { setEditor } = useEditorStore();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { isSynced } = useProviderStore();
|
const { isSynced: isConnectedToCollabServer } = useProviderStore();
|
||||||
|
|
||||||
const { isEditable, isLoading } = useIsCollaborativeEditable(doc);
|
const { isEditable, isLoading } = useIsCollaborativeEditable(doc);
|
||||||
const isConnectedToCollabServer = isSynced;
|
|
||||||
const readOnly = !doc.abilities.partial_update || !isEditable || isLoading;
|
const readOnly = !doc.abilities.partial_update || !isEditable || isLoading;
|
||||||
const isDeletedDoc = !!doc.deleted_at;
|
const isDeletedDoc = !!doc.deleted_at;
|
||||||
|
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ interface DocEditorProps {
|
|||||||
export const DocEditor = ({ doc, versionId }: DocEditorProps) => {
|
export const DocEditor = ({ doc, versionId }: DocEditorProps) => {
|
||||||
const { isDesktop } = useResponsiveStore();
|
const { isDesktop } = useResponsiveStore();
|
||||||
const isVersion = !!versionId && typeof versionId === 'string';
|
const isVersion = !!versionId && typeof versionId === 'string';
|
||||||
const { provider } = useProviderStore();
|
const { provider, isReady } = useProviderStore();
|
||||||
|
|
||||||
// TODO: Use skeleton instead of loading
|
// TODO: Use skeleton instead of loading
|
||||||
if (!provider) {
|
if (!provider || !isReady) {
|
||||||
return <Loading />;
|
return <Loading />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export interface UseCollaborationStore {
|
|||||||
destroyProvider: () => void;
|
destroyProvider: () => void;
|
||||||
provider: HocuspocusProvider | undefined;
|
provider: HocuspocusProvider | undefined;
|
||||||
isConnected: boolean;
|
isConnected: boolean;
|
||||||
|
isReady: boolean;
|
||||||
isSynced: boolean;
|
isSynced: boolean;
|
||||||
hasLostConnection: boolean;
|
hasLostConnection: boolean;
|
||||||
resetLostConnection: () => void;
|
resetLostConnection: () => void;
|
||||||
@@ -22,6 +23,7 @@ export interface UseCollaborationStore {
|
|||||||
const defaultValues = {
|
const defaultValues = {
|
||||||
provider: undefined,
|
provider: undefined,
|
||||||
isConnected: false,
|
isConnected: false,
|
||||||
|
isReady: false,
|
||||||
isSynced: false,
|
isSynced: false,
|
||||||
hasLostConnection: false,
|
hasLostConnection: false,
|
||||||
};
|
};
|
||||||
@@ -46,14 +48,18 @@ export const useProviderStore = create<UseCollaborationStore>((set, get) => ({
|
|||||||
onDisconnect(data) {
|
onDisconnect(data) {
|
||||||
// Attempt to reconnect if the disconnection was clean (initiated by the client or server)
|
// Attempt to reconnect if the disconnection was clean (initiated by the client or server)
|
||||||
if ((data.event as ExtendedCloseEvent).wasClean) {
|
if ((data.event as ExtendedCloseEvent).wasClean) {
|
||||||
provider.connect();
|
void provider.connect();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onAuthenticationFailed() {
|
||||||
|
set({ isReady: true });
|
||||||
|
},
|
||||||
onStatus: ({ status }) => {
|
onStatus: ({ status }) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const nextConnected = status === WebSocketStatus.Connected;
|
const nextConnected = status === WebSocketStatus.Connected;
|
||||||
return {
|
return {
|
||||||
isConnected: nextConnected,
|
isConnected: nextConnected,
|
||||||
|
isReady: state.isReady || status === WebSocketStatus.Disconnected,
|
||||||
hasLostConnection:
|
hasLostConnection:
|
||||||
state.isConnected && !nextConnected
|
state.isConnected && !nextConnected
|
||||||
? true
|
? true
|
||||||
@@ -62,7 +68,7 @@ export const useProviderStore = create<UseCollaborationStore>((set, get) => ({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
onSynced: ({ state }) => {
|
onSynced: ({ state }) => {
|
||||||
set({ isSynced: state });
|
set({ isSynced: state, isReady: true });
|
||||||
},
|
},
|
||||||
onClose(data) {
|
onClose(data) {
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user