- liveWall.ts: WallLink carries layers (short stable keys), z/lat/lon, base, tourMs, kiosk and an f.<name> filter pass-through for #235; parse/serialise stay pure and round-trip; old inc/cam/tour links parse. - MapView: overlays/basemap/viewport come from initialLayers/initialBase/ initialView (defaults unchanged); onViewStateChange reports layer, base and region-view changes (debounced). Initially-checked overlays now start "shown" (react-leaflet adds a checked overlay before the control can fire overlayadd). Closing an incident flies back to the pre-drill view (#137). - wallPrefs.ts: per-browser last layers/base/view in localStorage, every access wrapped; precedence hash > remembered > defaults. - LiveView: one effect mirrors wall + map state into the hash; hash edits remount the map with the new view; Copy view link; configurable tour. - Kiosk (?kiosk=1 or #kiosk): no nav, fullscreen button (auto on first touch), Screen Wake Lock re-acquired on visibility, reconnect banner (offline / useCalls error) with backoff reload, 18-20 px type via a data-kiosk attribute and Tailwind arbitrary variants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
52 lines
1.9 KiB
TypeScript
52 lines
1.9 KiB
TypeScript
/**
|
|
* Per-viewer memory of the live wall's map view (server-26#234): the last
|
|
* overlays, basemap and viewport this browser used. A convenience only —
|
|
* every access is wrapped, and a blocked/empty store (private window, cleared
|
|
* site data) simply yields {} so the wall falls back to its defaults.
|
|
*
|
|
* Precedence when the wall opens: URL hash > this store > defaults
|
|
* (resolveWallView below). Nothing here is shared with other viewers.
|
|
*/
|
|
import {
|
|
DEFAULT_BASE, DEFAULT_LAYERS, isBaseKey, normaliseLayers, parseViewport,
|
|
type WallView,
|
|
} from "@/lib/liveWall";
|
|
|
|
const KEY = "drb.wall.view.v1";
|
|
|
|
export function loadWallPrefs(): WallView {
|
|
try {
|
|
const raw = window.localStorage.getItem(KEY);
|
|
if (!raw) return {};
|
|
const v: unknown = JSON.parse(raw);
|
|
if (!v || typeof v !== "object") return {};
|
|
const o = v as Record<string, unknown>;
|
|
const out: WallView = {};
|
|
if (Array.isArray(o.layers)) out.layers = normaliseLayers(o.layers.filter((x): x is string => typeof x === "string"));
|
|
if (typeof o.base === "string" && isBaseKey(o.base)) out.base = o.base;
|
|
const view = o.view && typeof o.view === "object" ? (o.view as Record<string, unknown>) : null;
|
|
const vp = view ? parseViewport(view.z, view.lat, view.lon) : undefined;
|
|
if (vp) out.view = vp;
|
|
return out;
|
|
} catch {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
export function saveWallPrefs(v: WallView): void {
|
|
try {
|
|
window.localStorage.setItem(KEY, JSON.stringify({ layers: v.layers, base: v.base, view: v.view }));
|
|
} catch {
|
|
/* storage blocked or full — the URL hash still carries the view */
|
|
}
|
|
}
|
|
|
|
/** Field-by-field: hash wins, then what this browser remembers, then defaults. */
|
|
export function resolveWallView(hash: WallView, stored: WallView): Required<Pick<WallView, "layers" | "base">> & Pick<WallView, "view"> {
|
|
return {
|
|
layers: hash.layers ?? stored.layers ?? DEFAULT_LAYERS,
|
|
base: hash.base ?? stored.base ?? DEFAULT_BASE,
|
|
view: hash.view ?? stored.view,
|
|
};
|
|
}
|