use-socket-manager.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import { useState, useEffect, useCallback } from 'react'
  2. import SocketIoShim from '@/ide/connection/SocketIoShim'
  3. import type { Socket } from '@/features/ide-react/connection/types/socket'
  4. import type { DebugInfo, SocketState } from './types'
  5. export function useSocketManager() {
  6. const [socket, setSocket] = useState<Socket | null>(null)
  7. const [socketState, setSocketState] = useState<SocketState>({
  8. connected: false,
  9. connecting: false,
  10. lastError: '',
  11. })
  12. const [debugInfo, setDebugInfo] = useState<DebugInfo>({
  13. sent: 0,
  14. received: 0,
  15. latency: null,
  16. maxLatency: null,
  17. onLine: null,
  18. clockDelta: null,
  19. client: null,
  20. lastReceived: null,
  21. })
  22. const connectSocket = useCallback(() => {
  23. const parsedURL = new URL('/socket.io', window.origin)
  24. setSocketState(prev => ({
  25. ...prev,
  26. connecting: true,
  27. lastAttempt: Date.now(),
  28. }))
  29. const newSocket = SocketIoShim.connect(parsedURL.origin, {
  30. resource: parsedURL.pathname.slice(1),
  31. 'auto connect': false,
  32. 'connect timeout': 30 * 1000,
  33. 'force new connection': true,
  34. query: new URLSearchParams({ debugging: 'true' }).toString(),
  35. reconnect: false,
  36. }) as unknown as Socket
  37. setSocket(newSocket)
  38. return newSocket
  39. }, [])
  40. const disconnectSocket = useCallback(() => {
  41. socket?.disconnect()
  42. setSocket(null)
  43. setSocketState(prev => ({
  44. ...prev,
  45. connected: false,
  46. connecting: false,
  47. lastError: 'Manually disconnected',
  48. }))
  49. }, [socket])
  50. const forceReconnect = useCallback(() => {
  51. disconnectSocket()
  52. setTimeout(connectSocket, 1000)
  53. }, [disconnectSocket, connectSocket])
  54. useEffect(() => {
  55. connectSocket()
  56. }, [connectSocket])
  57. useEffect(() => {
  58. if (!socket) return
  59. const statsInterval = setInterval(() => {
  60. if (socket.socket.connected) {
  61. setDebugInfo(prev => ({ ...prev, sent: prev.sent + 1 }))
  62. socket.emit('debug', { time: Date.now() }, (info: any) => {
  63. const beforeTime = info.data.time
  64. const now = Date.now()
  65. const latency = now - beforeTime
  66. const clockDelta = (beforeTime + beforeTime) / 2 - info.serverTime
  67. setDebugInfo(prev => ({
  68. ...prev,
  69. received: prev.received + 1,
  70. latency,
  71. maxLatency: Math.max(prev.maxLatency ?? 0, latency),
  72. clockDelta,
  73. client: info.client,
  74. lastReceived: now,
  75. }))
  76. })
  77. }
  78. }, 2000)
  79. socket.on('connect', () => {
  80. setSocketState(prev => ({
  81. ...prev,
  82. connected: true,
  83. connecting: false,
  84. lastSuccess: Date.now(),
  85. lastError: '',
  86. }))
  87. })
  88. socket.on('disconnect', (reason: string) => {
  89. setSocketState(prev => ({
  90. ...prev,
  91. connected: false,
  92. connecting: false,
  93. lastError: `Disconnected: ${reason}`,
  94. }))
  95. })
  96. socket.on('connect_error', (error: Error) => {
  97. setSocketState(prev => ({
  98. ...prev,
  99. connecting: false,
  100. lastError: `Connection error: ${error?.message || 'Unknown'}`,
  101. }))
  102. })
  103. socket.socket.connect()
  104. return () => {
  105. clearInterval(statsInterval)
  106. socket.disconnect()
  107. }
  108. }, [socket])
  109. useEffect(() => {
  110. const updateNetworkInfo = () => {
  111. if ('connection' in navigator) {
  112. setDebugInfo(prev => ({ ...prev, onLine: navigator.onLine }))
  113. }
  114. }
  115. window.addEventListener('online', updateNetworkInfo)
  116. window.addEventListener('offline', updateNetworkInfo)
  117. updateNetworkInfo()
  118. return () => {
  119. window.removeEventListener('online', updateNetworkInfo)
  120. window.removeEventListener('offline', updateNetworkInfo)
  121. }
  122. }, [])
  123. return {
  124. socketState,
  125. debugInfo,
  126. connectSocket,
  127. disconnectSocket,
  128. forceReconnect,
  129. socket,
  130. }
  131. }