feat: task 2.9 connection status + per-device staleness fade — Phase 2 done
- src/live/last-seen.ts: formatLastSeen(ts, now) — "now" / "Ns ago" /
"Nm ago" / "HH:MM".
- src/live/use-staleness.ts: useStalenessTick(intervalMs=1000) hook
re-renders subscribers every interval with the current epoch ms.
- src/ui/components/connection-chip.tsx: <ConnectionChip /> bottom-left,
three states (connected / connecting+reconnecting / disconnected),
aria-live polite + role status.
- src/map/layers/map-positions.tsx: every FeatureProps now carries
staleSec; both symbol layers interpolate icon-opacity (and text-opacity
on the non-selected layer) on staleSec — 0-60s full opacity,
5min faded, 30min very faded. Update effect rebuilds features at the
staleness tick rate (1Hz).
- src/routes/_authed/monitor.tsx: renders <ConnectionChip />.
- src/live/index.ts: re-exports formatLastSeen + useStalenessTick.
Strategy A (faded marker via interpolation) chosen over Strategy B
(separate warning-badge layer) per the task's open-question
resolution; simpler and easier to extend later.
Deviations:
1. stalenessTick is its own hook in src/live/use-staleness.ts rather
than a property on the position store — keeps the store clean of
UI-driven re-render concerns; the hook is reusable.
2. <DeviceLastSeen deviceId> standalone component skipped — the SPA
doesn't have a sidebar yet (Phase 3.4); the per-marker fade IS the
indicator for v1.
Bundle: main 396KB / 121KB gz — small bump from 2.8.
🎉 Phase 2 — Live monitoring map — complete. All 9 tasks shipped.
End-to-end: login -> /monitor -> event auto-selects -> snapshot
positions render -> live updates flow via WS through the rAF
coalescer -> staleness fades stale markers -> connection chip
surfaces WS state. Dogfood-blocking work for Rally Albania 2026 done.
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Format a position-recorded timestamp as a human-readable "age" string.
|
||||
*
|
||||
* - < 5 s → "now"
|
||||
* - < 60 s → "Ns ago"
|
||||
* - < 1 hr → "Nm ago"
|
||||
* - else → wall-clock time ("HH:MM")
|
||||
*
|
||||
* Used by the connection chip ("last live HH:MM") and by Phase 3.4's
|
||||
* per-device detail panel.
|
||||
*/
|
||||
export function formatLastSeen(ts: number, now: number = Date.now()): string {
|
||||
const ageMs = Math.max(0, now - ts);
|
||||
if (ageMs < 5_000) return 'now';
|
||||
if (ageMs < 60_000) return `${Math.floor(ageMs / 1000)}s ago`;
|
||||
if (ageMs < 60 * 60_000) return `${Math.floor(ageMs / 60_000)}m ago`;
|
||||
const d = new Date(ts);
|
||||
return d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
|
||||
}
|
||||
Reference in New Issue
Block a user