UNPKG

expo-osm-sdk

Version:

OpenStreetMap component for React Native with Expo

237 lines 8.99 kB
"use strict"; /** * useGeofencing Hook * * Real-time geofencing for location-based apps * * Features: * - Circle and polygon geofences * - Enter, exit, and dwell events * - Multiple geofence monitoring * - Performance optimized * - TypeScript support * * @example * ```tsx * const { activeGeofences, isInGeofence } = useGeofencing(geofences, { * onEnter: (event) => console.log('Entered:', event.geofenceName), * onExit: (event) => console.log('Exited:', event.geofenceName), * checkInterval: 5000, * }); * ``` */ Object.defineProperty(exports, "__esModule", { value: true }); exports.useGeofencing = useGeofencing; exports.useSingleGeofence = useSingleGeofence; const react_1 = require("react"); const useLocationTracking_1 = require("./useLocationTracking"); const geofencing_1 = require("../utils/geofencing"); /** * Hook for monitoring geofences and detecting enter/exit/dwell events * * @param mapRef Reference to OSMView component * @param geofences Array of geofences to monitor * @param options Configuration options * @returns Geofencing state and controls */ function useGeofencing(mapRef, geofences, options = {}) { const { checkInterval = 5000, // Check every 5 seconds dwellThreshold = 60000, // 1 minute enableHighAccuracy = true, onEnter, onExit, onDwell, onEvent, } = options; // State const [activeGeofences, setActiveGeofences] = (0, react_1.useState)([]); const [geofenceStates, setGeofenceStates] = (0, react_1.useState)(new Map()); const [events, setEvents] = (0, react_1.useState)([]); // Refs for callbacks (avoid re-creating interval on callback changes) const onEnterRef = (0, react_1.useRef)(onEnter); const onExitRef = (0, react_1.useRef)(onExit); const onDwellRef = (0, react_1.useRef)(onDwell); const onEventRef = (0, react_1.useRef)(onEvent); // Ref for dwell check intervals const dwellCheckIntervalRef = (0, react_1.useRef)(null); // Update refs when callbacks change (0, react_1.useEffect)(() => { onEnterRef.current = onEnter; onExitRef.current = onExit; onDwellRef.current = onDwell; onEventRef.current = onEvent; }, [onEnter, onExit, onDwell, onEvent]); // Track location const { currentLocation, isTracking } = (0, useLocationTracking_1.useLocationTracking)(mapRef, { autoStart: true, }); // Validate geofences on mount (0, react_1.useEffect)(() => { const invalidGeofences = geofences.filter(g => !(0, geofencing_1.validateGeofence)(g)); if (invalidGeofences.length > 0) { console.warn(`Found ${invalidGeofences.length} invalid geofences`); } }, [geofences]); /** * Emit a geofence event */ const emitEvent = (0, react_1.useCallback)((event) => { // Add to events array setEvents(prev => [...prev, event]); // Call specific callback if (event.type === 'enter' && onEnterRef.current) { onEnterRef.current(event); } else if (event.type === 'exit' && onExitRef.current) { onExitRef.current(event); } else if (event.type === 'dwell' && onDwellRef.current) { onDwellRef.current(event); } // Call general callback if (onEventRef.current) { onEventRef.current(event); } console.log(`Geofence ${event.type}:`, event.geofenceId, event.geofenceName); }, []); /** * Check all geofences for current location */ const checkGeofences = (0, react_1.useCallback)(() => { if (!currentLocation) return; const now = Date.now(); const newActiveGeofences = []; const newStates = new Map(geofenceStates); geofences.forEach(geofence => { const isInside = (0, geofencing_1.isPointInGeofence)(currentLocation, geofence); const wasInside = activeGeofences.includes(geofence.id); if (isInside) { newActiveGeofences.push(geofence.id); if (!wasInside) { // ENTER event const distance = Math.abs((0, geofencing_1.distanceToGeofence)(currentLocation, geofence)); emitEvent({ geofenceId: geofence.id, geofenceName: geofence.name, type: 'enter', coordinate: currentLocation, timestamp: now, distance, metadata: geofence.metadata, }); // Initialize state newStates.set(geofence.id, { geofenceId: geofence.id, enteredAt: now, lastUpdate: now, dwellTime: 0, }); } else { // Still inside - update state const state = newStates.get(geofence.id); if (state) { state.lastUpdate = now; state.dwellTime = now - state.enteredAt; newStates.set(geofence.id, state); } } } else if (wasInside) { // EXIT event const distance = Math.abs((0, geofencing_1.distanceToGeofence)(currentLocation, geofence)); emitEvent({ geofenceId: geofence.id, geofenceName: geofence.name, type: 'exit', coordinate: currentLocation, timestamp: now, distance, metadata: geofence.metadata, }); // Remove state newStates.delete(geofence.id); } }); setActiveGeofences(newActiveGeofences); setGeofenceStates(newStates); }, [currentLocation, geofences, activeGeofences, geofenceStates, emitEvent]); /** * Check for dwell events */ const checkDwellEvents = (0, react_1.useCallback)(() => { const now = Date.now(); geofenceStates.forEach((state, geofenceId) => { const dwellTime = now - state.enteredAt; // Check if we just crossed the dwell threshold if (dwellTime >= dwellThreshold && state.dwellTime < dwellThreshold) { const geofence = geofences.find(g => g.id === geofenceId); if (geofence && currentLocation) { emitEvent({ geofenceId, geofenceName: geofence.name, type: 'dwell', coordinate: currentLocation, timestamp: now, metadata: geofence.metadata, }); } } }); }, [geofenceStates, dwellThreshold, geofences, currentLocation, emitEvent]); // Set up geofence checking interval (0, react_1.useEffect)(() => { if (!currentLocation) return; // Initial check checkGeofences(); // Set up interval const intervalId = setInterval(checkGeofences, checkInterval); return () => clearInterval(intervalId); }, [currentLocation, checkGeofences, checkInterval]); // Set up dwell checking interval (more frequent) (0, react_1.useEffect)(() => { if (geofenceStates.size === 0) return; // Check dwell every 1 second dwellCheckIntervalRef.current = setInterval(checkDwellEvents, 1000); return () => { if (dwellCheckIntervalRef.current) { clearInterval(dwellCheckIntervalRef.current); } }; }, [checkDwellEvents, geofenceStates.size]); /** * Check if user is inside a specific geofence */ const isInGeofence = (0, react_1.useCallback)((geofenceId) => { return activeGeofences.includes(geofenceId); }, [activeGeofences]); /** * Get dwell time for a specific geofence */ const getDwellTime = (0, react_1.useCallback)((geofenceId) => { const state = geofenceStates.get(geofenceId); if (!state) return 0; return Date.now() - state.enteredAt; }, [geofenceStates]); return { activeGeofences, geofenceStates, isInGeofence, getDwellTime, checkGeofences, currentLocation, isTracking, events, }; } /** * Simple helper hook for single geofence monitoring */ function useSingleGeofence(mapRef, geofence, options = {}) { const result = useGeofencing(mapRef, [geofence], options); return { ...result, isInside: result.isInGeofence(geofence.id), dwellTime: result.getDwellTime(geofence.id), }; } //# sourceMappingURL=useGeofencing.js.map