Pull Request Overview
- Opened on October 12, 2026
- Status Merged
- Commit count 5 with first commit October 12, 2026
Total Delta
Open Days
Test Delta
How long has this pull request spent in each phase of its lifecycle?
Data pending calculation for pull request
GO-7584 Discussions: replies to replies and deleted-comment placeholders
GO-7584. This is the desktop side of heart GO-7167 (anyproto/anytype-heart#3324, soft delete for discussion comments). It also fixes replies to replies, which were invisible on desktop.
Replies to replies
Discussions show one level: a top-level post, then its replies. That is a display rule (GO-6807). replyToMessageId can point at any message in a thread, because Android's composer replies to the message you tap and API agents reply to the message they answer.
The desktop filed each reply under its direct replyToMessageId, so a reply to a reply landed under an id nothing renders. The message stayed counted as unread, and as an unread mention when it answered your own comment, so the badge could never clear. DROID-4492 asks for the same rule on Android.
Now a reply goes to the thread of the top-level post its chain leads to. Replies stay flat and ordered by orderId, and the desktop composer still replies to the top-level post.
-
lib/util/commentThread.ts(new, pure, unit-tested) places each message in its thread. A reply that arrives before its parent waits until the parent comes. One whose parent never arrives (deleted before GO-7167) stays hidden, as does a reply cycle. -
store/comment.tskeeps per-discussion lookups by id and by thread.- Live messages go into the existing observable list in order, so a new comment re-renders one list instead of the whole discussion.
- Every dispatcher event (add, update, delete, reactions, read/mention/sync status) is routed by id, so nothing scans posts and replies any more.
- The discussion is opened on subscribe (
startLoad). Updates and removals that arrive while its pages load are replayed over the loaded copies. A load that finishes after the section closed or reopened is dropped. - Your own message has no
orderIduntil itsChatAddevent, becauseChatAddMessagereturns only the id. It goes last until the event arrives, then moves into place.
-
Removed: the partial loaders (loadReplies,loadOlderReplies,loadPostsAroundMessage,loadRepliesAroundMessage,onLoadMore), the "load more" controls with their text keys and styles, andreplyCount. The section already loads the whole discussion before rendering, so a deep link becomes a lookup.replyCountis now the thread's length. -
Scroll-stop no longer reads anorderIdrange. Each visible comment is already read on its own, and in a threaded layout a range between seen comments also covers unseen replies in other threads.
Deleted comments (tombstones)
With GO-7167, heart keeps a deleted discussion comment in place with deleted: true and empty content, and sends it as Event.Chat.Update.
- The mapper reads
deletedintoisDeleted. TheChatUpdatepath now also carriesmodifiedAt,isDeletedand, for a tombstone, the read flags. - A tombstone, post or reply, renders as "Comment deleted". It keeps the avatar, author and date, and has no actions, reactions, attachments or context menu.
-
A deleted post closes its thread. Heart refuses a reply to a tombstone, and replies go to the top-level post. The thread has no reply input, and its replies have no reply action or quote-into-thread. - Deleting your own comment marks it deleted in place. Before, the comment and its whole thread vanished from the deleter's view until reload.
- The comment counter counts only live posts. A comment deleted elsewhere leaves edit mode, even if Edit was picked from a menu opened before the deletion.
Middleware
The protos with ChatMessage.deleted are in heart develop, not in a release yet. The PR doesn't bump middleware.version. The mapper reads the field defensively, so this works against the current middleware (no tombstones yet) and picks them up once the middleware has GO-7167.
Test plan
- [ ] A discussion with replies to replies (posted from Android or through the API) shows them at the end of the right thread, and unread/mention badges clear when they're scrolled into view
- [ ] Delete your own post that has replies: the placeholder appears in place, the replies stay, and the thread has no reply input. A second device shows the same
- [ ] Delete your own reply: the placeholder appears in its place
- [ ] Delete a comment through API v2 (
delete_chat_message): the placeholder appears live - [ ] Post and reply: they appear at the bottom immediately and stay there
- [ ] A deep link (
messageId=) to a reply-to-reply scrolls to it and highlights it
New tests: lib/util/commentThread.test.ts, store/comment.test.ts, plus two cases in lib/api/mapper.test.ts. bun run typecheck passes apart from the 4 errors already on develop in chatStatus/session.test.ts. Lint is clean. The full vitest run has the same failures as develop and nothing new. Not yet checked in the app against a heart with GO-7167.
Spec: docs/plans/2026-10-12-discussion-threads-spec.md.
π€ Generated with Claude Code
PR was closed without comments.