mobileapp / src /hooks /useRides.ts
Antaram Dev Bot
feat: complete ANTARAM.ORG ride-sharing app frontend
5c876be
import { useState, useEffect, useCallback } from 'react';
import { eq, desc, and, inArray } from 'drizzle-orm';
import { useDatabase } from './useDatabase';
import { rides } from '../db/schema';
// ─── Types ─────────────────────────────────────────────────────────────────────
type RideStatus = 'searching' | 'matched' | 'in_progress' | 'completed' | 'cancelled';
interface RideData {
id: string;
userId: string;
originAddress: string;
originLatitude: number;
originLongitude: number;
destinationAddress: string;
destinationLatitude: number;
destinationLongitude: number;
routePolyline: string | null;
scheduledTime: string | null;
status: RideStatus;
seatPrice: number | null;
totalDistance: number | null;
estimatedDuration: number | null;
createdAt: string | null;
updatedAt: string | null;
}
interface NewRide {
id: string;
userId: string;
originAddress: string;
originLatitude: number;
originLongitude: number;
destinationAddress: string;
destinationLatitude: number;
destinationLongitude: number;
routePolyline?: string;
scheduledTime?: string;
seatPrice?: number;
totalDistance?: number;
estimatedDuration?: number;
}
interface UseRidesReturn {
rides: RideData[];
activeRide: RideData | null;
loading: boolean;
createRide: (data: NewRide) => Promise<RideData | null>;
updateRideStatus: (rideId: string, status: RideStatus) => Promise<void>;
getRideById: (rideId: string) => Promise<RideData | null>;
refresh: () => Promise<void>;
}
// ─── Hook ──────────────────────────────────────────────────────────────────────
export function useRides(statusFilter?: RideStatus[]): UseRidesReturn {
const { db } = useDatabase();
const [ridesList, setRidesList] = useState<RideData[]>([]);
const [loading, setLoading] = useState(true);
// ── Load rides ───────────────────────────────────────────────────────────────
const loadRides = useCallback(async () => {
try {
setLoading(true);
const activeStatuses: RideStatus[] = ['searching', 'matched', 'in_progress'];
// Always load active rides
const allRides = await db
.select()
.from(rides)
.orderBy(desc(rides.createdAt))
.all();
let filtered: typeof allRides = allRides;
if (statusFilter && statusFilter.length > 0) {
filtered = allRides.filter((r) => statusFilter.includes(r.status as RideStatus));
}
setRidesList(filtered as RideData[]);
} catch (error) {
console.error('[useRides] Failed to load rides:', error);
setRidesList([]);
} finally {
setLoading(false);
}
}, [db, statusFilter]);
useEffect(() => {
loadRides();
}, [loadRides]);
// ── Active ride (first non-completed, non-cancelled) ─────────────────────────
const activeRide =
ridesList.find(
(r) => r.status === 'searching' || r.status === 'matched' || r.status === 'in_progress',
) ?? null;
// ── Create ride ──────────────────────────────────────────────────────────────
const createRide = useCallback(
async (data: NewRide): Promise<RideData | null> => {
try {
await db.insert(rides).values({
id: data.id,
userId: data.userId,
originAddress: data.originAddress,
originLatitude: data.originLatitude,
originLongitude: data.originLongitude,
destinationAddress: data.destinationAddress,
destinationLatitude: data.destinationLatitude,
destinationLongitude: data.destinationLongitude,
routePolyline: data.routePolyline ?? null,
scheduledTime: data.scheduledTime ?? null,
seatPrice: data.seatPrice ?? null,
totalDistance: data.totalDistance ?? null,
estimatedDuration: data.estimatedDuration ?? null,
}).run();
await loadRides();
const created = ridesList.find((r) => r.id === data.id);
return (created as RideData) ?? null;
} catch (error) {
console.error('[useRides] Failed to create ride:', error);
return null;
}
},
[db, loadRides, ridesList],
);
// ── Update ride status ───────────────────────────────────────────────────────
const updateRideStatus = useCallback(
async (rideId: string, status: RideStatus) => {
try {
await db
.update(rides)
.set({ status, updatedAt: new Date().toISOString() })
.where(eq(rides.id, rideId))
.run();
await loadRides();
} catch (error) {
console.error('[useRides] Failed to update ride status:', error);
}
},
[db, loadRides],
);
// ── Get ride by ID ───────────────────────────────────────────────────────────
const getRideById = useCallback(
async (rideId: string): Promise<RideData | null> => {
try {
const results = await db
.select()
.from(rides)
.where(eq(rides.id, rideId))
.all();
return (results[0] as RideData) ?? null;
} catch (error) {
console.error('[useRides] Failed to get ride:', error);
return null;
}
},
[db],
);
return {
rides: ridesList,
activeRide,
loading,
createRide,
updateRideStatus,
getRideById,
refresh: loadRides,
};
}