as-app/src/location/backgroundGeolocationService.js
2026-01-12 18:28:22 +01:00

122 lines
3.2 KiB
JavaScript

import BackgroundGeolocation from "react-native-background-geolocation";
import { createLogger } from "~/lib/logger";
import { BACKGROUND_SCOPES } from "~/lib/logger/scopes";
import { BASE_GEOLOCATION_CONFIG } from "./backgroundGeolocationConfig";
const bgGeoLogger = createLogger({
module: BACKGROUND_SCOPES.GEOLOCATION,
feature: "bg-geo-service",
});
let readyPromise = null;
let lastReadyState = null;
let subscriptions = [];
let handlersSignature = null;
export async function ensureBackgroundGeolocationReady(
config = BASE_GEOLOCATION_CONFIG,
) {
if (readyPromise) return readyPromise;
readyPromise = (async () => {
bgGeoLogger.info("Calling BackgroundGeolocation.ready");
const state = await BackgroundGeolocation.ready(config);
lastReadyState = state;
bgGeoLogger.info("BackgroundGeolocation is ready", {
enabled: state?.enabled,
isMoving: state?.isMoving,
trackingMode: state?.trackingMode,
schedulerEnabled: state?.schedulerEnabled,
});
return state;
})().catch((error) => {
// Allow retry if ready fails.
readyPromise = null;
lastReadyState = null;
bgGeoLogger.error("BackgroundGeolocation.ready failed", {
error: error?.message,
stack: error?.stack,
code: error?.code,
});
throw error;
});
return readyPromise;
}
export function getLastReadyState() {
return lastReadyState;
}
export function setBackgroundGeolocationEventHandlers({
onLocation,
onLocationError,
onHttp,
onMotionChange,
onActivityChange,
onProviderChange,
onConnectivityChange,
onEnabledChange,
} = {}) {
// Avoid duplicate registration when `trackLocation()` is called multiple times.
// We use a simple signature so calling with identical functions is a no-op.
const sig = [
onLocation ? "L1" : "L0",
onHttp ? "H1" : "H0",
onMotionChange ? "M1" : "M0",
onActivityChange ? "A1" : "A0",
onProviderChange ? "P1" : "P0",
onConnectivityChange ? "C1" : "C0",
onEnabledChange ? "E1" : "E0",
].join("-");
if (handlersSignature === sig && subscriptions.length) {
return;
}
subscriptions.forEach((s) => s?.remove?.());
subscriptions = [];
if (onLocation) {
subscriptions.push(
BackgroundGeolocation.onLocation(onLocation, onLocationError),
);
}
if (onHttp) {
subscriptions.push(BackgroundGeolocation.onHttp(onHttp));
}
if (onMotionChange) {
subscriptions.push(BackgroundGeolocation.onMotionChange(onMotionChange));
}
if (onActivityChange) {
subscriptions.push(
BackgroundGeolocation.onActivityChange(onActivityChange),
);
}
if (onProviderChange) {
subscriptions.push(
BackgroundGeolocation.onProviderChange(onProviderChange),
);
}
if (onConnectivityChange) {
subscriptions.push(
BackgroundGeolocation.onConnectivityChange(onConnectivityChange),
);
}
if (onEnabledChange) {
subscriptions.push(BackgroundGeolocation.onEnabledChange(onEnabledChange));
}
handlersSignature = sig;
}
export async function stopBackgroundGeolocation() {
await ensureBackgroundGeolocationReady();
return BackgroundGeolocation.stop();
}
export async function startBackgroundGeolocation() {
await ensureBackgroundGeolocationReady();
return BackgroundGeolocation.start();
}