From bf64c4678badea6c52ed4c2b8aff1b5b41a65e46 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Mon, 16 Mar 2026 22:52:32 +0100 Subject: [PATCH] fix: prevent logout 401 and login double-attempt caused by LWW layout push race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit resetUserStores() was abusing populateFromReady('', [], [], []) to clear the space store. Its LWW timestamp logic fired an async pushLayoutToOrigin when _layoutUpdatedAt > 0, which read a null token from localStorage (already removed on logout, not yet set on login). The 401 response triggered handleUnauthorized(), deleting the freshly-stored login token and forcing a full page reload — requiring users to log in twice. Replace with a proper reset() method that synchronously sets all state to initial values with no LWW comparison or API side effects. --- packages/web/src/stores/authStore.ts | 2 +- packages/web/src/stores/spaceStore.ts | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/web/src/stores/authStore.ts b/packages/web/src/stores/authStore.ts index 2c2a1788..e2cc341e 100644 --- a/packages/web/src/stores/authStore.ts +++ b/packages/web/src/stores/authStore.ts @@ -31,7 +31,7 @@ interface AuthState { function resetUserStores() { clearSelfIds(); useChatStore.getState().clearAllMessages(); - useSpaceStore.getState().populateFromReady('', [], [], []); + useSpaceStore.getState().reset(); useSocialStore.getState().reset(); useVoiceStore.getState().resetSession(); useInstanceStore.getState().reset(); diff --git a/packages/web/src/stores/spaceStore.ts b/packages/web/src/stores/spaceStore.ts index 397e4543..a2d71ae1 100644 --- a/packages/web/src/stores/spaceStore.ts +++ b/packages/web/src/stores/spaceStore.ts @@ -82,6 +82,7 @@ interface SpaceState { removeInstanceSpaces: (origin: string) => void; transferOwnership: (spaceId: string, newOwnerId: string) => Promise; findExistingDmForUser: (targetUser: { id: string; homeUserId?: string | null }) => { dm: DmChannel; origin: string } | null; + reset: () => void; } /** @@ -129,6 +130,28 @@ export const useSpaceStore = create((set, get) => ({ categoryOriginMap: new Map(), _layoutUpdatedAt: 0, + reset: () => { + _myUserIdByOrigin.clear(); + set({ + spaces: [], + currentSpaceId: null, + channels: [], + categories: [], + members: [], + roles: [], + folders: [], + spaceLayout: null, + dmChannels: [], + channelToSpaceMap: new Map(), + channelLastMessageIds: new Map(), + spacePermissions: new Map(), + channelPermissions: new Map(), + channelOriginMap: new Map(), + categoryOriginMap: new Map(), + _layoutUpdatedAt: 0, + }); + }, + setSpaces: (spaces) => set({ spaces }), setCurrentSpace: (spaceId) => set({ currentSpaceId: spaceId }), setChannels: (channels) => set({ channels }),