chore: fix gps

This commit is contained in:
2026-02-21 15:22:17 +01:00
parent bf4df84ffe
commit 7c8ea006f6
9 changed files with 383 additions and 563 deletions
@@ -23,7 +23,7 @@ import {
getCommandByID,
} from "../../api/api_admin";
import { geocodeAddress, calculateRoute } from "../../api/tomtom";
import type { RouteInfo, LatLng } from "../../api/tomtom";
import type { RouteInfo } from "../../api/tomtom";
import type { DeliveryPerson } from "../../api/types";
import { STATUS_LABELS, getStatusColors } from "../../utils/constants";
import LoadingSpinner from "../../components/ui/LoadingSpinner";
@@ -32,8 +32,6 @@ import Badge from "../../components/ui/Badge";
import TomTomMap, {
TomTomMapRef,
TomTomMarker,
TomTomRoute,
TomTomDestination,
} from "../../components/TomTomMap";
const MAP_HEIGHT = 280;
@@ -60,9 +58,6 @@ export default function DeliveryScreen() {
const [selectedLivreur, setSelectedLivreur] =
useState<DeliveryPerson | null>(null);
const [routeInfo, setRouteInfo] = useState<RouteInfo | null>(null);
const [destinationCoords, setDestinationCoords] = useState<LatLng | null>(
null,
);
const [routeLoading, setRouteLoading] = useState(false);
const loadData = useCallback(async () => {
@@ -111,30 +106,13 @@ export default function DeliveryScreen() {
}));
}, [livreursWithGPS, selectedLivreur, statusColors, colors.textMuted]);
// 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]);
// --------------------------------------------------
// Track livreur — calcul de route
// Track livreur — calcul de route via ref TomTomMap
// --------------------------------------------------
const trackLivreur = useCallback(
async (livreur: DeliveryPerson) => {
setSelectedLivreur(livreur);
setRouteInfo(null);
setDestinationCoords(null);
if (!livreur.stats.current_command) return;
@@ -154,24 +132,16 @@ export default function DeliveryScreen() {
setRouteLoading(false);
return;
}
setDestinationCoords(dest);
const origin: LatLng = {
const origin = {
latitude: livreur.location.latitude,
longitude: livreur.location.longitude,
};
const result = await calculateRoute(origin, dest);
if (result) {
setRouteInfo(result.route);
// Fit la map sur les deux points
const ref = mapFullscreen ? fullscreenMapRef : mapRef;
ref.current?.fitToCoordinates([
{
latitude: origin.latitude,
longitude: origin.longitude,
},
{ latitude: dest.latitude, longitude: dest.longitude },
]);
const activeRef = mapFullscreen ? fullscreenMapRef : mapRef;
activeRef.current?.calcRoute(origin, dest);
}
} catch {
/* silent */
@@ -184,7 +154,6 @@ export default function DeliveryScreen() {
const clearRoute = () => {
setSelectedLivreur(null);
setRouteInfo(null);
setDestinationCoords(null);
};
// --------------------------------------------------
@@ -599,8 +568,6 @@ export default function DeliveryScreen() {
ref={mapRef}
style={styles.map}
markers={tomtomMarkers}
route={tomtomRoute}
destination={tomtomDestination}
initialCenter={{
latitude: livreursWithGPS[0].location.latitude,
longitude: livreursWithGPS[0].location.longitude,
@@ -708,8 +675,6 @@ export default function DeliveryScreen() {
ref={fullscreenMapRef}
style={styles.fullscreenMap}
markers={tomtomMarkers}
route={tomtomRoute}
destination={tomtomDestination}
initialCenter={{
latitude: livreursWithGPS[0].location.latitude,
longitude:
@@ -4,6 +4,7 @@ import React, {
useCallback,
useRef,
useMemo,
RefObject,
} from "react";
import {
View,
@@ -33,17 +34,8 @@ import {
updateDeliveryStatus,
updateMyLocation,
} from "../../api/api_delivery";
import {
geocodeAddress,
calculateRoute,
maneuverIcons,
maneuverTranslations,
} from "../../api/tomtom";
import type {
RouteInfo,
NavigationInstruction,
LatLng,
} from "../../api/tomtom";
import { geocodeAddress, calculateRoute } from "../../api/tomtom";
import type { RouteInfo } from "../../api/tomtom";
import type { DeliveryStatus, DeliveryItem, QueueInfo } from "../../api/types";
import LoadingSpinner from "../../components/ui/LoadingSpinner";
import Card from "../../components/ui/Card";
@@ -54,10 +46,10 @@ 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;
const LOCATION_INTERVAL_MS = 15000;
@@ -91,6 +83,7 @@ export default function DashboardScreen() {
lat: number;
lng: number;
} | null>(null);
const lastCoordsRef = useRef<{ lat: number; lng: number } | null>(null);
const [lastUpdate, setLastUpdate] = useState<Date | null>(null);
const locationInterval = useRef<ReturnType<typeof setInterval> | null>(
null,
@@ -102,17 +95,9 @@ export default function DashboardScreen() {
const fullscreenMapRef = useRef<TomTomMapRef>(null);
const [mapFullscreen, setMapFullscreen] = useState(false);
// TomTom routing
const [routeInfo, setRouteInfo] = useState<RouteInfo | null>(null);
const [instructions, setInstructions] = useState<NavigationInstruction[]>(
[],
);
const [destinationCoords, setDestinationCoords] = useState<LatLng | null>(
null,
);
const [showInstructions, setShowInstructions] = useState(false);
const [currentInstructionIdx, setCurrentInstructionIdx] = useState(0);
const [routeLoading, setRouteLoading] = useState(false);
const [routeInfo, setRouteInfo] = useState<RouteInfo | null>(null);
const pendingRouteAddress = useRef<string | null>(null);
const { alert, showError, showSuccess, hideAlert } = useAlert();
const STATUS_COLORS: Record<string, string> = useMemo(
@@ -124,10 +109,6 @@ export default function DashboardScreen() {
[colors],
);
// --------------------------------------------------
// Construire les props pour TomTomMap
// --------------------------------------------------
// Marker du livreur (position courante)
const driverMarkers: TomTomMarker[] = useMemo(() => {
if (!lastCoords) return [];
@@ -148,21 +129,42 @@ export default function DashboardScreen() {
];
}, [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]);
// --------------------------------------------------
// TomTom Route calculation
// Geocode + calcul dans DashboardScreen pour récupérer routeInfo
// --------------------------------------------------
const calcRoute = useCallback(async (address: string, targetRef?: RefObject<TomTomMapRef | null>) => {
const coords = lastCoordsRef.current;
if (!coords) {
pendingRouteAddress.current = address;
return;
}
pendingRouteAddress.current = address;
const ref = targetRef ?? mapRef;
if (!ref.current) return;
// Destination TomTom
const tomtomDestination: TomTomDestination | null = useMemo(() => {
if (!destinationCoords) return null;
return {
latitude: destinationCoords.latitude,
longitude: destinationCoords.longitude,
color: colors.danger,
};
}, [destinationCoords, colors.danger]);
setRouteLoading(true);
try {
const origin = { latitude: coords.lat, longitude: coords.lng };
const dest = await geocodeAddress(address);
if (!dest) {
setRouteLoading(false);
return;
}
const result = await calculateRoute(origin, dest);
if (result) {
setRouteInfo(result.route);
ref.current?.calcRoute(origin, dest);
} else {
setRouteInfo(null);
ref.current?.calcRoute(origin, dest);
}
} catch {
/* silent */
} finally {
setRouteLoading(false);
}
}, []);
// --------------------------------------------------
// Data
@@ -245,64 +247,45 @@ export default function DashboardScreen() {
(d) =>
d.status === "in_progress" || d.status === "en_route",
) || enriched.find((d) => d.status === "assigned");
if (activeDelivery && activeDelivery.adresse && lastCoords) {
if (activeDelivery && activeDelivery.adresse) {
calcRoute(activeDelivery.adresse);
}
} catch {
/* ignore */
}
setLoading(false);
}, [lastCoords]);
}, [calcRoute]);
useEffect(() => {
loadData();
}, [loadData]);
// Quand GPS devient disponible, rejouer la route en attente
useEffect(() => {
if (lastCoords && pendingRouteAddress.current) {
calcRoute(pendingRouteAddress.current);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [lastCoords]);
// Quand le fullscreen s'ouvre, rejouer la route sur la carte fullscreen
useEffect(() => {
if (!mapFullscreen) return;
const addr = pendingRouteAddress.current;
const coords = lastCoordsRef.current;
if (!addr || !coords) return;
setTimeout(() => {
calcRoute(addr, fullscreenMapRef);
}, 1200);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mapFullscreen]);
const onRefresh = async () => {
setRefreshing(true);
await loadData();
setRefreshing(false);
};
// --------------------------------------------------
// TomTom Route calculation
// --------------------------------------------------
const calcRoute = useCallback(
async (address: string) => {
if (!lastCoords) return;
setRouteLoading(true);
try {
const dest = await geocodeAddress(address);
if (!dest) {
setRouteLoading(false);
return;
}
setDestinationCoords(dest);
const result = await calculateRoute(
{ latitude: lastCoords.lat, longitude: lastCoords.lng },
dest,
);
if (result) {
setRouteInfo(result.route);
setInstructions(result.instructions);
setCurrentInstructionIdx(0);
// 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 */
}
setRouteLoading(false);
},
[lastCoords],
);
// --------------------------------------------------
// Location tracking
// --------------------------------------------------
@@ -327,18 +310,26 @@ export default function DashboardScreen() {
!("coords" in loc) ||
!loc.coords
) {
console.log("GPS invalide — update ignoré");
return;
}
const coords = (loc as Location.LocationObject).coords;
const { latitude, longitude } = coords;
const wasNull = !lastCoordsRef.current;
lastCoordsRef.current = { lat: latitude, lng: longitude };
setLastCoords({ lat: latitude, lng: longitude });
setLastUpdate(new Date());
await updateMyLocation(latitude, longitude);
} catch (err) {
console.log("GPS error:", err);
// Si c'est la première position GPS et qu'une adresse était en attente, rejouer
if (wasNull && pendingRouteAddress.current) {
const addr = pendingRouteAddress.current;
setTimeout(() => {
calcRoute(addr);
}, 1000);
}
} catch {
/* silent */
} finally {
sendingRef.current = false;
}
@@ -435,9 +426,6 @@ export default function DashboardScreen() {
const res = await updateDeliveryStatus(deliveryId, "livre", lat, lng);
if (res.success) {
showSuccess("Succès", "Livraison terminée");
setRouteInfo(null);
setDestinationCoords(null);
setInstructions([]);
loadData();
} else {
showError("Erreur", res.error || "Erreur");
@@ -450,98 +438,6 @@ export default function DashboardScreen() {
Linking.openURL(url);
};
// --------------------------------------------------
// Render: Navigation instruction bar
// --------------------------------------------------
const renderInstructionBar = () => {
if (instructions.length === 0 || !routeInfo) return null;
const current = instructions[currentInstructionIdx];
const next = instructions[currentInstructionIdx + 1];
const iconName =
maneuverIcons[current?.maneuver] || maneuverIcons.DEFAULT;
return (
<View style={styles.instructionBar}>
<View style={styles.currentInstruction}>
<View style={styles.instructionIconBox}>
<Ionicons
name={iconName as any}
size={22}
color={colors.white}
/>
</View>
<View style={{ flex: 1 }}>
<Text style={styles.instructionText} numberOfLines={2}>
{current?.instruction || "Suivez l'itinéraire"}
</Text>
{current?.streetName && (
<Text style={styles.instructionStreet}>
{current.streetName}
</Text>
)}
</View>
<Text style={styles.instructionDist}>
{current?.distance}
</Text>
</View>
{next && (
<View style={styles.nextInstruction}>
<Text style={styles.nextLabel}>Puis</Text>
<Ionicons
name={
(maneuverIcons[next.maneuver] ||
maneuverIcons.DEFAULT) as any
}
size={14}
color={colors.textMuted}
/>
<Text style={styles.nextText} numberOfLines={1}>
{next.instruction}
</Text>
</View>
)}
<View style={styles.routeSummary}>
<View style={styles.routeInfoChip}>
<Ionicons
name="speedometer-outline"
size={14}
color={colors.accent}
/>
<Text style={styles.routeInfoValue}>
{routeInfo.distance}
</Text>
</View>
<View style={styles.routeInfoChip}>
<Ionicons
name="time-outline"
size={14}
color={colors.accent}
/>
<Text style={styles.routeInfoValue}>
{routeInfo.duration}
</Text>
</View>
<TouchableOpacity
style={styles.showStepsBtn}
onPress={() => setShowInstructions(!showInstructions)}
>
<Ionicons
name="list-outline"
size={14}
color={colors.white}
/>
<Text style={styles.showStepsBtnText}>
{showInstructions ? "Masquer" : "Étapes"} (
{instructions.length})
</Text>
</TouchableOpacity>
</View>
</View>
);
};
// --------------------------------------------------
// Render delivery card
// --------------------------------------------------
@@ -713,23 +609,22 @@ export default function DashboardScreen() {
// --------------------------------------------------
const renderHeader = () => (
<View>
{/* Carte TomTom */}
{lastCoords ? (
<View style={styles.mapContainer}>
<TomTomMap
ref={mapRef}
style={styles.map}
markers={driverMarkers}
route={tomtomRoute}
destination={tomtomDestination}
initialCenter={{
latitude: lastCoords.lat,
longitude: lastCoords.lng,
}}
initialZoom={14}
/>
{/* Carte TomTom — toujours montée pour que le ref soit disponible */}
<View style={styles.mapContainer}>
<TomTomMap
ref={mapRef}
style={styles.map}
markers={driverMarkers}
initialCenter={
lastCoords
? { latitude: lastCoords.lat, longitude: lastCoords.lng }
: { latitude: 48.8566, longitude: 2.3522 }
}
initialZoom={14}
/>
{/* GPS overlay */}
{/* Overlay GPS */}
{lastCoords ? (
<View style={styles.gpsOverlay}>
<View
style={[
@@ -754,126 +649,55 @@ export default function DashboardScreen() {
</Text>
)}
</View>
{/* Bouton plein écran */}
<TouchableOpacity
style={styles.expandBtn}
onPress={() => setMapFullscreen(true)}
activeOpacity={0.7}
>
) : (
<View style={styles.gpsOverlay}>
<Ionicons
name="expand-outline"
size={20}
color={colors.white}
name="location-outline"
size={16}
color={colors.textMuted}
/>
</TouchableOpacity>
{/* Indicateur calcul de route */}
{routeLoading && (
<View style={styles.routeLoadingOverlay}>
<Text style={styles.routeLoadingText}>
Calcul de l'itinéraire...
</Text>
</View>
)}
</View>
) : (
<View style={styles.noMapBox}>
<Ionicons
name="location-outline"
size={32}
color={colors.textMuted}
/>
<Text style={styles.noMapText}>
Récupération de la position GPS...
</Text>
</View>
)}
{/* Instructions navigation */}
{renderInstructionBar()}
{/* Liste complète des étapes */}
{showInstructions && instructions.length > 0 && (
<View style={styles.allInstructionsBox}>
<View style={styles.allInstructionsHeader}>
<Text style={styles.allInstructionsTitle}>
Étapes de l'itinéraire
<Text style={styles.gpsOverlayText}>
Récupération GPS...
</Text>
<TouchableOpacity
onPress={() => setShowInstructions(false)}
>
<Ionicons
name="close"
size={20}
color={colors.textMuted}
/>
</TouchableOpacity>
</View>
<ScrollView style={{ maxHeight: 250 }} nestedScrollEnabled>
{instructions.map((inst, idx) => {
const icoName =
maneuverIcons[inst.maneuver] ||
maneuverIcons.DEFAULT;
const isCurrent = idx === currentInstructionIdx;
const isPassed = idx < currentInstructionIdx;
return (
<View
key={idx}
style={[
styles.stepRow,
isCurrent && styles.stepRowActive,
isPassed && styles.stepRowPassed,
]}
>
<View
style={[
styles.stepIcon,
isCurrent && {
backgroundColor:
colors.accent + "30",
},
]}
>
<Ionicons
name={icoName as any}
size={16}
color={
isCurrent
? colors.accent
: isPassed
? colors.textMuted
: colors.textSecondary
}
/>
</View>
<View style={{ flex: 1 }}>
<Text
style={[
styles.stepText,
isPassed && {
color: colors.textMuted,
},
]}
numberOfLines={2}
>
{inst.instruction}
</Text>
{inst.streetName && (
<Text style={styles.stepStreet}>
{inst.streetName}
</Text>
)}
</View>
<Text style={styles.stepDist}>
{inst.distance}
</Text>
</View>
);
})}
</ScrollView>
</View>
)}
)}
{/* Overlay infos trajet */}
{routeInfo && !routeLoading && (
<View style={styles.routeInfoOverlay}>
<View style={styles.routeInfoChip}>
<Ionicons name="time-outline" size={13} color={colors.white} />
<Text style={styles.routeInfoOverlayText}>{routeInfo.duration}</Text>
</View>
<View style={styles.routeInfoChip}>
<Ionicons name="navigate-outline" size={13} color={colors.white} />
<Text style={styles.routeInfoOverlayText}>{routeInfo.distance}</Text>
</View>
</View>
)}
{/* Bouton plein écran */}
<TouchableOpacity
style={styles.expandBtn}
onPress={() => setMapFullscreen(true)}
activeOpacity={0.7}
>
<Ionicons
name="expand-outline"
size={20}
color={colors.white}
/>
</TouchableOpacity>
{/* Indicateur calcul de route */}
{routeLoading && (
<View style={styles.routeLoadingOverlay}>
<Text style={styles.routeLoadingText}>
Calcul de l'itinéraire...
</Text>
</View>
)}
</View>
{/* Queue info */}
{queue && queue.queue_size > 0 && (
@@ -1004,6 +828,28 @@ export default function DashboardScreen() {
alignItems: "center",
},
routeInfoOverlay: {
position: "absolute",
top: spacing.s,
left: spacing.s,
flexDirection: "row",
gap: spacing.xs,
},
routeInfoChip: {
flexDirection: "row",
alignItems: "center",
gap: 4,
backgroundColor: "rgba(0,0,0,0.7)",
paddingVertical: 4,
paddingHorizontal: spacing.s,
borderRadius: 12,
},
routeInfoOverlayText: {
color: colors.white,
fontSize: fontSize.xs,
fontWeight: "700",
},
// Instructions
instructionBar: {
backgroundColor: colors.bgSecondary,
@@ -1069,20 +915,6 @@ export default function DashboardScreen() {
borderTopWidth: 1,
borderTopColor: colors.borderSubtle,
},
routeInfoChip: {
flexDirection: "row",
alignItems: "center",
gap: 4,
backgroundColor: colors.accent + "15",
paddingVertical: 4,
paddingHorizontal: spacing.s,
borderRadius: 12,
},
routeInfoValue: {
color: colors.accent,
fontSize: fontSize.xs,
fontWeight: "600",
},
showStepsBtn: {
flexDirection: "row",
alignItems: "center",
@@ -1410,20 +1242,17 @@ export default function DashboardScreen() {
>
<StatusBar hidden={mapFullscreen} />
<View style={styles.fullscreenContainer}>
{lastCoords && (
<TomTomMap
ref={fullscreenMapRef}
style={styles.fullscreenMap}
markers={driverMarkers}
route={tomtomRoute}
destination={tomtomDestination}
initialCenter={{
latitude: lastCoords.lat,
longitude: lastCoords.lng,
}}
initialZoom={15}
/>
)}
<TomTomMap
ref={fullscreenMapRef}
style={styles.fullscreenMap}
markers={driverMarkers}
initialCenter={
lastCoords
? { latitude: lastCoords.lat, longitude: lastCoords.lng }
: { latitude: 48.8566, longitude: 2.3522 }
}
initialZoom={15}
/>
{/* Top bar */}
<View style={styles.fullscreenTopBar}>
@@ -1438,54 +1267,45 @@ export default function DashboardScreen() {
/>
</TouchableOpacity>
<Text style={styles.fullscreenTitle}>
{routeInfo
? `${routeInfo.distance} · ${routeInfo.duration}`
: "GPS en direct"}
{pendingRouteAddress.current ? "Itinéraire en cours" : "GPS en direct"}
</Text>
<View style={{ width: 40 }} />
</View>
{/* Instruction bar en plein écran */}
{instructions.length > 0 && (
<View style={styles.fullscreenInstructionBar}>
<View style={styles.instructionIconBox}>
<Ionicons
name={
(maneuverIcons[
instructions[currentInstructionIdx]
?.maneuver
] || maneuverIcons.DEFAULT) as any
}
size={22}
color={colors.white}
/>
</View>
<View style={{ flex: 1 }}>
<Text
style={styles.instructionText}
numberOfLines={2}
>
{instructions[currentInstructionIdx]
?.instruction || "Suivez l'itinéraire"}
</Text>
{instructions[currentInstructionIdx]
?.streetName && (
<Text style={styles.instructionStreet}>
{
instructions[currentInstructionIdx]
.streetName
}
</Text>
)}
</View>
<Text style={styles.instructionDist}>
{instructions[currentInstructionIdx]?.distance}
</Text>
</View>
)}
{/* Bottom info */}
<View style={styles.fullscreenBottomBar}>
{/* Adresse de destination */}
{pendingRouteAddress.current && (
<View style={styles.fullscreenInfoRow}>
<Ionicons
name="location"
size={16}
color={colors.danger}
/>
<Text
style={[styles.fullscreenInfoText, { flex: 1 }]}
numberOfLines={2}
>
{pendingRouteAddress.current}
</Text>
</View>
)}
{/* Infos trajet */}
{routeInfo && (
<View style={styles.fullscreenInfoRow}>
<View style={styles.routeInfoChip}>
<Ionicons name="time-outline" size={14} color={colors.white} />
<Text style={styles.routeInfoOverlayText}>{routeInfo.duration}</Text>
</View>
<View style={[styles.routeInfoChip, { marginLeft: spacing.s }]}>
<Ionicons name="navigate-outline" size={14} color={colors.white} />
<Text style={styles.routeInfoOverlayText}>{routeInfo.distance}</Text>
</View>
</View>
)}
{/* GPS status */}
<View style={styles.fullscreenInfoRow}>
<View
style={[
@@ -1500,41 +1320,25 @@ export default function DashboardScreen() {
<Text style={styles.fullscreenInfoText}>
GPS {locationEnabled ? "actif" : "inactif"}
</Text>
</View>
{lastCoords && (
<Text style={styles.fullscreenCoords}>
{lastCoords.lat.toFixed(6)},{" "}
{lastCoords.lng.toFixed(6)}
</Text>
)}
{status && (
<View
style={[
styles.fullscreenStatusBadge,
{
backgroundColor:
STATUS_COLORS[status.status] + "30",
},
]}
>
<View
style={[
styles.dot,
{
backgroundColor:
STATUS_COLORS[status.status],
},
]}
/>
<Text
style={[
styles.fullscreenStatusText,
{ color: STATUS_COLORS[status.status] },
]}
>
{STATUS_LABELS[status.status]}
{lastCoords && (
<Text style={[styles.fullscreenCoords, { marginLeft: 8 }]}>
{lastCoords.lat.toFixed(4)}, {lastCoords.lng.toFixed(4)}
</Text>
</View>
)}
</View>
{/* Bouton recalculer */}
{pendingRouteAddress.current && lastCoords && (
<TouchableOpacity
style={styles.calcRouteBtn}
onPress={() => calcRoute(pendingRouteAddress.current!, fullscreenMapRef)}
activeOpacity={0.7}
>
<Ionicons name="navigate" size={16} color={colors.white} />
<Text style={styles.calcRouteBtnText}>
Recalculer l'itinéraire
</Text>
</TouchableOpacity>
)}
</View>
</View>