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' },
});