diff options
| author | Ariel Costas Guerrero <ariel@costas.dev> | 2026-03-16 14:13:26 +0100 |
|---|---|---|
| committer | Ariel Costas Guerrero <ariel@costas.dev> | 2026-03-16 14:13:35 +0100 |
| commit | ac626a9c2edc2e528eb0b39002c836a747b2fc16 (patch) | |
| tree | 981a9f8b02370f9a54459a8171cdd9a32aa11c15 | |
| parent | 8942cf3c705bbc78a6b3317599658e9bb86dd31b (diff) | |
feat: enhance geolocation handling with loading state and improve button click behavior
| -rw-r--r-- | src/frontend/app/components/PlannerOverlay.tsx | 8 | ||||
| -rw-r--r-- | src/frontend/app/components/shared/AppMap.tsx | 9 | ||||
| -rw-r--r-- | src/frontend/app/contexts/MapContext.tsx | 2 | ||||
| -rw-r--r-- | src/frontend/app/hooks/useGeolocation.ts | 17 |
4 files changed, 23 insertions, 13 deletions
diff --git a/src/frontend/app/components/PlannerOverlay.tsx b/src/frontend/app/components/PlannerOverlay.tsx index d953c2e..d42bd94 100644 --- a/src/frontend/app/components/PlannerOverlay.tsx +++ b/src/frontend/app/components/PlannerOverlay.tsx @@ -534,7 +534,7 @@ export const PlannerOverlay: React.FC<PlannerOverlayProps> = ({ <button type="button" className="flex w-full items-center justify-between px-4 py-3 text-left hover:bg-slate-50 dark:hover:bg-slate-800 disabled:opacity-50 transition-colors duration-200" - onClick={() => setOriginFromCurrentLocation} + onClick={() => setOriginFromCurrentLocation()} disabled={locationLoading} > <div className="flex items-center gap-2"> @@ -550,8 +550,10 @@ export const PlannerOverlay: React.FC<PlannerOverlayProps> = ({ </div> </div> </div> - <div className="text-lg text-slate-600 dark:text-slate-400"> - {locationLoading ? "…" : ""} + <div className="flex items-center"> + {locationLoading && ( + <div className="w-4 h-4 border-2 border-primary-500 border-t-transparent rounded-full animate-spin" /> + )} </div> </button> </li> diff --git a/src/frontend/app/components/shared/AppMap.tsx b/src/frontend/app/components/shared/AppMap.tsx index f4c8658..8d0aa64 100644 --- a/src/frontend/app/components/shared/AppMap.tsx +++ b/src/frontend/app/components/shared/AppMap.tsx @@ -8,6 +8,7 @@ import { useRef, useState, } from "react"; +import { useTranslation } from "react-i18next"; import Map, { GeolocateControl, NavigationControl, @@ -15,7 +16,6 @@ import Map, { type MapRef, type StyleSpecification, } from "react-map-gl/maplibre"; -import { useTranslation } from "react-i18next"; import { useLocation } from "react-router"; import { useApp } from "~/AppContext"; import { APP_CONSTANTS } from "~/config/constants"; @@ -82,7 +82,6 @@ export const AppMap = forwardRef<MapRef, AppMapProps>( mapState, updateMapState, setUserLocation, - setLocationPermission, showTraffic: settingsShowTraffic, showCameras: settingsShowCameras, mapPositionMode, @@ -213,11 +212,13 @@ export const AppMap = forwardRef<MapRef, AppMapProps>( {showGeolocate && ( <GeolocateControl position="bottom-right" - positionOptions={{ enableHighAccuracy: false }} + positionOptions={{ + maximumAge: 1000 * 60 * 60 * 4, + enableHighAccuracy: false, + }} onGeolocate={(e) => { const { latitude, longitude } = e.coords; setUserLocation([latitude, longitude]); - setLocationPermission(true); }} /> )} diff --git a/src/frontend/app/contexts/MapContext.tsx b/src/frontend/app/contexts/MapContext.tsx index d93fefc..2c199be 100644 --- a/src/frontend/app/contexts/MapContext.tsx +++ b/src/frontend/app/contexts/MapContext.tsx @@ -72,6 +72,7 @@ export const MapProvider = ({ children }: { children: ReactNode }) => { const setLocationPermission = useCallback( (hasLocationPermission: boolean) => { setMapState((prev) => { + if (prev.hasLocationPermission === hasLocationPermission) return prev; const newState = { ...prev, hasLocationPermission }; localStorage.setItem("mapState", JSON.stringify(newState)); return newState; @@ -103,7 +104,6 @@ export const MapProvider = ({ children }: { children: ReactNode }) => { (position) => { const { latitude, longitude } = position.coords; setUserLocation([latitude, longitude]); - setLocationPermission(true); }, (error) => { if (error.code === GeolocationPositionError.PERMISSION_DENIED) { diff --git a/src/frontend/app/hooks/useGeolocation.ts b/src/frontend/app/hooks/useGeolocation.ts index 878420b..78e9096 100644 --- a/src/frontend/app/hooks/useGeolocation.ts +++ b/src/frontend/app/hooks/useGeolocation.ts @@ -1,16 +1,18 @@ -import { useCallback } from "react"; -import { useMap } from "../contexts/MapContext"; import type { LngLatLike } from "maplibre-gl"; +import { useCallback, useState } from "react"; +import { useMap } from "../contexts/MapContext"; export interface UseGeolocationResult { userLocation: { latitude: number; longitude: number } | null; hasLocationPermission: boolean; + isLoading: boolean; requestLocation: () => void; } -function lngLatToCoords( - loc: LngLatLike -): { latitude: number; longitude: number } { +function lngLatToCoords(loc: LngLatLike): { + latitude: number; + longitude: number; +} { if (Array.isArray(loc)) { // This codebase stores location as [latitude, longitude] (not the standard // MapLibre [lng, lat] GeoJSON order). See MapContext.tsx where arrays are @@ -36,16 +38,20 @@ function lngLatToCoords( */ export function useGeolocation(): UseGeolocationResult { const { mapState, setUserLocation, setLocationPermission } = useMap(); + const [isLoading, setIsLoading] = useState(false); const requestLocation = useCallback(() => { if (typeof window === "undefined" || !("geolocation" in navigator)) return; + setIsLoading(true); navigator.geolocation.getCurrentPosition( (pos) => { setUserLocation([pos.coords.latitude, pos.coords.longitude]); setLocationPermission(true); + setIsLoading(false); }, () => { setLocationPermission(false); + setIsLoading(false); }, { enableHighAccuracy: false, maximumAge: 60000, timeout: 10000 } ); @@ -57,6 +63,7 @@ export function useGeolocation(): UseGeolocationResult { return { userLocation, hasLocationPermission: mapState.hasLocationPermission, + isLoading, requestLocation, }; } |
