fix(pwa): restore the dashboard snapshot only for its own account

On a cold start the IndexedDB snapshot was restored before
/api/auth/me had answered, and the owner check was skipped while the
account was unknown. On a shared browser the previous account's
dashboard could be put into the next account's cache until the refetch
landed.

The restore now waits until the signed-in account is known and runs
again when it changes. A snapshot whose owner is not that account, or
that was written before any account was known, is discarded and wiped.

Because the restore now lands after the page has mounted, it fills
queries that have no data yet instead of skipping every query the cache
already knows; data the page holds is still never overwritten.
This commit is contained in:
Marc-André Bombeck committed 2026-11-11 20:36:25 +01:00
1 parent 0df65ce396
commit 433b92a67f
5 files changed
+152 -44

No files matched your search

+8 -8
View File
@@ -278,18 +278,18 @@ function QueryVisibilityRefreshBridge() {
return null;
}
// ── Offline query persistence ────────────────────────
// ── Dashboard snapshot persistence ───────────────────
//
// v1.18.6 — hydrate the last-synced query cache from IndexedDB before the
// first authenticated paint, then debounce-persist successful reads back.
// Combined with the service worker's allowlisted stale-while-revalidate API
// branch, an installed PWA opened offline renders last-known data instead of
// empty skeletons. Build-version + age gated; cleared on logout.
// Fill the dashboard reads from the IndexedDB snapshot as a warm start, then
// debounce-persist them back. The restore waits for `/api/auth/me`: the
// snapshot belongs to one account, and only a known account can be compared
// with its owner. Build-version + age gated; cleared at every session end.
function QueryPersistenceBridge() {
const queryClient = useQueryClient();
const transition = useRecordSessionTransition();
const userId = useAuth().user?.id ?? null;
useEffect(() => {
if (transition.phase !== "ready") return;
if (transition.phase !== "ready" || userId === null) return;
let stop: (() => void) | undefined;
let cancelled = false;
void restorePersistedQueryCache(
@@ -305,7 +305,7 @@ function QueryPersistenceBridge() {
cancelled = true;
stop?.();
};
}, [queryClient, transition.phase]);
}, [queryClient, transition.phase, userId]);
return null;
}