Files
points-of-interest/client/src/hooks/useUserLocation.ts
T
2025-10-10 10:04:04 +02:00

89 lines
2.4 KiB
TypeScript

import { useCallback, useEffect, useRef, useState } from 'react'
import type { LatLng } from '@/types/api'
function geolocationErrorMessage(error: GeolocationPositionError): string {
switch (error.code) {
case error.PERMISSION_DENIED:
return 'Location access denied. Enable it to view nearby pings.'
case error.POSITION_UNAVAILABLE:
return 'Unable to determine your position. Try again.'
case error.TIMEOUT:
return 'Timed out while fetching your location.'
default:
return 'Failed to retrieve your location.'
}
}
export function useUserLocation() {
const [location, setLocation] = useState<LatLng | null>(null)
const [error, setError] = useState<string | null>(null)
const [isRequesting, setIsRequesting] = useState<boolean>(false)
const watchIdRef = useRef<number | null>(null)
const clearWatch = useCallback(() => {
if (typeof navigator === 'undefined' || !navigator.geolocation) {
return
}
if (watchIdRef.current !== null) {
navigator.geolocation.clearWatch(watchIdRef.current)
watchIdRef.current = null
}
}, [])
const start = useCallback(() => {
if (typeof navigator === 'undefined' || !navigator.geolocation) {
setError('Geolocation is not supported in this browser.')
setIsRequesting(false)
return
}
clearWatch()
setIsRequesting(true)
setError(null)
navigator.geolocation.getCurrentPosition(
(position) => {
setLocation({ lat: position.coords.latitude, lng: position.coords.longitude })
setIsRequesting(false)
setError(null)
},
(geoError) => {
setError(geolocationErrorMessage(geoError))
setIsRequesting(false)
},
{ enableHighAccuracy: true, timeout: 10000 },
)
const watchId = navigator.geolocation.watchPosition(
(position) => {
setLocation({ lat: position.coords.latitude, lng: position.coords.longitude })
setError(null)
setIsRequesting(false)
},
(geoError) => {
setError(geolocationErrorMessage(geoError))
setIsRequesting(false)
},
{ enableHighAccuracy: true, maximumAge: 15000, timeout: 10000 },
)
watchIdRef.current = watchId
}, [clearWatch])
useEffect(() => {
start()
return () => {
clearWatch()
}
}, [clearWatch, start])
return {
location,
error,
isRequesting,
refresh: start,
}
}