From 23f2863bdca4e747a7054bbc6394f1434a4b5763 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Tue, 7 Apr 2026 21:38:11 +0200 Subject: [PATCH] fix: seed DM read states for federated users on first access When a federated user first accesses DMs on a remote instance, all channels appear unread because no read_states rows exist. Seed missing read states to the latest message during the ready payload build. The S2S relay keeps things in sync going forward. --- packages/server/src/ws/handler.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/server/src/ws/handler.ts b/packages/server/src/ws/handler.ts index 43412e5e..f1457f30 100644 --- a/packages/server/src/ws/handler.ts +++ b/packages/server/src/ws/handler.ts @@ -1220,6 +1220,33 @@ function buildReadyPayload(userId: string): { visibleChannelIdSet.add(dm.id); } + // Seed read states for federated users' DM channels that have no existing read state. + // This handles the bootstrap: DMs existed before cross-instance access was enabled, + // so the remote instance has no read state history. Mark as read (latest message). + // Going forward, the S2S read_state_update relay keeps things in sync. + if (isFederated && dmChannels.length > 0) { + const dmIds = dmChannels.map(dm => dm.id); + const existingDmReadStates = batchInArray( + dmIds, + ids => db.select({ channelId: schema.readStates.channelId }) + .from(schema.readStates) + .where(and(eq(schema.readStates.userId, userId), inArray(schema.readStates.channelId, ids))) + .all(), + ); + const hasReadState = new Set(existingDmReadStates.map(rs => rs.channelId)); + const now = Date.now(); + for (const dm of dmChannels) { + if (!hasReadState.has(dm.id) && dm.lastMessage) { + db.insert(schema.readStates).values({ + userId, + channelId: dm.id, + lastReadMessageId: dm.lastMessage.id, + updatedAt: now, + }).run(); + } + } + } + // Get Space Folders const folderRows = db.select() .from(schema.spaceFolders)