sg-delivery-customer/app/services/locationServices.ts

183 lines
5.4 KiB
TypeScript

import { Platform, PermissionsAndroid, Alert, Linking } from 'react-native';
import Geolocation from 'react-native-geolocation-service';
import type { GeoPosition, GeoError } from 'react-native-geolocation-service';
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
export interface LatLng {
latitude: number;
longitude: number;
}
export interface LocationResult {
coords: LatLng;
address: string;
}
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const GOOGLE_MAPS_API_KEY = 'AIzaSyDKaKVyQCmSpHMXcMkNCccSXpILpuvwsVM';
/** Default region (Bengaluru) used as a fallback before the user's location loads. */
export const DEFAULT_LOCATION: LatLng = {
latitude: 12.9352,
longitude: 77.6245,
};
// ---------------------------------------------------------------------------
// Permission helpers
// ---------------------------------------------------------------------------
/**
* Request location permission from the user.
* Returns `true` if permission was granted, `false` otherwise.
*/
export const requestLocationPermission = async (): Promise<boolean> => {
if (Platform.OS === 'ios') {
try {
const result = await Geolocation.requestAuthorization('whenInUse');
return result === 'granted';
} catch {
return false;
}
}
// Android
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: 'Location Permission',
message: 'This app needs access to your location to set it on the map.',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
);
console.log('granted', granted);
return granted === PermissionsAndroid.RESULTS.GRANTED;
} catch {
return false;
}
};
/**
* Show an alert when the user denies location permission, with an option
* to open app settings.
*/
export const showPermissionDeniedAlert = (): void => {
Alert.alert(
'Location Permission Required',
'Please enable location permission in your device settings to use this feature.',
[
{ text: 'Cancel', style: 'cancel' },
{ text: 'Open Settings', onPress: () => Linking.openSettings() },
],
);
};
// ---------------------------------------------------------------------------
// Position helpers
// ---------------------------------------------------------------------------
/**
* Get the device's current GPS position.
* Wraps the callback-based Geolocation API in a Promise.
*/
export const getCurrentPosition = async (): Promise<LatLng> => {
try {
console.log('get current position');
console.log('Geolocation:', Geolocation);
console.log('getCurrentPosition:', Geolocation?.getCurrentPosition);
const location = await new Promise<LatLng>((resolve, reject) => {
Geolocation.getCurrentPosition(
(position: GeoPosition) => {
resolve({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
});
},
(error: GeoError) => {
console.log('Geolocation Error:', error);
reject(error);
},
{
enableHighAccuracy: true,
timeout: 15000,
maximumAge: 10000,
forceRequestLocation: true,
showLocationDialog: true,
},
);
});
return location;
} catch (error) {
console.error('Failed to get current position:', error);
throw error;
}
};
// ---------------------------------------------------------------------------
// Geocoding
// ---------------------------------------------------------------------------
/**
* Reverse-geocode a lat/lng pair into a human-readable address string
* using the Google Maps Geocoding API.
*/
export const reverseGeocode = async (coords: LatLng): Promise<string> => {
try {
const url =
`https://maps.googleapis.com/maps/api/geocode/json` +
`?latlng=${coords.latitude},${coords.longitude}` +
`&key=${GOOGLE_MAPS_API_KEY}`;
const response = await fetch(url);
const data = await response.json();
if (data.status === 'OK' && data.results.length > 0) {
// Return the most specific formatted address.
return data.results[0].formatted_address;
}
return 'Address not found';
} catch (error) {
console.log('reverseGeocode error', error);
return 'Unable to fetch address';
}
};
// ---------------------------------------------------------------------------
// Combined helper
// ---------------------------------------------------------------------------
/**
* All-in-one helper: request permissions → get position → reverse geocode.
* Returns both the coordinates and the human-readable address.
*
* Throws if permission is denied or position cannot be obtained.
*/
export const getCurrentLocationWithAddress =
async (): Promise<LocationResult> => {
const hasPermission = await requestLocationPermission();
if (!hasPermission) {
showPermissionDeniedAlert();
throw new Error('Location permission denied');
}
const coords = await getCurrentPosition();
const address = await reverseGeocode(coords);
console.log('coords', coords);
console.log('address', address);
return { coords, address };
};