import { useEffect, useRef, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import type { SightingType } from "../types/types"; import { useSoundOnChange } from "react-sounds"; import switchSound from "../assets/sounds/ui/switch.mp3"; async function fetchSighting( url: string | undefined, ref: number ): Promise { const res = await fetch(`${url}${ref}`); if (!res.ok) throw new Error(String(res.status)); return res.json(); } export function useSightingFeed(url: string | undefined) { const [sightings, setSightings] = useState([]); const [selectedRef, setSelectedRef] = useState(null); const [sessionStarted, setSessionStarted] = useState(false); const [sessionList, setSessionList] = useState([]); const mostRecent = sightings[0] ?? null; const latestRef = mostRecent?.ref ?? null; const [selectedSighting, setSelectedSighting] = useState( null ); useSoundOnChange(switchSound, latestRef, { volume: 1, }); const currentRef = useRef(-1); const lastValidTimestamp = useRef(Date.now()); const query = useQuery({ queryKey: ["sighting-feed", url], enabled: !!url, queryFn: () => fetchSighting(url, currentRef.current), refetchInterval: (q) => { const data = q.state.data as SightingType | undefined; const now = Date.now(); if (data && data.ref !== -1) { lastValidTimestamp.current = now; return 100; } if (now - lastValidTimestamp.current > 60_000) { currentRef.current = -1; lastValidTimestamp.current = now; } return 400; }, refetchIntervalInBackground: true, refetchOnWindowFocus: false, retry: false, staleTime: 0, }); useEffect(() => { if (sessionStarted) { if (!mostRecent) return; setSessionList([...sessionList, mostRecent]); } }, [mostRecent, sessionList, sessionStarted]); useEffect(() => { const data = query.data; if (!data) return; const now = Date.now(); if (data.ref === -1) { // setSightings((prev) => { // if (prev[0]?.ref === data.ref) return prev; // const dedupPrev = prev.filter((s) => s.ref !== data.ref); // return [data, ...dedupPrev].slice(0, 7); // }); return; } // if (Notification.permission === "granted") { // new Notification("New Sighting!", { // body: `Ref: ${data.ref}`, // icon: "/MAV-blue.svg", // }); // } currentRef.current = data.ref; lastValidTimestamp.current = now; setSightings((prev) => { if (prev[0]?.ref === data.ref) return prev; const dedupPrev = prev.filter((s) => s.ref !== data.ref); return [data, ...dedupPrev].slice(0, 7); }); setSelectedRef(data.ref); }, [query.data]); useEffect(() => { if (query.error) { // console.error("Sighting feed error:", query.error); } }, [query.error]); return { sightings, selectedRef, setSelectedRef, mostRecent, selectedSighting, sessionList, sessionStarted, setSessionStarted, setSelectedSighting, data: query.data, isLoading: query.isLoading, isFetching: query.isFetching, isError: query.isError, error: query.error as Error | null, refetch: query.refetch, }; }