Say who is in a group, out of the list the connection already keeps - #128
Conversation
A group's header drew its name and nothing under it. The one thing this side had was `Chat::participants`, which is not a roster: it fills as senders are *seen*, so counting it told a fifty-person group it had one member, and saying nothing was the honest answer left. The membership list lives on the connection. The library keeps one per group because the send path needs it, patches it as add/remove notifications arrive, and invalidates it when the server calls its snapshot stale — so the front end now asks the daemon for it (`ClientRequest::GroupMembers`, answered with a `GroupRoster`, protocol 27), and the session reads it through `Groups::query_info`: cached, free on a repeat, and a miss sends the participant hash so an unchanged group is answered `not-modified` rather than downloaded. The line names this account as "You" first, then whoever the address book or a push name has named, and counts the rest — a member nobody has ever named is counted rather than spelled out, since a LID-addressed group would otherwise read "Unknown contact" six times over. An all-stranger group therefore reads "50 members", which is the answer `participants` could never give. Asked on every open rather than once, because that is what keeps it current after somebody joins; what is already known stays on screen until the new answer lands. Closes #126. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EQsp672XkWkr5ypjZwsvtj
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Warning Review limit reachedNext included review available in 35 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Team Run ID: 📒 Files selected for processing (17)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Both branches added an entry to `docs/gotchas.md` at the same place and nothing else overlapped. Kept both, with the tray's unread icon beside the tray reasoning it continues and the roster entry after it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EQsp672XkWkr5ypjZwsvtj
Closes #126.
What was wrong
A group's header drew its name and nothing under it. WhatsApp puts the members there; here the line was blank, and deliberately so — the comment in
chat_header.rssaid why:That is the whole of the bug. The map a front end holds per chat answers "who has spoken here lately", and the header had nothing else to read.
Where the answer was
On the connection, and it was already being maintained: the library keeps a participant list per group because the send path needs one, patches it as add/remove notifications arrive (
handlers/notification/groups.rs), and invalidates it when the server calls its snapshot stale. So this adds no polling and no new source of truth — it asks the side that has one.ClientRequest::GroupMembers, answered withDaemonMessage::GroupMembers { id, roster }. Addressed to the client that asked rather than published, like a page of history: it is one line in one window's header.transport.rs.Groups::query_info, notget_metadata. The first is the cached, send-oriented view: a group opened twice costs nothing the second time, and a miss sends the participant phash, so an unchanged group is answerednot-modifiedrather than downloaded. The second has no cache in front of it at all, and what it adds — subject, description, admin roles — is drawn nowhere yet.Action::needs_network), because the common answer is a read of a list already held: gating it would empty the line for the length of a blip and put it back only when the conversation was opened again. A query that does reach the wire fails on its own and says asking again may work.What the line says
Names come from the same
NameBooka bubble's sender comes from, so nobody is "Ana" over their message and a number in the line above it. Then:You, Ana, Bruno, Carla, Davi, Elena and 42 others.50 members— which is the answerparticipantscould never give, and is the fact the names were standing in for.Typing still outranks it: who is typing is a fact about now, and a roster changes when somebody joins.
Asked on every open
Rather than once per session, because that is what keeps the line current after a join or a leave — and it normally costs nothing, per the cache above. A request in flight blocks a second one, and whatever is already known stays on screen until the new answer lands, so re-opening a conversation never blanks the line.
MembersLostreleases the guard on a refusal so the next open asks again, exactly as a lost page does.Rosters are cleared with the rest of the account state on logout: group membership is the departing account's view of who is where.
Verification
cargo fmt --all -- --checkandcargo clippy --workspace --all-targets --all-features -- -D warningsclean;cargo test --workspace --all-featuresgreen (41 test binaries, no failures).New tests:
chat_header: the line for a small group (You, Ana, Bruno), the cut-off with the count, the all-stranger group (50 members, and1 member/1 otherfor the singular), an empty roster drawing nothing, and the existing subtitle cases carried over.ipc:a_group_roster_survives_both_directions_of_the_wirepins the answer's exact bytes, absences included — a member nobody has named carries nonamekey and everybody but the reader carries nois_self— plus the request's bytes in the existing literal-JSON table.Not run here: the wasm build (nightly plus
-Z build-std). The changed GUI code is platform-agnostic and names nothing per target.Not in scope
The rest of what
get_metadatacarries — description, admin badges, a members screen you can open. Those want a screen rather than a line, and each needs the uncached query this deliberately avoids.🤖 Generated with Claude Code
https://claude.ai/code/session_01EQsp672XkWkr5ypjZwsvtj
Generated by Claude Code
Summary by cubic
Closes #126. Group chat headers now show who is in the group instead of leaving the subtitle blank when no one is typing; typing indicators still take priority.
GroupRosterthroughClientRequest::GroupMembers, using cachedGroups::query_infodata without adding polling.NameBook, and counts unnamed or remaining members; empty rosters still produce no subtitle.Migration
Written for commit 69d5254. Summary will update on new commits.