mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-22 14:51:45 +00:00
fix: prevent splash-forever on slow first-load after clearing storage
Three changes: - tripSyncManager: add interrupt() so trip page load can stop competing background bundle sync requests; also try clearing blobCache before falling back to full clearAll() on QuotaExceededError - TripPlannerPage: call tripSyncManager.interrupt() when mounting so loadTrip gets network priority over background syncAll - TripPlannerPage: show a 'go back to dashboard' link after 12 seconds on the splash screen so users are never stuck with no escape
This commit is contained in:
@@ -185,6 +185,11 @@ export async function clearTripData(tripId: number): Promise<void> {
|
|||||||
await offlineDb.trips.delete(tripId);
|
await offlineDb.trips.delete(tripId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Clear cached file blobs only — frees significant quota without losing trip data. */
|
||||||
|
export async function clearBlobCache(): Promise<void> {
|
||||||
|
await offlineDb.blobCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/** Wipe the entire offline database (called on logout). */
|
/** Wipe the entire offline database (called on logout). */
|
||||||
export async function clearAll(): Promise<void> {
|
export async function clearAll(): Promise<void> {
|
||||||
await offlineDb.delete();
|
await offlineDb.delete();
|
||||||
|
|||||||
@@ -925,6 +925,7 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'trip.tabs.budget': 'الميزانية',
|
'trip.tabs.budget': 'الميزانية',
|
||||||
'trip.tabs.files': 'الملفات',
|
'trip.tabs.files': 'الملفات',
|
||||||
'trip.loading': 'جارٍ تحميل الرحلة...',
|
'trip.loading': 'جارٍ تحميل الرحلة...',
|
||||||
|
'trip.splash.goBack': 'Taking too long? Go back to dashboard',
|
||||||
'trip.loadingPhotos': 'جارٍ تحميل صور الأماكن...',
|
'trip.loadingPhotos': 'جارٍ تحميل صور الأماكن...',
|
||||||
'trip.mobilePlan': 'الخطة',
|
'trip.mobilePlan': 'الخطة',
|
||||||
'trip.mobilePlaces': 'الأماكن',
|
'trip.mobilePlaces': 'الأماكن',
|
||||||
|
|||||||
@@ -909,6 +909,7 @@ const br: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'trip.confirm.deletePlace': 'Tem certeza de que deseja excluir este lugar?',
|
'trip.confirm.deletePlace': 'Tem certeza de que deseja excluir este lugar?',
|
||||||
'trip.confirm.deletePlaces': 'Excluir {count} lugares?',
|
'trip.confirm.deletePlaces': 'Excluir {count} lugares?',
|
||||||
'trip.toast.placesDeleted': '{count} lugares excluídos',
|
'trip.toast.placesDeleted': '{count} lugares excluídos',
|
||||||
|
'trip.splash.goBack': 'Taking too long? Go back to dashboard',
|
||||||
'trip.loadingPhotos': 'Carregando fotos dos lugares...',
|
'trip.loadingPhotos': 'Carregando fotos dos lugares...',
|
||||||
|
|
||||||
// Day Plan Sidebar
|
// Day Plan Sidebar
|
||||||
|
|||||||
@@ -923,6 +923,7 @@ const cs: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'trip.tabs.budget': 'Rozpočet',
|
'trip.tabs.budget': 'Rozpočet',
|
||||||
'trip.tabs.files': 'Soubory',
|
'trip.tabs.files': 'Soubory',
|
||||||
'trip.loading': 'Načítání cesty...',
|
'trip.loading': 'Načítání cesty...',
|
||||||
|
'trip.splash.goBack': 'Taking too long? Go back to dashboard',
|
||||||
'trip.loadingPhotos': 'Načítání fotek míst...',
|
'trip.loadingPhotos': 'Načítání fotek míst...',
|
||||||
'trip.mobilePlan': 'Plán',
|
'trip.mobilePlan': 'Plán',
|
||||||
'trip.mobilePlaces': 'Místa',
|
'trip.mobilePlaces': 'Místa',
|
||||||
|
|||||||
@@ -928,6 +928,7 @@ const de: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'trip.tabs.budget': 'Budget',
|
'trip.tabs.budget': 'Budget',
|
||||||
'trip.tabs.files': 'Dateien',
|
'trip.tabs.files': 'Dateien',
|
||||||
'trip.loading': 'Reise wird geladen...',
|
'trip.loading': 'Reise wird geladen...',
|
||||||
|
'trip.splash.goBack': 'Taking too long? Go back to dashboard',
|
||||||
'trip.loadingPhotos': 'Fotos der Orte werden geladen...',
|
'trip.loadingPhotos': 'Fotos der Orte werden geladen...',
|
||||||
'trip.mobilePlan': 'Planung',
|
'trip.mobilePlan': 'Planung',
|
||||||
'trip.mobilePlaces': 'Orte',
|
'trip.mobilePlaces': 'Orte',
|
||||||
|
|||||||
@@ -1000,6 +1000,7 @@ const en: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'trip.tabs.files': 'Files',
|
'trip.tabs.files': 'Files',
|
||||||
'trip.loading': 'Loading trip...',
|
'trip.loading': 'Loading trip...',
|
||||||
'trip.loadingPhotos': 'Loading place photos...',
|
'trip.loadingPhotos': 'Loading place photos...',
|
||||||
|
'trip.splash.goBack': 'Taking too long? Go back to dashboard',
|
||||||
'trip.mobilePlan': 'Plan',
|
'trip.mobilePlan': 'Plan',
|
||||||
'trip.mobilePlaces': 'Places',
|
'trip.mobilePlaces': 'Places',
|
||||||
'trip.toast.placeUpdated': 'Place updated',
|
'trip.toast.placeUpdated': 'Place updated',
|
||||||
|
|||||||
@@ -898,6 +898,7 @@ const es: Record<string, string> = {
|
|||||||
'trip.tabs.budget': 'Presupuesto',
|
'trip.tabs.budget': 'Presupuesto',
|
||||||
'trip.tabs.files': 'Archivos',
|
'trip.tabs.files': 'Archivos',
|
||||||
'trip.loading': 'Cargando viaje...',
|
'trip.loading': 'Cargando viaje...',
|
||||||
|
'trip.splash.goBack': 'Taking too long? Go back to dashboard',
|
||||||
'trip.loadingPhotos': 'Cargando fotos de los lugares...',
|
'trip.loadingPhotos': 'Cargando fotos de los lugares...',
|
||||||
'trip.mobilePlan': 'Plan',
|
'trip.mobilePlan': 'Plan',
|
||||||
'trip.mobilePlaces': 'Lugares',
|
'trip.mobilePlaces': 'Lugares',
|
||||||
|
|||||||
@@ -922,6 +922,7 @@ const fr: Record<string, string> = {
|
|||||||
'trip.tabs.budget': 'Budget',
|
'trip.tabs.budget': 'Budget',
|
||||||
'trip.tabs.files': 'Fichiers',
|
'trip.tabs.files': 'Fichiers',
|
||||||
'trip.loading': 'Chargement du voyage…',
|
'trip.loading': 'Chargement du voyage…',
|
||||||
|
'trip.splash.goBack': 'Taking too long? Go back to dashboard',
|
||||||
'trip.loadingPhotos': 'Chargement des photos des lieux...',
|
'trip.loadingPhotos': 'Chargement des photos des lieux...',
|
||||||
'trip.mobilePlan': 'Plan',
|
'trip.mobilePlan': 'Plan',
|
||||||
'trip.mobilePlaces': 'Lieux',
|
'trip.mobilePlaces': 'Lieux',
|
||||||
|
|||||||
@@ -937,6 +937,7 @@ const hu: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'trip.confirm.deletePlace': 'Biztosan törölni szeretnéd ezt a helyet?',
|
'trip.confirm.deletePlace': 'Biztosan törölni szeretnéd ezt a helyet?',
|
||||||
'trip.confirm.deletePlaces': '{count} helyet töröl?',
|
'trip.confirm.deletePlaces': '{count} helyet töröl?',
|
||||||
'trip.toast.placesDeleted': '{count} hely törölve',
|
'trip.toast.placesDeleted': '{count} hely törölve',
|
||||||
|
'trip.splash.goBack': 'Taking too long? Go back to dashboard',
|
||||||
'trip.loadingPhotos': 'Helyek fotóinak betöltése...',
|
'trip.loadingPhotos': 'Helyek fotóinak betöltése...',
|
||||||
|
|
||||||
// Napi terv oldalsáv
|
// Napi terv oldalsáv
|
||||||
|
|||||||
@@ -983,6 +983,7 @@ const id: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'trip.tabs.budget': 'Anggaran',
|
'trip.tabs.budget': 'Anggaran',
|
||||||
'trip.tabs.files': 'File',
|
'trip.tabs.files': 'File',
|
||||||
'trip.loading': 'Memuat perjalanan...',
|
'trip.loading': 'Memuat perjalanan...',
|
||||||
|
'trip.splash.goBack': 'Taking too long? Go back to dashboard',
|
||||||
'trip.loadingPhotos': 'Memuat foto tempat...',
|
'trip.loadingPhotos': 'Memuat foto tempat...',
|
||||||
'trip.mobilePlan': 'Rencana',
|
'trip.mobilePlan': 'Rencana',
|
||||||
'trip.mobilePlaces': 'Tempat',
|
'trip.mobilePlaces': 'Tempat',
|
||||||
|
|||||||
@@ -937,6 +937,7 @@ const it: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'trip.confirm.deletePlace': 'Sei sicuro di voler eliminare questo luogo?',
|
'trip.confirm.deletePlace': 'Sei sicuro di voler eliminare questo luogo?',
|
||||||
'trip.confirm.deletePlaces': 'Eliminare {count} luoghi?',
|
'trip.confirm.deletePlaces': 'Eliminare {count} luoghi?',
|
||||||
'trip.toast.placesDeleted': '{count} luoghi eliminati',
|
'trip.toast.placesDeleted': '{count} luoghi eliminati',
|
||||||
|
'trip.splash.goBack': 'Taking too long? Go back to dashboard',
|
||||||
'trip.loadingPhotos': 'Caricamento foto dei luoghi...',
|
'trip.loadingPhotos': 'Caricamento foto dei luoghi...',
|
||||||
|
|
||||||
// Day Plan Sidebar
|
// Day Plan Sidebar
|
||||||
|
|||||||
@@ -922,6 +922,7 @@ const nl: Record<string, string> = {
|
|||||||
'trip.tabs.budget': 'Budget',
|
'trip.tabs.budget': 'Budget',
|
||||||
'trip.tabs.files': 'Bestanden',
|
'trip.tabs.files': 'Bestanden',
|
||||||
'trip.loading': 'Reis laden...',
|
'trip.loading': 'Reis laden...',
|
||||||
|
'trip.splash.goBack': 'Taking too long? Go back to dashboard',
|
||||||
'trip.loadingPhotos': 'Plaatsfoto laden...',
|
'trip.loadingPhotos': 'Plaatsfoto laden...',
|
||||||
'trip.mobilePlan': 'Plan',
|
'trip.mobilePlan': 'Plan',
|
||||||
'trip.mobilePlaces': 'Plaatsen',
|
'trip.mobilePlaces': 'Plaatsen',
|
||||||
|
|||||||
@@ -1764,6 +1764,7 @@ const pl: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'login.setNewPassword': 'Ustaw nowe hasło',
|
'login.setNewPassword': 'Ustaw nowe hasło',
|
||||||
'login.setNewPasswordHint': 'Musisz zmienić hasło.',
|
'login.setNewPasswordHint': 'Musisz zmienić hasło.',
|
||||||
'atlas.searchCountry': 'Szukaj kraju...',
|
'atlas.searchCountry': 'Szukaj kraju...',
|
||||||
|
'trip.splash.goBack': 'Taking too long? Go back to dashboard',
|
||||||
'trip.loadingPhotos': 'Ładowanie zdjęć...',
|
'trip.loadingPhotos': 'Ładowanie zdjęć...',
|
||||||
'places.importNaverList': 'Lista Naver',
|
'places.importNaverList': 'Lista Naver',
|
||||||
'places.importList': 'Import listy',
|
'places.importList': 'Import listy',
|
||||||
|
|||||||
@@ -922,6 +922,7 @@ const ru: Record<string, string> = {
|
|||||||
'trip.tabs.budget': 'Бюджет',
|
'trip.tabs.budget': 'Бюджет',
|
||||||
'trip.tabs.files': 'Файлы',
|
'trip.tabs.files': 'Файлы',
|
||||||
'trip.loading': 'Загрузка поездки...',
|
'trip.loading': 'Загрузка поездки...',
|
||||||
|
'trip.splash.goBack': 'Taking too long? Go back to dashboard',
|
||||||
'trip.loadingPhotos': 'Загрузка фото мест...',
|
'trip.loadingPhotos': 'Загрузка фото мест...',
|
||||||
'trip.mobilePlan': 'План',
|
'trip.mobilePlan': 'План',
|
||||||
'trip.mobilePlaces': 'Места',
|
'trip.mobilePlaces': 'Места',
|
||||||
|
|||||||
@@ -922,6 +922,7 @@ const zh: Record<string, string> = {
|
|||||||
'trip.tabs.budget': '预算',
|
'trip.tabs.budget': '预算',
|
||||||
'trip.tabs.files': '文件',
|
'trip.tabs.files': '文件',
|
||||||
'trip.loading': '加载旅行中...',
|
'trip.loading': '加载旅行中...',
|
||||||
|
'trip.splash.goBack': 'Taking too long? Go back to dashboard',
|
||||||
'trip.loadingPhotos': '正在加载地点照片...',
|
'trip.loadingPhotos': '正在加载地点照片...',
|
||||||
'trip.mobilePlan': '计划',
|
'trip.mobilePlan': '计划',
|
||||||
'trip.mobilePlaces': '地点',
|
'trip.mobilePlaces': '地点',
|
||||||
|
|||||||
@@ -982,6 +982,7 @@ const zhTw: Record<string, string> = {
|
|||||||
'trip.tabs.budget': '預算',
|
'trip.tabs.budget': '預算',
|
||||||
'trip.tabs.files': '檔案',
|
'trip.tabs.files': '檔案',
|
||||||
'trip.loading': '載入旅行中...',
|
'trip.loading': '載入旅行中...',
|
||||||
|
'trip.splash.goBack': 'Taking too long? Go back to dashboard',
|
||||||
'trip.loadingPhotos': '正在載入地點照片...',
|
'trip.loadingPhotos': '正在載入地點照片...',
|
||||||
'trip.mobilePlan': '計劃',
|
'trip.mobilePlan': '計劃',
|
||||||
'trip.mobilePlaces': '地點',
|
'trip.mobilePlaces': '地點',
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import { useTranslation } from '../i18n'
|
|||||||
import { addonsApi, accommodationsApi, authApi, tripsApi, assignmentsApi, mapsApi } from '../api/client'
|
import { addonsApi, accommodationsApi, authApi, tripsApi, assignmentsApi, mapsApi } from '../api/client'
|
||||||
import { accommodationRepo } from '../repo/accommodationRepo'
|
import { accommodationRepo } from '../repo/accommodationRepo'
|
||||||
import { offlineDb } from '../db/offlineDb'
|
import { offlineDb } from '../db/offlineDb'
|
||||||
|
import { tripSyncManager } from '../sync/tripSyncManager'
|
||||||
import { useAuthStore } from '../store/authStore'
|
import { useAuthStore } from '../store/authStore'
|
||||||
import ConfirmDialog from '../components/shared/ConfirmDialog'
|
import ConfirmDialog from '../components/shared/ConfirmDialog'
|
||||||
import { useResizablePanels } from '../hooks/useResizablePanels'
|
import { useResizablePanels } from '../hooks/useResizablePanels'
|
||||||
@@ -328,6 +329,8 @@ export default function TripPlannerPage(): React.ReactElement | null {
|
|||||||
// Load trip + files (needed for place inspector file section)
|
// Load trip + files (needed for place inspector file section)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (tripId) {
|
if (tripId) {
|
||||||
|
// Stop background sync so its bundle requests don't compete with loadTrip
|
||||||
|
tripSyncManager.interrupt()
|
||||||
tripActions.loadTrip(tripId).catch(() => { toast.error(t('trip.toast.loadError')); navigate('/dashboard') })
|
tripActions.loadTrip(tripId).catch(() => { toast.error(t('trip.toast.loadError')); navigate('/dashboard') })
|
||||||
tripActions.loadFiles(tripId)
|
tripActions.loadFiles(tripId)
|
||||||
loadAccommodations()
|
loadAccommodations()
|
||||||
@@ -726,12 +729,18 @@ export default function TripPlannerPage(): React.ReactElement | null {
|
|||||||
|
|
||||||
// Splash screen — show for initial load + a brief moment for photos to start loading
|
// Splash screen — show for initial load + a brief moment for photos to start loading
|
||||||
const [splashDone, setSplashDone] = useState(false)
|
const [splashDone, setSplashDone] = useState(false)
|
||||||
|
const [slowLoad, setSlowLoad] = useState(false)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isLoading && trip) {
|
if (!isLoading && trip) {
|
||||||
const timer = setTimeout(() => setSplashDone(true), 1500)
|
const timer = setTimeout(() => setSplashDone(true), 1500)
|
||||||
return () => clearTimeout(timer)
|
return () => clearTimeout(timer)
|
||||||
}
|
}
|
||||||
}, [isLoading, trip])
|
}, [isLoading, trip])
|
||||||
|
// Show escape hatch after 12 seconds on splash (covers slow first-load scenarios)
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setTimeout(() => setSlowLoad(true), 12000)
|
||||||
|
return () => clearTimeout(timer)
|
||||||
|
}, [])
|
||||||
|
|
||||||
if (isLoading || !splashDone) {
|
if (isLoading || !splashDone) {
|
||||||
return (
|
return (
|
||||||
@@ -771,6 +780,18 @@ export default function TripPlannerPage(): React.ReactElement | null {
|
|||||||
}} />
|
}} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
{slowLoad && (
|
||||||
|
<button
|
||||||
|
onClick={() => navigate('/dashboard')}
|
||||||
|
style={{
|
||||||
|
marginTop: 24, appearance: 'none', border: 'none', cursor: 'pointer',
|
||||||
|
fontFamily: 'inherit', background: 'transparent',
|
||||||
|
color: 'var(--text-faint)', fontSize: 13, textDecoration: 'underline',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('trip.splash.goBack')}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
upsertCategories,
|
upsertCategories,
|
||||||
upsertSyncMeta,
|
upsertSyncMeta,
|
||||||
clearTripData,
|
clearTripData,
|
||||||
|
clearBlobCache,
|
||||||
clearAll,
|
clearAll,
|
||||||
} from '../db/offlineDb'
|
} from '../db/offlineDb'
|
||||||
import { prefetchTilesForTrip } from './tilePrefetcher'
|
import { prefetchTilesForTrip } from './tilePrefetcher'
|
||||||
@@ -135,6 +136,7 @@ async function cacheFilesForTrip(files: TripFile[]): Promise<void> {
|
|||||||
// ── Public API ────────────────────────────────────────────────────────────────
|
// ── Public API ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
let _syncing = false
|
let _syncing = false
|
||||||
|
let _interrupted = false
|
||||||
|
|
||||||
export const tripSyncManager = {
|
export const tripSyncManager = {
|
||||||
/**
|
/**
|
||||||
@@ -145,6 +147,7 @@ export const tripSyncManager = {
|
|||||||
async syncAll(): Promise<void> {
|
async syncAll(): Promise<void> {
|
||||||
if (_syncing || !navigator.onLine) return
|
if (_syncing || !navigator.onLine) return
|
||||||
_syncing = true
|
_syncing = true
|
||||||
|
_interrupted = false
|
||||||
try {
|
try {
|
||||||
const { trips } = await tripsApi.list() as { trips: Trip[] }
|
const { trips } = await tripsApi.list() as { trips: Trip[] }
|
||||||
|
|
||||||
@@ -152,9 +155,10 @@ export const tripSyncManager = {
|
|||||||
const stale = trips.filter(isStale)
|
const stale = trips.filter(isStale)
|
||||||
await Promise.all(stale.map(t => clearTripData(t.id).catch(console.error)))
|
await Promise.all(stale.map(t => clearTripData(t.id).catch(console.error)))
|
||||||
|
|
||||||
// Sync eligible trips
|
// Sync eligible trips — stop early if interrupted (e.g. user navigated to a trip page)
|
||||||
const toSync = trips.filter(shouldCache)
|
const toSync = trips.filter(shouldCache)
|
||||||
for (const trip of toSync) {
|
for (const trip of toSync) {
|
||||||
|
if (_interrupted) break
|
||||||
try {
|
try {
|
||||||
await syncTrip(trip.id)
|
await syncTrip(trip.id)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -165,11 +169,19 @@ export const tripSyncManager = {
|
|||||||
await syncTrip(trip.id)
|
await syncTrip(trip.id)
|
||||||
} catch (retryErr) {
|
} catch (retryErr) {
|
||||||
if (isQuotaError(retryErr)) {
|
if (isQuotaError(retryErr)) {
|
||||||
console.warn('[tripSync] quota still exceeded after eviction — clearing all IDB data')
|
// Trip data + blob cache — free largest storage first before nuking everything
|
||||||
await clearAll()
|
console.warn('[tripSync] quota still exceeded — clearing blob cache and retrying')
|
||||||
return
|
await clearBlobCache()
|
||||||
|
try {
|
||||||
|
await syncTrip(trip.id)
|
||||||
|
} catch {
|
||||||
|
console.warn('[tripSync] quota still exceeded after blob eviction — clearing all IDB data')
|
||||||
|
await clearAll()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error(`[tripSync] failed for trip ${trip.id} after eviction:`, retryErr)
|
||||||
}
|
}
|
||||||
console.error(`[tripSync] failed for trip ${trip.id} after eviction:`, retryErr)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error(`[tripSync] failed for trip ${trip.id}:`, err)
|
console.error(`[tripSync] failed for trip ${trip.id}:`, err)
|
||||||
@@ -177,6 +189,8 @@ export const tripSyncManager = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_interrupted) return
|
||||||
|
|
||||||
// Cache global user data (tags + categories) — fire-and-forget
|
// Cache global user data (tags + categories) — fire-and-forget
|
||||||
tagsApi.list().then(d => upsertTags(d.tags)).catch(() => {})
|
tagsApi.list().then(d => upsertTags(d.tags)).catch(() => {})
|
||||||
categoriesApi.list().then(d => upsertCategories(d.categories)).catch(() => {})
|
categoriesApi.list().then(d => upsertCategories(d.categories)).catch(() => {})
|
||||||
@@ -184,6 +198,7 @@ export const tripSyncManager = {
|
|||||||
// Cache file blobs + map tiles in background (don't block syncAll)
|
// Cache file blobs + map tiles in background (don't block syncAll)
|
||||||
const tileUrl = useSettingsStore.getState().settings.map_tile_url || undefined
|
const tileUrl = useSettingsStore.getState().settings.map_tile_url || undefined
|
||||||
for (const trip of toSync) {
|
for (const trip of toSync) {
|
||||||
|
if (_interrupted) break
|
||||||
const files = await offlineDb.tripFiles.where('trip_id').equals(trip.id).toArray()
|
const files = await offlineDb.tripFiles.where('trip_id').equals(trip.id).toArray()
|
||||||
cacheFilesForTrip(files).catch(console.error)
|
cacheFilesForTrip(files).catch(console.error)
|
||||||
|
|
||||||
@@ -195,8 +210,17 @@ export const tripSyncManager = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal syncAll to stop after the current in-flight bundle request.
|
||||||
|
* Call when the user navigates to a trip page so loadTrip gets priority.
|
||||||
|
*/
|
||||||
|
interrupt(): void {
|
||||||
|
_interrupted = true
|
||||||
|
},
|
||||||
|
|
||||||
/** Reset syncing flag — useful in tests. */
|
/** Reset syncing flag — useful in tests. */
|
||||||
_resetSyncing(): void {
|
_resetSyncing(): void {
|
||||||
_syncing = false
|
_syncing = false
|
||||||
|
_interrupted = false
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user