# Messages Tab **Nav id**: `messages` (default tab on load) **Icon**: ✦ --- ## What it shows A full-screen chat thread between Quinn and the client. Quinn's messages appear on the right (gold-tinted bubbles), client messages on the left. Newest messages scroll into view automatically. --- ## Data flow ### Initial load `fetchMessages(token)` — `GET /vip/messages/:token` — fetches the full message history on mount and on window focus. After fetch, `markRead(token)` fires — `PUT /vip/messages/:token/read?side=client` — clears the unread badge server-side. ### Real-time updates `useMessageStream` (`src/hooks/useMessageStream.ts`) opens a Server-Sent Events connection to `GET /vip/messages/:token/stream`. Incoming events are merged into local state, deduped by `msg.id`. If SSE is unavailable or disconnects, the hook falls back to polling via `fetchMessages` on a timer. ### Sending `sendMessage(token, text)` — `POST /vip/messages/:token` — appends the new message optimistically to local state on success. Keyboard shortcut: Enter submits, Shift+Enter inserts a newline. --- ## Push notifications When `Notification.permission === 'default'`, a soft prompt appears above the composer. On approval, `registerPushSubscription(token)` sends the browser's `PushSubscription` to `PUT /vip/push/:token/push-subscription`. The server stores it and sends a Web Push on new inbound Quinn messages. Push state machine: `idle → requesting → granted | denied`. Once `granted` or `denied`, the prompt is hidden. --- ## Error states | Condition | Behavior | |-----------|---------| | `invalid_token` on fetch | Sets `invalidToken = true` → portal renders "link no longer valid" | | `invalid_token` on send | Same | | Network error on fetch | Error card with "Try again" button | | Network error on send | `error` state shown inline | --- ## Non-obvious details - `bottomRef` div at the end of the message list is scrolled into view whenever messages change **and** the messages tab is active — prevents unwanted scroll when on other tabs. - Messages tab is the only tab with a `