A poll in Amethyst currently ends its life as a percentage bar and four avatars in the feed. This proposes a dedicated results screen that answers the two questions people actually ask — how many votes each answer got, and who voted for what — shared between Android and Desktop.
The inline card is deliberately compact, and that compactness is the whole problem: no vote counts anywhere, at most four voter avatars, and no route to the fifth. Desktop already grew a voter popup to work around it, which is the demand showing up as a divergence.
The +171 chip is a dead end — it counts people you cannot reach. Percentages appear
without their counts, and on a multiple-choice poll the denominator is wrong
(PollResponsesCache.kt:52).
One change to the card itself: the vote count beside each percentage. The count under the options and the avatar stack both become the entry point to the full results screen — so the +171 stops being a dead end.
Summary above detail: totals, then per-option breakdown, then every voter with the option they chose on the same row. Try the option chips — they filter the list below.
The tally is already a live StateFlow — new votes animate in without asking.
The refresh action re-runs the backfill against the poll's own relays, which is the only
part that can be stale.
Voters is distinct pubkeys; selections is how many boxes were ticked. They are identical on a single-choice poll and diverge on multiple choice — which is exactly where today's percentages go wrong (§03).
Each row gains N of M voters. The winner keeps the existing green treatment
from RenderClosedItem so the screen reads as the card it came
from, and the bar keeps the same 800 ms tween.
Desktop already allows re-voting; Android does not. This strip makes the behaviour the same on both, and puts your vote where you don't have to hunt the list for yourself.
Option chips scope the voter list only — the bars above stay put, so the totals can't appear to change while you browse. An audience filter, a sort control and voter search are deliberately held back; the list is ordered you → follows → rest, and it's not yet clear which of the three people would actually reach for.
UserLine, unmodified
Not a new row, and not an edited one: the results screen calls the app's existing
UserLine and passes the vote as its
trailingContent, a parameter it already has. So the second line is the
NIP-05 identifier the way the app draws it — local part, verified mark, domain,
no @ — with a root identifier showing the domain alone (row eight) and the
npub appearing only where there is no NIP-05 to show (row six).
A NIP-45 count against the poll's relays gives the "~412" — so a partial
tally can admit it instead of presenting itself as final. The second line accounts for
every response that was dropped and why.
Same poll, same votes, two readings. 240 people voted and ticked 456 boxes between them. This is the one decision that blocks the tally work, because it changes the numbers on the existing feed card too.
Every one of these is a moment where a number could look like a bug unless the screen says what happened.
Desktop navigates by DeckColumnType, not routes, so results open as a
column beside the feed. The existing voter popup stays as the quick peek and grows a
See all voters footer that opens this column.
The results page would inherit all of these, so they land first. The four tally bugs share one
root cause — ResponseTally never sees the
PollEvent, so it cannot know the poll's type, its valid option codes,
or its window. The fifth is separate: Android asks the wrong relays for votes. Both fixes
improve the feed card on their own.
| What breaks | Impact | Consequence | Where |
|---|---|---|---|
| Multiple-choice denominator | wrong number | A voter who picks three options counts three times. Desktop's footer and its own bars already disagree about this. | PollResponsesCache.kt:52 |
Every response tag counted |
spec | NIP-88 keeps only the first tag on a single-choice poll. One malformed vote lands in every bucket. | PollResponsesCache.kt:129 |
| Votes after the deadline | spec | Only createdAt is compared, so a late vote beats a valid earlier one and closed polls keep moving. |
Note.kt:1685 |
| Unknown option codes | wrong number | A code that isn't in the poll creates a phantom bucket that still drags every real percentage down. | PollResponsesCache.kt:129 |
| Poll relays never queried | missing data | Android reads votes from the author's inbox relays, not the relays the poll publishes votes to — so its tallies are short. Desktop patched this per-card. | FilterRepliesAndReactionsToNotes.kt:89 |
The first two phases are bug fixes wearing a feature's clothes — worth shipping even if the screen never gets built.
PollTallyPolicy in commons + tests. Fixes all four numbers above.fetchAllPages backfill.