Live wall: saved views in the URL hash, per-viewer memory, kiosk mode (#234)
- 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>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7bee87b147
commit
afd2bc463d
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user