diff options
| author | Adam Malczewski <[email protected]> | 2026-10-21 01:04:35 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-10-21 01:04:35 +0900 |
| commit | c61b4d66921e7fe5d7d1e2a988e96785f6aedd9f (patch) | |
| tree | 8130a35661a66d28197f5f52fff036a6be523656 /src/features/workspaces/tile-layout-store.svelte.ts | |
| parent | b3a539260aa15efd197ce06c1073f0f25fee0c15 (diff) | |
| download | dispatch-web-c61b4d66921e7fe5d7d1e2a988e96785f6aedd9f.tar.gz dispatch-web-c61b4d66921e7fe5d7d1e2a988e96785f6aedd9f.zip | |
fix(review-1.3): render home tiles before layout revalidation
Diffstat (limited to 'src/features/workspaces/tile-layout-store.svelte.ts')
| -rw-r--r-- | src/features/workspaces/tile-layout-store.svelte.ts | 34 |
1 files changed, 24 insertions, 10 deletions
diff --git a/src/features/workspaces/tile-layout-store.svelte.ts b/src/features/workspaces/tile-layout-store.svelte.ts index a910c73..98a5c33 100644 --- a/src/features/workspaces/tile-layout-store.svelte.ts +++ b/src/features/workspaces/tile-layout-store.svelte.ts @@ -2,6 +2,7 @@ import type { TileLayoutData as ContractTileLayoutData, TileGeometryMode, } from "@dispatch/tile-layout-contract"; +import { createLocalStore } from "../../adapters/local-storage"; import type { TileLayoutHttp } from "./adapter/tile-layout-http"; import { type Cell, @@ -25,7 +26,7 @@ import { * * - **Orientation selection** — `setMode(mode)` (the home page's effect on * the resolved grid geometry) is the boot + orientation-change trigger: - * an orientation CHANGE resets to the local empty default and probes that + * an orientation CHANGE seeds from its cached server layout and probes that * orientation's document fresh. Same-mode calls are a no-op. The store * probes its INITIAL mode on creation (the boot probe). * - **Server-backed** — a successful probe applies the server document; @@ -38,7 +39,7 @@ import { * clients; the client filters by mode). * - **Quiet degrade** — any probe/write failure (backend unreachable, routes * not served) keeps the local state with EPHEMERAL edits (in-memory only — - * the retired localStorage write path is gone); logged once, no error UI. + * only server-confirmed layouts are cached); logged once, no error UI. * - **Sync gating** — `sync(ids)` no-ops while the first probe for an * orientation is in flight (`providerState === "unknown"`): merging the * live workspace list into an unprobed empty document would PUT a wiped @@ -61,7 +62,7 @@ export interface TileLayoutStore { /** * Select the active orientation and probe its document. Same-mode calls * are a no-op (the boot probe fires on creation for the initial mode); - * an orientation CHANGE resets to the local default + probes fresh. + * an orientation CHANGE seeds from its cache + probes fresh. */ setMode(mode: TileGeometryMode): void; /** Re-probe the ACTIVE orientation's document (reconnect catch-up). */ @@ -96,6 +97,10 @@ export interface TileLayoutStore { export interface TileLayoutStoreOptions { /** The injected HTTP edge (the `home-tile-layout` plugin's routes). */ readonly http: TileLayoutHttp; + /** Initial viewport orientation (default wide). */ + readonly initialMode?: TileGeometryMode; + /** Injected localStorage for server-layout snapshots; absent → no cache. */ + readonly storage?: Storage | undefined; /** Edit-PUT debounce window in ms (default 300; tests shrink it). */ readonly debounceMs?: number; /** The quiet-failure hook (default `console.warn` — never an error UI). */ @@ -109,11 +114,16 @@ export function createTileLayoutStore(opts: TileLayoutStoreOptions): TileLayoutS const debounceMs = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS; const warn = opts.warn ?? ((message: string) => console.warn(message)); - let mode = $state<TileGeometryMode>("wide"); + const cache = { + wide: createLocalStore<TileLayoutData>("dispatch.tile-layout.wide", { storage: opts.storage }), + tall: createLocalStore<TileLayoutData>("dispatch.tile-layout.tall", { storage: opts.storage }), + }; + const initialMode = opts.initialMode ?? "wide"; + let mode = $state<TileGeometryMode>(initialMode); // The ACTIVE orientation's document — the server's copy once a probe has // landed (sanitized defensively; the backend validates strictly), the - // empty default until then. - let data = $state<TileLayoutData>(sanitizeLayout(null)); + // cached server snapshot (or empty default) until then. Never PUT the seed. + let data = $state<TileLayoutData>(sanitizeLayout(cache[initialMode].load())); let providerState = $state<TileLayoutProviderState>("unknown"); /** The last document known to come from the server (GET / WS push / PUT echo) — the rollback target. */ @@ -141,6 +151,7 @@ export function createTileLayoutStore(opts: TileLayoutStoreOptions): TileLayoutS function applyServerLayout(next: TileLayoutData): void { lastServerLayout = next; data = next; + cache[mode].save(next); } function clearPutTimer(): void { @@ -154,7 +165,10 @@ export function createTileLayoutStore(opts: TileLayoutStoreOptions): TileLayoutS putTimer = null; // Read the CURRENT document at fire time: a newer edit supersedes the // debounced snapshot (one in-flight write per window, last wins). - const result = await http.putLayout(mode, { version: 2, tiles: data.tiles }); + const writtenMode = mode; + const result = await http.putLayout(writtenMode, { version: 2, tiles: data.tiles }); + if (result.ok) cache[writtenMode].save(sanitizeLayout(result.layout)); + if (writtenMode !== mode) return; if (result.ok) { notePresent(); // The server's validated echo is the new rollback target. @@ -227,12 +241,12 @@ export function createTileLayoutStore(opts: TileLayoutStoreOptions): TileLayoutS setMode(next) { if (next === mode) return; mode = next; - // The document belongs to the OLD orientation now: reset to the local - // default while the probe resolves (never flash the other + // The document belongs to the OLD orientation now: seed from this + // mode's cache while the probe resolves (never flash the other // orientation's arrangement), and drop any queued edit. clearPutTimer(); lastServerLayout = null; - data = sanitizeLayout(null); + data = sanitizeLayout(cache[mode].load()); providerState = "unknown"; void runProbe(); }, |
