diff --git a/client/src/sync/tripSyncManager.ts b/client/src/sync/tripSyncManager.ts index 5a9c6d716..6a953ed8d 100644 --- a/client/src/sync/tripSyncManager.ts +++ b/client/src/sync/tripSyncManager.ts @@ -56,6 +56,17 @@ function todayStr(): string { return new Date().toISOString().slice(0, 10) } +/** + * Run work once the browser has nothing better to do, with a ceiling so it + * still happens on browsers without requestIdleCallback (Safari) or in a tab + * that never goes idle. + */ +function whenIdle(fn: () => void | Promise): void { + const run = () => { void Promise.resolve(fn()).catch(console.error) } + if (typeof requestIdleCallback === 'function') requestIdleCallback(run, { timeout: 10_000 }) + else setTimeout(run, 2_000) +} + function shouldCache(trip: Trip): boolean { if (!trip.end_date) return true // no end date → cache forever return trip.end_date >= todayStr() // ongoing or future @@ -195,11 +206,20 @@ export const tripSyncManager = { for (const trip of toSync) { const files = await offlineDb.tripFiles.where('trip_id').equals(trip.id).toArray() cacheFilesForTrip(files).catch(console.error) + } - if (cacheTiles) { - const places = await offlineDb.places.where('trip_id').equals(trip.id).toArray() - prefetchTilesForTrip(trip.id, places, tileUrl).catch(console.error) - } + // Map tiles last, and only once the browser goes idle. syncAll runs right + // after login, where the app is still mounting the first screen — starting + // a bulk tile download into that leaves the UI waiting behind our own + // background traffic. + if (cacheTiles) { + whenIdle(async () => { + for (const trip of toSync) { + if (!isAuthed() || !navigator.onLine) return + const places = await offlineDb.places.where('trip_id').equals(trip.id).toArray() + await prefetchTilesForTrip(trip.id, places, tileUrl).catch(console.error) + } + }) } } finally { _syncing = false @@ -209,9 +229,9 @@ export const tripSyncManager = { /** * "Prepare for offline" (#1135 ask 1): a fully-awaited sync the user runs while * still online so everything they need is guaranteed on-device before they go - * offline. Unlike syncAll, this AWAITS file-blob and map-tile downloads and - * reports progress, so the UI can show a real completion state instead of - * resolving the moment the requests are merely dispatched. + * offline. Unlike syncAll it awaits the file-blob and map-tile downloads up + * front instead of deferring them to idle time, reports progress, and forces + * the tile prefetch to run even for a bbox we believe is already cached. * * Returns the number of trips prepared. */