Skip to content

feat(chat): resolve message capabilities and stage edits optimistically - #1392

Merged
bmc08gt merged 1 commit into
code/cashfrom
feat/chat-message-capabilities
Sep 2, 2026
Merged

feat(chat): resolve message capabilities and stage edits optimistically#1392
bmc08gt merged 1 commit into
code/cashfrom
feat/chat-message-capabilities

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Editing and deleting a chat message need three things the Android transcript did not have: a single
answer to what the viewer may do to a message, a way to show the result before the server confirms
it, and a mutation path on the coordinator. This adds all three; nothing calls them yet.

resolveCapabilities is the one place edit and delete availability is decided. It takes the message
and a MessagePolicy (today just the edit window) and returns a Set<MessageCapability>. Group
roles, when they arrive, become another input to that function rather than another branch at every
menu site — which is why the policy is a parameter and not a constant.

PendingMutation is the optimistic overlay. ChatMessage.applying(mutation) returns the message as
the user should already see it, and it no-ops once eventSequence has moved past the sequence the
mutation was staged against, so a server update that has already landed wins over a stale local one.
MessagingDelegate stages a mutation before the RPC, reconciles it against the response, and drops
it on failure, so a rejected edit rolls back to the stored text rather than sticking.

The RPCs and the storage this needs were already in place — Deleted content, event_sequence and
last_edited_ts_epoch_ms all exist in the published contract package and in Room — so there is no
proto or migration change here.

Rules the tests pin down:

  • a deleted message offers nothing, including to its own sender
  • a cash message offers reply only, so a long-press on it has no menu to open
  • an unsent message (eventSequence == 0) offers nothing until it lands
  • edit is gated on the window and on the message being the viewer's own; delete is not window-gated
  • an overlay whose expected sequence is behind the stored message is discarded, not applied

Groundwork for editing and deleting chat messages. No UI yet.

Two seams, both in `:apps:flipcash:shared:chat`:

`resolveCapabilities` answers what the viewer may do to a message — copy,
reply, edit, delete — from the message and a `MessagePolicy`. Call sites read
the set; they do not re-derive it. Group roles become another argument here
rather than another branch at each menu. Cash is deliberately neither editable
nor deletable, and an unconfirmed message resolves to nothing because
`expected_event_sequence` is validated `>= 1`, so no valid request can be built
for one.

`PendingMutation` is the overlay shown between sending a mutation and the
server answering. It lives in memory, never in the database, so a rollback is
dropping a map entry rather than a compensating write. `ChatMessage.applying`
retires an overlay as soon as the stored row carries a higher `eventSequence`,
which lets `MessagingDelegate` persist the server's answer before it drops the
overlay without flashing the pre-edit text in between.

A conflict does not retry. The edit was written against a version that no
longer exists, so re-applying it would clobber whoever got there first;
instead the message is re-read and stored, and the caller is left to tell the
user.

Transport was already in place — `ChatMessagingController.editMessage` and
`deleteMessage`, the `Deleted` content type, and the `event_sequence` and
`last_edited_ts_epoch_ms` columns all exist. No proto or schema change.
@bmc08gt bmc08gt self-assigned this Sep 2, 2026
@github-actions github-actions Bot added the type: feature New functionality label Sep 2, 2026
@bmc08gt
bmc08gt merged commit c11645b into code/cash Sep 2, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feature New functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant