import {Registry} from "tc-shared/events"; import { ConnectionComponent, ConnectionStatus, ConnectionStatusEvents } from "tc-shared/ui/frames/footer/StatusDefinitions"; import * as React from "react"; import {useContext, useEffect, useRef, useState} from "react"; import {Translatable, VariadicTranslatable} from "tc-shared/ui/react-elements/i18n"; import {network} from "tc-shared/ui/frames/chat"; import {date_format} from "tc-shared/utils/DateUtils"; const cssStyle = require("./Renderer.scss"); export const StatusEvents = React.createContext>(undefined); const ConnectionStateRenderer = React.memo((props: { state: ConnectionStatus, isGeneral: boolean, onClick?: () => void }) => { let statusClass; let statusBody; let title; switch (props.state.type) { case "disconnected": title = tr("Not connected"); statusClass = cssStyle.disconnected; statusBody = Disconnected; break; case "connecting-signalling": statusClass = cssStyle.connecting; switch (props.state.state) { case "initializing": title = tr("Initializing connection"); statusBody = Initializing; break; case "connecting": title = tr("Connecting to target"); statusBody = Connecting; break; case "authentication": title = tr("Authenticating"); statusBody = Authenticating; break; } break; case "connecting-voice": title = tr("Establishing audio connection"); statusClass = cssStyle.connecting; if(props.isGeneral) { statusBody = Audio connecting; } else { statusBody = Connecting; } break; case "connecting-video": title = tr("Establishing video connection"); statusClass = cssStyle.connecting; if(props.isGeneral) { statusBody = Video connecting; } else { statusBody = Connecting; } break; case "unhealthy": title = props.state.reason; statusClass = cssStyle.unhealthy; statusBody = Unhealthy; break; case "healthy": title = tr("Connection is healthy"); statusClass = cssStyle.healthy; statusBody = Healthy; break; default: statusClass = cssStyle.unhealthy; statusBody = Invalid state; break; } return
{statusBody}
; }) export const StatusTextRenderer = React.memo(() => { const events = useContext(StatusEvents); const [ status, setStatus ] = useState(() => { events.fire("query_connection_status"); return { type: "disconnected" }; }); events.reactUse("notify_connection_status", event => setStatus(event.status), undefined, []); return (
Connection status:
events.fire("action_toggle_component_detail")} />
); }); const ComponentStatusRenderer = React.memo((props: { component: ConnectionComponent }) => { const events = useContext(StatusEvents); const [ status, setStatus ] = useState(() => { events.fire("query_component_status", { component: props.component }); return { type: "disconnected" }; }); events.reactUse("notify_component_status", event => event.component === props.component && setStatus(event.status), undefined, []); let title; switch (props.component) { case "signaling": title = Control; break; case "video": title = Video; break; case "voice": title = Voice; break; } let body; switch (status.type) { case "healthy": body = (
Incoming:
{network.byteSizeToString(status.bytesReceived)}
Outgoing:
{network.byteSizeToString(status.bytesSend)}
); break; case "connecting-signalling": case "connecting-voice": case "connecting-video": case "disconnected": case "unsupported": let text; switch (props.component) { case "signaling": text = The control component is the main server connection. All actions are transceived by this connection.; break; case "video": text = The video component transmits and receives video data. It's used to transmit camara and screen data.; break; case "voice": text = The voice component transmits and receives audio data.; break; } body = (
{text}
); break; case "unhealthy": let errorText; if(status.retryTimestamp) { let time = Math.ceil((status.retryTimestamp - Date.now()) / 1000); let minutes = Math.floor(time / 60); let seconds = time % 60; errorText = {(minutes > 0 ? minutes + "m" : "") + seconds + "s"}; } else { errorText = Error occurred. No retry.; } body = (
{errorText}
{status.reason}
); break; } return (<>
{title}
{body} ); }) export const StatusDetailRenderer = () => { const events = useContext(StatusEvents); const refContainer = useRef(); const refShowId = useRef(0); const [ shown, setShown ] = useState(() => { events.fire("query_component_detail_state"); return false; }); events.reactUse("notify_component_detail_state", event => { if(event.shown) { refShowId.current++; } setShown(event.shown); }); useEffect(() => { if(!shown) { return; } const listener = (event: MouseEvent) => { const target = event.target as HTMLDivElement; if(!refContainer.current?.contains(target)) { events.fire("action_toggle_component_detail", { shown: false }); } }; document.addEventListener("click", listener); return () => document.removeEventListener("click", listener); }, [shown]); return (
Connection Details
) };