aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/app/hooks/useGeolocation.ts
blob: 78e909639ecd7132eb5f1d4407907b4d33b9434a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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;
} {
  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
    // set as [position.coords.latitude, position.coords.longitude], and AppMap.tsx
    // where getLatitude(center) returns center[0].
    return { latitude: loc[0], longitude: loc[1] };
  }
  if ("lat" in loc) {
    return {
      latitude: loc.lat,
      longitude: "lng" in loc ? (loc as any).lng : (loc as any).lon,
    };
  }
  return { latitude: 0, longitude: 0 };
}

/**
 * Provides the current user location from the global MapContext.
 * Location updates are driven by the MapContext's watchPosition subscription
 * (started automatically when geolocation permission is granted).
 *
 * Call `requestLocation()` to prompt the user for permission and start tracking.
 */
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 }
    );
  }, [setUserLocation, setLocationPermission]);

  const rawLoc = mapState.userLocation;
  const userLocation = rawLoc ? lngLatToCoords(rawLoc) : null;

  return {
    userLocation,
    hasLocationPermission: mapState.hasLocationPermission,
    isLoading,
    requestLocation,
  };
}