Files
time_to_leave/apps/web/src/hooks/useOriginStation.ts
T
fegger eac4f6e216 Revamp branding, UI styling, and HAFAS integration
- Update BRAND_GUIDELINES.md with new melting clock logo concept
- Rebrand UI components with new violet-to-pink gradient color scheme
- Implement HAFAS authentication headers and GET support in route
- Update logo SVGs to match new brand guidelines
- Fix HAFAS time format to exclude millisecond padding
- Update default station to Mödling Bahnhof
- Bump workspace package versions to 1.0.0
2026-05-12 09:44:37 +02:00

101 lines
2.8 KiB
TypeScript

import { useState, useEffect } from "react";
import type { Station } from "@timetoleave/core";
import { DEFAULT_STATION_NAME, DEFAULT_STATION_EXT_ID } from "@/lib/constants";
import { useGeolocation } from "./useGeolocation";
interface HafasLocation {
type: string;
name: string;
extId: string;
lat: number;
lon: number;
}
export function useOriginStation() {
const [station, setStation] = useState<Station | null>(null);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);
const { location, state } = useGeolocation();
useEffect(() => {
let isMounted = true;
const fetchNearestStation = async () => {
if (!location || state !== "granted") {
if (isMounted) {
setStation({ name: DEFAULT_STATION_NAME, extId: DEFAULT_STATION_EXT_ID });
}
return;
}
setLoading(true);
setError(null);
try {
const { latitude, longitude } = location.coords;
const body = {
svcReqL: [
{
meth: "LocMatch",
req: {
input: {
loc: {
crd: {
x: Math.round(longitude * 1e6),
y: Math.round(latitude * 1e6),
},
type: "S",
},
maxLoc: 5,
field: "S",
},
},
},
],
};
const response = await fetch("/api/hafas", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (!response.ok) {
throw new Error(`HAFAS request failed with status ${response.status}`);
}
const data = await response.json();
const match = data?.svcResL?.[0]?.res?.match?.locL ?? [];
const stations: Station[] = (match as HafasLocation[])
.filter((l) => l.type === "S")
.map((l) => ({ name: l.name, extId: l.extId, lat: l.lat, lng: l.lon }));
if (!isMounted) return;
if (stations.length > 0) {
setStation(stations[0]);
} else {
setStation({ name: DEFAULT_STATION_NAME, extId: DEFAULT_STATION_EXT_ID });
}
setLoading(false);
} catch (err: unknown) {
if (isMounted) {
const message = err instanceof Error ? err.message : "Failed to find nearest station";
setError(message);
setStation({ name: DEFAULT_STATION_NAME, extId: DEFAULT_STATION_EXT_ID });
setLoading(false);
}
}
};
fetchNearestStation();
return () => {
isMounted = false;
};
}, [location, state]);
return { station, loading, error };
}