Conversations page, and know what is unread

/api/v1/conversations answered one page, never unread, its read endpoint
did nothing, and DELETE was missing; each conversation cost a query per
member. Now each conversation keeps its newest post (DmGroup.LastPostId,
set as posts arrive, learnt once by migration _013) and pages by it as
Mastodon does, and each persona's ConversationState holds what it read and
what it took off its list:

- unread when someone else wrote last, after what the persona read;
- read marks it so, and writing in a conversation reads it;
- DELETE takes it off the list until a newer message brings it back.

The list reads its states, newest posts, members and accounts in a few
queries per page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-05 05:37:28 +02:00
1 parent ea5e607779
commit 17922f4dfd
11 files changed
+214 -27

No files matched your search

@@ -186,7 +186,7 @@ namespace PrivaPub.Federation.Inbox.Handlers
await DB.Default.Update<PostEntity>().MatchID(parent.ID).Modify(b => b.Inc(p => p.RepliesCount, 1)).ExecuteAsync(token);
await _fanout.Distribute(post, token);
if (conversation != default)
await DB.Default.Update<DmGroup>().MatchID(conversation.ID).Modify(g => g.UpdatedAt, DateTime.UtcNow).ExecuteAsync(token);
await Domain.Statuses.ConversationStates.Posted(conversation.ID, post.ID, default, token);
if (group is { IsCircle: false })
await _groups.Announce(group, activity.AsObject(), note.Id, isNewPost: true, token);
}