Ring room members when a huddle starts (LC-611) #570
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/lc611-huddle-ring"
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-611 for the room-wide ring. Targeted invites (AC2) are not in this change - see the bottom.
The problem
Starting a huddle broadcast exactly one
ChatEvent::VoiceJoinedviabroadcast_to_room(ws.rs:2079), which only reaches sockets already subscribed to that room - i.e. people who can see the huddle bar anyway. Everyone else learned nothing. An ongoing group call was something you discovered, not something you were invited to.What this does
A new
ChatEvent::HuddleStartedis addressed to a single recipient (to_user_id) and fanned out overlist_room_member_ids, so it reaches members wherever they are in the app. The client drains it into a dismissible banner offering Join, whose link carries?huddle=1sovoice.jsjoins on arrival. Join is one click from anywhere, rather than a trip to the room to hunt for the bar.Both of the ticket's blockers are sidestepped, not solved
This is the part worth reviewing, because it is a deliberate departure from the plan.
The ringing slot is untouched. LC-611 said
RingingSlothad to be generalized to N callees. It does not, because the premise does not hold: that slot arbitrates DM glare - two peers inviting each other at once, where exactly one must win - and a huddle has no such contest. Two people starting a huddle in the same room simultaneously both land in the samevoice_roomsentry and are, by definition, already in a call together. Generalizing the slot would have meant inventing a loser for a race with no stakes.It would also have meant editing
try_start_ringing,RingingResult, and the TTL, all of which the 1:1 DM path depends on and none of which has test coverage today (LC-614) - while LC-596 AC4 requires that path to keep its current behaviour. The safest change to untested call signaling was no change to it. Nothing incall.js,relay_call_signal, orHub::ringingis modified.#lc-call-rootis untouched too. A room huddle is better served by a banner than by a modal ring seizing the page, and reusing a shell built around one remote video would have forced the N-peer rework for no benefit here. The ticket's concern about maintaining two ring UIs still stands, but a banner is not a second ring UI - it is a notification.Structure
The fan-out and its policy live in a new
server/src/huddle_ring.rs, not inroutes::ws. Who gets rung and when is domain logic, andws.rsis already very large.Three guards, each proven load-bearing
Each was deleted in turn; each fails exactly its own test and no others.
except_mentionssuppresses as well. A huddle starting is not a mention - nobody addressed you by name - so "interrupt me only for mentions" has to mean it.allows_huddle_ring()is a separate predicate fromallows_unread_bump()despite matching it today, because if the two ever diverge it should be in the direction of ringing less.Ignore is client-side only: it drops the banner and remembers the room for that page. Nothing reaches the server, so nobody learns you declined, per the ticket.
The banner text is built with
textContent, neverinnerHTML- room names and display names are user-controlled.Full suite green (182 test binaries, up from 181),
just checkclean, en/es Fluent symmetry holds.Not in this change
Targeted invites (AC2). Ringing the room is the part that removes the discovery problem; inviting named people is a different interaction that probably wants a picker and may want a stronger affordance than a banner. Worth its own PR rather than bolting a member picker onto this one.
AC5's "join via the ring" test. The fan-out and all three policy guards are covered server-side. The click-to-join path is
?huddle=1invoice.js, which is browser behaviour with no test harness in this repo - there are no JS tests. Flagging rather than pretending it is covered.🤖 Generated with Claude Code
https://claude.ai/code/session_01FhDVMViNHqUbwmfe2aXTv5