Attention cues review fixes (#238)

- NEW lane: Gate A notice pinned below the rows (it scrolled off with >3
  entries, same rule as the rail, #46); rows at text-xs for the wall.
- Mobile chip moved off MapView's centred "Live" chip, which it covered.
- Keyword cues re-resolve to the incident once correlation links the call,
  so the lane row is clickable and the pin pulses.
- Enabling notifications sends one confirmation notification; a browser
  that grants permission but throws on construct (mobile Chrome) now shows
  "unsupported" instead of a switched-on control that never delivers.
- useActiveIncidentsState: includeMetadataChanges so `loaded` flips on a
  metadata-only server sync (quiet org / offline start) instead of waiting
  for the first incident change and swallowing it into the baseline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-10-05 03:52:02 -04:00
co-authored by Claude Opus 5.5
parent eb0faf2aea
commit 292c4fffd9
4 changed files with 53 additions and 17 deletions
+11 -2
View File
@@ -404,7 +404,16 @@ export function useAttention({ ready, resetKey, incidents, calls, keywords, visi
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ready, resetKey, incidents, calls, kwKey]);
const lane = useMemo(() => laneEntries(visible, seenAt, log, now, visibleCallIds), [visible, seenAt, log, now, visibleCallIds]);
const pulse = useMemo(() => pulseIds(visibleIds, seenAt, log, now), [visibleIds, seenAt, log, now]);
// A keyword usually fires on the transcript, before correlation links the
// call. Re-resolve unlinked events against the calls as they are now, so
// the lane row becomes the (clickable) incident and its pin pulses.
const linkedLog = useMemo(() => {
if (!log.some((e) => !e.incidentIds.length && e.callId)) return log;
const links = new Map<string, string[]>();
for (const c of calls) { const ids = callIncidentIds(c); if (ids.length) links.set(c.call_id, ids); }
return log.map((e) => (e.incidentIds.length || !e.callId || !links.has(e.callId) ? e : { ...e, incidentIds: links.get(e.callId)! }));
}, [log, calls]);
const lane = useMemo(() => laneEntries(visible, seenAt, linkedLog, now, visibleCallIds), [visible, seenAt, linkedLog, now, visibleCallIds]);
const pulse = useMemo(() => pulseIds(visibleIds, seenAt, linkedLog, now), [visibleIds, seenAt, linkedLog, now]);
return { lane, pulseIds: pulse, now };
}