File size: 2,901 Bytes
201b13c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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,
  }
}