import { useEffect, useState } from "react" import { fetchLatestReport, getWebSocketUrl } from "../lib/api" export default function useRealtimeInspection() { const [latestInspection, setLatestInspection] = useState(null) const [connectionState, setConnectionState] = useState("connecting") const [lastEventAt, setLastEventAt] = useState(null) const [streamError, setStreamError] = useState("") useEffect(() => { let socket let reconnectTimer let heartbeatTimer let refreshTimer let active = true let retryCount = 0 async function hydrateLatest() { try { const latest = await fetchLatestReport() if (active && latest?.timestamp) { setLatestInspection((current) => { if (!current || current.timestamp !== latest.timestamp) { return latest } return current }) setLastEventAt(latest.timestamp) } } catch { if (active) { setStreamError("Latest inspection snapshot is unavailable right now.") } } } function scheduleReconnect() { if (!active) return setConnectionState("reconnecting") const delay = Math.min(8000, 1200 * 2 ** retryCount) retryCount += 1 reconnectTimer = window.setTimeout(connect, delay) } function connect() { if (!active) return setConnectionState("connecting") socket = new WebSocket(getWebSocketUrl("/ws")) socket.onopen = () => { retryCount = 0 setConnectionState("connected") setStreamError("") heartbeatTimer = window.setInterval(() => { if (socket?.readyState === WebSocket.OPEN) { socket.send(JSON.stringify({ type: "ping" })) } }, 15000) } socket.onmessage = (event) => { const payload = JSON.parse(event.data) if (payload.type === "inspection.update" || payload.type === "inspection.snapshot") { setLatestInspection(payload) setLastEventAt(payload.timestamp) return } if (payload.type === "pong") { setConnectionState("connected") } } socket.onerror = () => { setStreamError("Live stream interrupted. Reconnecting to the inspection server.") } socket.onclose = () => { window.clearInterval(heartbeatTimer) if (active) { scheduleReconnect() } } } hydrateLatest() connect() refreshTimer = window.setInterval(hydrateLatest, 6000) return () => { active = false window.clearTimeout(reconnectTimer) window.clearInterval(heartbeatTimer) window.clearInterval(refreshTimer) if (socket) { socket.close() } } }, []) return { latestInspection, connectionState, lastEventAt, streamError, } }