chore: update map

This commit is contained in:
2026-02-19 08:47:19 +01:00
parent 5afa907609
commit 3308adf1ea
2 changed files with 146 additions and 281 deletions
+3 -5
View File
@@ -6,7 +6,7 @@
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "dark",
"newArchEnabled": true,
"newArchEnabled": false,
"splash": {
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
@@ -24,6 +24,7 @@
},
"edgeToEdgeEnabled": true,
"permissions": [
"android.permission.ACCESS_BACKGROUND_LOCATION",
"android.permission.ACCESS_COARSE_LOCATION",
"android.permission.ACCESS_FINE_LOCATION"
]
@@ -31,10 +32,7 @@
"web": {
"favicon": "./assets/favicon.png"
},
"plugins": [
"expo-font",
"expo-location"
],
"plugins": ["expo-font", "expo-location"],
"extra": {
"eas": {
"projectId": "fcb7a0bc-5b2f-453d-ba16-9f97d9f0e440"
@@ -21,7 +21,6 @@ import {
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import * as Location from "expo-location";
import MapView, { Marker, Polyline, PROVIDER_DEFAULT } from "react-native-maps";
import { spacing, fontSize, borderRadius } from "../../theme";
import { useTheme } from "../../context/ThemeContext";
import {
@@ -52,6 +51,12 @@ import Badge from "../../components/ui/Badge";
import Button from "../../components/ui/Button";
import AlertModal from "../../components/ui/AlertModal";
import { useAlert } from "../../hooks/useAlert";
import TomTomMap, {
TomTomMapRef,
TomTomMarker,
TomTomRoute,
TomTomDestination,
} from "../../components/TomTomMap";
const { width: SCREEN_WIDTH } = Dimensions.get("window");
const MAP_HEIGHT = 260;
@@ -91,8 +96,10 @@ export default function DashboardScreen() {
null,
);
const appState = useRef(AppState.currentState);
const mapRef = useRef<MapView | null>(null);
const fullscreenMapRef = useRef<MapView | null>(null);
// TomTom map refs
const mapRef = useRef<TomTomMapRef>(null);
const fullscreenMapRef = useRef<TomTomMapRef>(null);
const [mapFullscreen, setMapFullscreen] = useState(false);
// TomTom routing
@@ -117,6 +124,46 @@ export default function DashboardScreen() {
[colors],
);
// --------------------------------------------------
// Construire les props pour TomTomMap
// --------------------------------------------------
// Marker du livreur (position courante)
const driverMarkers: TomTomMarker[] = useMemo(() => {
if (!lastCoords) return [];
return [
{
id: "driver",
latitude: lastCoords.lat,
longitude: lastCoords.lng,
color: colors.success,
label: "Ma position",
description: lastUpdate
? lastUpdate.toLocaleTimeString("fr-FR", {
hour: "2-digit",
minute: "2-digit",
})
: undefined,
},
];
}, [lastCoords, lastUpdate, colors.success]);
// Route TomTom
const tomtomRoute: TomTomRoute | null = useMemo(() => {
if (!routeInfo || routeInfo.coordinates.length === 0) return null;
return { coordinates: routeInfo.coordinates, color: "#4285F4" };
}, [routeInfo]);
// Destination TomTom
const tomtomDestination: TomTomDestination | null = useMemo(() => {
if (!destinationCoords) return null;
return {
latitude: destinationCoords.latitude,
longitude: destinationCoords.longitude,
color: colors.danger,
};
}, [destinationCoords, colors.danger]);
// --------------------------------------------------
// Data
// --------------------------------------------------
@@ -137,7 +184,6 @@ export default function DashboardScreen() {
? deliveriesRes.deliveries
: [];
// Include queue commands
const queueCommands: DeliveryItem[] = [];
if (queueRes.success && queueRes.queue_info?.commands) {
for (const cmd of queueRes.queue_info.commands) {
@@ -194,7 +240,6 @@ export default function DashboardScreen() {
setDeliveries(enriched);
// Auto-calculate route for first in_progress / assigned delivery
const activeDelivery =
enriched.find(
(d) =>
@@ -227,7 +272,6 @@ export default function DashboardScreen() {
if (!lastCoords) return;
setRouteLoading(true);
try {
// Geocode destination
const dest = await geocodeAddress(address);
if (!dest) {
setRouteLoading(false);
@@ -235,7 +279,6 @@ export default function DashboardScreen() {
}
setDestinationCoords(dest);
// Calculate route
const result = await calculateRoute(
{ latitude: lastCoords.lat, longitude: lastCoords.lng },
dest,
@@ -245,30 +288,12 @@ export default function DashboardScreen() {
setInstructions(result.instructions);
setCurrentInstructionIdx(0);
// Fit map to show both points
if (mapRef.current) {
mapRef.current.fitToCoordinates(
[
{
latitude: lastCoords.lat,
longitude: lastCoords.lng,
},
{
latitude: dest.latitude,
longitude: dest.longitude,
},
],
{
edgePadding: {
top: 80,
right: 60,
bottom: 80,
left: 60,
},
animated: true,
},
);
}
// Fit la map pour afficher driver + destination
const coords = [
{ latitude: lastCoords.lat, longitude: lastCoords.lng },
{ latitude: dest.latitude, longitude: dest.longitude },
];
mapRef.current?.fitToCoordinates(coords);
}
} catch {
/* silent */
@@ -286,7 +311,6 @@ export default function DashboardScreen() {
const sendCurrentLocation = useCallback(async () => {
if (sendingRef.current) return;
sendingRef.current = true;
try {
const loc = await Promise.race([
Location.getCurrentPositionAsync({
@@ -297,7 +321,6 @@ export default function DashboardScreen() {
),
]);
// validation runtime + typage TS
if (
!loc ||
typeof loc !== "object" ||
@@ -313,7 +336,6 @@ export default function DashboardScreen() {
setLastCoords({ lat: latitude, lng: longitude });
setLastUpdate(new Date());
await updateMyLocation(latitude, longitude);
} catch (err) {
console.log("GPS error:", err);
@@ -440,7 +462,6 @@ export default function DashboardScreen() {
return (
<View style={styles.instructionBar}>
{/* Current instruction */}
<View style={styles.currentInstruction}>
<View style={styles.instructionIconBox}>
<Ionicons
@@ -464,7 +485,6 @@ export default function DashboardScreen() {
</Text>
</View>
{/* Next instruction preview */}
{next && (
<View style={styles.nextInstruction}>
<Text style={styles.nextLabel}>Puis</Text>
@@ -482,7 +502,6 @@ export default function DashboardScreen() {
</View>
)}
{/* Route summary + toggle all instructions */}
<View style={styles.routeSummary}>
<View style={styles.routeInfoChip}>
<Ionicons
@@ -534,7 +553,6 @@ export default function DashboardScreen() {
return (
<Card style={{ marginBottom: spacing.m }}>
{/* Header */}
<View style={styles.cardHeader}>
<View style={{ flex: 1 }}>
<Text style={styles.deliveryId}>
@@ -568,7 +586,6 @@ export default function DashboardScreen() {
/>
</View>
{/* Address + navigation */}
<TouchableOpacity
style={styles.addressRow}
onPress={() => openNavigation(item.adresse)}
@@ -589,7 +606,6 @@ export default function DashboardScreen() {
/>
</TouchableOpacity>
{/* Calculate route button for active delivery */}
{isActive && lastCoords && (
<TouchableOpacity
style={styles.calcRouteBtn}
@@ -609,7 +625,6 @@ export default function DashboardScreen() {
</TouchableOpacity>
)}
{/* Items / Products */}
{item.items && item.items.length > 0 && (
<View style={styles.itemsSection}>
<View style={styles.itemsHeader}>
@@ -652,7 +667,6 @@ export default function DashboardScreen() {
</Text>
)}
{/* Client phone */}
{item.clientPhone && (
<TouchableOpacity
style={styles.phoneRow}
@@ -669,7 +683,6 @@ export default function DashboardScreen() {
</TouchableOpacity>
)}
{/* Action buttons */}
{(item.status === "pending" || item.status === "assigned") && (
<Button
title="Démarrer la livraison"
@@ -696,76 +709,25 @@ export default function DashboardScreen() {
};
// --------------------------------------------------
// Header with map + route + status + GPS
// Header avec TomTomMap
// --------------------------------------------------
const renderHeader = () => (
<View>
{/* Map with route */}
{lastCoords && (
{/* Carte TomTom */}
{lastCoords ? (
<View style={styles.mapContainer}>
<MapView
<TomTomMap
ref={mapRef}
provider={PROVIDER_DEFAULT}
style={styles.map}
initialRegion={{
latitude: lastCoords.lat,
longitude: lastCoords.lng,
latitudeDelta: 0.02,
longitudeDelta: 0.02,
}}
showsUserLocation={false}
showsMyLocationButton={false}
>
{/* Driver marker */}
<Marker
coordinate={{
markers={driverMarkers}
route={tomtomRoute}
destination={tomtomDestination}
initialCenter={{
latitude: lastCoords.lat,
longitude: lastCoords.lng,
}}
title="Ma position"
>
<View style={styles.driverMarkerOuter}>
<View style={styles.driverMarkerInner}>
<Ionicons
name="bicycle"
size={16}
color={colors.white}
initialZoom={14}
/>
</View>
</View>
</Marker>
{/* Destination marker */}
{destinationCoords && (
<Marker
coordinate={{
latitude: destinationCoords.latitude,
longitude: destinationCoords.longitude,
}}
title="Destination"
>
<View style={styles.destMarkerOuter}>
<View style={styles.destMarkerInner}>
<Ionicons
name="flag"
size={14}
color={colors.white}
/>
</View>
</View>
</Marker>
)}
{/* Route polyline */}
{routeInfo && routeInfo.coordinates.length > 0 && (
<Polyline
coordinates={routeInfo.coordinates}
strokeColor="#4285F4"
strokeWidth={5}
lineDashPattern={[0]}
/>
)}
</MapView>
{/* GPS overlay */}
<View style={styles.gpsOverlay}>
@@ -793,7 +755,7 @@ export default function DashboardScreen() {
)}
</View>
{/* Fullscreen button */}
{/* Bouton plein écran */}
<TouchableOpacity
style={styles.expandBtn}
onPress={() => setMapFullscreen(true)}
@@ -806,7 +768,7 @@ export default function DashboardScreen() {
/>
</TouchableOpacity>
{/* Route loading indicator */}
{/* Indicateur calcul de route */}
{routeLoading && (
<View style={styles.routeLoadingOverlay}>
<Text style={styles.routeLoadingText}>
@@ -815,10 +777,7 @@ export default function DashboardScreen() {
</View>
)}
</View>
)}
{/* No location fallback */}
{!lastCoords && (
) : (
<View style={styles.noMapBox}>
<Ionicons
name="location-outline"
@@ -831,10 +790,10 @@ export default function DashboardScreen() {
</View>
)}
{/* Navigation instructions */}
{/* Instructions navigation */}
{renderInstructionBar()}
{/* All instructions list */}
{/* Liste complète des étapes */}
{showInstructions && instructions.length > 0 && (
<View style={styles.allInstructionsBox}>
<View style={styles.allInstructionsHeader}>
@@ -937,12 +896,14 @@ export default function DashboardScreen() {
</View>
);
// --------------------------------------------------
// Styles
// --------------------------------------------------
const styles = useMemo(
() =>
StyleSheet.create({
container: { flex: 1, backgroundColor: colors.bgPrimary },
// Status
statusBar: {
padding: spacing.l,
backgroundColor: colors.bgSecondary,
@@ -979,8 +940,9 @@ export default function DashboardScreen() {
overflow: "hidden",
marginBottom: spacing.m,
position: "relative",
height: MAP_HEIGHT,
},
map: { width: "100%", height: MAP_HEIGHT },
map: { flex: 1 },
gpsOverlay: {
position: "absolute",
bottom: spacing.s,
@@ -1001,40 +963,6 @@ export default function DashboardScreen() {
},
gpsOverlayTime: { color: colors.textMuted, fontSize: 10 },
// Markers
driverMarkerOuter: {
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: colors.success + "40",
justifyContent: "center",
alignItems: "center",
},
driverMarkerInner: {
width: 28,
height: 28,
borderRadius: 14,
backgroundColor: colors.success,
justifyContent: "center",
alignItems: "center",
},
destMarkerOuter: {
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: colors.danger + "40",
justifyContent: "center",
alignItems: "center",
},
destMarkerInner: {
width: 28,
height: 28,
borderRadius: 14,
backgroundColor: colors.danger,
justifyContent: "center",
alignItems: "center",
},
noMapBox: {
alignItems: "center",
paddingVertical: spacing.xl,
@@ -1048,7 +976,6 @@ export default function DashboardScreen() {
marginTop: spacing.s,
},
// Route loading
routeLoadingOverlay: {
position: "absolute",
top: 0,
@@ -1065,7 +992,19 @@ export default function DashboardScreen() {
fontWeight: "600",
},
// Navigation instructions
expandBtn: {
position: "absolute",
top: spacing.s,
right: spacing.s,
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: "rgba(0,0,0,0.6)",
justifyContent: "center",
alignItems: "center",
},
// Instructions
instructionBar: {
backgroundColor: colors.bgSecondary,
borderRadius: borderRadius.md,
@@ -1202,10 +1141,7 @@ export default function DashboardScreen() {
justifyContent: "center",
alignItems: "center",
},
stepText: {
color: colors.textWhite,
fontSize: fontSize.sm,
},
stepText: { color: colors.textWhite, fontSize: fontSize.sm },
stepStreet: {
color: colors.textMuted,
fontSize: fontSize.xs,
@@ -1217,7 +1153,7 @@ export default function DashboardScreen() {
fontWeight: "600",
},
// Calculate route button
// Calc route button
calcRouteBtn: {
flexDirection: "row",
alignItems: "center",
@@ -1252,7 +1188,6 @@ export default function DashboardScreen() {
fontWeight: "600",
},
// Section
sectionTitle: {
color: colors.textWhite,
fontSize: fontSize.lg,
@@ -1277,7 +1212,6 @@ export default function DashboardScreen() {
marginTop: 2,
},
// Address
addressRow: {
flexDirection: "row",
alignItems: "center",
@@ -1294,7 +1228,6 @@ export default function DashboardScreen() {
fontSize: fontSize.sm,
},
// Items
itemsSection: {
backgroundColor: colors.bgInput,
borderRadius: borderRadius.sm,
@@ -1361,7 +1294,6 @@ export default function DashboardScreen() {
marginTop: spacing.xs,
},
// Phone
phoneRow: {
flexDirection: "row",
alignItems: "center",
@@ -1377,27 +1309,12 @@ export default function DashboardScreen() {
fontSize: fontSize.md,
},
// Expand button
expandBtn: {
position: "absolute",
top: spacing.s,
right: spacing.s,
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: "rgba(0,0,0,0.6)",
justifyContent: "center",
alignItems: "center",
},
// Fullscreen map
// Fullscreen map modal
fullscreenContainer: {
flex: 1,
backgroundColor: colors.bgPrimary,
},
fullscreenMap: {
...StyleSheet.absoluteFillObject,
},
fullscreenMap: { ...StyleSheet.absoluteFillObject },
fullscreenTopBar: {
position: "absolute",
top: 0,
@@ -1484,7 +1401,7 @@ export default function DashboardScreen() {
return (
<View style={styles.container}>
{/* Fullscreen map modal */}
{/* ── Modal plein écran avec TomTomMap ── */}
<Modal
visible={mapFullscreen}
animationType="fade"
@@ -1494,70 +1411,18 @@ export default function DashboardScreen() {
<StatusBar hidden={mapFullscreen} />
<View style={styles.fullscreenContainer}>
{lastCoords && (
<MapView
<TomTomMap
ref={fullscreenMapRef}
provider={PROVIDER_DEFAULT}
style={styles.fullscreenMap}
initialRegion={{
latitude: lastCoords.lat,
longitude: lastCoords.lng,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
}}
showsUserLocation={false}
showsMyLocationButton={false}
showsCompass
showsScale
>
{/* Driver */}
<Marker
coordinate={{
markers={driverMarkers}
route={tomtomRoute}
destination={tomtomDestination}
initialCenter={{
latitude: lastCoords.lat,
longitude: lastCoords.lng,
}}
title="Ma position"
>
<View style={styles.driverMarkerOuter}>
<View style={styles.driverMarkerInner}>
<Ionicons
name="bicycle"
size={16}
color={colors.white}
initialZoom={15}
/>
</View>
</View>
</Marker>
{/* Destination */}
{destinationCoords && (
<Marker
coordinate={{
latitude: destinationCoords.latitude,
longitude: destinationCoords.longitude,
}}
title="Destination"
>
<View style={styles.destMarkerOuter}>
<View style={styles.destMarkerInner}>
<Ionicons
name="flag"
size={14}
color={colors.white}
/>
</View>
</View>
</Marker>
)}
{/* Route */}
{routeInfo && routeInfo.coordinates.length > 0 && (
<Polyline
coordinates={routeInfo.coordinates}
strokeColor="#4285F4"
strokeWidth={5}
/>
)}
</MapView>
)}
{/* Top bar */}
@@ -1580,7 +1445,7 @@ export default function DashboardScreen() {
<View style={{ width: 40 }} />
</View>
{/* Fullscreen instruction bar */}
{/* Instruction bar en plein écran */}
{instructions.length > 0 && (
<View style={styles.fullscreenInstructionBar}>
<View style={styles.instructionIconBox}>
@@ -1675,7 +1540,7 @@ export default function DashboardScreen() {
</View>
</Modal>
{/* Status bar */}
{/* ── Barre de statut ── */}
<View style={styles.statusBar}>
<Text style={styles.statusLabel}>Mon statut:</Text>
<View style={styles.statusBtns}>
@@ -1712,6 +1577,7 @@ export default function DashboardScreen() {
</View>
</View>
{/* ── Liste des livraisons ── */}
<FlatList
data={deliveries}
keyExtractor={(item, index) => (item.id ?? index).toString()}
@@ -1734,6 +1600,7 @@ export default function DashboardScreen() {
}
ListHeaderComponent={renderHeader()}
/>
<AlertModal
visible={alert.visible}
type={alert.type}