feat: implement prefetching of initial email data

This commit is contained in:
Linus Rath
2026-05-11 15:17:30 +02:00
parent b0640c9ecc
commit b3dc2e32b8
3 changed files with 78 additions and 32 deletions
+31
View File
@@ -72,6 +72,10 @@ interface EmailStore {
// JMAP operations
fetchMailboxes: (client: IJMAPClient) => Promise<void>;
fetchEmails: (client: IJMAPClient, mailboxId?: string) => Promise<void>;
// 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.
prefetchInitialData: (client: IJMAPClient) => Promise<void>;
loadMoreEmails: (client: IJMAPClient) => Promise<void>;
fetchEmailContent: (client: IJMAPClient, emailId: string) => Promise<Email | null>;
fetchQuota: (client: IJMAPClient) => Promise<void>;
@@ -350,6 +354,33 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
}
},
prefetchInitialData: async (client) => {
// Coalesce overlapping callers (e.g. login() and a slow home-page useEffect
// racing for the same fetch). The promise is stashed on the client so we
// don't need a separate keyed map and stale entries can't outlive the client.
const target = client as IJMAPClient & { __prefetchPromise?: Promise<void> };
if (target.__prefetchPromise) return target.__prefetchPromise;
target.__prefetchPromise = (async () => {
try {
await Promise.all([
get().fetchMailboxes(client),
get().fetchQuota(client),
]);
const { selectedMailbox } = get();
if (selectedMailbox) {
await get().fetchEmails(client, selectedMailbox);
} else {
await get().fetchEmails(client);
}
// Tag counts can finish whenever; don't block the prefetch on them.
void get().fetchTagCounts(client);
} finally {
delete target.__prefetchPromise;
}
})();
return target.__prefetchPromise;
},
fetchEmails: async (client, mailboxId) => {
set({ isLoading: true, error: null }); // Keep previous emails visible during transition
try {