import * as React from "react"; import {useContext, useEffect, useState} from "react"; import { ClientCountryInfo, ClientForumInfo, ClientGroupInfo, ClientInfoEvents, ClientInfoOnline, ClientStatusInfo, ClientVersionInfo, ClientVolumeInfo, OptionalClientInfoInfo } from "tc-shared/ui/frames/side/ClientInfoDefinitions"; import {Registry} from "tc-shared/events"; import {ClientAvatar, getGlobalAvatarManagerFactory} from "tc-shared/file/Avatars"; import {AvatarRenderer} from "tc-shared/ui/react-elements/Avatar"; import {Translatable} from "tc-shared/ui/react-elements/i18n"; import {LoadingDots} from "tc-shared/ui/react-elements/LoadingDots"; import {ClientTag} from "tc-shared/ui/tree/EntryTags"; import {guid} from "tc-shared/crypto/uid"; import {useDependentState} from "tc-shared/ui/react-elements/Helper"; import {format_online_time} from "tc-shared/utils/TimeUtils"; import {ClientIcon} from "svg-sprites/client-icons"; import {ClientIconRenderer} from "tc-shared/ui/react-elements/Icons"; import {getIconManager} from "tc-shared/file/Icons"; import {RemoteIconRenderer} from "tc-shared/ui/react-elements/Icon"; const cssStyle = require("./ClientInfoRenderer.scss"); const EventsContext = React.createContext>(undefined); const ClientContext = React.createContext(undefined); const Avatar = React.memo(() => { const events = useContext(EventsContext); const client = useContext(ClientContext); let avatar: "loading" | ClientAvatar; if(client.type === "none") { avatar = "loading"; } else { avatar = getGlobalAvatarManagerFactory().getManager(client.handlerId).resolveClientAvatar({ id: client.clientId, clientUniqueId: client.clientUniqueId, database_id: client.clientDatabaseId }); } return (
events.fire("action_edit_avatar")}> {tr("Upload
) }); const ClientName = React.memo(() => { const events = useContext(EventsContext); const client = useContext(ClientContext); const [ name, setName ] = useDependentState(() => { if(client.type !== "none") { events.fire("query_client_name"); } return null; }, [ client.contextHash ]); events.reactUse("notify_client_name", event => setName(event.name), undefined, []); return (
{name === null || client.type === "none" ?
loading
: }
); }); const ClientDescription = React.memo(() => { const events = useContext(EventsContext); const client = useContext(ClientContext); const [ description, setDescription ] = useDependentState(() => { if(client.type !== "none") { events.fire("query_client_description"); } return null; }, [ client.contextHash ]); events.reactUse("notify_client_description", event => { setDescription(event.description ? event.description : null); }, undefined, []); return (
{description === undefined || description === null ? null :
{description}
}
); }); const InfoBlock = (props: { imageUrl?: string, clientIcon?: ClientIcon, children: [React.ReactElement, React.ReactElement], valueClass?: string }) => { return (
{props.imageUrl ? {""} : }
{props.children[0]}
{props.children[1]}
) }; const ClientOnlineSince = React.memo(() => { const events = useContext(EventsContext); const client = useContext(ClientContext); const [ onlineInfo, setOnlineInfo ] = useDependentState(() => { if(client.type !== "none") { events.fire("query_online"); } return undefined; }, [ client.contextHash ]); const [ revision, setRevision ] = useState(0); events.reactUse("notify_online", event => setOnlineInfo(event.status), undefined, []); let onlineBody; if(client.type === "none" || !onlineInfo) { onlineBody = loading ; } else if(onlineInfo.joinTimestamp === 0) { onlineBody = Join timestamp not logged; } else if(onlineInfo.leaveTimestamp === 0) { const onlineTime = Date.now() / 1000 - onlineInfo.joinTimestamp; onlineBody = {format_online_time(onlineTime)}; } else { const onlineTime = onlineInfo.leaveTimestamp - onlineInfo.joinTimestamp; onlineBody = {format_online_time(onlineTime)} (left view); } useEffect(() => { if(!onlineInfo || onlineInfo.leaveTimestamp !== 0 || onlineInfo.joinTimestamp === 0) { return; } const timeout = setTimeout(() => setRevision(revision + 1), 900); return () => clearTimeout(timeout); }); return ( Online since {onlineBody} ); }); const ClientCountry = React.memo(() => { const events = useContext(EventsContext); const client = useContext(ClientContext); const [ country, setCountry ] = useDependentState(() => { if(client.type !== "none") { events.fire("query_country"); } return undefined; }, [ client.contextHash ]); events.reactUse("notify_country", event => setCountry(event.country), undefined, []); return ( Country <>
{country?.name || tr("Unknown")} ); }); const ClientVolume = React.memo(() => { const events = useContext(EventsContext); const client = useContext(ClientContext); const [ volume, setVolume ] = useDependentState(() => { if(client.type !== "none") { events.fire("query_volume"); } return undefined; }, [ client.contextHash ]); events.reactUse("notify_volume", event => setVolume(event.volume), undefined, []); if(client.type === "self" || client.type === "none") { return null; } let body; if(volume) { let text = (volume.volume * 100).toFixed(0) + "%"; if(volume.muted) { text += " (" + tr("Muted") + ")"; } body = {text}; } else { body = loading ; } return ( Volume {body} ); }); const ClientForumAccount = React.memo(() => { const events = useContext(EventsContext); const client = useContext(ClientContext); const [ forum, setForum ] = useDependentState(() => { if(client.type !== "none") { events.fire("query_forum"); } return undefined; }, [ client.contextHash ]); events.reactUse("notify_forum", event => setForum(event.forum), undefined, []); if(!forum) { return null; } let text = forum.nickname; if((forum.flags & 0x01) > 0) { text += " (" + tr("Banned") + ")"; } if((forum.flags & 0x02) > 0) { text += " (" + tr("Stuff") + ")"; } if((forum.flags & 0x04) > 0) { text += " (" + tr("Premium") + ")"; } return ( TeaSpeak Forum account {text} ); }); const ClientVersion = React.memo(() => { const events = useContext(EventsContext); const client = useContext(ClientContext); const [ version, setVersion ] = useDependentState(() => { if(client.type !== "none") { events.fire("query_version"); } return undefined; }, [ client.contextHash ]); events.reactUse("notify_version", event => setVersion(event.version), undefined, []); let body; if(version) { let platform = version.platform; if(platform.indexOf("Win32") != 0 && (version.version.indexOf("Win64") != -1 || version.version.indexOf("WOW64") != -1)) { platform = platform.replace("Win32", "Win64"); } body = {version.version.split(" ")[0]} on {platform}; } else { body = loading ; } return ( Version {body} ); }); const ClientStatusEntry = (props: { icon: ClientIcon, children: React.ReactElement }) => (
{props.children}
); const ClientStatus = React.memo(() => { const events = useContext(EventsContext); const client = useContext(ClientContext); const [ status, setStatus ] = useDependentState(() => { if(client.type !== "none") { events.fire("query_status"); } return undefined; }, [ client.contextHash ]); events.reactUse("notify_status", event => setStatus(event.status), undefined, []); let elements = []; if(status) { if(status.away) { let message = typeof status.away === "string" ? " (" + status.away + ")" : undefined; elements.push(<>Away {message}); } if(status.speakerDisabled) { elements.push(Speakers/Headphones disabled); } if(status.microphoneDisabled) { elements.push(Microphone disabled); } if(status.speakerMuted) { elements.push(Speakers/Headphones Muted); } if(status.microphoneMuted) { elements.push(Microphone Muted); } } if(elements.length === 0) { return null; } return ( Status <>{elements} ); }); const FullInfoButton = () => { const events = useContext(EventsContext); const client = useContext(ClientContext); const [ onlineInfo, setOnlineInfo ] = useDependentState(() => { if(client.type !== "none") { events.fire("query_online"); } return undefined; }, [ client.contextHash ]); events.reactUse("notify_online", event => setOnlineInfo(event.status), undefined, []); if(!onlineInfo || onlineInfo.leaveTimestamp !== 0) { return null; } return (
events.fire("action_show_full_info")} key={"button"}> Full Info
); } const GroupRenderer = (props: { group: ClientGroupInfo }) => { const icon = getIconManager().resolveIcon(props.group.groupIcon.iconId, props.group.groupIcon.serverUniqueId, props.group.groupIcon.handlerId); return (
{props.group.groupName}
) }; const ChannelGroupRenderer = () => { const events = useContext(EventsContext); const client = useContext(ClientContext); const [ channelGroup, setChannelGroup ] = useDependentState(() => { if(client.type !== "none") { events.fire("query_channel_group"); } return undefined; }, [ client.contextHash ]); events.reactUse("notify_channel_group", event => setChannelGroup(event.group), undefined, []); let body; if(channelGroup) { body = ; } else { body = loading ; } return ( Channel group <>{body} ); }; const ServerGroupRenderer = () => { const events = useContext(EventsContext); const client = useContext(ClientContext); const [ serverGroups, setServerGroups ] = useDependentState(() => { if(client.type !== "none") { events.fire("query_server_groups"); } return undefined; }, [ client.contextHash ]); events.reactUse("notify_server_groups", event => setServerGroups(event.groups), undefined, []); let body; if(serverGroups) { body = serverGroups.map(group => ); } else { body = loading ; } return ( Server groups <>{body} ); }; const ConnectedClientInfoBlock = () => { const client = useContext(ClientContext); if(client.type === "query" || client.type === "none") { return null; } return ( ); } const ClientInfoProvider = () => { const events = useContext(EventsContext); const [ client, setClient ] = useState(() => { events.fire("query_client"); return { type: "none", contextHash: guid() }; }); events.reactUse("notify_client", event => { if(event.info) { setClient({ contextHash: guid(), type: event.info.type, handlerId: event.info.handlerId, clientUniqueId: event.info.clientUniqueId, clientId: event.info.clientId, clientDatabaseId: event.info.clientDatabaseId }); } else if(client.type !== "none") { setClient({ type: "none", contextHash: guid() }); } }); return (
); } export const ClientInfoRenderer = (props: { events: Registry }) => ( );