fix(federation): resolve persistent unread indicator on federated DMs

Federated relay messages can have local snowflake IDs that don't match
chronological (createdAt) order — a message sent earlier on a remote
instance can arrive later and get a higher local ID. This caused a
permanent mismatch between the ready event's lastMessage (MAX id) and
the acked message (last in createdAt display order), making federated
DM channels appear unread after every server restart.

- Server: change ready event DM lastMessage query from MAX(id) to
  ORDER BY created_at DESC (matching the DM REST API)
- Frontend: change ackChannel to ack MAX(id) among loaded messages
  instead of last in display order (consistent with server comparison)
- Fix federated username display fallback in UserDiscoverCard
This commit is contained in:
Jannis Braun
2026-03-27 03:05:26 +01:00
parent becd5c8ac8
commit 6df80aa1f0
3 changed files with 27 additions and 8 deletions
+14 -5
View File
@@ -623,13 +623,22 @@ export const useChatStore = create<ChatState>((set, get) => ({
const msgs = get().messages.get(channelId);
if (!msgs || msgs.length === 0) return;
// Walk backward to find the last server-confirmed (non-temp) message
// Find the highest server-confirmed (non-temp) message ID.
// We use MAX(id) rather than "last in display order" because federated
// relay messages can have local snowflake IDs that don't match createdAt
// order — the ack must cover the highest ID to stay consistent with the
// server's read-state comparison (which uses BigInt ID comparison).
let messageId: string | null = null;
for (let i = msgs.length - 1; i >= 0; i--) {
const msg = msgs[i];
let maxId = 0n;
for (const msg of msgs) {
if (msg && !msg.id.startsWith('temp_')) {
messageId = msg.id;
break;
try {
const id = BigInt(msg.id);
if (id > maxId) {
maxId = id;
messageId = msg.id;
}
} catch { /* non-numeric ID — skip */ }
}
}
if (!messageId) return; // All messages are temp — nothing to ack yet