From 523cb0c4b369b19f8bf4cf74a75049a1f2f112e4 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Thu, 26 Mar 2026 17:55:58 +0100 Subject: [PATCH] fix(federation): deduplicate relay DM messages in chatStore When a user has federated WS connections, they can receive both the original message (from their home instance) and the relay copy (from the remote instance). The relay copy has a different message ID but carries sourceMessageId pointing to the original. The dedup now checks both directions: incoming relay copy vs existing original, and incoming original vs existing relay copy. --- packages/web/src/stores/chatStore.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/web/src/stores/chatStore.ts b/packages/web/src/stores/chatStore.ts index d3e90486..deead698 100644 --- a/packages/web/src/stores/chatStore.ts +++ b/packages/web/src/stores/chatStore.ts @@ -392,6 +392,13 @@ export const useChatStore = create((set, get) => ({ const current = newMessages.get(channelId) ?? []; // Avoid duplicates if (current.find(m => m.id === normalizedMessage.id)) return state; + // Federation relay dedup: skip if this is a relay copy of a message we + // already have (sourceMessageId matches an existing ID), or if we already + // have the relay copy and the original is now arriving (existing + // sourceMessageId matches incoming ID). + if ('sourceMessageId' in normalizedMessage && normalizedMessage.sourceMessageId + && current.find(m => m.id === normalizedMessage.sourceMessageId)) return state; + if (current.find(m => 'sourceMessageId' in m && m.sourceMessageId === normalizedMessage.id)) return state; // Remove any optimistic temp message with same content (no userId check — // federated messages arrive with a different replicated user ID). // Normalize both sides: empty string and null are equivalent (server stores null for empty content).