Skip to content

feat(chat): suggest @mentions from the server's pool - #1647

Merged
bmc08gt merged 9 commits into
code/cashfrom
feat/mention-suggestions
Oct 2, 2026
Merged

bmc08gt merged 9 commits into
code/cashfrom
feat/mention-suggestions

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Adds the @-mention picker to the group-chat composer, backed by the server's ranked pool from Chat.GetMentionSuggestions (flipcash2-client-protocol 0.15.0, code-payments/flipcash2-client-protocol#22).

  • ChatController wraps the RPC. ServerRosterSearchSource fetches the pool once per composing session, filters it locally as the user types in the server's order, and moves the sender of each new message to the front through MentionSuggestionPool, fed from EventStreamDelegate.
  • The only client-side narrowing is the typed query. There's no membership check, so someone the server offers after they left the group still shows.
  • A failed fetch, DENIED or NOT_FOUND returns no suggestions. There's no local fallback, so the user can still type a handle by hand.
  • RosterSearchSource stays as the seam, with the server pool as its only implementation. MentionText carries the NFKD/lowercase folding used for local filtering.
  • The picker shows up to 4 rows, 3 with the reply strip open, and scrolls past that. The card, the input row and the transcript move together as the list opens, narrows, or gives a row to the reply strip.

This doesn't depend on roster sync. Chat.GetRoster stays private, #1622 is closed, and no Room schema or migration ships here.

@bmc08gt bmc08gt self-assigned this Oct 1, 2026
@github-actions github-actions Bot added type: feature New functionality area: network gRPC, connectivity, API, exchange rates area: build-system Gradle, convention plugins, build-logic and removed type: feature New functionality labels Oct 1, 2026
@bmc08gt
bmc08gt force-pushed the feat/mention-suggestions branch from 0e7cfd6 to 9e8723c Compare October 1, 2026 18:34
@bmc08gt
bmc08gt changed the base branch from feat/roster-search to code/cash October 1, 2026 18:34
@bmc08gt
bmc08gt marked this pull request as ready for review October 1, 2026 18:35
…ypes

The composer's reply strip becomes one slot in an ordered accessory stack:
mention suggestions, then the reply strip, then the input row. Both cards use
the reply surface's enter and exit spring.

The picker opens on the whitespace-free word ending at the cursor when it
starts with "@" at the start of the text or after whitespace, so "a@b" never
opens it. It searches RosterSearchSource with that word, cancelling the last
search when the word changes, and lists only members with a username. It is
for groups only and closes on whitespace, a moved cursor, a deleted "@", a
send or a pick. A pick replaces the word with "@username " as plain text.
The first page of the roster is refreshed once per screen visit, the first
time the picker opens.

The list shows up to 4 rows, 3 with a reply strip open, and 2 when that many
would leave the transcript under 120dp. Row values come from the prototype;
the visual pass against the design is still to do.
… loads

The first-page roster refresh runs in the background the first time the
picker opens, so its results reached the list only on the next keystroke.
When it succeeds, the open word now searches again, so a member who joined
since the last read shows up without more typing. A refresh that lands
after the list closed leaves it closed.
Rows take the design's metrics (mention row set, node 10633:68; spec, node
10662:64911): 14dp side padding and a 10dp gap, dividers inset 56dp so they
start under the name, the display name at 12/16 bold in the member's own
colour from the palette the reply strip uses for a quoted author, and the
username at 14/18 medium in #EBEBF5 at 60%.

The card keeps the reply strip's radius and hairline so the two stacked
cards match, and avatars without a picture keep the app's shared initials
fallback.
Picking a member before an existing space or newline added a second space. The mention now takes no trailing space when whitespace already follows the word, and the cursor lands after that whitespace. At the end of the text or before any other character it still adds one.
The input field paints ChatInputDefaults.ContainerColor over its haze;
the mention card and reply strip had the haze alone, so on the Seeker
they sampled rgb 25 against the field's 30. Both now paint the same
constant over the same haze.
Wire Chat.GetMentionSuggestions through ChatApi, ChatService, ChatRepository and
ChatController. ServerRosterSearchSource, the only RosterSearchSource, fetches the
pool once per composing session, filters it locally with a word-prefix rule while
keeping the server's order, and moves each new message's sender to the front from
the event stream.

A failed fetch (DENIED, NOT_FOUND, transport) returns an empty result with no
fallback; the user can still type a handle by hand.
Opening the mention list or the reply strip moved the transcript to its final inset on the first
frame, ahead of the card. AnimatedVisibility's expandVertically reports the finished size to the
lookahead pass, and the scaffold insets the transcript from that pass. ComposerAccessorySlot sizes
the card from the reply spring in every pass instead, so the card, the input row and the transcript
share one height each frame.

The bar's fade was sized from the bar's height a frame late, which held the composer one frame
high whenever a card above it closed. It now matches the bar's size.

The mention list's height follows the rows on show, on the same spring: a narrower query shrinks
the card instead of cutting rows, and opening the reply strip takes a row (4 to 3) without a jump.
Dropped rows fade and the rest slide up into the gap. When the matches change, the list goes back
to the top; keeping the old row in view scrolled the list while the card grew from 1 row to 4.
@bmc08gt
bmc08gt force-pushed the feat/mention-suggestions branch from 9e8723c to 64d55ee Compare October 2, 2026 13:17
@github-actions github-actions Bot added the type: feature New functionality label Oct 2, 2026
The server's pool is shown without a membership check, so a person it offers after they left the
group still appears.
@bmc08gt
bmc08gt merged commit a1e40d5 into code/cash Oct 2, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: build-system Gradle, convention plugins, build-logic area: network gRPC, connectivity, API, exchange rates type: feature New functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant