From 721c2e34a15a948dfde945ad786db7c68b605bf5 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 11:50:19 +0200 Subject: [PATCH] fix: sign out of the last account instead of staying signed in --- src/stores/__tests__/auth-store.test.ts | 25 +++++++++++++++++++++++++ src/stores/auth-store.ts | 8 +++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/stores/__tests__/auth-store.test.ts b/src/stores/__tests__/auth-store.test.ts index 1027bc41..9e301a75 100644 --- a/src/stores/__tests__/auth-store.test.ts +++ b/src/stores/__tests__/auth-store.test.ts @@ -129,6 +129,31 @@ describe('auth-store', () => { expect(state.serverUrl).toBeNull(); expect(state.username).toBeNull(); }); + + // logout used to read the account list from a snapshot taken before it + // removed the account: the removed account still looked like one to + // switch to, the "switch" to the active account returned at once, and + // signing out of the only account left the app signed in. + it('signs out of the only account', async () => { + const id = 'me@mail.example.com'; + useAccountStore.setState({ + accounts: [{ + id, serverUrl: 'https://mail.example.com', username: 'me', + displayName: 'me', email: 'me', avatarColor: '#000', lastLoginAt: 0, + isConnected: true, hasError: false, isDefault: true, + }], + activeAccountId: id, + defaultAccountId: id, + }); + useAuthStore.setState({ isAuthenticated: true, activeAccountId: id, serverUrl: 'x', username: 'y' }); + + await useAuthStore.getState().logout(); + + expect(useAccountStore.getState().accounts).toEqual([]); + const state = useAuthStore.getState(); + expect(state.isAuthenticated).toBe(false); + expect(state.activeAccountId).toBeNull(); + }); }); describe('mail the viewer held in memory', () => { diff --git a/src/stores/auth-store.ts b/src/stores/auth-store.ts index 5900ea41..d9de9835 100644 --- a/src/stores/auth-store.ts +++ b/src/stores/auth-store.ts @@ -602,10 +602,12 @@ export const useAuthStore = create((set, get) => ({ jmapClient.reset(); clearAccountFeatureStores(currentId); - // Switch to next remaining account, if any - const remaining = accountStore.accounts; + // Switch to next remaining account, if any. Read the store again: + // `accountStore` is the state from before removeAccount. + const after = useAccountStore.getState(); + const remaining = after.accounts.filter((a) => a.id !== currentId); if (remaining.length > 0) { - const next = accountStore.getDefaultAccount() ?? remaining[0]; + const next = after.getDefaultAccount() ?? remaining[0]; try { await get().switchAccount(next.id); return;