import { Popover } from '@headlessui/react' import { useEffect, useState } from 'react' import { ActionIcon, ActionIconProps } from './ActionIcon' import { ConnectingType, ConnectingTypeGroup, DisconnectingType, EngineConnectionState, EngineConnectionStateType, ErrorType, initialConnectingTypeGroupState, } from '../lang/std/engineConnection' import { engineCommandManager } from '../lib/singletons' import Tooltip from './Tooltip' export enum NetworkHealthState { Ok, Issue, Disconnected, } export const NETWORK_HEALTH_TEXT: Record = { [NetworkHealthState.Ok]: 'Connected', [NetworkHealthState.Issue]: 'Problem', [NetworkHealthState.Disconnected]: 'Offline', } type IconColorConfig = { icon: string bg: string } const hasIssueToIcon: Record< string | number | symbol, ActionIconProps['icon'] > = { true: 'close', undefined: 'horizontalDash', false: 'checkmark', } const hasIssueToIconColors: Record = { true: { icon: 'text-destroy-80 dark:text-destroy-10', bg: 'bg-destroy-10 dark:bg-destroy-80', }, undefined: { icon: 'text-chalkboard-70 dark:text-chalkboard-30', bg: 'bg-chalkboard-30 dark:bg-chalkboard-70', }, false: { icon: 'text-chalkboard-110 dark:!text-chalkboard-10', bg: 'bg-transparent dark:bg-transparent', }, } const overallConnectionStateColor: Record = { [NetworkHealthState.Ok]: { icon: 'text-energy-80 dark:text-energy-10', bg: 'bg-energy-10/30 dark:bg-energy-80/50', }, [NetworkHealthState.Issue]: { icon: 'text-destroy-80 dark:text-destroy-10', bg: 'bg-destroy-10 dark:bg-destroy-80/80', }, [NetworkHealthState.Disconnected]: { icon: 'text-destroy-80 dark:text-destroy-10', bg: 'bg-destroy-10 dark:bg-destroy-80', }, } const overallConnectionStateIcon: Record< NetworkHealthState, ActionIconProps['icon'] > = { [NetworkHealthState.Ok]: 'network', [NetworkHealthState.Issue]: 'networkCrossedOut', [NetworkHealthState.Disconnected]: 'networkCrossedOut', } export function useNetworkStatus() { const [steps, setSteps] = useState(initialConnectingTypeGroupState) const [internetConnected, setInternetConnected] = useState(true) const [overallState, setOverallState] = useState( NetworkHealthState.Ok ) const [hasCopied, setHasCopied] = useState(false) const [error, setError] = useState(undefined) const issues: Record = { [ConnectingTypeGroup.WebSocket]: steps[ConnectingTypeGroup.WebSocket].some( (a: [ConnectingType, boolean | undefined]) => a[1] === false ), [ConnectingTypeGroup.ICE]: steps[ConnectingTypeGroup.ICE].some( (a: [ConnectingType, boolean | undefined]) => a[1] === false ), [ConnectingTypeGroup.WebRTC]: steps[ConnectingTypeGroup.WebRTC].some( (a: [ConnectingType, boolean | undefined]) => a[1] === false ), } const hasIssues: boolean = issues[ConnectingTypeGroup.WebSocket] || issues[ConnectingTypeGroup.ICE] || issues[ConnectingTypeGroup.WebRTC] useEffect(() => { setOverallState( !internetConnected ? NetworkHealthState.Disconnected : hasIssues ? NetworkHealthState.Issue : NetworkHealthState.Ok ) }, [hasIssues, internetConnected]) useEffect(() => { const onlineCallback = () => { setSteps(initialConnectingTypeGroupState) setInternetConnected(true) } const offlineCallback = () => { setInternetConnected(false) } window.addEventListener('online', onlineCallback) window.addEventListener('offline', offlineCallback) return () => { window.removeEventListener('online', onlineCallback) window.removeEventListener('offline', offlineCallback) } }, []) useEffect(() => { engineCommandManager.onConnectionStateChange( (engineConnectionState: EngineConnectionState) => { let hasSetAStep = false if ( engineConnectionState.type === EngineConnectionStateType.Connecting ) { const groups = Object.values(steps) for (let group of groups) { for (let step of group) { if (step[0] !== engineConnectionState.value.type) continue step[1] = true hasSetAStep = true } } } if ( engineConnectionState.type === EngineConnectionStateType.Disconnecting ) { const groups = Object.values(steps) for (let group of groups) { for (let step of group) { if ( engineConnectionState.value.type === DisconnectingType.Error ) { if ( engineConnectionState.value.value.lastConnectingValue ?.type === step[0] ) { step[1] = false hasSetAStep = true } } } if (engineConnectionState.value.type === DisconnectingType.Error) { setError(engineConnectionState.value.value) } } } if (hasSetAStep) { setSteps(steps) } } ) }, []) return { hasIssues, overallState, internetConnected, steps, issues, error, setHasCopied, hasCopied, } } export const NetworkHealthIndicator = () => { const { hasIssues, overallState, internetConnected, steps, issues, error, setHasCopied, hasCopied, } = useNetworkStatus() return ( Network Health Network Health ({NETWORK_HEALTH_TEXT[overallState]})

Network health

{NETWORK_HEALTH_TEXT[overallState]}

    {Object.keys(steps).map((name) => (
  • {name}

    {internetConnected ? ( ) : ( )}
    {issues[name as ConnectingTypeGroup] && ( )}
  • ))}
) }