"use client"; import { useEffect, useState } from "react"; import { collection, onSnapshot, orderBy, query, where, FirestoreError } from "firebase/firestore"; import { db } from "@/lib/firebase"; import type { AircraftTrailPoint } from "@/lib/types"; // Trail points live at aircraft/{icao}/positions (written by c2-core // telemetry.py on every position change, TTL-deleted after ~24h). The same // icao can fly several legs a day, so only the latest continuous stretch is // "this flight": a gap longer than FLIGHT_GAP_MS starts a new one. const LOOKBACK_MS = 6 * 60 * 60 * 1000; const FLIGHT_GAP_MS = 20 * 60 * 1000; function currentFlight(points: AircraftTrailPoint[]): AircraftTrailPoint[] { let start = 0; for (let i = 1; i < points.length; i++) { if (new Date(points[i].t).getTime() - new Date(points[i - 1].t).getTime() > FLIGHT_GAP_MS) start = i; } return points.slice(start); } /** Live flight path for one aircraft; pass null to subscribe to nothing. */ export function useAircraftTrail(icao: string | null) { const [trail, setTrail] = useState([]); useEffect(() => { setTrail([]); if (!icao) return; // `t` is Python's isoformat() in UTC ("...T17:06:48.755123+00:00"), so it // sorts and range-filters correctly as a string against toISOString()'s // "...T17:06:48.755Z" down to the second — no composite index needed. const since = new Date(Date.now() - LOOKBACK_MS).toISOString(); const q = query(collection(db, "aircraft", icao, "positions"), where("t", ">=", since), orderBy("t")); return onSnapshot( q, (snap) => setTrail(currentFlight(snap.docs.map((d) => d.data() as AircraftTrailPoint))), (err: FirestoreError) => console.error("useAircraftTrail:", err), ); }, [icao]); return trail; }