Files
buzz/mobile/ios/RunnerTests
Tom Brow 4a101752fb fix(mobile): never show emoji shortcodes in reaction pills (#9411)
## Context

A reaction slot should never show an emoji shortcode as text. Today it
does in two cases:

- An agent's reaction is a kind 7 event with content `:shortcode:` and
no NIP-30 `emoji` tag, so the pill printed `:approve:` in tiny text
instead of the community emoji.
- A custom emoji whose image fails to load fell back to the shortcode
text.

## Behavior

- A tagless `:shortcode:` reaction resolves against the community
custom-emoji palette and renders that image. An `emoji` tag on the event
still wins.
- A shortcode with no palette entry, or whose image fails to load, shows
a neutral placeholder glyph (the muted sparkles icon) instead of text.
The shortcode survives only as the semantics label.
- This applies to every reaction slot: the timeline pills, the combined
reaction tray, the Flutter reaction detail sheet, and the native iOS
reactions sheet (which is told which shortcodes have no image, so
`NativeMessageGlyph` draws the placeholder). System-message rows reuse
the same reaction row.
- Unicode emoji are unchanged.

The icon is the existing `BuzzIcons.sparkles`. Pareto note: the palette
lookup is the shared `reactionEmojiUrl` helper, so there is no new
resolver, and the pill only watches the palette when the reaction has no
tag.

### Validation

- `just mobile-check mobile-test`: format and analyze clean; full suite
passes (4773 passed, 74 skipped).
- New widget tests (journeys, in
`reaction_row_test/shortcode_tests.dart`): a tagless known shortcode
renders its image; an unknown shortcode and a failed image render the
placeholder and never the text; unicode is unchanged; the native sheet
payload marks image-less shortcodes. Reverting the fix makes both new
journeys fail.
- iOS: added a Swift unit test for the native glyph placeholder
(`NativeMessageImageLoaderTests`); not run locally, the app run below
exercises the same code.
- Automated app run on a cloned iOS simulator with a fixture entry point
(mocked media, synthetic palette): confirmed the pills before and after,
and opened the reaction sheet from a long-press to confirm the
placeholder renders there too. The clone was deleted afterwards.

## Before / after

Captures use synthetic shortcodes and a synthetic palette.

<img width="300" alt="reaction-pills-before"
src="https://raw.githubusercontent.com/block/buzz/75b22a00d92df2db389ef90d35d476b285b60374/pr-9411--reaction-pills-before.png">

<img width="300" alt="reaction-pills-after"
src="https://raw.githubusercontent.com/block/buzz/75b22a00d92df2db389ef90d35d476b285b60374/pr-9411--reaction-pills-after.png">

Reaction sheet after:

<img width="300" alt="reaction-sheet-after"
src="https://raw.githubusercontent.com/block/buzz/75b22a00d92df2db389ef90d35d476b285b60374/pr-9411--reaction-sheet-after.png">

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: Tom Brow <tomb@squareup.com>
Co-authored-by: Tom Brow <tomb@squareup.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: studio <dc2fc481343672df31a689840c09299972e704dac592e0da6f18486646a7ee3e@buzz.block.builderlab.xyz>
2026-11-12 13:38:37 -08:00
..