feat(polls): "See who voted" — per-answer voter list

The poll card showed vote counts but never who voted, even though
computePollState already parses a sender for every response. Surface it:

- tallyResponses now also returns voters: Map<answerId, senderId[]>, built in the
  same latest-response-per-sender loop as the counts, so voters can never disagree
  with the numbers (voters.get(id).length === counts.get(id)). +5 unit tests.
- PollContent adds a "Show who voted" toggle, shown only when results are visible
  (disclosed live, or undisclosed after end — so a secret ballot stays secret).
  When on, each answer lists its voters' display names (getMemberName), rendered
  as a sibling of the answer button so the radiogroup keyboard model is untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 21:56:50 -04:00
co-authored by Claude Opus 4.8
parent 5cce94edba
commit 1aaea09fc9
4 changed files with 91 additions and 5 deletions
+8 -1
View File
@@ -102,6 +102,9 @@ export type PollResponse = {
export type PollTally = {
/** answerId → number of voters who selected it. */
counts: Map<string, number>;
/** answerId → the sender ids who selected it (latest response per sender). Always
* consistent with `counts` — `voters.get(id).length === counts.get(id)`. */
voters: Map<string, string[]>;
/** answer ids the current user selected (from their latest response). */
myVotes: Set<string>;
/** number of distinct voters counted. */
@@ -126,6 +129,7 @@ export function tallyResponses(responses: PollResponse[], myUserId: string): Pol
}
const counts = new Map<string, number>();
const voters = new Map<string, string[]>();
const myVotes = new Set<string>();
let total = 0;
for (const [sender, r] of latestBySender) {
@@ -133,11 +137,14 @@ export function tallyResponses(responses: PollResponse[], myUserId: string): Pol
total += 1;
for (const id of r.answerIds) {
counts.set(id, (counts.get(id) ?? 0) + 1);
const list = voters.get(id);
if (list) list.push(sender);
else voters.set(id, [sender]);
if (sender === myUserId) myVotes.add(id);
}
}
return { counts, myVotes, total };
return { counts, voters, myVotes, total };
}
/**