Spaces:
Running
Running
| /** | |
| * Sync triggers β register event listeners that flush the mutation queue | |
| * and/or run a full trip sync based on the connectivity trigger source. | |
| * | |
| * Trigger matrix: | |
| * window 'online' β flush mutations + full syncAll (network truly back) | |
| * visibilitychange visible β flush mutations only (avoid hammering server on tab switch) | |
| * periodic 30s β flush mutations only | |
| * WS reconnect β flush mutations only (no syncAll β avoids rate-limiter | |
| * on server restart / socket timeout while already online) | |
| * | |
| * Call `registerSyncTriggers()` once on app mount. | |
| * Call `unregisterSyncTriggers()` on unmount / logout. | |
| */ | |
| import { mutationQueue } from './mutationQueue' | |
| import { tripSyncManager } from './tripSyncManager' | |
| import { setPreReconnectHook, setRefetchCallback, getActiveTrips } from '../api/websocket' | |
| import { useTripStore } from '../store/tripStore' | |
| const PERIODIC_MS = 30_000 | |
| let _intervalId: ReturnType<typeof setInterval> | null = null | |
| let _registered = false | |
| /** Pull the latest server state for every open trip into the Zustand store. */ | |
| function rehydrateActiveTrips() { | |
| const store = useTripStore.getState() | |
| for (const tripId of getActiveTrips()) { | |
| store.hydrateActiveTrip(tripId).catch(console.error) | |
| } | |
| } | |
| /** | |
| * Network came back β flush local writes first, then re-seed Dexie for all | |
| * cacheable trips and re-hydrate the open trip's store so a collaborator's | |
| * edits made while we were offline appear without navigating away. | |
| */ | |
| function onOnline() { | |
| mutationQueue.flush() | |
| .catch(console.error) | |
| .finally(() => { | |
| tripSyncManager.syncAll().catch(console.error) | |
| rehydrateActiveTrips() | |
| }) | |
| } | |
| /** Tab became visible β flush only; don't trigger a potentially expensive syncAll. */ | |
| function onVisibility() { | |
| if (!document.hidden && navigator.onLine) { | |
| mutationQueue.flush().catch(console.error) | |
| } | |
| } | |
| /** Periodic heartbeat β drain any lingering pending mutations. */ | |
| function onPeriodic() { | |
| if (navigator.onLine) { | |
| mutationQueue.flush().catch(console.error) | |
| } | |
| } | |
| export function registerSyncTriggers(): void { | |
| if (_registered) return | |
| _registered = true | |
| // WS reconnect: flush mutations only β no syncAll to avoid triggering rate | |
| // limiters when the socket drops and reconnects while the device is online. | |
| setPreReconnectHook(() => mutationQueue.flush()) | |
| // After the reconnect flush, pull canonical state for the open trip back into | |
| // the store (the WS layer awaits the flush hook before invoking this). | |
| setRefetchCallback(tripId => { | |
| useTripStore.getState().hydrateActiveTrip(tripId).catch(console.error) | |
| }) | |
| window.addEventListener('online', onOnline) | |
| document.addEventListener('visibilitychange', onVisibility) | |
| _intervalId = setInterval(onPeriodic, PERIODIC_MS) | |
| } | |
| export function unregisterSyncTriggers(): void { | |
| if (!_registered) return | |
| _registered = false | |
| setPreReconnectHook(null) | |
| setRefetchCallback(null) | |
| window.removeEventListener('online', onOnline) | |
| document.removeEventListener('visibilitychange', onVisibility) | |
| if (_intervalId !== null) { | |
| clearInterval(_intervalId) | |
| _intervalId = null | |
| } | |
| } | |