Fix DM presence indicator never updating live (#63)

The sidebar shows every DM's online/offline dot at once, but the only
existing signal for a presence change (member_updated) is broadcast to
a room's own channel, which Presence only delivers to a connection that
currently has that specific room joined -- never true for a DM sitting
unopened in the sidebar. Add a dedicated per-user broadcast
(dm_presence_update) sent to each of a user's DM partners on their own
per-user channel whenever their global online/offline state changes, so
the sidebar dot updates without needing that DM to be the open room.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-27 20:41:57 -06:00
co-authored by Claude Sonnet 5
parent d42bf114dd
commit 4cc3823adf
7 changed files with 142 additions and 5 deletions
+8
View File
@@ -77,6 +77,14 @@ export function ChatShellPage() {
: r,
),
)
} else if (envelope.type === 'dm_presence_update') {
setRooms((prev) =>
prev.map((r) =>
r.dm_partner && r.dm_partner.user_id === envelope.user_id
? { ...r, dm_partner: { ...r.dm_partner, status: envelope.status } }
: r,
),
)
}
}),
[socket, refreshRooms, refreshMembers, roomId],
+12
View File
@@ -218,6 +218,17 @@ export interface ChatUnreadUpdateEnvelope {
mentioned: boolean
}
// #63: sent on the recipient's own per-user channel, one per DM partner,
// whenever that partner's global online/offline state changes -- lets the
// sidebar's presence dot (MyRoomItem.dm_partner.status) stay live without
// needing that DM to be the currently open room (member_updated's
// room-channel delivery doesn't reach an unopened DM at all).
export interface ChatDmPresenceUpdateEnvelope {
type: 'dm_presence_update'
user_id: string
status: 'online' | 'offline'
}
// #49: delivered over this same socket, alongside the existing Web Push
// send, to every eligible offline member regardless of push-subscription
// status -- see backend/app/services/message_events.py's
@@ -243,6 +254,7 @@ export type ServerEnvelope =
| ChatMemberUpdatedEnvelope
| ChatUnreadUpdateEnvelope
| ChatDesktopNotificationEnvelope
| ChatDmPresenceUpdateEnvelope
export interface AdminUser {
id: string