fix(reactions): dedup unicode reaction so it shows once#129
Conversation
The emoji picker hands toggleReaction a unicode emoji's shortcode name (e.g. `hamburger`), so the optimistic pill was keyed by the shortcode. The gateway's reaction.add echo arrives keyed by the glyph (`🍔`) — what the server stored — so the two never matched and the echo spawned a second pill instead of merging into the optimistic one. Canonicalize unicode reactions to their glyph in the dedup key via a new _emojiKey helper, used by toggleReaction's lookup, applyReaction's match-or-create (and stored on the reaction), and _emojiName. Now the optimistic pill and its gateway echo collapse to one, and the existing includesMe guard correctly blocks the double count. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
After the PR introduced _emojiKey (which resolves unicode emoji to their glyph), _emojiName(r) now always returns a canonical glyph for unicode reactions. clearReactionEmoji was left comparing that canonical glyph against the raw emojiName from the gateway, which can arrive as a shortcode (e.g. `hamburger`). The filter then matched nothing and reactions were never cleared. Fix: canonicalise the incoming name through _emojiKey before the comparison, consistent with how applyReaction and toggleReaction already handle their inputs. Tests added in accord_messages_reactions_test.dart covering: - shortcode optimistic pill + glyph gateway echo collapse to one - existing pill tap, REST-loaded shortcode removed via glyph - custom emoji still keyed by name - clearReactionEmoji with shortcode / glyph / cross-emoji isolation - count decrement and includesMe guarding https://claude.ai/code/session_01AoJpr7zMvJzXkTrgoxxRuJ
Review findings — ranked by severity🔴 HIGH — Bug:
|
… the match The REST-loaded-shortcode remove test seeded the pill with the default includesMe:false, so applyReaction's own-remove guard (isOwn && !includesMe) bailed before the canonical name match it meant to verify. Seed me:true so the toggle-off proceeds and the glyph→shortcode match drives the count to zero. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
Adding a unicode emoji reaction from the emoji picker showed up twice (two pills). This fixes it to show a single pill at the correct count.
Why
The emoji picker passes
toggleReactiona unicode emoji's shortcode name (e.g.hamburger), so the optimistic pill was stored/keyed by that shortcode. The gateway'sreaction.addecho arrives keyed by the glyph (🍔) — what the server actually stored. The dedup key (_emojiName) returned the shortcode for the optimistic pill and the glyph for the echo, so they never matched: the echo created a second pill instead of merging, and theincludesMedouble-count guard never fired.Tapping an existing pill didn't double, because that path already passes the glyph — only the picker path passed a shortcode.
How
Introduce a canonical
_emojiKey(name, id)helper that resolves unicode emoji to their glyph (custom emoji still key on name, with the id carried separately). All three reaction touchpoints now route through it:toggleReaction— the "do I already have this reaction?" lookupapplyReaction— the match-or-create index, and it now stores the canonical key as the reaction's name_emojiName— delegates to_emojiKeySo
hamburger(optimistic) and🍔(gateway echo) both normalize to🍔and collapse to one pill.Notes for reviewers
resolveEmojiGlyph, so storing the glyph as the reaction name renders unchanged.flutter analyzelocally (the toolchain requires macOS 14+; this machine is on 13.0) — relying on CI. The change is self-contained;resolveEmojiGlyph/parseEmojiTokenwere already imported and used in the file.🤖 Generated with Claude Code