From 48d199515a5b35a7ede4c278289f80cf4b95d3a7 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Sat, 2 May 2026 16:37:22 +0200 Subject: [PATCH] feat(web): chatStore dedups WS echo against pendingMessageStore (FIFO) --- packages/web/src/stores/chatStore.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/web/src/stores/chatStore.ts b/packages/web/src/stores/chatStore.ts index cd48831e..f180b2c8 100644 --- a/packages/web/src/stores/chatStore.ts +++ b/packages/web/src/stores/chatStore.ts @@ -5,6 +5,7 @@ import { isDmChannel, getChannelOrigin, getApiForOrigin, useSpaceStore } from '. import { useAuthStore } from './authStore'; import { normalizeMessageAssets } from '../utils/assetUrls'; import { sortDmChannels } from '../utils/dmSorting'; +import { usePendingMessageStore } from './pendingMessageStore'; const MAX_MESSAGES_PER_CHANNEL = 200; const MAX_CACHED_CHANNELS = 20; @@ -364,6 +365,10 @@ export const useChatStore = create((set, get) => ({ const current = newMessages.get(channelId) ?? []; // Avoid duplicates if (current.find(m => m.id === normalizedMessage.id)) return state; + // Dedup against pendingMessageStore by (content, sortedAttachmentIds). + // No userId check — federation relays arrive with replicated user IDs. + const sortedAttIds = (normalizedMessage.attachments ?? []).map((a) => a.id).sort(); + usePendingMessageStore.getState().matchAndRemove(channelId, normalizedMessage.content ?? '', sortedAttIds); // Remove any optimistic temp message with same content. // Don't require userId match — for federated messages the home user ID // differs from the replicated user ID, but content match is sufficient @@ -397,6 +402,10 @@ export const useChatStore = create((set, get) => ({ 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; + // Dedup against pendingMessageStore by (content, sortedAttachmentIds). + // No userId check — federation relays arrive with replicated user IDs. + const sortedAttIds = (normalizedMessage.attachments ?? []).map((a) => a.id).sort(); + usePendingMessageStore.getState().matchAndRemove(channelId, normalizedMessage.content ?? '', sortedAttIds); // 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).