fix(app): end a page switch quietly when another one takes over (#845)

Switching pages again before the previous page finished preparing
aborted the first switch, which rejected with an AbortError. Callers
start page switches without awaiting them, so the rejection surfaced as
an error toast.
This commit is contained in:
Danila Poyarkov 2026-10-03 20:18:15 +04:00 committed by GitHub
parent 67dbd7156f
commit 4895e55c22
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 29 additions and 1 deletions

View file

@ -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.

View file

@ -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'

View file

@ -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)
})