Show an in-call indicator when a room has an active huddle (LC-612) #572
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/lc612-sidebar-incall"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes LC-612. The sidebar workstream of LC-596.
The blocker was routing, not markup
An active huddle was invisible from the sidebar because
VoiceJoined/VoiceLeftare broadcast to the room topic - only sockets that already have the room open receive them. Those are exactly the people who do not need a sidebar hint. So the existing events could not drive this; it needed per-member fan-out.What this adds
ChatEvent::HuddlePresenceis addressed to one member (to_user_id) and fanned out overlist_room_member_idson every join and leave, carrying the post-change participant count. It renders as a small pulsing green dot + count on the room row (#incall-room-{id}), OOB-swapped exactly like the unread badge. A fresh page load seeds the same count fromhub.voice_room_users, so the indicator is correct on load, not only after the next event -SidebarRoomgained anin_callfield for that.The fan-out lives in
huddle_ring.rs(broadcast_presence), next to the LC-611 ring it shares a shape with.Two deliberate differences from the ring
Scope
Non-
is_voicerooms only, matching the ring. A persistent voice channel is a place people go to deliberately, and its membership is enclave-scoped rather than inroom_members, so the per-member fan-out here would not reach the right people for it. Theis_voiceseed is hard-coded to 0 andbroadcast_presenceearly-returns on it.Tests
Five, in
huddle_ring.rsandstage_control.rs:broadcast_presencetobroadcast_to_room, which then misses them (the two membership-sensitive tests fail, the voice-channel one still passes because it asserts absence)Full suite green (182 test binaries),
just checkclean, en/es Fluent symmetry holds.Note
The count is read from the live in-memory hub on both the seed and the fan-out, so it reflects true presence and does not depend on the huddle having sent any message. No new DB state.
🤖 Generated with Claude Code
https://claude.ai/code/session_01FhDVMViNHqUbwmfe2aXTv5
An ongoing group call was invisible from the sidebar: `VoiceJoined`/`VoiceLeft` are broadcast to the room topic, so only people who already have the room open ever heard about a huddle. The whole point of a sidebar hint is to reach the members who are looking somewhere else, so those events were the wrong primitive. A new `ChatEvent::HuddlePresence` is addressed to one member (`to_user_id`) and fanned out over `list_room_member_ids` on every join and leave, carrying the post-change participant count. It renders as a small pulsing green dot + count on the room row (`#incall-room-{id}`), swapped OOB exactly like the unread badge. A fresh page load seeds the same count straight from `hub.voice_room_users`, so the indicator is correct on load, not only after the next event. Two deliberate differences from the LC-611 ring, which shares the fan-out shape: - It ignores the room mute mode. The ring is an interruption and mute suppresses it; this dot is passive presence, and hiding it on a muted room would mean you could not see a call happening in a room you muted but still belong to. - It fires on every roster change, not just the first join, because the count itself changes each time. Scoped to ad-hoc huddles (non-`is_voice`), matching the ring: a persistent voice channel is a place people go to deliberately, and its membership is enclave-scoped rather than in `room_members`, so the per-member fan-out here would not reach the right people for it anyway. Tests: presence reaches a member who does NOT have the room open (proven load-bearing by reverting to `broadcast_to_room`, which then misses them), carries the right count as the huddle grows and shrinks to 0, ignores mute, and skips voice channels; plus a render test that the sidebar seeds the pill on initial load. New Fluent key `sidebar-in-call` in en + es. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhDVMViNHqUbwmfe2aXTv5