diff --git a/services/web/frontend/js/features/socket-diagnostics/components/socket-diagnostics.tsx b/services/web/frontend/js/features/socket-diagnostics/components/socket-diagnostics.tsx
index cbf16bcb4e..498a9ecc23 100644
--- a/services/web/frontend/js/features/socket-diagnostics/components/socket-diagnostics.tsx
+++ b/services/web/frontend/js/features/socket-diagnostics/components/socket-diagnostics.tsx
@@ -7,9 +7,10 @@ import {
DiagnosticItem,
ErrorAlert,
} from './diagnostic-component'
-import { Container } from 'react-bootstrap-5'
+import { Col, Container, Row } from 'react-bootstrap-5'
import MaterialIcon from '@/shared/components/material-icon'
import OLFormCheckbox from '@/features/ui/components/ol/ol-form-checkbox'
+import { CopyToClipboard } from '@/shared/components/copy-to-clipboard'
type NetworkInformation = {
downlink: number
@@ -19,33 +20,38 @@ type NetworkInformation = {
type: string
}
-const NavigatorInfo = () => {
+const navigatorInfo = (): string[] => {
if (!('connection' in navigator)) {
- return
Network Information API not supported
+ return ['Network Information API not supported']
}
const connection = navigator.connection as NetworkInformation
- return (
- <>
- Downlink: {connection.downlink} Mbps
- Effective Type: {connection.effectiveType}
- Round Trip Time: {connection.rtt} ms
- Save Data: {connection.saveData ? 'Enabled' : 'Disabled'}
- Platform: {navigator.platform}
- {/* @ts-ignore */}
- Device Memory: {navigator.deviceMemory}
- Hardware Concurrency: {navigator.hardwareConcurrency}
- >
- )
+
+ return [
+ `Downlink: ${connection.downlink} Mbps`,
+ `Effective Type: ${connection.effectiveType}`,
+ `Round Trip Time: ${connection.rtt} ms`,
+ `Save Data: ${connection.saveData ? 'Enabled' : 'Disabled'}`,
+ `Platform: ${navigator.platform}`,
+ // @ts-ignore
+ `Device Memory: ${navigator.deviceMemory}`,
+ `Hardware Concurrency: ${navigator.hardwareConcurrency}`,
+ ]
}
const useCurrentTime = () => {
- const [time, setTime] = React.useState(new Date())
+ const [, setTime] = React.useState(new Date())
useEffect(() => {
const interval = setInterval(() => setTime(new Date()), 1000)
return () => clearInterval(interval)
}, [])
- return time
+}
+
+type DiagnosticProps = {
+ icon: string
+ label: string
+ text: string[]
+ type?: 'success' | 'danger'
}
export const SocketDiagnostics = () => {
@@ -58,7 +64,9 @@ export const SocketDiagnostics = () => {
autoping,
setAutoping,
} = useSocketManager()
- const now = useCurrentTime()
+ useCurrentTime()
+
+ const now = new Date()
const getConnectionState = (): ConnectionStatus => {
if (socketState.connected) return 'connected'
@@ -74,12 +82,108 @@ export const SocketDiagnostics = () => {
!!debugInfo.unansweredSince &&
now.getTime() - debugInfo.unansweredSince >= 3000
+ const diagnosticProps: DiagnosticProps[] = [
+ {
+ icon: 'network_ping',
+ label: 'Ping Count',
+ text: [
+ `${debugInfo.received} / ${debugInfo.sent}`,
+ lastReceivedS !== null ? `Last received ${lastReceivedS}s ago` : null,
+ ].filter(Boolean) as string[],
+ type: isLate === null ? undefined : isLate ? 'danger' : 'success',
+ },
+ {
+ icon: 'schedule',
+ label: 'Latency',
+ text: [
+ debugInfo.latency
+ ? `${debugInfo.latency} ms\nMax: ${debugInfo.maxLatency} ms`
+ : '-',
+ ],
+ type: debugInfo.latency && debugInfo.latency < 450 ? 'success' : 'danger',
+ },
+ {
+ icon: 'difference',
+ label: 'Clock Delta',
+ text: [
+ debugInfo.clockDelta === null
+ ? '-'
+ : `${Math.round(debugInfo.clockDelta / 1000)}s`,
+ ],
+ type:
+ debugInfo.clockDelta !== null && Math.abs(debugInfo.clockDelta) < 1500
+ ? 'success'
+ : 'danger',
+ },
+ {
+ icon: 'signal_cellular_alt',
+ label: 'Online',
+ text: [debugInfo.onLine?.toString() ?? '-'],
+ type: debugInfo.onLine ? 'success' : 'danger',
+ },
+ {
+ icon: 'schedule',
+ label: 'Current time',
+ text: [now.toUTCString()],
+ },
+ {
+ icon: 'hourglass',
+ label: 'Connection time',
+ text: [
+ debugInfo.client?.connectedAt
+ ? `${new Date(debugInfo.client.connectedAt).toUTCString()} (${Math.round(
+ (Date.now() - debugInfo.client.connectedAt) / 1000
+ )}s)`
+ : '-',
+ ],
+ },
+ {
+ icon: 'local_shipping',
+ label: 'Transport',
+ text: [socket?.socket.transport?.name ?? '-'],
+ },
+ {
+ icon: 'badge',
+ label: 'Client Public ID',
+ text: [debugInfo.client?.publicId ?? '-'],
+ },
+ {
+ icon: 'pin',
+ label: 'IP Address',
+ text: [debugInfo.client?.remoteIp ?? '-'],
+ },
+ {
+ icon: 'web',
+ label: 'User agent',
+ text: [debugInfo.client?.userAgent ?? '-'],
+ },
+ {
+ icon: 'directions_boat',
+ label: 'Navigator info',
+ text: navigatorInfo(),
+ },
+ ]
+
+ const diagnosticItems = diagnosticProps.map(item => (
+ (
+ {t}
+ ))}
+ type={item.type}
+ />
+ ))
+ const cutAtIndex = 7
+ const leftItems = diagnosticItems.slice(0, cutAtIndex)
+ const rightItems = diagnosticItems.slice(cutAtIndex)
+
return (
Socket Diagnostics
-
-
+
{
onClick={disconnectSocket}
disabled={!socketState.connected}
/>
+ setAutoping(e.target.checked)}
+ />
{socketState.lastError &&
}
-
- Connection Stats
-
-
-
setAutoping(e.target.checked)}
- />
-
- {debugInfo.received} / {debugInfo.sent}
- {lastReceivedS !== null && (
- <>
-
- Last received {lastReceivedS}s ago
- >
- )}
- >
- }
- type={isLate === null ? undefined : isLate ? 'danger' : 'success'}
- />
-
-
- {debugInfo.latency} ms
-
- Max: {debugInfo.maxLatency} ms
- >
- ) : (
- '-'
- )
- }
- type={
- debugInfo.latency
- ? debugInfo.latency < 450
- ? 'success'
- : 'danger'
- : undefined
- }
- />
-
-
-
-
-
- {new Date(debugInfo.client.connectedAt).toUTCString()} (
- {Math.round(
- (Date.now() - debugInfo.client.connectedAt) / 1000
- )}
- s)
- >
- ) : (
- '-'
- )
- }
- />
-
-
-
-
-
- }
- />
+
+
+ Connection Stats
+
+
+ [`${item.label}:`, ...item.text].join('\n'))
+ .join('\n\n')}
+ tooltipId="copy-debug-info"
+ kind="text"
+ />
+
+
+ {leftItems}
+ {rightItems}
+
)