From c989d5463736a694683134bf122a24522860cf5c Mon Sep 17 00:00:00 2001 From: Andrew Attali Date: Sun, 19 Jul 2026 00:27:19 +0200 Subject: [PATCH] fix: wait for room list to be loaded before fetching pack info --- .changeset/fix-sliding-sync-subspaces.md | 5 +++++ src/client/slidingSync.test.ts | 23 +++++++++++++++++++++++ src/client/slidingSync.ts | 10 ++++++++++ 3 files changed, 38 insertions(+) create mode 100644 .changeset/fix-sliding-sync-subspaces.md diff --git a/.changeset/fix-sliding-sync-subspaces.md b/.changeset/fix-sliding-sync-subspaces.md new file mode 100644 index 0000000000..5dcb5d2a08 --- /dev/null +++ b/.changeset/fix-sliding-sync-subspaces.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix a regression making subspaces with emote packs being listed as separate spaces on sliding-sync. diff --git a/src/client/slidingSync.test.ts b/src/client/slidingSync.test.ts index 20bb1f6899..f710022e58 100644 --- a/src/client/slidingSync.test.ts +++ b/src/client/slidingSync.test.ts @@ -333,6 +333,7 @@ describe('SlidingSyncManager room subscription coordination', () => { it('uses the active subscription while a room is also an image-pack room', () => { const manager = makeManager(makeMockMx()); const roomId = '!pack:example.com'; + (manager as unknown as { listsFullyLoaded: boolean }).listsFullyLoaded = true; manager.setImagePackSubscriptions([roomId]); manager.subscribeToRoom(roomId); @@ -346,6 +347,7 @@ describe('SlidingSyncManager room subscription coordination', () => { it('restores the image-pack subscription when the room is no longer active', () => { const manager = makeManager(makeMockMx()); const roomId = '!pack:example.com'; + (manager as unknown as { listsFullyLoaded: boolean }).listsFullyLoaded = true; manager.setImagePackSubscriptions([roomId]); manager.subscribeToRoom(roomId); @@ -400,6 +402,7 @@ describe('SlidingSyncManager room subscription coordination', () => { it('removes image-pack subscriptions which are no longer configured', () => { const manager = makeManager(makeMockMx()); + (manager as unknown as { listsFullyLoaded: boolean }).listsFullyLoaded = true; manager.setImagePackSubscriptions(['!old:example.com', '!current:example.com']); manager.setImagePackSubscriptions(['!current:example.com']); @@ -409,6 +412,26 @@ describe('SlidingSyncManager room subscription coordination', () => { ); }); + it('defers image-pack subscriptions until lists are loaded so pack spaces get the composite key', () => { + const manager = makeManager(makeMockMx()); + const roomId = '!spacepack:example.com'; + + manager.setImagePackSubscriptions([roomId]); + expect(mocks.slidingSyncInstance.modifyRoomSubscriptions).not.toHaveBeenCalled(); + + manager.setSpaceSubscriptions([roomId]); + (manager as unknown as { listsFullyLoaded: boolean }).listsFullyLoaded = true; + (manager as unknown as { flushDeferredSubscriptions: () => void }).flushDeferredSubscriptions(); + + expect(mocks.slidingSyncInstance.useCustomSubscription).toHaveBeenLastCalledWith( + roomId, + 'space_image_packs' + ); + expect(mocks.slidingSyncInstance.modifyRoomSubscriptions).toHaveBeenLastCalledWith( + new Set([roomId]) + ); + }); + it('registers the composite space+image-pack subscription', () => { makeManager(makeMockMx()); diff --git a/src/client/slidingSync.ts b/src/client/slidingSync.ts index 031d9cffaf..a49da4555e 100644 --- a/src/client/slidingSync.ts +++ b/src/client/slidingSync.ts @@ -240,6 +240,8 @@ export class SlidingSyncManager { private readonly imagePackRoomSubscriptions = new Set(); + private deferredImagePackSubscriptions: Set | null = null; + private roomSubscriptionSyncQueued = false; private readonly roomTimelineLimit: number; @@ -998,6 +1000,10 @@ export class SlidingSyncManager { const spaces = this.deferredSpaceSubscriptions; this.deferredSpaceSubscriptions = new Set(); this.setSpaceSubscriptions(spaces); + + const imagePacks = this.deferredImagePackSubscriptions; + this.deferredImagePackSubscriptions = null; + if (imagePacks) this.setImagePackSubscriptions(imagePacks); } private queueRoomSubscriptionSync(): void { @@ -1053,6 +1059,10 @@ export class SlidingSyncManager { public setImagePackSubscriptions(roomIds: Iterable): void { if (this.disposed) return; const next = new Set(roomIds); + if (!this.listsFullyLoaded) { + this.deferredImagePackSubscriptions = next; + return; + } const unchanged = next.size === this.imagePackRoomSubscriptions.size && [...next].every((roomId) => this.imagePackRoomSubscriptions.has(roomId));