Commit Graph
12 Commits
Author SHA1 Message Date
Matthew Meszaros 1c734411a3 feat: keep warmup out of Unibox notifications and the unread badge: tie reply notifications to their unibox message (notifications.unibox_email_id, migrations 000202-000204) so they are removed with it and read when it is read, link them to the conversation, count the badge as unread Inbox conversations without snoozed ones and refetch it instead of incrementing, hold a live arrival from the sender of an unconfirmed warmup send until its id is known, and never send warmup without a token (#659) 2026-09-23 07:30:09 -07:00
Matthew Meszaros 7a86186f5b feat: close Advisor findings about a mailbox, campaign or step inside its delete transaction, keep re-evaluating workspaces that still hold open findings after their last mailbox is gone, re-evaluate the Advisor right after a mailbox change or a campaign/step delete with coalesced reruns, and refresh the unread badge, inbox, campaigns, analytics and Advisor caches on every mailbox removal including a teammate's 2026-09-22 20:29:13 -07:00
Matthew Meszaros 02c8d8ec27 feat: read the header's connection quality from the heartbeat roundtrip the socket provider already measures instead of an interval that compared itself against its own last tick and so reported a healthy socket as a slow one on almost every pass 2026-09-04 04:03:01 -07:00
Matthew Meszaros bb1f96ba79 feat: shorten the new socket lifecycle comments in SocketProvider, RealtimeManager and their channel tests to the one-line constraint-stating form CLAUDE.md asks for, since the implementation narrative they carried is already in the PR description and the regression tests 2026-08-27 03:10:36 -07:00
Matthew Meszaros dc1746aa2d feat: fix the dashboard socket layer's channel bookkeeping so a topic held by two surfaces survives one of them unmounting and a workspace switch actually leaves the old organization's channel: leaveChannel deleted the whole channelsRef entry, and that entry owns the handler map, so every other subscribeToChannel registration on that topic died with it and the unsubscribe closures they held pointed at an orphaned Set, meaning the first component out silently deafened the second; it now refcounts holders in holdersRef (incremented before joinChannel's already-joined bail-out, so the second surface to ask for a live topic is actually counted) and only the last holder out leaves, resets the entry in place instead of dropping it while subscribers remain, discards it from the unsubscribe closure once nothing is listening and nobody holds the join, sends phx_leave only for a channel that was really joined or joining, and drops the pointless 'leaving' state flip that was set and nulled in the same tick and read by nothing; refcounting alone would have been actively harmful, because RealtimeManager's org effect joined with no cleanup and re-ran on every isConnected false->true cycle, so the count climbed on every reconnect and never returned to zero and switching workspaces would decrement without leaving and keep feeding the client the previous organization's events, so that effect is now balanced with a cleanup matching the user-channel effect above it and prevOrgIdRef is gone, which also fixes a latent defect of its own where switching organizations while the socket was down left the old topic in desiredTopicsRef and rejoinChannels brought the abandoned workspace back on reconnect; covered by 11 new tests over the fake-socket rig, including a real RealtimeManager mounted against the real provider, and each combination of half-fixes fails the matrix 2026-08-27 02:53:33 -07:00
Matthew Meszaros 22fddad234 feat: stop the dashboard-wide reload flashing: the reconnect catch-up invalidated every query on any socket RE-connect, so a flapping websocket refetch-stormed the whole app; now it only fires after a real outage (over 5s down) and at most once every 30s 2026-07-19 09:16:55 +02:00
Matthew Meszaros 8af2950d0b feat: reconcile missed realtime events by invalidating all queries when the websocket reconnects after a gap 2026-06-11 08:25:00 +02:00
Matthew Meszaros a06a525cf9 feat: live team presence across the dashboard (online avatar stack in header, viewing/replying indicators on unibox threads and rows, editing collaborators in the automation builder, campaign and contact viewers) plus audit-spine query invalidation 2026-06-11 08:04:11 +02:00
Matthew Meszaros 379e056a24 feat: fix organization sync query handling 2026-06-04 09:30:36 +02:00
Matthew Meszaros 0c49d3c607 feat: improve app routing and browser chrome 2026-06-04 08:55:26 +02:00
Matthew Meszaros bbd76187e7 fix(web): use user.id (UUID) for the user:* channel, not email
The realtime channel handler `def join("user:" <> user_id, ...)` checks
`socket.assigns.user_id == user_id`, where socket.assigns.user_id is
the JWT `sub` claim (UUID). The frontend was building the topic from
user.email — every join was REFUSED.

Added `id: string` to the frontend User type (the backend already
serializes it as "id") and switched the channel topic in
RealtimeManager to use it.

After this + the previous round of WS fixes:
  CONNECTED TO RealtimeWeb.UserSocket in 481µs
  JOINED user:11111111-0000-0000-0000-000000000001 in 15µs
2026-05-23 05:35:53 +00:00
Matthew Meszaros 141bc54974 Add sample auth UI theme 2026-02-10 19:30:47 +01:00