fix: prevent loading flash when switching to a cached account

This commit is contained in:
Linus Rath
2026-07-22 17:42:48 +02:00
parent 7beaf991e8
commit 5818e60401
2 changed files with 28 additions and 13 deletions
+8 -3
View File
@@ -980,11 +980,16 @@ export default function Home() {
await refreshScheduledMetadata(client); 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) { if (selectedMailboxId) {
await fetchEmails(client, selectedMailboxId); await fetchEmails(client, selectedMailboxId, { background });
} else { } else {
await fetchEmails(client); await fetchEmails(client, undefined, { background });
} }
fetchTagCounts(client); fetchTagCounts(client);
+20 -10
View File
@@ -148,7 +148,7 @@ interface EmailStore {
// JMAP operations // JMAP operations
fetchMailboxes: (client: IJMAPClient) => Promise<void>; fetchMailboxes: (client: IJMAPClient) => Promise<void>;
fetchEmails: (client: IJMAPClient, mailboxId?: string) => Promise<void>; fetchEmails: (client: IJMAPClient, mailboxId?: string, opts?: { background?: boolean }) => Promise<void>;
// Eager post-login bootstrap: fires mailboxes/quota/emails so the round-trips // 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 // 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. // times; later calls are no-ops while a prior one is in flight.
@@ -1035,8 +1035,12 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
return target.__prefetchPromise; return target.__prefetchPromise;
}, },
fetchEmails: async (client, mailboxId) => { fetchEmails: async (client, mailboxId, opts) => {
set({ isLoading: true, error: null }); // Keep previous emails visible during transition // 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 { try {
const targetMailboxId = mailboxId || get().selectedMailbox; const targetMailboxId = mailboxId || get().selectedMailbox;
if (targetMailboxId === VIRTUAL_SCHEDULED_MAILBOX_ID) { if (targetMailboxId === VIRTUAL_SCHEDULED_MAILBOX_ID) {
@@ -1093,13 +1097,19 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
void get().fetchThreadEmailCounts(client); void get().fetchThreadEmailCounts(client);
} catch (error) { } catch (error) {
console.error('Failed to fetch emails:', error); console.error('Failed to fetch emails:', error);
set({ // A failed background refresh must not wipe the list it was refreshing -
error: error instanceof Error ? error.message : "Failed to fetch emails", // keep the restored/prefetched emails visible and just surface the error.
isLoading: false, if (background) {
emails: [], set({ error: error instanceof Error ? error.message : "Failed to fetch emails" });
hasMoreEmails: false, } else {
totalEmails: 0 set({
}); error: error instanceof Error ? error.message : "Failed to fetch emails",
isLoading: false,
emails: [],
hasMoreEmails: false,
totalEmails: 0
});
}
} }
}, },