mobileapp / src /hooks /useLocation.ts
Antaram Dev Bot
feat: complete ANTARAM.ORG ride-sharing app frontend
5c876be
import { useState, useCallback } from 'react';
import * as Location from 'expo-location';
// ─── Types ─────────────────────────────────────────────────────────────────────
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>;
}
// ─── Hook ──────────────────────────────────────────────────────────────────────
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);
// ── Fetch current position ───────────────────────────────────────────────────
const fetchLocation = useCallback(async () => {
try {
setLoading(true);
setError(null);
// Request foreground permissions
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setError('Location permission was denied. Please enable it in settings.');
setLoading(false);
return;
}
// Get current position
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);
}
}, []);
// ── Reverse geocode (optional, stores coordinates only for now) ──────────────
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}`);
}
},
[],
);
// ── Refresh: fetch + geocode ─────────────────────────────────────────────────
const refresh = useCallback(async () => {
await fetchLocation();
// If we got a location, attempt reverse geocode
// We read from state in a microtask since setState is async
// Instead, we inline the logic:
}, [fetchLocation]);
// We override refresh to also do geocoding when location is available
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);
// Attempt reverse geocode
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,
};
}