From f362f5530c58d6a2616de07363f0da8959c3553a Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Thu, 26 Mar 2026 14:45:04 +0100 Subject: [PATCH] fix(federation): prevent duplicate DM sidebar entries from relay broadcasts When a relay-created dm_message_created arrives for an unknown channel, check if an existing DM has the same member (by homeUserId). If so, route the message to the existing channel instead of adding a duplicate sidebar entry. Prevents the transient duplicate that required page refresh to dedup. --- packages/web/src/hooks/useWebSocket.ts | 27 +++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/web/src/hooks/useWebSocket.ts b/packages/web/src/hooks/useWebSocket.ts index 202cce20..8fc96323 100644 --- a/packages/web/src/hooks/useWebSocket.ts +++ b/packages/web/src/hooks/useWebSocket.ts @@ -533,9 +533,34 @@ function handleEvent(origin: string, event: ServerEvent): void { } } } - addRealtimeMessage(event.message.dmChannelId, event.message as any); const { dmChannels: currentDmChannels, setDmChannels: setDms, addDmChannel: addDmCh } = useSpaceStore.getState(); const knownDm = currentDmChannels.find(dm => dm.id === event.message.dmChannelId); + + // Check if this is a relay-created channel that duplicates an existing DM + // (same conversation, different channel ID). If so, skip adding a new sidebar entry + // and route the message to the existing channel instead. + if (!knownDm) { + const msgUser = event.message.user; + const msgHomeUserId = msgUser?.homeUserId || msgUser?.id; + if (msgHomeUserId) { + const existingDm = currentDmChannels.find(dm => + dm.members.length === 2 && + dm.members.some(m => (m.homeUserId || m.id) === msgHomeUserId), + ); + if (existingDm) { + // Route message to the existing channel instead of creating a duplicate + addRealtimeMessage(existingDm.id, { ...event.message, dmChannelId: existingDm.id } as any); + const updatedDms = currentDmChannels.map(dm => + dm.id === existingDm.id ? { ...dm, lastMessage: event.message } : dm, + ); + updatedDms.sort((a, b) => (b.lastMessage?.createdAt ?? b.createdAt) - (a.lastMessage?.createdAt ?? a.createdAt)); + setDms(updatedDms); + break; + } + } + } + + addRealtimeMessage(event.message.dmChannelId, event.message as any); if (!knownDm) { addDmCh({ id: event.message.dmChannelId,