"use client"; import { useEffect, useRef, useState } from "react"; import type L from "leaflet"; import { c2api } from "@/lib/c2api"; import type { Ny511Response } from "@/lib/types"; // 511NY cameras/events for the current map view (server-26#183). Unlike // useAircraft/useVessels this is not a Firestore listener: the data is public // and statewide, so c2-core caches it in memory and serves a bbox slice. // Fetches only while `enabled` (the overlay is on), on pan/zoom (debounced), // and on a poll matching the server's events TTL. const POLL_MS = 2 * 60 * 1000; const MOVE_DEBOUNCE_MS = 400; const BBOX_PAD = 0.2; // fetch a little past the edges so small pans don't blank the layer export function use511(map: L.Map, layers: "cameras" | "events", enabled: boolean) { const [data, setData] = useState({}); const [error, setError] = useState(null); const seq = useRef(0); useEffect(() => { if (!enabled) { setData({}); return; } let debounce: ReturnType | undefined; const load = async () => { const b = map.getBounds().pad(BBOX_PAD); const mine = ++seq.current; try { const res = await c2api.get511( { south: b.getSouth(), west: b.getWest(), north: b.getNorth(), east: b.getEast() }, layers, ); if (mine !== seq.current) return; // a newer pan's response wins setData(res); setError(null); } catch (e) { if (mine !== seq.current) return; console.error("use511:", e); setError(e instanceof Error ? e.message : String(e)); } }; const onMove = () => { clearTimeout(debounce); debounce = setTimeout(load, MOVE_DEBOUNCE_MS); }; load(); map.on("moveend", onMove); const poll = setInterval(load, POLL_MS); return () => { map.off("moveend", onMove); clearTimeout(debounce); clearInterval(poll); seq.current++; // drop any in-flight response }; }, [map, layers, enabled]); return { data, error }; }