block / block/buzz

iOS: mention autocomplete list grows under the finger — late relay results shift rows up and steal the tap

Open
#6,300 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

## Summary

On iOS, the mention autocomplete popover appears with the channel-member matches first, and then ~0.3–1s later grows as debounced relay search results land. Because the popover is **bottom-anchored above the composer**, appending rows pushes the existing rows *upward* — so a row that the user is already reaching for moves out from under their finger, and a newly arrived candidate takes over the screen position it just vacated.

Reported case: typing `@bu` in a channel shows **Buzzpert** (a channel member) alone. While tapping it, **Bumble** and other `bu*` agents arrive, Buzzpert shifts up, and the tap lands on Bumble — the wrong agent gets mentioned and the message goes to the wrong recipient.

This is not a ranking bug — the ranking is stable and correct. It is a layout + timing interaction: a tappable surface mutates under the user's finger with no settle guard.

## Steps to reproduce

1. Open a channel that has an agent as a member whose name shares a prefix with agents outside the channel (e.g. `Buzzpert` in-channel, `Bumble` on the relay).
2. Type `@bu` in the composer.
3. Immediately tap the first suggestion.

Expected: the tap selects what was under the finger when the touch began.
Actual: after ~0.3–1s the list grows, the rows shift up, and the tap selects a different candidate.

## Mechanism

Two independent pieces combine:

**1. The candidate list arrives in two waves.**
`mobile/lib/features/channels/mentions/mention_candidates_provider.dart`
- `mentionUserSearchProvider` (`:22-53`) is debounced by `_mentionSearchDebounce = 250ms` (`:15`, `:28`) and then does a relay round trip (`session.queryRelay`, `:35`).
- `mentionCandidatesProvider` (`:70-113`) folds those late results in at `:97-99` (`.asData?.value ?? const []`), so the first frame renders members only, and a later frame renders members + global search results.

**2. The popover is bottom-anchored, so growth moves existing rows.**
`mobile/lib/features/channels/compose_bar/dock.dart:131` positions the overlay panel with `bottom: layoutInfo.overlaySize.height - overlayAnchorY` — the bottom edge is pinned just above the composer and the panel expands upward. `mobile/lib/features/channels/compose_bar/suggestions.dart:29-33` is a `shrinkWrap` `ListView` inside a `maxHeight: 240` box, so height tracks item count directly.

Ranking (`mention_ranking.dart:36-41`) puts channel members in group 0 and non-member agents in group 3, i.e. late-arriving outsiders append at the **bottom** of the list — which, with a fixed bottom edge, is precisely the screen position the previously-last row occupied.

There is no hit-test guard: nothing suppresses or re-targets a tap that begins before the list changes.

## Notes on scope

Reporting the problem, not prescribing the fix. Some shapes that would address it, for whatever the mobile composer owners think fits:

- Reserve the layout: keep the panel's height stable while a search is in flight (skeleton rows or a fixed minimum), so arrival does not move anything.
- Ignore taps for a short window after the item list changes, or resolve a tap against the candidate identity captured at touch-down rather than the index at touch-up.
- Anchor growth downward, or otherwise keep the first row's screen position invariant.

The general principle: a surface the user can tap should not reflow under them, and if it must, the tap should resolve to what they aimed at.

## Environment

- iOS, App Store build **0.10.0** (released 2026-08-13).
- Code path unchanged in `main` @ `93114c9c` and in `mobile-v0.12.0-rc.1`.

## Related but different

- #1660 (closed) — desktop, stale suggestion committed on Tab. Same family (async list vs. commit), different surface and long fixed.
- #5542, #3840 — *which* candidates appear. This issue is about *when* they appear and what that does to the layout; the candidate set here is correct.

Contributor guide

Open the contributing guide

Research direction

Start with mobile/lib/features/channels/mentions/mention_candidates_provider.dart, then inspect compose_bar/dock.dart and suggestions.dart to reproduce how late relay results change the popover geometry. Check mention_ranking.dart for ordering and run the @bu reproduction on iOS. Done means a suggestion does not move under an active touch, and the tap selects the intended candidate after late results arrive.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart, flutter, ios
Domain
frontend, mobile
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.