import {AbstractModal} from "tc-shared/ui/react-elements/modal/Definitions"; import React, {useContext, useEffect, useRef, useState} from "react"; import {Translatable, VariadicTranslatable} from "tc-shared/ui/react-elements/i18n"; import {IpcRegistryDescription, Registry} from "tc-events"; import {ModalServerBandwidthEvents} from "tc-shared/ui/modal/server-bandwidth/Definitions"; import ImageTop from "./serveredit_3.png"; import {ServerConnectionInfo, ServerConnectionInfoResult} from "tc-shared/tree/ServerDefinitions"; import {joinClassList, useTr} from "tc-shared/ui/react-elements/Helper"; import {network} from "tc-shared/ui/frames/chat"; import binarySizeToString = network.binarySizeToString; import {Graph} from "tc-shared/ui/elements/NetGraph"; import ResizeObserver from "resize-observer-polyfill"; import {Tooltip} from "tc-shared/ui/react-elements/Tooltip"; const cssStyle = require("./Renderer.scss"); /* */ const EventsContext = React.createContext>(undefined); const ConnectionInfoContext = React.createContext({ status: "loading" }); const TopContainerStatistic = React.memo((props: { children: [ React.ReactNode, (info: ServerConnectionInfo) => React.ReactNode, (info: ServerConnectionInfo) => React.ReactNode ] }) => (
{props.children[0]}
{props.children[1]}
{props.children[2]}
)); const CurrentConnectionInfoProvider = React.memo((props: { children }) => { const events = useContext(EventsContext); const [ info, setInfo ] = useState(undefined); events.reactUse("notify_connection_info", event => setInfo(event.info)); return ( {props.children} ) }); /* We're caching this so the tooltip can work properly */ const NoPermissionRenderer = React.memo((props: { failedPermission: string }) => ( ( {props.failedPermission} )} spawnHover={true} key={"no-permissions"} > No Permission )); const CurrentConnectionInfoVariable = React.memo((props: { children: (info: ServerConnectionInfo) => React.ReactNode }) => { const info = useContext(ConnectionInfoContext); switch (info.status) { case "loading": return loading; case "error": return error: {info.message}; case "no-permission": return case "success": return {props.children(info.result)}; default: break; } }); const CurrentBandwidthGraph = React.memo((props: { upload: keyof ServerConnectionInfo, download: keyof ServerConnectionInfo, children: React.ReactNode }) => { const events = useContext(EventsContext); const graph = useRef(new Graph()); const refCanvas = useRef(); const [ customInfo, setCustomInfo ] = useState<[number, number]>(undefined); useEffect(() => { const graphInstance = graph.current; graphInstance.maxGapSize(0); graphInstance.initialize(); graphInstance.initializeCanvas(refCanvas.current); graphInstance.resize(); graphInstance.callbackDetailedInfo = (upload, download) => setCustomInfo([upload, download]); graphInstance.callbackDetailedHide = () => setCustomInfo(undefined); const resizeObserver = new ResizeObserver(() => graphInstance.resize()); resizeObserver.observe(refCanvas.current); return () => { resizeObserver.disconnect(); graphInstance.callbackDetailedInfo = undefined; graphInstance.callbackDetailedHide = undefined; graph.current.finalize(); } }, []); events.reactUse("notify_connection_info", event => { const graphInstance = graph.current; if(!graphInstance) { /* should never happen */ return; } if(graphInstance.entryCount() === 0) { graphInstance.pushEntry({ timestamp: Date.now() - 400, download: undefined, upload: undefined }); } if(event.info.status === "success") { graphInstance.pushEntry({ timestamp: Date.now(), download: event.info.result[props.download], upload: event.info.result[props.upload], }); } else { graphInstance.pushEntry({ timestamp: Date.now(), download: undefined, upload: undefined }); } /* fade in the new data point within a second */ graphInstance.timeSpan.origin = Object.assign(graphInstance.calculateTimespan(), { time: Date.now() }); graphInstance.timeSpan.target = { begin: Date.now() - 120 * 1000, end: Date.now(), time: Date.now() + 200 }; graphInstance.cleanup(); }, undefined, []); let uploadValue, downloadValue; if(customInfo) { if(typeof customInfo[0] === "number") { uploadValue = binarySizeToString(customInfo[0]) + "/s"; } else { uploadValue = tr("Unknown"); } if(typeof customInfo[1] === "number") { downloadValue = binarySizeToString(customInfo[1]) + "/s"; } else { downloadValue = tr("Unknown"); } } else { uploadValue = {info => binarySizeToString(info[props.upload]) + "/s"}; downloadValue = {info => binarySizeToString(info[props.download]) + "/s"}; } return (
{props.children}
{uploadValue}
{downloadValue}
) }); class Modal extends AbstractModal { private readonly events: Registry; constructor(events: IpcRegistryDescription) { super(); this.events = Registry.fromIpcDescription(events); } protected onDestroy() { super.onDestroy(); this.events.destroy(); } renderBody(): React.ReactElement { return (
{""}
Transmitted packets {info => binarySizeToString(info.connection_packets_sent_total)} {info => binarySizeToString(info.connection_packets_received_total)} Transmitted bytes {info => binarySizeToString(info.connection_bytes_sent_total)} {info => binarySizeToString(info.connection_bytes_received_total)} Transferred file transfer bytes {info => binarySizeToString(info.connection_filetransfer_bytes_received_total)} {info => binarySizeToString(info.connection_filetransfer_bytes_sent_total)}
Current Bandwidth {/* TODO: connection_filetransfer_bandwidth_* is per minute and not per second */} Current File Transfer bandwidth
); } renderTitle(): string | React.ReactElement { return Server bandwidth usage; } } export default Modal;