fix(canvas): sanitize snapshot session.currentPageId before loadSnapshot — stale snapshots with missing pages caused currentPageId crash
Some checks failed
app-ci-deploy / test-build-deploy (push) Has been cancelled
Some checks failed
app-ci-deploy / test-build-deploy (push) Has been cancelled
- Pre-load: if session.currentPageId not in snapshot pages, reset to first available page - Post-load: if store instance.currentPageId still invalid, clear store for fresh start - On loadSnapshot error: clear store instead of silently failing with corrupt state - Root cause: teacher1 had a saved snapshot with currentPageId pointing to a deleted page
This commit is contained in:
parent
a375fbe37f
commit
9f58917c39
@ -1,5 +1,5 @@
|
||||
// External imports
|
||||
import { TLStore, getSnapshot, Editor, loadSnapshot } from '@tldraw/tldraw';
|
||||
import { TLStore, getSnapshot, Editor, loadSnapshot, TLINSTANCE_ID } from '@tldraw/tldraw';
|
||||
import logger from '../../debugConfig';
|
||||
import { SharedStoreService } from './sharedStoreService';
|
||||
|
||||
@ -109,17 +109,35 @@ export class NavigationSnapshotService {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanitize session.currentPageId against pages actually in the document
|
||||
const docStore = (snap.document as { store?: Record<string, { typeName: string; id: string }> })?.store ?? {};
|
||||
const pageIds = Object.values(docStore).filter((r) => (r as { typeName: string }).typeName === 'page').map((r) => r.id);
|
||||
const session = snap.session as { currentPageId?: string } | undefined;
|
||||
if (session?.currentPageId && pageIds.length > 0 && !pageIds.includes(session.currentPageId)) {
|
||||
logger.warn('snapshot-service', '⚠️ session.currentPageId not in snapshot pages — resetting to first page', {
|
||||
currentPageId: session.currentPageId, availablePages: pageIds
|
||||
});
|
||||
session.currentPageId = pageIds[0];
|
||||
}
|
||||
|
||||
const snapshotCopy = {
|
||||
schemaVersion: snap.schemaVersion || (snap.document as { schema?: { schemaVersion?: unknown } })?.schema?.schemaVersion,
|
||||
document: snap.document,
|
||||
session: snap.session,
|
||||
};
|
||||
|
||||
const targetStore = editor ? editor.store : store;
|
||||
try {
|
||||
if (editor) {
|
||||
loadSnapshot(editor.store, snapshotCopy as any);
|
||||
} else {
|
||||
loadSnapshot(store, snapshotCopy as any);
|
||||
loadSnapshot(targetStore, snapshotCopy as any);
|
||||
// Post-load validation: ensure instance.currentPageId is still valid
|
||||
const instance = targetStore.get(TLINSTANCE_ID);
|
||||
if (instance) {
|
||||
const pages = targetStore.allRecords().filter((r) => (r as { typeName: string }).typeName === 'page');
|
||||
const pageValid = pages.some((p) => p.id === instance.currentPageId);
|
||||
if (!pageValid) {
|
||||
logger.warn('snapshot-service', '⚠️ Post-load: currentPageId invalid — clearing corrupt store', { currentPageId: instance.currentPageId });
|
||||
targetStore.clear();
|
||||
}
|
||||
}
|
||||
logger.debug('snapshot-service', '✅ Snapshot loaded successfully');
|
||||
} catch (err) {
|
||||
@ -128,7 +146,8 @@ export class NavigationSnapshotService {
|
||||
if (isSchemaMigration) {
|
||||
logger.debug('snapshot-service', 'ℹ️ Schema migration warning (non-critical)', { error: msg });
|
||||
} else {
|
||||
logger.warn('snapshot-service', '⚠️ Unexpected loadSnapshot error', { error: msg });
|
||||
logger.warn('snapshot-service', '⚠️ Unexpected loadSnapshot error — clearing store to allow fresh start', { error: msg });
|
||||
targetStore.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user