import React, { createContext, useCallback, useEffect, useRef, useState, ReactNode } from "react"; import { Location } from "../types/location"; type LocationError = "permission_denied" | "location_error" | null; type LocationContextValue = { location: Location | null; loading: boolean; error: LocationError; requestLocation: () => void; }; export const LocationContext = createContext(undefined); export const LocationProvider = ({ children }: { children: ReactNode }) => { const [location, setLocation] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const currentRequest = useRef(0); const handleSuccess = (pos: GeolocationPosition) => { const loc: Location = { latitude: pos.coords.latitude, longitude: pos.coords.longitude, accuracy: pos.coords.accuracy, }; setLocation(loc); setError(null); setLoading(false); // Log once received // eslint-disable-next-line no-console console.info("User location obtained", loc); }; const handleError = (err: GeolocationPositionError) => { setLocation(null); setLoading(false); if (err.code === err.PERMISSION_DENIED) { setError("permission_denied"); // eslint-disable-next-line no-console console.warn("Geolocation permission denied", err.message); } else { setError("location_error"); // eslint-disable-next-line no-console console.warn("Geolocation error", err.message); } }; const performGetPosition = useCallback(() => { if (!("geolocation" in navigator)) { setLocation(null); setError("location_error"); setLoading(false); return; } const requestId = ++currentRequest.current; setLoading(true); setError(null); navigator.geolocation.getCurrentPosition( (pos) => { // ignore if a newer request started if (requestId !== currentRequest.current) return; handleSuccess(pos); }, (err) => { if (requestId !== currentRequest.current) return; handleError(err); }, { enableHighAccuracy: true, timeout: 10000, maximumAge: 0 }, ); }, []); const requestLocation = useCallback(() => { // If Permissions API exists, read the state first to provide immediate feedback if ((navigator as any).permissions && typeof (navigator as any).permissions.query === 'function') { try { ;(navigator as any).permissions.query({ name: 'geolocation' }).then((status: PermissionStatus) => { if (status.state === 'granted') { // permission already granted performGetPosition(); } else if (status.state === 'denied') { // permission denied setLoading(false); setError('permission_denied'); setLocation(null); } else { // prompt performGetPosition(); } }).catch(() => { // fallback to direct request performGetPosition(); }); } catch (e) { performGetPosition(); } } else { performGetPosition(); } }, [performGetPosition]); // Automatically request location on provider mount useEffect(() => { requestLocation(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( {children} ); };