From 5c60558d4fe01ea23c9db91d18d78f02dbe6e4ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 18 Jul 2026 14:42:18 +0200 Subject: [PATCH] perf(dashboard): gate the profile-tz batch window on snapshot mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Legacy mode (snapshot flag off) keeps its browser-local batch window unchanged — the profile-tz window + series prefetch run only in snapshot mode where a byte-identical dehydration key exists. --- src/app/page.tsx | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index f06433fdf..883dc9921 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -93,17 +93,23 @@ export default async function DashboardPage() { queryClient.setQueryData(queryKeys.dashboardSnapshot(locale), wireBody); queryClient.setQueryData(queryKeys.dashboardWidgets(), wireBody.layout); - // Above-the-fold prefetch — the profile-tz window is computed ONCE and - // threaded to the client so the batched-series key matches byte-for-byte. - batchWindow = computeBatchWindow(new Date(), user.timezone); + // Above-the-fold prefetch runs ONLY in snapshot mode: legacy mode derives + // its chart-type list from analytics queries the RSC did not run, so there + // is no byte-identical key to dehydrate the series under. In legacy mode + // `batchWindow` stays undefined and the client falls back to its own + // browser-local window exactly as before — no behaviour change off-flag. + const snapshotMode = isDashboardSnapshotEnabled(); + + // The profile-tz window is computed ONCE (snapshot mode only) and threaded + // to the client so the batched-series key matches byte-for-byte. + if (snapshotMode) { + batchWindow = computeBatchWindow(new Date(), user.timezone); + } const modules = await resolveModuleMap(user.id); const insightsEnabled = modules.insights !== false; - // Only prefetch the series in snapshot mode: legacy mode derives its - // type list from analytics queries the RSC did not run, so there is no - // byte-identical key to dehydrate under — the client fetches as before. - const seriesTypes = isDashboardSnapshotEnabled() + const seriesTypes = snapshotMode ? deriveBatchChartTypes( resolveDashboardLayout(wireBody.layout), ( @@ -120,7 +126,7 @@ export default async function DashboardPage() { ? loadDailyDigest(user).catch(() => undefined) : Promise.resolve(undefined); const seriesWork = - seriesTypes.length > 0 + batchWindow && seriesTypes.length > 0 ? readSeriesBatch( user.id, seriesTypes as MeasurementType[], @@ -143,7 +149,9 @@ export default async function DashboardPage() { JSON.parse(JSON.stringify(digest)), ); } - if (series !== undefined) { + // `series` is only ever set when `batchWindow` was (snapshot mode) — the + // explicit `batchWindow` guard just narrows the type for the key build. + if (series !== undefined && batchWindow) { queryClient.setQueryData( queryKeys.chartSeriesBatch( seriesTypes.join(","),