89 lines
2.3 KiB
TypeScript
89 lines
2.3 KiB
TypeScript
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
|
|
import type { Point } from '@/types/api'
|
|
|
|
function geolocationErrorMessage(error: GeolocationPositionError): string {
|
|
switch (error.code) {
|
|
case error.PERMISSION_DENIED:
|
|
return 'location.error.permissionDenied'
|
|
case error.POSITION_UNAVAILABLE:
|
|
return 'location.error.unavailable'
|
|
case error.TIMEOUT:
|
|
return 'location.error.timeout'
|
|
default:
|
|
return 'location.error.generic'
|
|
}
|
|
}
|
|
|
|
export function useUserLocation() {
|
|
const [location, setLocation] = useState<Point | 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('location.error.unsupported')
|
|
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,
|
|
}
|
|
}
|