reservation-front/src/context/LocationContext.tsx
2026-06-15 13:00:41 +03:30

115 lines
3.5 KiB
TypeScript

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<LocationContextValue | undefined>(undefined);
export const LocationProvider = ({ children }: { children: ReactNode }) => {
const [location, setLocation] = useState<Location | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<LocationError>(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 (
<LocationContext.Provider value={{ location, loading, error, requestLocation }}>
{children}
</LocationContext.Provider>
);
};