UNPKG

@gabriel-sisjr/react-native-background-location

Version:

React Native library for background location tracking using TurboModules. Track user location even when the app is minimized or in the background.

237 lines (227 loc) 6.49 kB
"use strict"; import { useState, useCallback, useEffect } from 'react'; import BackgroundLocationModule from "../NativeBackgroundLocation.js"; // Check if native module is available const isNativeModuleAvailable = () => { try { // Check if methods are available (works with Proxy mocks) // This must be checked first before checking if module exists if (typeof BackgroundLocationModule?.startTracking !== 'function') { return false; } // Check if module exists and is not null if (!BackgroundLocationModule || BackgroundLocationModule === null) { return false; } return true; } catch { return false; } }; /** * Hook to manage background location tracking * * Provides a complete interface for starting/stopping tracking, * managing trip data, and handling locations. * * @param options - Configuration options * * @example * ```tsx * function TrackingScreen() { * const { * isTracking, * tripId, * locations, * startTracking, * stopTracking, * error * } = useBackgroundLocation({ * autoStart: false, * onTrackingStart: (id) => console.log('Started:', id), * onError: (err) => console.error(err), * }); * * return ( * <View> * <Button onPress={startTracking}> * {isTracking ? 'Stop' : 'Start'} Tracking * </Button> * <Text>Locations: {locations.length}</Text> * </View> * ); * } * ``` */ export function useBackgroundLocation(options = {}) { const { autoStart = false, tripId: initialTripId, options: trackingOptions, onTrackingStart, onTrackingStop, onError } = options; const [tripId, setTripId] = useState(null); const [isTracking, setIsTracking] = useState(false); const [locations, setLocations] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); /** * Check tracking status on mount */ useEffect(() => { const checkStatus = async () => { if (!isNativeModuleAvailable()) { console.warn('BackgroundLocation not available - running in simulator or module not linked?'); return; } try { const status = await BackgroundLocationModule.isTracking(); setIsTracking(status.active); if (status.tripId) { setTripId(status.tripId); // Load existing locations const locs = await BackgroundLocationModule.getLocations(status.tripId); setLocations(locs); } } catch (err) { console.error('Error checking tracking status:', err); } }; checkStatus(); }, []); /** * Auto-start tracking if enabled */ useEffect(() => { if (autoStart && !isTracking) { startTracking(initialTripId, trackingOptions); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [autoStart, trackingOptions]); /** * Clear error state */ const clearError = useCallback(() => { setError(null); }, []); /** * Start tracking */ const startTracking = useCallback(async (customTripId, trackingOptionsOverride) => { if (!isNativeModuleAvailable()) { const simulatedId = customTripId || `simulator-trip-${Date.now()}`; setTripId(simulatedId); setIsTracking(true); console.warn('BackgroundLocation not available - using simulated trip ID'); return simulatedId; } setIsLoading(true); clearError(); try { // Use provided options or fallback to options from hook config const effectiveOptions = trackingOptionsOverride || trackingOptions; const effectiveTripId = await BackgroundLocationModule.startTracking(customTripId, effectiveOptions); setTripId(effectiveTripId); setIsTracking(true); setLocations([]); // Clear previous locations onTrackingStart?.(effectiveTripId); return effectiveTripId; } catch (err) { const trackingError = err instanceof Error ? err : new Error('Failed to start tracking'); setError(trackingError); onError?.(trackingError); return null; } finally { setIsLoading(false); } }, [clearError, onTrackingStart, onError, trackingOptions]); /** * Stop tracking */ const stopTracking = useCallback(async () => { if (!isNativeModuleAvailable()) { setIsTracking(false); console.warn('BackgroundLocation not available - simulated stop'); onTrackingStop?.(); return; } setIsLoading(true); clearError(); try { await BackgroundLocationModule.stopTracking(); setIsTracking(false); onTrackingStop?.(); } catch (err) { const stopError = err instanceof Error ? err : new Error('Failed to stop tracking'); setError(stopError); onError?.(stopError); throw stopError; } finally { setIsLoading(false); } }, [clearError, onTrackingStop, onError]); /** * Refresh locations for current trip */ const refreshLocations = useCallback(async () => { if (!tripId) { return; } if (!isNativeModuleAvailable()) { console.warn('BackgroundLocation not available'); return; } setIsLoading(true); clearError(); try { const locs = await BackgroundLocationModule.getLocations(tripId); setLocations(locs); } catch (err) { const locError = err instanceof Error ? err : new Error('Failed to get locations'); setError(locError); onError?.(locError); } finally { setIsLoading(false); } }, [tripId, clearError, onError]); /** * Clear all data for current trip */ const clearCurrentTrip = useCallback(async () => { if (!tripId) { return; } if (!isNativeModuleAvailable()) { setLocations([]); console.warn('BackgroundLocation not available'); return; } setIsLoading(true); clearError(); try { await BackgroundLocationModule.clearTrip(tripId); setLocations([]); } catch (err) { const tripError = err instanceof Error ? err : new Error('Failed to clear trip'); setError(tripError); onError?.(tripError); } finally { setIsLoading(false); } }, [tripId, clearError, onError]); return { tripId, isTracking, locations, isLoading, error, startTracking, stopTracking, refreshLocations, clearCurrentTrip, clearError }; } //# sourceMappingURL=useBackgroundLocation.js.map