feat(ui): who-reacted tooltip on reaction pills (LC-266) #311

Merged
longjacksonle merged 1 commit from feat/lc-266-who-reacted-tooltip into main 2026-06-14 05:26:45 +02:00

What

Hovering or focusing a reaction pill now reveals who reacted with that emoji (LC-266). Reactions previously showed only {emoji} {count}; now the pill carries a native title listing the reactors' display names - accessible by default, instant, no popover JS, no extra route.

How

  • The aggregate reaction queries (list_reactions / list_room_reactions) gain group_concat(user_id), so each Reaction carries its reactor_ids (users react at most once per emoji, so no dedupe).
  • A shared build_reactor_titles(state, &[Reaction]) helper resolves every distinct reactor across a render in ONE display_names_for_ids query, then comma-joins each pill's reactor names (display_name else username, capped at 20 with a +N overflow). Best-effort: a label-lookup error yields empty titles (no tooltip), never a 500.
  • The reaction bar is shared by all 8 render paths (room page bulk render, single-message rebuild, DM page, the POST .../reactions/{emoji} toggle, and the two WS reaction/edit re-renders), so the title threads through ReactionView::new - the tooltip stays correct after a reaction is added/removed and for other viewers over the WS.
  • reaction_bar.html renders title="{{ r.reactors_title }}" only when non-empty; Askama HTML-escapes the names in the attribute (no injection via display names).

The room-page path resolves all reactor names for the page in one query; single-message paths resolve that message's reactors. No new route, DB table, env var, or i18n string (a plain name list). Not operator-visible.

Tests

routes_reactions_authz::reaction_pill_titles_who_reacted reacts as a named user and asserts the pill title carries their name. just test and just test-saas pass (no Reaction-equality or LC-77 fixture regressions; fixtures carry no reactions).

QA note

Mostly server-rendered and test-covered, so low-risk. Worth a quick manual check: react to a message and hover the pill (shows your name); have a second user also react and confirm both names appear; remove a reaction and confirm the name drops; check a custom-emoji reaction also titles correctly.

## What Hovering or focusing a reaction pill now reveals who reacted with that emoji (LC-266). Reactions previously showed only `{emoji} {count}`; now the pill carries a native `title` listing the reactors' display names - accessible by default, instant, no popover JS, no extra route. ## How - The aggregate reaction queries (`list_reactions` / `list_room_reactions`) gain `group_concat(user_id)`, so each `Reaction` carries its `reactor_ids` (users react at most once per emoji, so no dedupe). - A shared `build_reactor_titles(state, &[Reaction])` helper resolves every distinct reactor across a render in ONE `display_names_for_ids` query, then comma-joins each pill's reactor names (display_name else username, capped at 20 with a `+N` overflow). Best-effort: a label-lookup error yields empty titles (no tooltip), never a 500. - The reaction bar is shared by all 8 render paths (room page bulk render, single-message rebuild, DM page, the `POST .../reactions/{emoji}` toggle, and the two WS reaction/edit re-renders), so the title threads through `ReactionView::new` - the tooltip stays correct after a reaction is added/removed and for other viewers over the WS. - `reaction_bar.html` renders `title="{{ r.reactors_title }}"` only when non-empty; Askama HTML-escapes the names in the attribute (no injection via display names). The room-page path resolves all reactor names for the page in one query; single-message paths resolve that message's reactors. No new route, DB table, env var, or i18n string (a plain name list). Not operator-visible. ## Tests `routes_reactions_authz::reaction_pill_titles_who_reacted` reacts as a named user and asserts the pill `title` carries their name. `just test` and `just test-saas` pass (no `Reaction`-equality or LC-77 fixture regressions; fixtures carry no reactions). ## QA note Mostly server-rendered and test-covered, so low-risk. Worth a quick manual check: react to a message and hover the pill (shows your name); have a second user also react and confirm both names appear; remove a reaction and confirm the name drops; check a custom-emoji reaction also titles correctly.
feat(ui): who-reacted tooltip on reaction pills (LC-266)
Some checks failed
check-secrets / Nosey parker (push) Successful in 5s
check-secrets / TruffleHog (push) Successful in 5s
check-secrets / Kingfisher (push) Successful in 5s
check-secrets / Nosey parker (pull_request) Successful in 4s
check-secrets / Kingfisher (pull_request) Successful in 5s
check-secrets / TruffleHog (pull_request) Successful in 5s
Check / clippy + fmt + tests (pull_request) Failing after 16s
Create release / Create release from merged PR (pull_request) Has been skipped
d495540a43
Hovering or focusing a reaction pill now reveals who reacted with that emoji, as a native title attribute (accessible, zero-latency, no popover JS, no extra route).

The aggregate reaction queries gain group_concat(user_id) so each Reaction carries its reactor ids; a shared build_reactor_titles helper resolves every distinct reactor across a render in ONE display_names_for_ids query, then comma-joins each pill's reactor display names (display_name else username, capped at 20 with a +N overflow). It is best-effort: a label-lookup error yields empty titles (no tooltip) rather than a 500. Because the reaction bar is shared by all 8 render paths (room page, single-message rebuild, DM page, the toggle response, and the WS reaction/edit re-renders), the title threads through ReactionView::new so the tooltip stays correct after a reaction is added or removed. Names are HTML-escaped by Askama in the attribute.

Strings need no i18n (a plain name list). Test asserts the pill titles the reactor's name.

#LC-266
#LC-267

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch feat/lc-266-who-reacted-tooltip 2026-06-14 05:26:45 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!311
No description provided.