import {AbstractModal} from "tc-shared/ui/react-elements/modal/Definitions"; import React, {useContext, useEffect, useRef, useState} from "react"; import {IpcRegistryDescription, Registry} from "tc-events"; import {ModalServerInfoEvents, ModalServerInfoVariables} from "tc-shared/ui/modal/server-info/Definitions"; import {UiVariableConsumer} from "tc-shared/ui/utils/Variable"; import {createIpcUiVariableConsumer, IpcVariableDescriptor} from "tc-shared/ui/utils/IpcVariable"; import {HostBannerRenderer} from "tc-shared/ui/frames/HostBannerRenderer"; import ImageServerEdit1 from "./serveredit_1.png"; import ImageServerEdit2 from "./serveredit_2.png"; import ImageServerEdit3 from "./serveredit_3.png"; import {Translatable} from "tc-shared/ui/react-elements/i18n"; import {joinClassList} from "tc-shared/ui/react-elements/Helper"; import {CountryCode} from "tc-shared/ui/react-elements/CountryCode"; import moment from "moment"; import {tr} from "tc-shared/i18n/localize"; import {format_online_time} from "tc-shared/utils/TimeUtils"; import {IconTooltip} from "tc-shared/ui/react-elements/Tooltip"; import {Button} from "tc-shared/ui/react-elements/Button"; import {ServerConnectionInfo} from "tc-shared/tree/ServerDefinitions"; const cssStyle = require("./Renderer.scss"); const EventContext = React.createContext>(undefined); const VariablesContext = React.createContext>(undefined); const Group = React.memo((props: { children: React.ReactElement[], reverse: boolean }) => (
{props.children[0]}
{...props.children.slice(1)}
)); const HostBanner = React.memo(() => { const variables = useContext(VariablesContext); const hostBanner = variables.useReadOnly("hostBanner", undefined, { status: "none" }); if(hostBanner.status === "none") { return null; } else { return (
); } }); const TitleRenderer = React.memo(() => { return <>Server Info; }); const VariablePropertyName: {[T in keyof ModalServerInfoVariables]?: () => React.ReactElement } = { name: () => Server name, region: () => Server region, slots: () => Slots, firstRun: () => First run, uptime: () => Uptime, ipAddress: () => Ip Address, version: () => Version, platform: () => Platform, uniqueId: () => Global unique id, channelCount: () => Current channels, voiceDataEncryption: () => Voice data encryption, securityLevel: () => Minimal security level, complainsUntilBan: () => Complains until ban }; const VariableProperty = (props: { property: T, children?: (value: ModalServerInfoVariables[T]) => React.ReactNode }) => { const variables = useContext(VariablesContext); const value = variables.useReadOnly(props.property, undefined, undefined); return (
{(VariablePropertyName[props.property] || (() => props.property))()}
{(props.children || ((value) => value))(value)}
) }; const ConnectionProperty = React.memo((props: { children: [ React.ReactNode, (value: ServerConnectionInfo ) => React.ReactNode ] }) => { const variables = useContext(VariablesContext); const value = variables.useReadOnly("connectionInfo", undefined, { status: "loading" }); let body; switch (value.status) { case "loading": body = loading; break; case "error": body = error: {value.message}; break; case "no-permission": body = No Permission; break; case "success": body = {props.children[1](value.result)}; break; default: break; } return (
{props.children[0]}
{body}
) }); const ServerFirstRun = React.memo(() => ( {value => ( value > 0 ? moment(value * 1000).format('MMMM Do YYYY, h:mm:ss a') : tr("Unknown") )} )); const ServerSlots = React.memo(() => ( {value => { if(!value) { return "--"; } let text = value.used + "/" + value.max; if(value.reserved > 0) { text += " (" + value.reserved + " " + tr("Reserved") + ")"; } if(value.queries > 1) { text += " +" + value.queries + " " + tr("Queries"); } else if(value.queries === 1) { text += " " + tr("+1 Query"); } return text; }} )); const OnlineTimestampRenderer = React.memo((props: { timestamp: number }) => { const initialRenderTimestamp = useRef(Date.now()); const [ , setRenderedTimestamp ] = useState(0); const difference = props.timestamp + Math.ceil((Date.now() - initialRenderTimestamp.current) / 1000); useEffect(() => { const interval = setInterval(() => { setRenderedTimestamp(Date.now()); }, 900); return () => clearInterval(interval); }, []); return <>{format_online_time(difference)}; }); const kVersionsRegex = /(.*)\[Build: ([0-9]+)]/; const VersionsTimestamp = (props: { timestamp: string }) => { if(!props.timestamp) { return null; } const match = props.timestamp.match(kVersionsRegex); if(!match || !match[2]) { return {props.timestamp}; } return (
{"Build timestamp: " + moment(parseInt(match[2]) * 1000).format("YYYY-MM-DD HH:mm Z")} {match[1].trim()}
); }; const ButtonRefresh = React.memo(() => { const variables = useContext(VariablesContext); const events = useContext(EventContext); const nextRefresh = variables.useReadOnly("refreshAllowed"); const [ renderTimestamp, setRenderTimestamp ] = useState(Date.now()); const allowed = nextRefresh.status === "loaded" && renderTimestamp >= nextRefresh.value; useEffect(() => { if(nextRefresh.status !== "loaded" || allowed) { return; } const time = nextRefresh.value - Date.now(); const timeout = setTimeout(() => setRenderTimestamp(Date.now()), time); return () => clearTimeout(timeout); }, [ nextRefresh.value ]); return ( ); }); const Buttons = React.memo(() => { const events = useContext(EventContext); return (
) }) class Modal extends AbstractModal { private readonly events: Registry; private readonly variables: UiVariableConsumer; constructor(events: IpcRegistryDescription, variables: IpcVariableDescriptor) { super(); this.events = Registry.fromIpcDescription(events); this.variables = createIpcUiVariableConsumer(variables); } protected onDestroy() { super.onDestroy(); this.events.destroy(); this.variables.destroy(); } renderBody(): React.ReactElement { return (
{""} {value => } {value => } {""} {value => }
Average ping {value => value.connection_ping.toFixed(2) + " ms"} Average packet loss {value => value.connection_packetloss_total.toFixed(2) + " %"}
{""} {value => { switch(value) { case "global-off": return Globally off; case "global-on": return Globally on; case "channel-individual": return Individually configured per channel; case "unknown": default: return Unknown; } }}
); } renderTitle(): string | React.ReactElement { return ; } } export default Modal;