socket-diagnostics.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import React from 'react'
  2. import type { ConnectionStatus } from './types'
  3. import { useSocketManager } from './use-socket-manager'
  4. import {
  5. ActionButton,
  6. ConnectionBadge,
  7. DiagnosticItem,
  8. ErrorAlert,
  9. } from './diagnostic-component'
  10. import { Container } from 'react-bootstrap-5'
  11. import MaterialIcon from '@/shared/components/material-icon'
  12. type NetworkInformation = {
  13. downlink: number
  14. effectiveType: string
  15. rtt: number
  16. saveData: boolean
  17. type: string
  18. }
  19. const NavigatorInfo = () => {
  20. if (!('connection' in navigator)) {
  21. return <div>Network Information API not supported</div>
  22. }
  23. const connection = navigator.connection as NetworkInformation
  24. return (
  25. <>
  26. <div>Downlink: {connection.downlink} Mbps</div>
  27. <div>Effective Type: {connection.effectiveType}</div>
  28. <div>Round Trip Time: {connection.rtt} ms</div>
  29. <div>Save Data: {connection.saveData ? 'Enabled' : 'Disabled'}</div>
  30. <div>Platform: {navigator.platform}</div>
  31. {/* @ts-ignore */}
  32. <div>Device Memory: {navigator.deviceMemory}</div>
  33. <div>Hardware Concurrency: {navigator.hardwareConcurrency}</div>
  34. </>
  35. )
  36. }
  37. export const SocketDiagnostics = () => {
  38. const { socketState, debugInfo, disconnectSocket, forceReconnect, socket } =
  39. useSocketManager()
  40. const getConnectionState = (): ConnectionStatus => {
  41. if (socketState.connected) return 'connected'
  42. if (socketState.connecting) return 'connecting'
  43. return 'disconnected'
  44. }
  45. const lastReceivedS = debugInfo.lastReceived
  46. ? Math.round((Date.now() - debugInfo.lastReceived) / 1000)
  47. : null
  48. return (
  49. <Container>
  50. <h1>Socket Diagnostics</h1>
  51. <ConnectionBadge state={getConnectionState()} />
  52. <div className="d-flex gap-2 mt-3">
  53. <ActionButton
  54. label="Reconnect"
  55. icon="refresh"
  56. onClick={forceReconnect}
  57. />
  58. <ActionButton
  59. label="Disconnect"
  60. icon="close"
  61. onClick={disconnectSocket}
  62. disabled={!socketState.connected}
  63. />
  64. </div>
  65. {socketState.lastError && <ErrorAlert message={socketState.lastError} />}
  66. <div className="card p-4 mt-3">
  67. <h3 className="text-lg">
  68. <MaterialIcon type="speed" /> Connection Stats
  69. </h3>
  70. <div className="space-y-2">
  71. <DiagnosticItem
  72. icon="network_ping"
  73. label="Ping Count"
  74. value={
  75. <>
  76. {debugInfo.received} / {debugInfo.sent}
  77. {lastReceivedS !== null && (
  78. <>
  79. <br />
  80. Last received {lastReceivedS}s ago
  81. </>
  82. )}
  83. </>
  84. }
  85. type={
  86. lastReceivedS !== null
  87. ? lastReceivedS < 4
  88. ? 'success'
  89. : 'danger'
  90. : undefined
  91. }
  92. />
  93. <DiagnosticItem
  94. icon="schedule"
  95. label="Latency"
  96. value={
  97. debugInfo.latency ? (
  98. <>
  99. {debugInfo.latency} ms
  100. <br />
  101. Max: {debugInfo.maxLatency} ms
  102. </>
  103. ) : (
  104. '-'
  105. )
  106. }
  107. type={
  108. debugInfo.latency
  109. ? debugInfo.latency < 150
  110. ? 'success'
  111. : 'danger'
  112. : undefined
  113. }
  114. />
  115. <DiagnosticItem
  116. icon="difference"
  117. label="Clock Delta"
  118. value={
  119. debugInfo.clockDelta === null
  120. ? '-'
  121. : `${Math.round(debugInfo.clockDelta / 1000)}s`
  122. }
  123. type={
  124. debugInfo.clockDelta !== null
  125. ? Math.abs(debugInfo.clockDelta) < 1500
  126. ? 'success'
  127. : 'danger'
  128. : undefined
  129. }
  130. />
  131. <DiagnosticItem
  132. icon="signal_cellular_alt"
  133. label="Online"
  134. value={debugInfo.onLine?.toString() ?? '-'}
  135. type={debugInfo.onLine ? 'success' : 'danger'}
  136. />
  137. <DiagnosticItem
  138. icon="schedule"
  139. label="Current time"
  140. value={new Date().toUTCString()}
  141. />
  142. <DiagnosticItem
  143. icon="hourglass"
  144. label="Connection time"
  145. value={
  146. debugInfo.client?.connectedAt ? (
  147. <>
  148. {new Date(debugInfo.client.connectedAt).toUTCString()} (
  149. {Math.round(
  150. (Date.now() - debugInfo.client.connectedAt) / 1000
  151. )}
  152. s)
  153. </>
  154. ) : (
  155. '-'
  156. )
  157. }
  158. />
  159. <DiagnosticItem
  160. icon="local_shipping"
  161. label="Transport"
  162. value={socket?.socket.transport?.name ?? '-'}
  163. />
  164. <DiagnosticItem
  165. icon="badge"
  166. label="Client Public ID"
  167. value={debugInfo.client?.publicId ?? '-'}
  168. />
  169. <DiagnosticItem
  170. icon="pin"
  171. label="IP Address"
  172. value={debugInfo.client?.remoteIp ?? '-'}
  173. />
  174. <DiagnosticItem
  175. icon="web"
  176. label="User agent"
  177. value={debugInfo.client?.userAgent ?? '-'}
  178. />
  179. <DiagnosticItem
  180. icon="directions_boat"
  181. label="Navigator info"
  182. value={<NavigatorInfo />}
  183. />
  184. </div>
  185. </div>
  186. </Container>
  187. )
  188. }