| import { useState, useCallback } from 'react'; |
| import * as Location from 'expo-location'; |
|
|
| |
|
|
| interface LocationData { |
| latitude: number; |
| longitude: number; |
| accuracy: number | null; |
| altitude: number | null; |
| heading: number | null; |
| speed: number | null; |
| } |
|
|
| interface UseLocationReturn { |
| location: LocationData | null; |
| address: string | null; |
| error: string | null; |
| loading: boolean; |
| refresh: () => Promise<void>; |
| } |
|
|
| |
|
|
| export function useLocation(): UseLocationReturn { |
| const [location, setLocation] = useState<LocationData | null>(null); |
| const [address, setAddress] = useState<string | null>(null); |
| const [error, setError] = useState<string | null>(null); |
| const [loading, setLoading] = useState(false); |
|
|
| |
|
|
| const fetchLocation = useCallback(async () => { |
| try { |
| setLoading(true); |
| setError(null); |
|
|
| |
| const { status } = await Location.requestForegroundPermissionsAsync(); |
|
|
| if (status !== 'granted') { |
| setError('Location permission was denied. Please enable it in settings.'); |
| setLoading(false); |
| return; |
| } |
|
|
| |
| const position = await Location.getCurrentPositionAsync({ |
| accuracy: Location.Accuracy.Balanced, |
| }); |
|
|
| const coords: LocationData = { |
| latitude: position.coords.latitude, |
| longitude: position.coords.longitude, |
| accuracy: position.coords.accuracy ?? null, |
| altitude: position.coords.altitude ?? null, |
| heading: position.coords.heading ?? null, |
| speed: position.coords.speed ?? null, |
| }; |
|
|
| setLocation(coords); |
| } catch (err) { |
| const message = |
| err instanceof Error ? err.message : 'Failed to get location'; |
| setError(message); |
| setLocation(null); |
| } finally { |
| setLoading(false); |
| } |
| }, []); |
|
|
| |
|
|
| const reverseGeocode = useCallback( |
| async (latitude: number, longitude: number) => { |
| try { |
| const result = await Location.reverseGeocodeAsync({ |
| latitude, |
| longitude, |
| }); |
|
|
| if (result.length > 0) { |
| const place = result[0]; |
| const parts = [ |
| place.name, |
| place.street, |
| place.city, |
| place.region, |
| place.postalCode, |
| place.country, |
| ].filter(Boolean); |
|
|
| const fullAddress = parts.join(', '); |
| setAddress(fullAddress || `${latitude}, ${longitude}`); |
| } else { |
| setAddress(`${latitude}, ${longitude}`); |
| } |
| } catch { |
| setAddress(`${latitude}, ${longitude}`); |
| } |
| }, |
| [], |
| ); |
|
|
| |
|
|
| const refresh = useCallback(async () => { |
| await fetchLocation(); |
| |
| |
| |
| }, [fetchLocation]); |
|
|
| |
| const refreshWithGeocode = useCallback(async () => { |
| try { |
| setLoading(true); |
| setError(null); |
|
|
| const { status } = await Location.requestForegroundPermissionsAsync(); |
|
|
| if (status !== 'granted') { |
| setError('Location permission was denied.'); |
| setLoading(false); |
| return; |
| } |
|
|
| const position = await Location.getCurrentPositionAsync({ |
| accuracy: Location.Accuracy.Balanced, |
| }); |
|
|
| const coords: LocationData = { |
| latitude: position.coords.latitude, |
| longitude: position.coords.longitude, |
| accuracy: position.coords.accuracy ?? null, |
| altitude: position.coords.altitude ?? null, |
| heading: position.coords.heading ?? null, |
| speed: position.coords.speed ?? null, |
| }; |
|
|
| setLocation(coords); |
|
|
| |
| const result = await Location.reverseGeocodeAsync({ |
| latitude: coords.latitude, |
| longitude: coords.longitude, |
| }); |
|
|
| if (result.length > 0) { |
| const place = result[0]; |
| const parts = [ |
| place.name, |
| place.street, |
| place.city, |
| place.region, |
| place.postalCode, |
| place.country, |
| ].filter(Boolean); |
|
|
| setAddress(parts.join(', ') || `${coords.latitude}, ${coords.longitude}`); |
| } else { |
| setAddress(`${coords.latitude}, ${coords.longitude}`); |
| } |
| } catch (err) { |
| const message = |
| err instanceof Error ? err.message : 'Failed to get location'; |
| setError(message); |
| setLocation(null); |
| } finally { |
| setLoading(false); |
| } |
| }, []); |
|
|
| return { |
| location, |
| address, |
| error, |
| loading, |
| refresh: refreshWithGeocode, |
| }; |
| } |
|
|