Live wall: saved views in the URL hash, per-viewer memory, kiosk mode (#234) #265

Merged
logan merged 2 commits from feat/234-saved-views into main 2026-10-05 03:03:26 -04:00
Owner

The wall now comes back exactly as it was left: overlays, basemap and viewport ride in the URL hash and are remembered per browser. ?kiosk=1 (or #kiosk) turns it into a wall display.

Saved views

  • Hash keys (all optional, unknown keys ignored, old inc/cam/tour links still work): layers=nodes,inc,paths,air,ais,dotcam,vs,traffic,radar, z/lat/lon, base=dark|light|streets, tourMs, kiosk, and f.<name>=<value>, which passes through untouched as WallLink.filter for #235.
  • Precedence: hash > this browser's last view (localStorage, every access in try/catch) > defaults (Nodes, Active Incidents, Incident Paths, as before).
  • The address bar is always a working link to the current view. The new Copy view link button copies it. Editing the hash by hand remounts the map with the new view.
  • The persisted viewport is the region view, so a reload doesn't come back zoomed into one incident.
  • Folds #137: closing an incident flies back to where the viewer was before drilling in.

Kiosk

  • Nav hidden on /, so the wall gets the full screen.
  • Fullscreen button. Fullscreen also turns on at the first touch or keypress, because browsers require a gesture; a browser launched with its own kiosk flag needs no click.
  • Screen Wake Lock, re-acquired on visibilitychange. Where the browser doesn't support it, this is skipped silently.
  • Reconnect banner: shown when offline or when useCalls returns an error. In kiosk mode the page reloads with backoff (30 s up to 10 min); everyone gets a Retry button. The hooks are untouched.
  • 18-20 px type through data-kiosk on the LiveView root plus Tailwind arbitrary variants (no globals.css). Checked against the Tailwind 3.4.19 compiler output.

Fix found on the way: react-leaflet adds a checked overlay to the map before registering it with LayersControl, so no overlayadd fires for it. useOverlayShown now starts from the initial layer set. Otherwise a saved DOT Cameras / Traffic / Viewsheds layer would have shown up ticked but empty.

Not touched: WallPanels.tsx, c2api.ts, types.ts, the backend, discord files. The RadarLayer remount workaround is intact.

Verification

  • tsc --noEmit exits 0 (fresh scratch copy).
  • c2-core pytest: 854 passed, 4 warnings.
  • Hash parse/serialise round-trip checked with an ad-hoc node script (not committed; no JS test runner exists).
  • Not browser-tested. The acceptance steps (tick Radar + Cameras then reload, open the copied URL in a second browser, kiosk after a browser restart, private window) still need a manual pass.

Closes #234
Closes #137

🤖 Generated with Claude Code

The wall now comes back exactly as it was left: overlays, basemap and viewport ride in the URL hash and are remembered per browser. `?kiosk=1` (or `#kiosk`) turns it into a wall display. **Saved views** - Hash keys (all optional, unknown keys ignored, old `inc/cam/tour` links still work): `layers=nodes,inc,paths,air,ais,dotcam,vs,traffic,radar`, `z/lat/lon`, `base=dark|light|streets`, `tourMs`, `kiosk`, and `f.<name>=<value>`, which passes through untouched as `WallLink.filter` for #235. - Precedence: hash > this browser's last view (localStorage, every access in try/catch) > defaults (Nodes, Active Incidents, Incident Paths, as before). - The address bar is always a working link to the current view. The new **Copy view link** button copies it. Editing the hash by hand remounts the map with the new view. - The persisted viewport is the *region* view, so a reload doesn't come back zoomed into one incident. - Folds #137: closing an incident flies back to where the viewer was before drilling in. **Kiosk** - Nav hidden on `/`, so the wall gets the full screen. - Fullscreen button. Fullscreen also turns on at the first touch or keypress, because browsers require a gesture; a browser launched with its own kiosk flag needs no click. - Screen Wake Lock, re-acquired on `visibilitychange`. Where the browser doesn't support it, this is skipped silently. - Reconnect banner: shown when offline or when `useCalls` returns an error. In kiosk mode the page reloads with backoff (30 s up to 10 min); everyone gets a Retry button. The hooks are untouched. - 18-20 px type through `data-kiosk` on the LiveView root plus Tailwind arbitrary variants (no globals.css). Checked against the Tailwind 3.4.19 compiler output. **Fix found on the way:** react-leaflet adds a `checked` overlay to the map *before* registering it with LayersControl, so no `overlayadd` fires for it. `useOverlayShown` now starts from the initial layer set. Otherwise a saved DOT Cameras / Traffic / Viewsheds layer would have shown up ticked but empty. **Not touched:** WallPanels.tsx, c2api.ts, types.ts, the backend, discord files. The RadarLayer remount workaround is intact. **Verification** - `tsc --noEmit` exits 0 (fresh scratch copy). - c2-core pytest: 854 passed, 4 warnings. - Hash parse/serialise round-trip checked with an ad-hoc node script (not committed; no JS test runner exists). - **Not browser-tested.** The acceptance steps (tick Radar + Cameras then reload, open the copied URL in a second browser, kiosk after a browser restart, private window) still need a manual pass. Closes #234 Closes #137 🤖 Generated with [Claude Code](https://claude.com/claude-code)
logan added 1 commit 2026-10-05 02:58:56 -04:00
- 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>
logan added 1 commit 2026-10-05 03:03:00 -04:00
Without it the URL had no z/lat/lon until the first pan, so a link copied
from an untouched wall opened at the other browser's remembered view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
logan merged commit 6d0d3f79ce into main 2026-10-05 03:03:26 -04:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logan/server-26#265