import BackgroundGeolocation from "react-native-background-geolocation"; import { memoryAsyncStorage } from "~/storage/memoryAsyncStorage"; import { STORAGE_KEYS } from "~/storage/storageKeys"; import { createLogger } from "~/lib/logger"; // Constants for persistence // const FORCE_SYNC_INTERVAL = 12 * 60 * 60 * 1000; const FORCE_SYNC_INTERVAL = 1 * 60 * 1000; // DEBUGGING const geolocBgLogger = createLogger({ service: "background-task", task: "headless", }); // Helper functions for persisting sync time const getLastSyncTime = async () => { try { const value = await memoryAsyncStorage.getItem( STORAGE_KEYS.GEOLOCATION_LAST_SYNC_TIME, ); return value ? parseInt(value, 10) : Date.now(); } catch (error) { return 0; } }; const setLastSyncTime = async (time) => { try { await memoryAsyncStorage.setItem( STORAGE_KEYS.GEOLOCATION_LAST_SYNC_TIME, time.toString(), ); } catch (error) { // silent error } }; // Shared heartbeat logic - mutualized between Android and iOS const executeSync = async () => { let syncPerformed = false; let syncSuccessful = false; try { syncPerformed = true; try { // Change pace to ensure location updates await BackgroundGeolocation.changePace(true); // Perform sync await BackgroundGeolocation.sync(); syncSuccessful = true; } catch (syncError) { syncSuccessful = false; } // Return result information for BackgroundFetch return { syncPerformed, syncSuccessful, }; } catch (error) { // Return error result for BackgroundFetch return { syncPerformed, syncSuccessful: false, error: error.message, }; } }; export const executeHeartbeatSync = async () => { const lastSyncTime = await getLastSyncTime(); const now = Date.now(); const timeSinceLastSync = now - lastSyncTime; if (timeSinceLastSync >= FORCE_SYNC_INTERVAL) { geolocBgLogger.info("Forcing location sync", { timeSinceLastSync, forceInterval: FORCE_SYNC_INTERVAL, }); try { const syncResult = await Promise.race([ executeSync(), new Promise((_, reject) => setTimeout(() => reject(new Error("changePace timeout")), 10000), ), ]); await setLastSyncTime(now); geolocBgLogger.info("Force sync completed successfully", { syncResult, }); return syncResult; } catch (syncError) { geolocBgLogger.error("Force sync failed", { error: syncError.message, timeSinceLastSync, }); return { syncPerformed: true, syncSuccessful: false, error: syncError.message, }; } } else { geolocBgLogger.debug("Sync not needed yet", { timeSinceLastSync, forceInterval: FORCE_SYNC_INTERVAL, timeUntilNextSync: FORCE_SYNC_INTERVAL - timeSinceLastSync, }); return { syncPerformed: false, syncSuccessful: true, }; } };