Files
buzz/mobile/test/features
Tom Brow ff284d00d3 mobile: stop typing prune ticks from rebuilding channel pages (#8695)
### Summary

Typing in the mobile composer felt slower than a native iOS text field.
A device trace on an iPhone 12 mini showed the cause: while anyone in
the channel is typing, the whole channel page rebuilds once per second,
including the visible message list.

- `ChannelTypingNotifier._prune()` runs on a 1 s timer and assigned a
new list to `state` on every tick, even when nothing had changed.
- `_ResolvedChannelDetailPage` watched the whole list and only filtered
out the current user afterwards, so every tick rebuilt the page and its
`PositionedList` (12 to 35 ms per frame, 8 to 19 ms for the message list
alone).
- Your own typing indicator echoes back from the relay, so the timer
kept running the whole time you typed.

In a 30-key trace, 33 of the 35 frames over 12 ms were these rebuilds,
1.00 s apart.

### Fix

- `_prune()` publishes only when the visible entries change.
`TypingEntry` now compares by value.
- The channel and thread pages watch `visibleTypingProvider`, a
`VisibleTyping` value that already excludes the current user and applies
the page's scope. Your own echo refreshes its expiry every 3 s, which
used to rebuild the page. Now it doesn't.

Other `channelTypingProvider` consumers are unchanged.

### Related issue

None found.

### Testing

- Provider tests: a typing entry held across five prune ticks notifies
listeners once, and the pages' shared `visibleTypingProvider` is not
notified by the user's own refreshed echo but is by another user.
Breaking either guard fails its test.
- `just mobile-check` and `just mobile-test` pass at 8b8a16845.

- Device trace on an iPhone 12 mini: 30 keys typed into a channel
composer, Profile builds with build tracing on both sides. Control is
6661d594a, which has the same page code as main.

  | | control | this PR (5ff754131) |
  |---|---|---|
| Channel page rebuilds while typing | about 1 per second (44 in 40 s) |
0 |
| Largest UI build per key, median / p90 | 4.9 / 29.3 ms | 1.0 / 1.4 ms
|
  | Keys with a build over 16.7 ms | 9/30 | 0/30 |
| Frames with UI work over 12 ms | 35 (33 page rebuilds) | 1 (a
semantics update from the test driver) |
  | ComposeBar frame, median | 10.2 ms | 3.0 ms |

The test driver turns on accessibility, so semantics runs on every frame
in both traces. About 1 key in 4 still has a slow frame with about 1 ms
of build and 2 ms of raster. A Time Profiler capture put that time in
the driver's accessibility queries on the main thread (52% of
main-thread samples while typing, none before the first automated tap),
so it is a test-harness artifact rather than app work.

- Dictation on an iPhone SE (2nd gen): a 15 s paragraph dictated into a
channel composer, Profile builds with build tracing.

  | | main | this PR (5ff754131) |
  |---|---|---|
| UI frame, median / p99 / max | 0.9 / 10.4 / 26.1 ms | 0.9 / 5.3 / 10.8
ms |
  | Frames over 16.7 ms | 10 | 0 |
| Channel page rebuilds | 26 (once a second) | 6 (composer growth, none
over budget) |

Each dictation update costs about 1 ms. The caret stayed at the end and
nothing flickered on either build.

buzz-review-completed

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

---------

Signed-off-by: Tom Brow <tomb@block.xyz>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 12:14:55 -07:00
..