Skip to content

feat(chat): group chat contract and service layer - #1468

Merged
bmc08gt merged 9 commits into
code/cashfrom
feat/group-chat-contract
Sep 16, 2026
Merged

bmc08gt merged 9 commits into
code/cashfrom
feat/group-chat-contract

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

Stack:

  1. feat(chat): group chat contract and service layer #1468 — contract and service layer (this PR)
  2. feat(chat): group chats in the conversation feed #1469 — feed and roster
  3. feat(chat): render a group conversation #1470 — UI

First of three. Everything group chats need below the feed and the UI, with nothing yet reading any of it.

  • GroupChats feature flag, off.
  • GetGroupChatFeed, JoinChat and LeaveChat wrapped end to end — API, typed errors, repository, controller.
  • Roster changes carried on the existing chat update stream, so a join or a removal arrives on the connection the transcript already holds open.
  • chat_metadata gains a group's title, picture, roster summary and rules, with the mapping to and from the row.

The RPCs are the reason this is its own PR: the layers above are worth reviewing against a contract that is already settled.

@bmc08gt bmc08gt self-assigned this Sep 16, 2026
@github-actions github-actions Bot added type: feature New functionality area: network gRPC, connectivity, API, exchange rates and removed type: feature New functionality labels Sep 16, 2026
Group chat RPCs and roster updates need the 0.7.0 contract. The version is
not on Maven Central yet, so resolution fails until it publishes.
Group chat support lands across three branches; this flag keeps the
in-progress surfaces off by default until all three are in. Nothing reads
it yet.
The arms mirror the proto Result enums: GetGroupChatFeed and LeaveChat
return {OK, DENIED, NOT_FOUND}, JoinChat adds RULES_NOT_SATISFIED. That
last one is the caller failing the chat's listener rules, which they can
act on, so it is not a NotifiableError.
Thin wrappers over the stub, matching getDmChatFeed: build, validate,
dispatch on IO. Nothing calls them yet.
leaveChat recovers a server NOT_FOUND to success: the caller is not on
the roster, which is the state the call was asked to produce, and
reporting it as a failure would surface an error for a no-op. The
recovery sits before ErrorUtils.handleError so it is not logged either.

The group feed takes no chat type filter — GetGroupChatFeedRequest
carries only query options, and the server decides what a group is.
Roster changes are convergent, not sequenced: each one carries the roster
summary after it, and a client applies one only when that version beats
the version it holds. So they get their own domain type rather than
joining ChatEvent, which is gap-detected by sequence.

A join addressed to the recipient carries the full chat snapshot, so
being added to a chat needs no refetch to appear in the list.

Member mapping moves out of toChatMetadata into toChatMember so the two
paths build a member identically — including the profile's user-id
fallback, without which an expired avatar URL can never be re-minted.
@bmc08gt
bmc08gt force-pushed the feat/group-chat-contract branch from 311f454 to 8a54a3d Compare September 16, 2026 15:34
@github-actions github-actions Bot added type: feature New functionality area: build-system Gradle, convention plugins, build-logic and removed type: feature New functionality labels Sep 16, 2026
@bmc08gt
bmc08gt merged commit 4c0c3fe into code/cash Sep 16, 2026
4 checks passed
@bmc08gt
bmc08gt deleted the feat/group-chat-contract branch September 16, 2026 16:16
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant