mobileapp / src /hooks /useMatches.ts
Antaram Dev Bot
feat: complete ANTARAM.ORG ride-sharing app frontend
5c876be
import { useState, useEffect, useCallback } from 'react';
import { eq, desc, and } from 'drizzle-orm';
import { useDatabase } from './useDatabase';
import { matches } from '../db/schema';
// ─── Types ─────────────────────────────────────────────────────────────────────
type MatchStatus = 'pending' | 'accepted' | 'rejected' | 'completed' | 'cancelled';
interface MatchData {
id: string;
rideId: string;
matchedUserId: string;
matchedUserFullName: string;
matchedUserAvatar: string | null;
matchedUserRating: number | null;
pickupPoint: string | null;
pickupLatitude: number | null;
pickupLongitude: number | null;
dropoffPoint: string | null;
dropoffLatitude: number | null;
dropoffLongitude: number | null;
status: MatchStatus;
createdAt: string | null;
updatedAt: string | null;
}
interface NewMatch {
id: string;
rideId: string;
matchedUserId: string;
matchedUserFullName: string;
matchedUserAvatar?: string;
matchedUserRating?: number;
pickupPoint?: string;
pickupLatitude?: number;
pickupLongitude?: number;
dropoffPoint?: string;
dropoffLatitude?: number;
dropoffLongitude?: number;
}
interface UseMatchesReturn {
matches: MatchData[];
pendingMatches: MatchData[];
activeMatch: MatchData | null;
loading: boolean;
createMatch: (data: NewMatch) => Promise<MatchData | null>;
updateMatchStatus: (matchId: string, status: MatchStatus) => Promise<void>;
acceptMatch: (matchId: string) => Promise<void>;
rejectMatch: (matchId: string) => Promise<void>;
refresh: () => Promise<void>;
}
// ─── Hook ──────────────────────────────────────────────────────────────────────
export function useMatches(rideId?: string): UseMatchesReturn {
const { db } = useDatabase();
const [matchesList, setMatchesList] = useState<MatchData[]>([]);
const [loading, setLoading] = useState(true);
// ── Load matches ─────────────────────────────────────────────────────────────
const loadMatches = useCallback(async () => {
try {
setLoading(true);
let results;
if (rideId) {
results = await db
.select()
.from(matches)
.where(eq(matches.rideId, rideId))
.orderBy(desc(matches.createdAt))
.all();
} else {
results = await db
.select()
.from(matches)
.orderBy(desc(matches.createdAt))
.all();
}
setMatchesList(results as MatchData[]);
} catch (error) {
console.error('[useMatches] Failed to load matches:', error);
setMatchesList([]);
} finally {
setLoading(false);
}
}, [db, rideId]);
useEffect(() => {
loadMatches();
}, [loadMatches]);
// ── Derived state ────────────────────────────────────────────────────────────
const pendingMatches = matchesList.filter((m) => m.status === 'pending');
const activeMatch =
matchesList.find((m) => m.status === 'accepted') ?? null;
// ── Create match ─────────────────────────────────────────────────────────────
const createMatch = useCallback(
async (data: NewMatch): Promise<MatchData | null> => {
try {
await db.insert(matches).values({
id: data.id,
rideId: data.rideId,
matchedUserId: data.matchedUserId,
matchedUserFullName: data.matchedUserFullName,
matchedUserAvatar: data.matchedUserAvatar ?? null,
matchedUserRating: data.matchedUserRating ?? 0,
pickupPoint: data.pickupPoint ?? null,
pickupLatitude: data.pickupLatitude ?? null,
pickupLongitude: data.pickupLongitude ?? null,
dropoffPoint: data.dropoffPoint ?? null,
dropoffLatitude: data.dropoffLatitude ?? null,
dropoffLongitude: data.dropoffLongitude ?? null,
}).run();
await loadMatches();
return matchesList.find((m) => m.id === data.id) ?? null;
} catch (error) {
console.error('[useMatches] Failed to create match:', error);
return null;
}
},
[db, loadMatches, matchesList],
);
// ── Update match status ──────────────────────────────────────────────────────
const updateMatchStatus = useCallback(
async (matchId: string, status: MatchStatus) => {
try {
await db
.update(matches)
.set({ status, updatedAt: new Date().toISOString() })
.where(eq(matches.id, matchId))
.run();
await loadMatches();
} catch (error) {
console.error('[useMatches] Failed to update match status:', error);
}
},
[db, loadMatches],
);
// ── Accept / Reject shortcuts ────────────────────────────────────────────────
const acceptMatch = useCallback(
(matchId: string) => updateMatchStatus(matchId, 'accepted'),
[updateMatchStatus],
);
const rejectMatch = useCallback(
(matchId: string) => updateMatchStatus(matchId, 'rejected'),
[updateMatchStatus],
);
return {
matches: matchesList,
pendingMatches,
activeMatch,
loading,
createMatch,
updateMatchStatus,
acceptMatch,
rejectMatch,
refresh: loadMatches,
};
}