From 9067908fef7c5507dffd6b60348a75cdf194cede Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sat, 31 Oct 2026 04:46:53 +0100 Subject: [PATCH] fix: open the inbox on a cold start instead of the folder the app was left in --- .../__tests__/email-cache-persist.test.ts | 27 +++++++++++++++++-- src/stores/email-store.ts | 12 ++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/stores/__tests__/email-cache-persist.test.ts b/src/stores/__tests__/email-cache-persist.test.ts index bb64eac1..3d2c2a71 100644 --- a/src/stores/__tests__/email-cache-persist.test.ts +++ b/src/stores/__tests__/email-cache-persist.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import type { Email } from '../../api/types'; +import type { Email, Mailbox } from '../../api/types'; import { boundEmailCache, PERSISTED_ROWS_PER_FOLDER, @@ -102,6 +102,8 @@ describe('boundEmailCache', () => { }); }); +const INBOX = { id: 'inbox', name: 'Inbox', role: 'inbox', isShared: false } as unknown as Mailbox; + describe('email-store persistence', () => { const { partialize, merge } = useEmailStore.persist.getOptions(); @@ -149,6 +151,7 @@ describe('email-store persistence', () => { it('restores the open folder into the list on hydration', () => { const live = rows('live', 3); const persisted = JSON.parse(JSON.stringify(partialize!(viewState({ + mailboxes: [INBOX], emails: live, totalEmails: 42, queryState: 'q2', @@ -170,7 +173,7 @@ describe('email-store persistence', () => { const legacy = { accountSnapshots: {}, activeAccountId: 'me', - mailboxes: [], + mailboxes: [INBOX], emailStates: {}, currentMailboxId: 'inbox', mailboxSnapshots: { inbox: snap }, @@ -190,4 +193,24 @@ describe('email-store persistence', () => { const initial = useEmailStore.getInitialState(); expect(merge!(undefined, initial)).toBe(initial); }); + + // A cold start reopened the folder (or the search) the app was left in; + // the webmail opens the Inbox (64b39c504). + it('opens the Inbox on a cold start, with its cached rows', () => { + const work = { id: 'work', name: 'Work', role: null, isShared: false } as unknown as Mailbox; + const persisted = JSON.parse(JSON.stringify(partialize!(viewState({ + mailboxes: [INBOX, work], + currentMailboxId: 'work', + emails: rows('work', 2), + totalEmails: 2, + mailboxSnapshots: { inbox: { emails: rows('in', 3), total: 3, queryState: 'qi' } }, + })))); + + const restored = merge!(persisted, useEmailStore.getInitialState()); + + expect(restored.currentMailboxId).toBe('inbox'); + expect(restored.emails.map((e) => e.id)).toEqual(['in0', 'in1', 'in2']); + // The folder's own rows are still cached for when it is opened. + expect(restored.mailboxSnapshots.work.emails).toHaveLength(2); + }); }); diff --git a/src/stores/email-store.ts b/src/stores/email-store.ts index b4a01730..38a7b237 100644 --- a/src/stores/email-store.ts +++ b/src/stores/email-store.ts @@ -912,6 +912,16 @@ function snapshotFromActive(state: EmailState): AccountSnapshot { }; } +/** + * A cold start opens the Inbox, not the folder the app was last left in + * (webmail 64b39c504); its cached rows still show at once. Snapshots of the + * other accounts keep their folder for an account switch. + */ +export function coldStartView(snap: AccountSnapshot): AccountSnapshot { + const inbox = snap.mailboxes?.find((m) => m.role === 'inbox' && !m.isShared); + return { ...snap, currentMailboxId: inbox?.id ?? null }; +} + function viewFromSnapshot(snap: AccountSnapshot | null): { mailboxes: Mailbox[]; mailboxState?: string; @@ -2416,7 +2426,7 @@ export const useEmailStore = create()( merge: (persisted, current) => { if (!persisted) return current; const cache = persisted as PersistedEmailCache; - return { ...current, ...cache, ...viewFromSnapshot(cache) }; + return { ...current, ...cache, ...viewFromSnapshot(coldStartView(cache)) }; }, }, ),