more targeted refresh

This commit is contained in:
Moon.eth
2026-07-29 12:51:33 +09:00
parent 4f682857ac
commit a42145c678
11 changed files with 229 additions and 4 deletions
+24
View File
@@ -0,0 +1,24 @@
import { describe, expect, it, vi } from 'vitest'
import { TimelineRefreshController } from './timeline-refresh'
describe('TimelineRefreshController', () => {
it('refreshes only the currently registered route target', () => {
const controller = new TimelineRefreshController()
const first = vi.fn()
const second = vi.fn()
const unregisterFirst = controller.register(first)
const unregisterSecond = controller.register(second)
expect(controller.refresh()).toBe(true)
expect(first).not.toHaveBeenCalled()
expect(second).toHaveBeenCalledOnce()
// A stale route cleanup cannot remove the current route's target.
unregisterFirst()
expect(controller.refresh()).toBe(true)
expect(second).toHaveBeenCalledTimes(2)
unregisterSecond()
expect(controller.refresh()).toBe(false)
})
})
+40
View File
@@ -0,0 +1,40 @@
/**
* The active route's primary timeline refresh.
*
* App owns one controller and routes register while mounted. The guarded
* cleanup matters during navigation: an old route must not clear a newer
* route's callback if Svelte mounts the replacement before destroying it.
*/
import { getContext, setContext } from 'svelte'
export type TimelineRefresh = () => void
export class TimelineRefreshController {
private current: TimelineRefresh | null = null
register(refresh: TimelineRefresh): () => void {
this.current = refresh
return () => {
if (this.current === refresh) this.current = null
}
}
refresh(): boolean {
if (!this.current) return false
this.current()
return true
}
}
export const TIMELINE_REFRESH = Symbol('plspace.timeline-refresh')
export function provideTimelineRefresh(): TimelineRefreshController {
const controller = new TimelineRefreshController()
setContext(TIMELINE_REFRESH, controller)
return controller
}
/** Routes remain independently renderable in tests without this context. */
export function useTimelineRefresh(): TimelineRefreshController | null {
return getContext<TimelineRefreshController | undefined>(TIMELINE_REFRESH) ?? null
}