// In-memory per-game cache. A game the player has opened once — or one warmed ahead of time // by the lobby preload (see lib/preload) — is kept here so opening it renders instantly from // the cache while a fresh fetch updates it in the background, removing the blank "loading" // flash and the full redraw on every lobby <-> game navigation. It is intentionally // process-memory only (no persistence): stale entries are corrected by the background // refresh, and the cache is cleared on logout. import type { MoveRecord, StateView } from './model'; export interface CachedGame { view: StateView; moves: MoveRecord[]; /** * The player's saved draft (composition) as the opaque JSON the server stores, so a warm * open paints the pending tiles already on the board with no rack→board jump. An empty * string means no draft; undefined means it has not been fetched for this entry yet. */ draft?: string; } const cache = new Map(); /** getCachedGame returns the last-seen state+history for a game, or undefined. */ export function getCachedGame(id: string): CachedGame | undefined { return cache.get(id); } /** * setCachedGame stores the latest state+history for a game. Omitting draft preserves the * cached composition: the live-event delta path advances view+moves without refetching the * draft and must not drop it, while passing an empty string clears it (a committed move does, * mirroring the server). */ export function setCachedGame(id: string, view: StateView, moves: MoveRecord[], draft?: string): void { const prev = cache.get(id); cache.set(id, { view, moves, draft: draft ?? prev?.draft }); } /** setCachedDraft updates only the saved draft of an already-cached game (a no-op otherwise), * keeping the cache coherent with the draft persisted while composing. */ export function setCachedDraft(id: string, draft: string): void { const prev = cache.get(id); if (prev) cache.set(id, { ...prev, draft }); } /** clearGameCache drops every cached game (called on logout). */ export function clearGameCache(): void { cache.clear(); }