feat(chat): render tombstones and the edited marker - #1393
Merged
Conversation
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.
Deleting a message is an in-place update of the row already on screen, not a remove and insert: a tombstone shares the text bubble's `itemContentType` and its key, and renders through the same `TextBubble` in a muted italic variant with no link detection and nothing selectable. Copy follows the deleter — "You deleted this message" or "This message was deleted"; a null `deletedBy` is a moderation removal and reads as someone else's. The "Edited" marker cannot ride the receipt line: the receipt attaches only to the newest confirmed self message, and most edited messages will not have one. It takes WhatsApp's position instead, pinned to the bubble's bottom-trailing corner, with the body reserving exactly its width through an empty inline placeholder in the text flow. The placeholder sits on the last line where there is room and wraps onto its own line where there isn't, without dragging the last word with it. It holds no glyphs, so selection and the accessibility tree see only the marker itself. A tombstone no longer anchors a receipt — captioning a deleted bubble "Read" describes the delivery of something that is not there any more — but it still belongs to its sender's group for bubble shaping, so the receipt search steps over it rather than treating it as a group boundary. The conversation list says "Message deleted" rather than going blank, which would otherwise read as a conversation with no messages at all.
bmc08gt
changed the base branch from
feat/chat-message-capabilities
to
code/cash
September 2, 2026 22:12
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #1392.
Gives a deleted message and an edited message something to look like. Still nothing that triggers
either — the surfaces that do land in the next PR.
A tombstone reuses the plain-text bubble: same item key, same content type, only the body and the
styling differ. That makes a delete an in-place update of the row already on screen rather than a
remove-and-insert, which is what a distinct content type would have cost. It reads "You deleted this
message" or "This message was deleted" depending on who deleted it, and the chat list preview shows
the same for the last message.
The "Edited" marker is pinned to the bubble's bottom-trailing corner, tucked beside the timestamp
rather than trailing the text. Reserving the space is an
InlineTextContentplaceholder at the endof the body, so the last line wraps around the marker instead of running under it. The placeholder
only reserves space; the marker itself is a real
Text, so it stays in the accessibility tree andis read out with the message.
Receipts are suppressed on a tombstone — a deleted message has no delivery state worth showing.