From 3464e9d99163e8e928da1a8181a011965d5c7e8a Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Wed, 1 Apr 2026 12:47:12 +0200 Subject: [PATCH] feat(web): filter remote DMs from populateFromReady, remove dedup and dead code DMs now come exclusively from the home instance's ready payload. Remote origins' DM channels are ignored. The deduplication logic that preferred home copies over remote copies is no longer needed. Removed dead loadDmChannels() (zero call sites). --- packages/web/src/stores/spaceStore.ts | 58 +++++---------------------- 1 file changed, 9 insertions(+), 49 deletions(-) diff --git a/packages/web/src/stores/spaceStore.ts b/packages/web/src/stores/spaceStore.ts index 43f9b10e..c09eeba7 100644 --- a/packages/web/src/stores/spaceStore.ts +++ b/packages/web/src/stores/spaceStore.ts @@ -58,7 +58,6 @@ interface SpaceState { leaveDm: (id: string) => Promise; loadSpaces: () => Promise; loadSpaceDetail: (spaceId: string) => Promise; - loadDmChannels: () => Promise; createSpace: (data: CreateSpaceRequest) => Promise; updateSpace: (spaceId: string, data: UpdateSpaceRequest) => Promise; deleteSpace: (spaceId: string) => Promise; @@ -282,15 +281,6 @@ export const useSpaceStore = create((set, get) => ({ } }, - loadDmChannels: async () => { - try { - const dmChannels = await api.dm.list(); - set({ dmChannels }); - } catch { - // Handle error silently - } - }, - createSpace: async (data: CreateSpaceRequest) => { const space = await api.spaces.create(data); const tagged: TaggedSpace = { ...space, _instanceOrigin: '' }; @@ -653,21 +643,26 @@ export const useSpaceStore = create((set, get) => ({ } } - // DM channels: process from any origin, normalize remote assets - const incomingDms = dmChannels || []; - if (!isHome) { + // S2S DM Unification: DMs are managed exclusively by the home instance. + // Skip DM channels from remote origins — they're replicas served by S2S relay. + const incomingDms = origin === '' ? (dmChannels ?? []) : []; + + // Normalize asset URLs for DMs (only relevant for home origin in practice) + if (origin !== '') { for (const dm of incomingDms) { for (const member of dm.members) { normalizeUserAssets(member, origin); } } } + for (const dm of incomingDms) { channelOriginMap.set(dm.id, origin); if (dm.lastMessage?.id) { channelLastMessageIds.set(dm.id, dm.lastMessage.id); } } + // Merge: remove DMs belonging to this origin from existing state, then append incoming const existingDmsFromOtherOrigins = get().dmChannels.filter(dm => { const dmOrigin = get().channelOriginMap.get(dm.id); @@ -675,44 +670,9 @@ export const useSpaceStore = create((set, get) => ({ }); const mergedDms = [...existingDmsFromOtherOrigins, ...incomingDms]; - // Deduplicate DMs: if the same 1-on-1 conversation exists from multiple origins, - // prefer the home-origin copy (empty string origin = home). This handles the case - // where the federation relay created a local copy of a remote DM. - const deduplicatedDms: typeof mergedDms = []; - const seenPairs = new Map(); // canonicalKey -> index in deduplicatedDms - - for (const dm of mergedDms) { - if (dm.members.length !== 2) { - // Group DMs: no dedup - deduplicatedDms.push(dm); - continue; - } - - // Build a canonical key from the two members' homeUserIds - const memberIds = dm.members.map(m => m.homeUserId || m.id).sort(); - const canonicalKey = memberIds.join(':'); - const dmOrigin = channelOriginMap.get(dm.id) ?? ''; - - const existingIdx = seenPairs.get(canonicalKey); - if (existingIdx !== undefined) { - // Duplicate found — keep the home-origin copy - const existingDm = deduplicatedDms[existingIdx]!; - const existingOrigin = channelOriginMap.get(existingDm.id) ?? ''; - - if (dmOrigin === '' && existingOrigin !== '') { - // New one is home, existing is remote — replace with home copy - deduplicatedDms[existingIdx] = dm; - } - // Otherwise keep existing (it's already home or first-seen) - } else { - seenPairs.set(canonicalKey, deduplicatedDms.length); - deduplicatedDms.push(dm); - } - } - const update: Partial = { spaces: mergedSpaces, - dmChannels: deduplicatedDms, + dmChannels: mergedDms, channelToSpaceMap, channelLastMessageIds, spacePermissions,