import { Redirect, router } from 'expo-router'; import LottieView from 'lottie-react-native'; import { useEffect, useState } from 'react'; import { Pressable, StyleSheet, Text, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import type { RiderRideView } from '@/lib/dispatch/protocol'; import { useSecondsLeft } from '@/lib/dispatch/useSecondsLeft'; import { useRide } from '@/lib/RideContext'; import { formatPrice, getRideType } from '@/lib/rideTypes'; export default function FindingDriverScreen() { const insets = useSafeAreaInsets(); const { ride, requesting, cancelRide, dismissRide } = useRide(); // Set when this screen is on its way out, so the ride disappearing doesn't redirect mid-transition. const [leaving, setLeaving] = useState(false); const status = ride?.status; useEffect(() => { if (status === 'completed') router.replace('/trip'); }, [status]); if (!ride || ride.status === 'completed') { if (requesting || leaving || ride) return ; return ; } const leave = (action: () => void) => { setLeaving(true); action(); router.back(); }; return ( {ride.status === 'auction' && leave(cancelRide)} />} {ride.status === 'matched' && } {ride.status === 'unmatched' && leave(dismissRide)} />} ); } function Header({ title, subtitle }: { title: string; subtitle: string }) { return ( {title} {subtitle} ); } function tripLine(ride: RiderRideView) { return `${getRideType(ride.rideTypeId).name} to ${ride.destination.title}`; } function Auction({ ride, onCancel }: { ride: RiderRideView; onCancel: () => void }) { const secondsLeft = useSecondsLeft(ride.closesAt); return ( <>
Offering drivers {formatPrice(ride.price)} Round {ride.round} · {secondsLeft}s left Cancel request ); } function Matched({ ride }: { ride: RiderRideView }) { const { driver, fare } = ride; if (!driver || fare === null) return null; const difference = fare - ride.quotedFare; return ( <>
{formatPrice(fare)} {difference < 0 ? `${formatPrice(-difference)} below your quote` : difference > 0 ? `${formatPrice(difference)} above your quote` : 'Your quoted fare'} {driver.name[0]} {driver.name} · ★ {driver.rating.toFixed(2)} {driver.car} {driver.plate} {ride.driverFinishingTrip && ( {driver.name} is finishing a nearby trip, then heading to you )} ); } function Unmatched({ ride, onBack }: { ride: RiderRideView; onBack: () => void }) { return ( <>
Back ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', paddingHorizontal: 24, justifyContent: 'space-between' }, header: { alignItems: 'center', gap: 6 }, title: { fontSize: 26, fontWeight: '700', color: '#111' }, subtitle: { fontSize: 15, color: '#666', textAlign: 'center' }, animation: { width: 260, height: 260, alignSelf: 'center' }, auctionInfo: { alignItems: 'center', gap: 4 }, offers: { fontSize: 18, fontWeight: '600', color: '#111' }, fareBox: { alignItems: 'center', gap: 4 }, fare: { fontSize: 40, fontWeight: '700', color: '#111' }, card: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#f4f4f4', borderRadius: 14, padding: 16 }, avatar: { width: 48, height: 48, borderRadius: 24, backgroundColor: '#111', alignItems: 'center', justifyContent: 'center', marginRight: 12 }, avatarText: { color: '#fff', fontSize: 20, fontWeight: '600' }, cardText: { flex: 1 }, driverName: { fontSize: 17, fontWeight: '600', color: '#111' }, muted: { color: '#666', marginTop: 2 }, centered: { textAlign: 'center' }, plate: { fontSize: 15, fontWeight: '700', color: '#111', backgroundColor: '#fff', paddingHorizontal: 8, paddingVertical: 4, borderRadius: 6 }, secondaryButton: { backgroundColor: '#eee', borderRadius: 10, paddingVertical: 16, alignItems: 'center' }, secondaryButtonText: { fontSize: 17, fontWeight: '600', color: '#111' }, primaryButton: { backgroundColor: '#111', borderRadius: 10, paddingVertical: 16, alignItems: 'center' }, primaryButtonText: { color: '#fff', fontSize: 17, fontWeight: '600' }, });