File size: 1,500 Bytes
a9df8b1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { useEffect, useRef } from 'react'
import { useAppStore } from '@/store/appStore'
import type { WsEvent } from '@/store/appStore'

type RawWsMessage = {
  type?: unknown
  payload?: unknown
  timestamp?: unknown
  event?: unknown
  data?: unknown
}

function normalizeWsMessage(raw: RawWsMessage): WsEvent | null {
  const type = typeof raw.type === 'string'
    ? raw.type
    : typeof raw.event === 'string'
      ? raw.event
      : ''

  if (!type) return null

  return {
    type,
    payload: 'payload' in raw ? raw.payload : raw.data,
    timestamp: typeof raw.timestamp === 'string' ? raw.timestamp : new Date().toISOString(),
  }
}

export function useWebSocket() {
  const wsRef = useRef<WebSocket | null>(null)
  const pushWsEvent = useAppStore(s => s.pushWsEvent)

  useEffect(() => {
    if (import.meta.env.VITE_SKILLOS_DISABLE_WS === '1') {
      return
    }

    const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'
    const ws = new WebSocket(`${protocol}://${window.location.host}/ws`)
    wsRef.current = ws

    ws.onmessage = (e) => {
      try {
        const msg = normalizeWsMessage(JSON.parse(e.data))
        if (msg) pushWsEvent(msg)
      } catch {
        // ignore
      }
    }

    const ping = setInterval(() => {
      if (ws.readyState === WebSocket.OPEN) {
        ws.send(JSON.stringify({ type: 'ping' }))
      }
    }, 30_000)

    return () => {
      clearInterval(ping)
      ws.close()
    }
  }, [pushWsEvent])

  return wsRef
}