From 5818e60401689f22454838aeddc157ff58ad90cc Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Wed, 22 Jul 2026 17:42:48 +0200 Subject: [PATCH] fix: prevent loading flash when switching to a cached account --- app/(main)/[locale]/page.tsx | 11 ++++++++--- stores/email-store.ts | 30 ++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/app/(main)/[locale]/page.tsx b/app/(main)/[locale]/page.tsx index ba9c7f79..d475056c 100644 --- a/app/(main)/[locale]/page.tsx +++ b/app/(main)/[locale]/page.tsx @@ -980,11 +980,16 @@ export default function Home() { await refreshScheduledMetadata(client); - // Fetch emails for the selected mailbox after scheduled metadata is available. + // Fetch emails for the selected mailbox after scheduled metadata is + // available. If the list is already populated (an account switch + // restored a cached snapshot, or login prefetched it), refresh in the + // background so the visible mail doesn't flash a loading overlay; only + // a genuine empty first load shows the skeleton. + const background = state.emails.length > 0; if (selectedMailboxId) { - await fetchEmails(client, selectedMailboxId); + await fetchEmails(client, selectedMailboxId, { background }); } else { - await fetchEmails(client); + await fetchEmails(client, undefined, { background }); } fetchTagCounts(client); diff --git a/stores/email-store.ts b/stores/email-store.ts index abaf065f..ad91b473 100644 --- a/stores/email-store.ts +++ b/stores/email-store.ts @@ -148,7 +148,7 @@ interface EmailStore { // JMAP operations fetchMailboxes: (client: IJMAPClient) => Promise; - fetchEmails: (client: IJMAPClient, mailboxId?: string) => Promise; + fetchEmails: (client: IJMAPClient, mailboxId?: string, opts?: { background?: boolean }) => Promise; // Eager post-login bootstrap: fires mailboxes/quota/emails so the round-trips // overlap with Next's soft-nav + home-page hydration. Safe to call multiple // times; later calls are no-ops while a prior one is in flight. @@ -1035,8 +1035,12 @@ export const useEmailStore = create((set, get) => ({ return target.__prefetchPromise; }, - fetchEmails: async (client, mailboxId) => { - set({ isLoading: true, error: null }); // Keep previous emails visible during transition + fetchEmails: async (client, mailboxId, opts) => { + // A background refresh (e.g. after an account switch restored a cached list) + // repopulates the list without showing the loading overlay, so switching to + // an already-visited account doesn't flash a spinner over the visible mail. + const background = opts?.background ?? false; + set(background ? { error: null } : { isLoading: true, error: null }); // Keep previous emails visible during transition try { const targetMailboxId = mailboxId || get().selectedMailbox; if (targetMailboxId === VIRTUAL_SCHEDULED_MAILBOX_ID) { @@ -1093,13 +1097,19 @@ export const useEmailStore = create((set, get) => ({ void get().fetchThreadEmailCounts(client); } catch (error) { console.error('Failed to fetch emails:', error); - set({ - error: error instanceof Error ? error.message : "Failed to fetch emails", - isLoading: false, - emails: [], - hasMoreEmails: false, - totalEmails: 0 - }); + // A failed background refresh must not wipe the list it was refreshing - + // keep the restored/prefetched emails visible and just surface the error. + if (background) { + set({ error: error instanceof Error ? error.message : "Failed to fetch emails" }); + } else { + set({ + error: error instanceof Error ? error.message : "Failed to fetch emails", + isLoading: false, + emails: [], + hasMoreEmails: false, + totalEmails: 0 + }); + } } },