import { router, useLocalSearchParams } from "expo-router"; import { useCallback, useEffect, useState } from "react"; import { ActivityIndicator, Alert, Image, Text, TouchableOpacity, View, } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import { CustomButton } from "@/components/custom-button"; import { Map } from "@/components/map"; import { icons, images } from "@/constants"; import { ApiError, fetchAPI } from "@/lib/fetch"; import { formatTime } from "@/lib/utils"; import { useLocationStore } from "@/store"; import type { Ride } from "@/types/type"; const POLL_MS = 3000; const statusLabel: Record = { requested: "Finding your driver…", accepted: "Driver assigned — heading to you", en_route: "On your trip", completed: "You've arrived!", cancelled: "Ride cancelled", }; // book-ride is now the live ride-status screen. The rider lands here after // requesting a ride and polls its status until it completes (or they cancel). const BookRide = () => { const { id } = useLocalSearchParams<{ id: string }>(); const rideId = Number(id); const setUserLocation = useLocationStore((s) => s.setUserLocation); const setDestinationLocation = useLocationStore((s) => s.setDestinationLocation); const [ride, setRide] = useState(null); const [loading, setLoading] = useState(true); const [cancelling, setCancelling] = useState(false); const [error, setError] = useState(null); const load = useCallback(async () => { try { const res = await fetchAPI(`/(api)/ride/${rideId}`); const r = res.data as Ride; setRide(r); // Keep the map's origin/destination in sync with the ride so the route // line renders even if the rider reached this screen via history. setUserLocation({ latitude: r.origin_latitude, longitude: r.origin_longitude, address: r.origin_address, }); setDestinationLocation({ latitude: r.destination_latitude, longitude: r.destination_longitude, address: r.destination_address, }); } catch (err) { console.log("[BOOK_RIDE_LOAD]: ", err); if (err instanceof ApiError && err.status === 404) { setError("Ride not found."); } } finally { setLoading(false); } }, [rideId, setUserLocation, setDestinationLocation]); useEffect(() => { void load(); }, [load]); // Poll while the ride is still in a non-terminal state. useEffect(() => { const status = ride?.status; if (!status || status === "completed" || status === "cancelled") return; const timer = setInterval(() => void load(), POLL_MS); return () => clearInterval(timer); }, [ride?.status, load]); const cancel = async () => { setCancelling(true); try { await fetchAPI(`/(api)/ride/${rideId}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ status: "cancelled" }), }); await load(); } catch (err) { console.log("[BOOK_RIDE_CANCEL]: ", err); Alert.alert("Error", "Could not cancel this ride. Please try again."); } finally { setCancelling(false); } }; if (loading) { return ( ); } if (error || !ride) { return ( {error ?? "Could not load this ride."} router.replace("/(root)/(tabs)/home")} className="mt-6" /> ); } const driver = ride.driver; const terminal = ride.status === "completed" || ride.status === "cancelled"; return ( {statusLabel[ride.status] ?? ride.status} {/* Searching state */} {ride.status === "requested" ? ( We're matching you with the nearest {ride.service} driver. ) : null} {/* Driver card — shown once a driver is assigned. */} {driver?.id ? ( {driver.first_name} {driver.last_name} {driver.rating?.toFixed(1) ?? "—"} {driver.car_model ? ( {driver.car_model} ) : null} {driver.service ?? ride.service} {ride.origin_address} {ride.destination_address} {ride.payment_status === "cash" ? "💵 Cash to driver" : "💳 Paid by card"} ${(ride.fare_price / 100).toFixed(2)} ) : null} {/* Completed summary */} {ride.status === "completed" ? ( Fare: ${(ride.fare_price / 100).toFixed(2)} Trip time {formatTime(ride.ride_time)} ) : null} {/* Cancelled */} {ride.status === "cancelled" ? ( This ride was cancelled. ) : null} {terminal ? ( router.replace("/(root)/(tabs)/home")} /> ) : ( {cancelling ? "Cancelling…" : "Cancel Ride"} )} ); }; export default BookRide;