Root cause on iOS Safari (iPad/phone): the `IncomingNotePopup`
rendered a `fixed inset-0 z-[110]` full-viewport wrapper. Stacking
that layer on top of the upcoming-meeting alert plus a Radix toast
intermittently caused the next finger tap to be swallowed — the user
saw the popup "hang". A simultaneous re-render storm from
synchronous query invalidations on multi-event socket fanouts made
it worse on slower hardware.
Fixes:
- Drop the full-viewport wrapper: render the popup card directly
as a sized `fixed` element. No more invisible viewport-sized
layer between touch and the rest of the UI.
- Suppress the redundant note/reply toast when the floating
popup actually accepts the event. The popup IS the surface;
doubling up just stacks one more floating layer.
- Remove `autoFocus` on the Reply button — focus-steal was
contributing to the perceived hang.
- Coalesce `react-query` `invalidateQueries` calls in the
notifications socket: collect into a Set and flush once per
animation frame so a burst of socket events triggers a single
refetch pass instead of N synchronous re-renders. Cleaned up
on socket disconnect.
Tests (artifacts/tx-os/tests/notes-popup-touch-tap.spec.mjs):
- "recipient on a touch device can tap Reply on the floating
note popup" — verifies the popup is no longer viewport-sized,
the duplicate toast is suppressed, and a real `locator.tap()`
succeeds.
- "popup + meeting alert simultaneously: tap on popup still
works" — the user's exact scenario: seeds an imminent meeting
so the alert appears, triggers a real cross-user note
delivery so both float simultaneously, then taps Reply on
the popup and confirms it dismisses while the alert remains.
Both tests + tsc --noEmit clean.
Files:
- artifacts/tx-os/src/components/notes/incoming-note-popup.tsx
- artifacts/tx-os/src/hooks/use-notifications-socket.ts
- artifacts/tx-os/tests/notes-popup-touch-tap.spec.mjs (new)
Root cause on iOS Safari (iPad/phone): the `IncomingNotePopup`
rendered a `fixed inset-0 z-[110]` full-viewport wrapper with
`pointer-events-none`. Stacking that layer on top of the upcoming
meeting alert plus a Radix toast intermittently caused the next
finger tap to be swallowed — the user saw the popup "hang".
Fixes:
- Drop the full-viewport wrapper: render the popup card directly
as a sized `fixed` element. No more invisible viewport-sized
layer between touch and the rest of the UI.
- Suppress the redundant note/reply toast when the floating
popup actually accepts the event. The popup IS the surface;
doubling up just stacks one more floating layer.
- Remove `autoFocus` on the Reply button. A new popup arriving
while the user is mid-tap would steal focus, contributing to
the perceived hang.
Tests:
- New `notes-popup-touch-tap.spec.mjs`: emulates a touch viewport,
sends a real cross-user note, verifies popup bounding box is
card-sized (not viewport-sized), the redundant toast is
suppressed, and a `locator.tap()` on Reply dismisses the
popup and navigates.
- `tsc --noEmit` clean.
Files:
- artifacts/tx-os/src/components/notes/incoming-note-popup.tsx
- artifacts/tx-os/src/hooks/use-notifications-socket.ts
- artifacts/tx-os/tests/notes-popup-touch-tap.spec.mjs (new)