Skip to content

feat(chats): show pending join requests on a hosted group's row - #1864

Merged
bmc08gt merged 2 commits into
code/cashfrom
feat/join-requests-chat-list
Oct 27, 2026
Merged

bmc08gt merged 2 commits into
code/cashfrom
feat/join-requests-chat-list

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

A private group you host now shows its pending join requests on its Chats row, as unread messages do. This implements the design note in code-payments/flipcash-client-orchestrator#69 (frames from node 12824:117).

  • Accessory: blue timestamp and the unread pill. The pill counts pending requests plus unread messages.
  • Subtitle: "Jasper wants to join" or "3 people want to join" in the unread colour. When there are unread messages too, " · " and the last message follow in the secondary colour, ellipsized at the end. A typing indicator still takes the slot while someone types.
  • Sort: by the later of last activity and the newest request's entered_at. The timestamp shows that same time. ChatsViewModel now draws rows in the projection's order. Before this, the feed order and the projection order always matched, so nothing reordered rows.
  • Unread: requests count for the Unread chip and filter, the Archived row's count, and the tab badge. The badge has its own source (observeUnreadChatListCount), so it now takes the lobbies. RealSessionController loads each hosted group's lobby, so the badge doesn't depend on Chats having been opened.
  • Archived: a hosted group with requests stays archived. Muted archived chats still don't add to the Archived count; this keeps the existing rule.
  • State.needsDecision had no reader, so it's removed.

The second commit reloads hosted lobbies when the event stream reconnects (on a network reconnect, when the heartbeat finds the stream down, and on return to the foreground), matching iOS's ConversationController.swift:606. LobbyUpdate isn't replayed after a gap, so without the reload a missed update leaves the row's count wrong.

Where the app's tokens differ from the Figma values, the app's tokens are used:

  • Type: Figma uses Inter SemiBold 16 (title) and 14 (subtitle). The row keeps the app's textMedium/textSmall and its font.
  • Blue: Figma's #0A84FF maps to CodeTheme.colors.indicator, the colour the unread timestamp already uses.
  • In the component set, the third variant's timestamp is black. That looks like a Figma slip, so it's blue here like the other variants.

A private group you host now shows who waits to join on its own Chats
row, the way unread messages show (node 12824:117). The pill counts
requests plus unread messages, the timestamp turns blue, and the
subtitle reads "Jasper wants to join" or "3 people want to join" in the
unread colour. With unread messages too, " · " and the last message
follow in grey, cut off at the end. Someone typing keeps the slot until
they stop.

The row sorts by the later of its last activity and its newest request,
so ChatListEntry gains pendingRequests and newestRequestMs and the
projection sorts by both. ChatsViewModel now draws rows in the
projection's order; until now feed order and projection order were the
same, so nothing read projection.main for ordering.

Requests count as unread for the Unread chip, the Archived row and the
tab badge. The badge has its own source, so observeUnreadChatListCount
takes the lobbies, and RealSessionController fetches each hosted
group's lobby as it appears; otherwise the badge would miss requests
until Chats was first opened. The Archived list reads the same lobbies.

State.needsDecision had no reader and goes. hostedPrivateGroups moves
to shared/chat for the session to use.
LobbyUpdate only arrives on the event stream, and the server doesn't
replay what was sent while the stream was down. A request that came or
went in that gap left the lobby, and now the Chats row's count, wrong
until something loaded it again. iOS reloads on reconnect
(ConversationController.swift:606); Android didn't.

LobbyUpdateSink gains reload(), and HostLobbies refetches every lobby
it holds. RealChatCoordinator calls it with the feed sync wherever the
stream comes back: the network reconnect, the heartbeat finding the
stream down, and returning to the foreground.
@bmc08gt bmc08gt self-assigned this Oct 27, 2026
@bmc08gt
bmc08gt merged commit 4288ebc into code/cash Oct 27, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant