expo-osm-sdk
Version:
OpenStreetMap component for React Native with Expo
237 lines • 8.99 kB
JavaScript
"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