diff --git a/CHANGELOG.md b/CHANGELOG.md index 428dd7536..5f7fb6bf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ ### Fixed +- Stop showing a “signal is aborted without reason” error when you switch pages again before the previous page has finished loading. - Export layers with two shadows as one `effects` prop instead of repeating the `shadow` attribute, background blurs as `backgroundBlur` instead of a layer blur, hidden children with `visible={false}` instead of leaving them out, and per-corner radii even when the uniform radius is 0. - Apply `strokeAlign`, `strokeDash`, `minH`, and `maxH` in design JSX, which were accepted but ignored, and make `minW` and `maxW` set the layer's minimum and maximum width rather than only clamping its initial width. - Render the canvas with the Vue SDK's `CanvasRoot` and `CanvasSurface`; CanvasKit never started there and the canvas stayed blank. diff --git a/src/app/editor/session/create.ts b/src/app/editor/session/create.ts index f8fa3c9b9..e066c8f73 100644 --- a/src/app/editor/session/create.ts +++ b/src/app/editor/session/create.ts @@ -181,7 +181,12 @@ export function createEditorStore(initialGraph?: SceneGraph) { } succeeded = true } catch (error) { - if (preparation.signal.aborted) throw error + if (preparation.signal.aborted) { + // Another switch took over; its page is the one to show, so this one ends quietly. + // A caller's own preparation reports its cancellation itself. + if (ownsPreparation) return + throw error + } if (ownsPreparation) { const presentationTimedOut = error instanceof Error && error.message === 'The operation was timed out' diff --git a/tests/app/editor/session/page-switch.test.ts b/tests/app/editor/session/page-switch.test.ts new file mode 100644 index 000000000..c5081b471 --- /dev/null +++ b/tests/app/editor/session/page-switch.test.ts @@ -0,0 +1,22 @@ +import 'fake-indexeddb/auto' +import { expect, test } from 'bun:test' + +import { createEditorStore } from '@/app/editor/session/create' + +test('a page switch overtaken by another resolves instead of rejecting', async () => { + const store = createEditorStore() + // Stand in for the canvas, which acknowledges each scene it presents. + store.onPreparationEvent('preparation:updated', (preparation) => { + if (preparation.phase === 'preparing-render') + store.preparationController.acknowledgePresentation(store.state.sceneVersion) + }) + const b = store.graph.addPage('B').id + const c = store.graph.addPage('C').id + + const first = store.switchPage(b) + const second = store.switchPage(c) + + await expect(first).resolves.toBeUndefined() + await second + expect(store.state.currentPageId).toBe(c) +})