import { MaterialCommunityIcons } from "@expo/vector-icons"; import * as Location from "expo-location"; import { router, useLocalSearchParams } from "expo-router"; import { useCallback, useEffect, useRef, useState } from "react"; import { ActivityIndicator, Text, TouchableOpacity, View } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import { CustomButton } from "@/components/custom-button"; import { PinAdjuster } from "@/components/pin-adjuster"; import { useT } from "@/lib/i18n"; import { addressForCoords } from "@/lib/reverse-geocode"; import { useLocationStore } from "@/store"; // "Move the pin to where you actually are." // // An address from autocomplete lands on whatever the geocoder considers the // centre of that place — which can be the wrong side of a building, the wrong // end of a long street, or the middle of a junction the driver can't stop in. // The rider knows the doorway; this screen lets them say so, for the pickup // and the drop-off alike. // // Reverse geocoding is debounced rather than run on every frame of the pan: // the label only has to be right once the map stops. const GEOCODE_DEBOUNCE_MS = 450; // Falls back to Beirut, matching the map's own default, so the screen always // has somewhere to open even before a fix arrives. const FALLBACK = { latitude: 33.8938, longitude: 35.5018 }; type Coords = { latitude: number; longitude: number }; const AdjustPin = () => { const t = useT(); const params = useLocalSearchParams<{ mode?: string }>(); const mode = params.mode === "destination" ? "destination" : "origin"; const { userLatitude, userLongitude, destinationLatitude, destinationLongitude, setUserLocation, setDestinationLocation, } = useLocationStore(); // Open on the point being edited. A destination that hasn't been chosen yet // starts at the rider instead of an arbitrary city centre, because the place // they're going is usually near the place they are. const initial: Coords = mode === "origin" ? { latitude: userLatitude ?? FALLBACK.latitude, longitude: userLongitude ?? FALLBACK.longitude, } : { latitude: destinationLatitude ?? userLatitude ?? FALLBACK.latitude, longitude: destinationLongitude ?? userLongitude ?? FALLBACK.longitude, }; const [coords, setCoords] = useState(initial); const [address, setAddress] = useState(null); const [resolving, setResolving] = useState(true); const debounce = useRef>(); const resolve = useCallback((next: Coords) => { setCoords(next); clearTimeout(debounce.current); debounce.current = setTimeout(async () => { const label = await addressForCoords(next.latitude, next.longitude); setAddress(label); setResolving(false); }, GEOCODE_DEBOUNCE_MS); }, []); // Label the point the screen opened on, so the card isn't blank on arrival. useEffect(() => { resolve(initial); return () => clearTimeout(debounce.current); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const confirm = () => { const payload = { latitude: coords.latitude, longitude: coords.longitude, address: address ?? t("common.yourLocation"), }; if (mode === "origin") setUserLocation(payload); else setDestinationLocation(payload); router.back(); }; // Jump back to the rider's own position — the usual reason to open this // screen is that the suggested pickup drifted away from where they're // standing. const recenter = async () => { try { const { status } = await Location.requestForegroundPermissionsAsync(); if (status !== "granted") return; const position = await Location.getLastKnownPositionAsync({ maxAge: 60_000, }); if (!position) return; setResolving(true); resolve({ latitude: position.coords.latitude, longitude: position.coords.longitude, }); } catch (error) { console.log("[ADJUST_PIN_RECENTER]: ", error); } }; return ( setResolving(true)} onSettled={resolve} /> router.back()} accessibilityLabel={t("common.back")} className="w-10 h-10 rounded-full bg-white dark:bg-neutral-900 items-center justify-center shadow-sm shadow-neutral-300 dark:shadow-neutral-950/40" > {mode === "origin" ? t("adjustPin.pickupLabel") : t("adjustPin.destinationLabel")} {resolving ? ( <> {t("adjustPin.locating")} ) : ( {address} )} {t("adjustPin.hint")} ); }; export default AdjustPin;