47 lines
1.5 KiB
TypeScript
47 lines
1.5 KiB
TypeScript
import { useState, useEffect, useRef } from 'react';
|
|
import { authHeaders, logNonAbortError } from '../lib/api';
|
|
import type { TransportMode } from './useTravelTime';
|
|
|
|
export interface Destination {
|
|
name: string;
|
|
slug: string;
|
|
place_type: string;
|
|
city?: string;
|
|
}
|
|
|
|
/** Fetches all travel-time destinations for a mode once, with client-side caching. */
|
|
export function useTravelDestinations(mode: TransportMode) {
|
|
const [destinations, setDestinations] = useState<Destination[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const cacheRef = useRef<Partial<Record<TransportMode, Destination[]>>>({});
|
|
|
|
useEffect(() => {
|
|
if (cacheRef.current[mode]) {
|
|
setDestinations(cacheRef.current[mode]!);
|
|
return;
|
|
}
|
|
|
|
const controller = new AbortController();
|
|
setLoading(true);
|
|
|
|
fetch(`/api/travel-destinations?mode=${mode}`, authHeaders({ signal: controller.signal }))
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
return res.json();
|
|
})
|
|
.then((data: { destinations: Destination[] }) => {
|
|
const normalized = data.destinations.map((d) => ({
|
|
...d,
|
|
city: d.city === 'City of London' ? 'London' : d.city,
|
|
}));
|
|
cacheRef.current[mode] = normalized;
|
|
setDestinations(normalized);
|
|
})
|
|
.catch((err) => logNonAbortError('travel destinations', err))
|
|
.finally(() => setLoading(false));
|
|
|
|
return () => controller.abort();
|
|
}, [mode]);
|
|
|
|
return { destinations, loading };
|
|
}
|