Files
buzz/mobile/lib/features/channels
Tom Brow c97c85d8fa perf(mobile): load only the directory when search opens (#9466)
Opening search on a list that is already loaded refreshed the whole
channel list to get the open-channel directory: the membership rosters
(kind 39002), member channel metadata, hidden DMs, Huddle starts, DM
names and the latest messages of every joined channel, then the
directory pages. Only the directory was what search needed; everything
else was already in the list and kept current by live updates.

Search now reads only the directory pages and merges them into the
current list: every channel you are not a member of is rebuilt from the
fresh directory (latest revision per channel, open and non-DM only, the
same rules as a full refresh), and joined channels keep their entries
untouched. When the list has not loaded yet, the full refresh runs as
before.

Races:
- A directory-only load is fenced by scope alone (community, identity,
disposal), not by the list-refresh generation, so it neither retires an
in-flight membership refresh nor is retired by one. A community or
identity switch retires it: its response is discarded and it no longer
owns its loading status, so returning to the first community (before or
after the old request ends) lets search load the directory again.
- Directory loads are ordered among themselves: only the newest one
writes the directory and its load status, so an older full refresh
cannot overwrite a newer search load and vice versa.
- A list refresh that merged an older directory and then installs after
a search load landed re-merges the newer directory, so the open channel
search just found does not disappear.
- A list refresh no longer resets a live directory-only load's status to
idle (that reset existed because a refresh used to discard the in-flight
directory, which it no longer does for this kind of load). A load
retired by a scope switch is not live, so the reset still applies to the
status it left behind.

## Results

iPhone 12 mini, profile build, Builderlab account. Typing a query into
search on a loaded list, more than 30 seconds after the last directory
load (two runs each, same numbers both times):

| | HTTP per search | What it read |
| --- | --- | --- |
| main | 3.59 MB | rosters 1.53 MB, channel metadata and directory 1.26
MB, latest messages 0.80 MB |
| This change | 1.26 MB | directory pages only |

Unjoined open channels still appear in results with their Open badge.

## Regressions

The tradeoff we accept: on a search open, joined channels' member
counts, unread badges and latest-message times are not re-read; they
show what the last full refresh found, as updated by live events since.
Live join and leave notices, metadata changes and new messages still
apply as they arrive, and the next ordinary refresh (launch, reconnect,
resume, pull to refresh) re-reads everything. The case that differs is a
change that live delivery missed on a healthy connection (for example a
roster change made while a live chunk was briefly refused): before,
opening search happened to repair it; now the next ordinary refresh
does.

Nothing else changes: open channels are listed under the same visibility
rules, a failed directory load still reports its error to search and
keeps the cached discovery, and the first load is unchanged.

## Testing

- Full mobile suite passes; `flutter analyze` clean.
- New tests: a search load on a loaded list issues only directory
queries (no roster, member metadata, hidden-DM, Huddle or message
reads), lists the new open channel, drops one that became private and
keeps the joined channel object unchanged; a membership refresh during
the load is kept and does not reset its status; a list refresh that
merged the directory before a search load landed keeps the newer
directory; a community switch discards the response; returning to the
first community, both while the old request is in flight and after it
ended, loads the directory again.
- Automated device run on the iPhone 12 mini: launch, two search
sessions typing a query, results list joined and unjoined open channels,
close and reopen search. Rerun on f216dd6afc (after the scope-switch
fix) with the same results: 1.26 MB HTTP per query, unjoined open
channels marked Open.

---------

Signed-off-by: Tom Brow <tomb@squareup.com>
Co-authored-by: Tom Brow <tomb@squareup.com>
Co-authored-by: studio <dc2fc481343672df31a689840c09299972e704dac592e0da6f18486646a7ee3e@buzz.block.builderlab.xyz>
2026-11-13 14:40:27 -08:00
..