173 lines
4.8 KiB
TypeScript
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;
|