173 lines
4.8 KiB
TypeScript

import React, { useEffect, useRef, useState } from 'react';
import { View, Text } from 'react-native';
import MapView, { Marker, Polyline, PROVIDER_GOOGLE } from 'react-native-maps';
import { useAppTheme } from '@theme';
import { getRouteDirections, getHaversineDistance } from '@services';
import { getStyles } from './TrackingMap.styles';
interface TrackingMapProps {
customerDropoff: { latitude: number; longitude: number };
driverLocation: {
latitude: number;
longitude: number;
heading?: number;
} | null;
showRoute?: boolean;
}
export const TrackingMap: React.FC<TrackingMapProps> = ({
customerDropoff,
driverLocation,
showRoute = true,
}) => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
const mapRef = useRef<MapView>(null);
const [routeCoords, setRouteCoords] = useState<
{ latitude: number; longitude: number }[]
>([]);
const lastFetchedLocation = useRef<{
latitude: number;
longitude: number;
} | null>(null);
const initialRegion = {
latitude: customerDropoff.latitude,
longitude: customerDropoff.longitude,
latitudeDelta: 0.02,
longitudeDelta: 0.02,
};
// console.log('[TrackingMap] driverLocation:', driverLocation);
// console.log('[TrackingMap] customerDropoff:', customerDropoff);
// console.log('[TrackingMap] routeCoords:', routeCoords);
// Fetch routing coordinates between driver and customer dropoff
useEffect(() => {
let isMounted = true;
if (showRoute && driverLocation && customerDropoff) {
const currentDriverPos = {
latitude: driverLocation.latitude,
longitude: driverLocation.longitude,
};
// Limit routing API calls if the driver has moved less than 50 meters
if (lastFetchedLocation.current) {
const dist = getHaversineDistance(
currentDriverPos,
lastFetchedLocation.current,
);
if (dist < 0.05 && routeCoords.length > 0) {
return;
}
}
getRouteDirections(currentDriverPos, customerDropoff)
.then(points => {
if (isMounted) {
setRouteCoords(points);
lastFetchedLocation.current = currentDriverPos;
}
})
.catch(err => {
console.error(
'[TrackingMap] Error fetching routing directions:',
err,
);
if (isMounted) {
// Fallback to straight line
setRouteCoords([currentDriverPos, customerDropoff]);
}
});
} else {
setRouteCoords([]);
lastFetchedLocation.current = null;
}
return () => {
isMounted = false;
};
}, [
showRoute,
driverLocation?.latitude,
driverLocation?.longitude,
customerDropoff?.latitude,
customerDropoff?.longitude,
]);
// Adjust camera to fit both the customer dropoff and driver location
useEffect(() => {
const coordsToFit: { latitude: number; longitude: number }[] = [];
if (customerDropoff) {
coordsToFit.push(customerDropoff);
}
if (driverLocation) {
coordsToFit.push({
latitude: driverLocation.latitude,
longitude: driverLocation.longitude,
});
}
if (coordsToFit.length > 0 && mapRef.current) {
const timer = setTimeout(() => {
mapRef.current?.fitToCoordinates(coordsToFit, {
edgePadding: { top: 80, right: 80, bottom: 80, left: 80 },
animated: true,
});
}, 500);
return () => clearTimeout(timer);
}
}, [
customerDropoff?.latitude,
customerDropoff?.longitude,
driverLocation?.latitude,
driverLocation?.longitude,
]);
return (
<View style={styles.container}>
<MapView
ref={mapRef}
provider={PROVIDER_GOOGLE}
style={styles.map}
initialRegion={initialRegion}
>
{/* Customer Dropoff Location Marker */}
{customerDropoff && (
<Marker
coordinate={customerDropoff}
title="Delivery Location"
pinColor="red"
/>
)}
{/* Live Driver Marker */}
{driverLocation && (
<Marker
coordinate={{
latitude: driverLocation.latitude,
longitude: driverLocation.longitude,
}}
title="Delivery Partner"
anchor={{ x: 0.5, y: 0.5 }}
rotation={driverLocation.heading ?? 0}
>
<View style={styles.driverMarker}>
<Text style={styles.driverEmoji}>🛵</Text>
</View>
</Marker>
)}
{/* Live Routing Polyline */}
{showRoute && routeCoords.length > 0 && (
<Polyline
coordinates={routeCoords}
strokeColor={colors.primary}
strokeWidth={4}
/>
)}
</MapView>
</View>
);
};
export default TrackingMap;