From e7430f1a54d69754fdd591ef064dccce8f48ccb3 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Thu, 23 Apr 2026 01:02:05 +0200 Subject: [PATCH] feat(federation): prune dmAlternatives on removeInstanceSpaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drops the given origin from every inner (origin→localId) map; removes the outer federatedId entry when its inner map becomes empty. Keeps the store from accumulating stale origin references across long sessions with connect/disconnect churn. --- .../stores/spaceStore.dmAlternatives.test.ts | 42 +++++++++++++++++++ packages/web/src/stores/spaceStore.ts | 9 ++++ 2 files changed, 51 insertions(+) diff --git a/packages/web/src/stores/spaceStore.dmAlternatives.test.ts b/packages/web/src/stores/spaceStore.dmAlternatives.test.ts index d08b7e78..073e96a4 100644 --- a/packages/web/src/stores/spaceStore.dmAlternatives.test.ts +++ b/packages/web/src/stores/spaceStore.dmAlternatives.test.ts @@ -113,4 +113,46 @@ describe('spaceStore.dmAlternatives', () => { expect(byOrigin?.get('https://remote.example')).toBe('remote-new'); expect(byOrigin?.size).toBe(1); }); + + it('removeInstanceSpaces drops the origin from every inner map', () => { + useSpaceStore.getState().populateFromReady( + '', + [], + [], + [makeDm('home-1', 'fed-aaa'), makeDm('home-2', 'fed-bbb')], + null, + 0, + ); + useSpaceStore.getState().populateFromReady( + 'https://remote.example', + [], + [], + [makeDm('remote-1', 'fed-aaa'), makeDm('remote-2', 'fed-bbb')], + null, + 0, + ); + + useSpaceStore.getState().removeInstanceSpaces('https://remote.example'); + + const alts = useSpaceStore.getState().dmAlternatives; + expect(alts.get('fed-aaa')?.has('https://remote.example')).toBe(false); + expect(alts.get('fed-aaa')?.get('')).toBe('home-1'); + expect(alts.get('fed-bbb')?.has('https://remote.example')).toBe(false); + expect(alts.get('fed-bbb')?.get('')).toBe('home-2'); + }); + + it('removeInstanceSpaces deletes federatedId entry if its inner map becomes empty', () => { + useSpaceStore.getState().populateFromReady( + 'https://remote.example', + [], + [], + [makeDm('remote-only', 'fed-solo')], + null, + 0, + ); + + useSpaceStore.getState().removeInstanceSpaces('https://remote.example'); + + expect(useSpaceStore.getState().dmAlternatives.has('fed-solo')).toBe(false); + }); }); diff --git a/packages/web/src/stores/spaceStore.ts b/packages/web/src/stores/spaceStore.ts index e01d1d93..3987d0a6 100644 --- a/packages/web/src/stores/spaceStore.ts +++ b/packages/web/src/stores/spaceStore.ts @@ -870,6 +870,14 @@ export const useSpaceStore = create((set, get) => ({ } } + // Prune dmAlternatives: drop this origin from every inner map. + const dmAlternatives = new Map>(); + for (const [fid, byOrigin] of state.dmAlternatives) { + const nextInner = new Map(byOrigin); + nextInner.delete(origin); + if (nextInner.size > 0) dmAlternatives.set(fid, nextInner); + } + return { spaces: remainingSpaces, channelToSpaceMap, @@ -877,6 +885,7 @@ export const useSpaceStore = create((set, get) => ({ channelPermissions, channelOriginMap, spacePermissions, + dmAlternatives, currentSpaceId: remainingSpaces.find(s => s.id === state.currentSpaceId) ? state.currentSpaceId : null,