diff --git a/shared/js/ConnectionManager.ts b/shared/js/ConnectionManager.ts index 62a4afd4..9a41ebb9 100644 --- a/shared/js/ConnectionManager.ts +++ b/shared/js/ConnectionManager.ts @@ -32,8 +32,8 @@ export class ConnectionManager { private _container_log_server: JQuery; private _container_channel_tree: JQuery; private _container_hostbanner: JQuery; - private _container_chat: JQuery; private containerChannelVideo: ReplaceableContainer; + private containerSideBar: HTMLDivElement; private containerFooter: HTMLDivElement; constructor() { @@ -41,10 +41,10 @@ export class ConnectionManager { this.event_registry.enableDebug("connection-manager"); this.containerChannelVideo = new ReplaceableContainer(document.getElementById("channel-video") as HTMLDivElement); + this.containerSideBar = document.getElementById("chat") as HTMLDivElement; this._container_log_server = $("#server-log"); this._container_channel_tree = $("#channelTree"); this._container_hostbanner = $("#hostbanner"); - this._container_chat = $("#chat"); this.containerFooter = document.getElementById("container-footer") as HTMLDivElement; this.set_active_connection(undefined); @@ -112,7 +112,6 @@ export class ConnectionManager { private set_active_connection_(handler: ConnectionHandler) { this._container_channel_tree.children().detach(); - this._container_chat.children().detach(); this._container_log_server.children().detach(); this._container_hostbanner.children().detach(); this.containerChannelVideo.replaceWith(handler?.video_frame.getContainer()); @@ -120,8 +119,8 @@ export class ConnectionManager { if(handler) { this._container_hostbanner.append(handler.hostbanner.html_tag); this._container_channel_tree.append(handler.channelTree.tag_tree()); - this._container_chat.append(handler.side_bar.html_tag()); this._container_log_server.append(handler.log.getHTMLTag()); + handler.side_bar.renderInto(this.containerSideBar); } const old_handler = this.active_handler; this.active_handler = handler; diff --git a/shared/js/settings.ts b/shared/js/settings.ts index 238a94db..ce660b26 100644 --- a/shared/js/settings.ts +++ b/shared/js/settings.ts @@ -369,6 +369,13 @@ export class Settings extends StaticSettings { valueType: "boolean", }; + static readonly KEY_CHAT_HIGHLIGHT_CODE: ValuedSettingsKey = { + key: 'chat_highlight_code', + defaultValue: true, + description: 'Enables code highlighting within the chat (Client restart required)', + valueType: "boolean", + }; + static readonly KEY_CHAT_TAG_URLS: ValuedSettingsKey = { key: 'chat_tag_urls', defaultValue: true, diff --git a/shared/js/text/bbcode/highlight.tsx b/shared/js/text/bbcode/highlight.tsx index 60469127..c0df10ed 100644 --- a/shared/js/text/bbcode/highlight.tsx +++ b/shared/js/text/bbcode/highlight.tsx @@ -11,6 +11,7 @@ import {rendererReact, rendererText} from "tc-shared/text/bbcode/renderer"; import {MenuEntryType, spawn_context_menu} from "tc-shared/ui/elements/ContextMenu"; import '!style-loader!css-loader!highlight.js/styles/darcula.css'; +import {Settings, settings} from "tc-shared/settings"; const registerLanguage = (name, language: Promise) => { language.then(lan => hljs.registerLanguage(name, lan)).catch(error => { @@ -86,6 +87,9 @@ loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { function: async () => { let reactId = 0; + if(!settings.static_global(Settings.KEY_CHAT_HIGHLIGHT_CODE)) { + return; + } /* override default parser */ rendererReact.registerCustomRenderer(new class extends ElementRenderer { tags(): string | string[] { diff --git a/shared/js/tree/Client.ts b/shared/js/tree/Client.ts index 349e03a9..9e2b6693 100644 --- a/shared/js/tree/Client.ts +++ b/shared/js/tree/Client.ts @@ -524,7 +524,7 @@ export class ClientEntry extends ChannelTreeEntry { conversation.setActiveClientEntry(this); privateConversations.setSelectedConversation(conversation); sideBar.showPrivateConversations(); - sideBar.private_conversations().focusInput(); + sideBar.privateConversationsController().focusInput(); } showContextMenu(x: number, y: number, on_close: () => void = undefined) { diff --git a/shared/js/ui/frames/SideBar.scss b/shared/js/ui/frames/SideBar.scss new file mode 100644 index 00000000..3ddd2a49 --- /dev/null +++ b/shared/js/ui/frames/SideBar.scss @@ -0,0 +1,37 @@ +.rendererContainer { + flex-grow: 1; + flex-shrink: 1; + display: flex; + flex-direction: column; + justify-content: stretch; + + min-height: 200px; + min-width: 200px; +} + +.container { + flex-grow: 1; + flex-shrink: 1; + + display: flex; + flex-direction: column; + justify-content: stretch; + + min-height: 200px; +} + +.frameContainer { + width: 100%; + + flex-grow: 1; + flex-shrink: 1; + + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + + min-width: 350px; + min-height: 16em; + + display: flex; + flex-direction: column; +} \ No newline at end of file diff --git a/shared/js/ui/frames/SideBarDefinitions.ts b/shared/js/ui/frames/SideBarDefinitions.ts new file mode 100644 index 00000000..737c42ec --- /dev/null +++ b/shared/js/ui/frames/SideBarDefinitions.ts @@ -0,0 +1,38 @@ +import {Registry} from "tc-shared/events"; +import {PrivateConversationUIEvents} from "tc-shared/ui/frames/side/PrivateConversationDefinitions"; +import {ConversationUIEvents} from "tc-shared/ui/frames/side/ConversationDefinitions"; +import {ClientInfoEvents} from "tc-shared/ui/frames/side/ClientInfoDefinitions"; + +/* TODO: Somehow outsource the event registries to IPC? */ + +export type SideBarType = "none" | "channel-chat" | "private-chat" | "client-info" | "music-manage"; +export interface SideBarTypeData { + "none": {}, + "channel-chat": { + events: Registry, + handlerId: string + }, + "private-chat": { + events: Registry, + handlerId: string + }, + "client-info": { + events: Registry, + }, + "music-manage": { + + } +} + +export type SideBarNotifyContentData = { + content: T, + data: SideBarTypeData[T] +} + +export interface SideBarEvents { + query_content: {}, + query_content_data: { content: SideBarType }, + + notify_content: { content: SideBarType }, + notify_content_data: SideBarNotifyContentData +} \ No newline at end of file diff --git a/shared/js/ui/frames/SideBarRenderer.tsx b/shared/js/ui/frames/SideBarRenderer.tsx new file mode 100644 index 00000000..0b379cde --- /dev/null +++ b/shared/js/ui/frames/SideBarRenderer.tsx @@ -0,0 +1,132 @@ +import {SideHeaderEvents, SideHeaderState} from "tc-shared/ui/frames/side/HeaderDefinitions"; +import {Registry} from "tc-shared/events"; +import React = require("react"); +import {SideHeaderRenderer} from "tc-shared/ui/frames/side/HeaderRenderer"; +import {ConversationPanel} from "tc-shared/ui/frames/side/AbstractConversationRenderer"; +import {SideBarEvents, SideBarType, SideBarTypeData} from "tc-shared/ui/frames/SideBarDefinitions"; +import {useContext, useState} from "react"; +import {ClientInfoRenderer} from "tc-shared/ui/frames/side/ClientInfoRenderer"; +import {PrivateConversationsPanel} from "tc-shared/ui/frames/side/PrivateConversationRenderer"; + +const cssStyle = require("./SideBar.scss"); + +const EventContent = React.createContext>(undefined); + +function useContentData(type: T) : SideBarTypeData[T] { + const events = useContext(EventContent); + const [ contentData, setContentData ] = useState(() => { + events.fire("query_content_data", { content: type }); + return undefined; + }); + events.reactUse("notify_content_data", event => event.content === type && setContentData(event.data)); + + return contentData; +} + +const ContentRendererChannelConversation = () => { + const contentData = useContentData("channel-chat"); + if(!contentData) { return null; } + + return ( + + ); +}; + +const ContentRendererPrivateConversation = () => { + const contentData = useContentData("private-chat"); + if(!contentData) { return null; } + + return ( + + ); +}; + +const ContentRendererClientInfo = () => { + const contentData = useContentData("client-info"); + if(!contentData) { return null; } + + return ( + + ); +}; + +const SideBarFrame = (props: { type: SideBarType }) => { + switch (props.type) { + case "channel-chat": + return ; + + case "private-chat": + return ; + + case "client-info": + return ; + + case "music-manage": + /* TODO! */ + + case "none": + default: + return null; + } +} + +const SideBarHeader = (props: { type: SideBarType, eventsHeader: Registry }) => { + let headerState: SideHeaderState; + switch (props.type) { + case "none": + headerState = { state: "none" }; + break; + + case "channel-chat": + headerState = { state: "conversation", mode: "channel" }; + break; + + case "private-chat": + headerState = { state: "conversation", mode: "private" }; + break; + + case "client-info": + headerState = { state: "client" }; + break; + + case "music-manage": + headerState = { state: "music-bot" }; + break; + } + + return ; +} + +export const SideBarRenderer = (props: { + handlerId: string, + events: Registry, + eventsHeader: Registry +}) => { + const [ content, setContent ] = useState(() => { + props.events.fire("query_content"); + return "none"; + }); + props.events.reactUse("notify_content", event => setContent(event.content)); + + return ( + +
+ +
+ +
+
+
+ ) +}; \ No newline at end of file diff --git a/shared/js/ui/frames/chat_frame.ts b/shared/js/ui/frames/chat_frame.ts index 055fc736..ebbadd24 100644 --- a/shared/js/ui/frames/chat_frame.ts +++ b/shared/js/ui/frames/chat_frame.ts @@ -1,35 +1,40 @@ -import {ClientEntry, LocalClientEntry, MusicClientEntry} from "../../tree/Client"; +import {ClientEntry, MusicClientEntry} from "../../tree/Client"; import {ConnectionHandler} from "../../ConnectionHandler"; import {MusicInfo} from "../../ui/frames/side/music_info"; import {ChannelConversationController} from "./side/ChannelConversationController"; import {PrivateConversationController} from "./side/PrivateConversationController"; import {ClientInfoController} from "tc-shared/ui/frames/side/ClientInfoController"; import {SideHeader} from "tc-shared/ui/frames/side/HeaderController"; +import * as ReactDOM from "react-dom"; +import {SideBarRenderer} from "tc-shared/ui/frames/SideBarRenderer"; +import * as React from "react"; +import {SideBarEvents, SideBarType} from "tc-shared/ui/frames/SideBarDefinitions"; +import {Registry} from "tc-shared/events"; -export enum FrameContent { - NONE, - PRIVATE_CHAT, - CHANNEL_CHAT, - CLIENT_INFO, - MUSIC_BOT -} +const cssStyle = require("./SideBar.scss"); export class Frame { readonly handle: ConnectionHandler; - private htmlTag: JQuery; - private containerChannelChat: JQuery; - private _content_type: FrameContent; + private htmlTag: HTMLDivElement; + private currentType: SideBarType; + + private uiEvents: Registry; private header: SideHeader; - private clientInfo: ClientInfoController; + private musicInfo: MusicInfo; + private clientInfo: ClientInfoController; private channelConversations: ChannelConversationController; private privateConversations: PrivateConversationController; constructor(handle: ConnectionHandler) { this.handle = handle; - this._content_type = FrameContent.NONE; + this.currentType = "none"; + this.uiEvents = new Registry(); + this.uiEvents.on("query_content", () => this.uiEvents.fire_react("notify_content", { content: this.currentType })); + this.uiEvents.on("query_content_data", event => this.sendContentData(event.content)); + this.privateConversations = new PrivateConversationController(handle); this.channelConversations = new ChannelConversationController(handle); this.clientInfo = new ClientInfoController(handle); @@ -42,9 +47,7 @@ export class Frame { this.showChannelConversations(); } - html_tag() : JQuery { return this.htmlTag; } - - content_type() : FrameContent { return this._content_type; } + html_tag() : HTMLDivElement { return this.htmlTag; } destroy() { this.header?.destroy(); @@ -56,6 +59,12 @@ export class Frame { this.clientInfo?.destroy(); this.clientInfo = undefined; + this.privateConversations?.destroy(); + this.privateConversations = undefined; + + this.channelConversations?.destroy(); + this.channelConversations = undefined; + this.musicInfo && this.musicInfo.destroy(); this.musicInfo = undefined; @@ -64,19 +73,24 @@ export class Frame { this.channelConversations && this.channelConversations.destroy(); this.channelConversations = undefined; + } - this.containerChannelChat && this.containerChannelChat.remove(); - this.containerChannelChat = undefined; + renderInto(container: HTMLDivElement) { + ReactDOM.render(React.createElement(SideBarRenderer, { + key: this.handle.handlerId, + handlerId: this.handle.handlerId, + events: this.uiEvents, + eventsHeader: this.header["uiEvents"], + }), container); } private createHtmlTag() { - this.htmlTag = $("#tmpl_frame_chat").renderTag(); - this.htmlTag.find(".container-info").replaceWith(this.header.getHtmlTag()); - this.containerChannelChat = this.htmlTag.find(".container-chat"); + this.htmlTag = document.createElement("div"); + this.htmlTag.classList.add(cssStyle.container); } - private_conversations() : PrivateConversationController { + privateConversationsController() : PrivateConversationController { return this.privateConversations; } @@ -88,73 +102,81 @@ export class Frame { return this.musicInfo; } - private clearSideBar() { - this._content_type = FrameContent.NONE; - this.containerChannelChat.children().detach(); + private setCurrentContent(type: SideBarType) { + if(this.currentType === type) { + return; + } + + this.currentType = type; + this.uiEvents.fire_react("notify_content", { content: this.currentType }); + } + + private sendContentData(content: SideBarType) { + switch (content) { + case "none": + this.uiEvents.fire_react("notify_content_data", { + content: "none", + data: {} + }); + break; + + case "channel-chat": + this.uiEvents.fire_react("notify_content_data", { + content: "channel-chat", + data: { + events: this.channelConversations["uiEvents"], + handlerId: this.handle.handlerId + } + }); + break; + + case "private-chat": + this.uiEvents.fire_react("notify_content_data", { + content: "private-chat", + data: { + events: this.privateConversations["uiEvents"], + handlerId: this.handle.handlerId + } + }); + break; + + case "client-info": + this.uiEvents.fire_react("notify_content_data", { + content: "client-info", + data: { + events: this.clientInfo["uiEvents"], + } + }); + break; + + case "music-manage": + this.uiEvents.fire_react("notify_content_data", { + content: "music-manage", + data: { } + }); + break; + } } showPrivateConversations() { - if(this._content_type === FrameContent.PRIVATE_CHAT) - return; - - this.header.setState({ state: "conversation", mode: "private" }); - - this.clearSideBar(); - this._content_type = FrameContent.PRIVATE_CHAT; - this.containerChannelChat.append(this.privateConversations.htmlTag); - this.privateConversations.handlePanelShow(); + this.setCurrentContent("private-chat"); } showChannelConversations() { - if(this._content_type === FrameContent.CHANNEL_CHAT) - return; - - this.header.setState({ state: "conversation", mode: "channel" }); - - this.clearSideBar(); - this._content_type = FrameContent.CHANNEL_CHAT; - this.containerChannelChat.append(this.channelConversations.htmlTag); - this.channelConversations.handlePanelShow(); + this.setCurrentContent("channel-chat"); } showClientInfo(client: ClientEntry) { this.clientInfo.setClient(client); - this.header.setState({ state: "client", ownClient: client instanceof LocalClientEntry }); - - if(this._content_type === FrameContent.CLIENT_INFO) - return; - - this.clearSideBar(); - this._content_type = FrameContent.CLIENT_INFO; - this.containerChannelChat.append(this.clientInfo.getHtmlTag()); + this.setCurrentContent("client-info"); } showMusicPlayer(client: MusicClientEntry) { this.musicInfo.set_current_bot(client); - - if(this._content_type === FrameContent.MUSIC_BOT) - return; - - this.header.setState({ state: "music-bot" }); - this.musicInfo.previous_frame_content = this._content_type; - this.clearSideBar(); - this._content_type = FrameContent.MUSIC_BOT; - this.containerChannelChat.append(this.musicInfo.html_tag()); + this.setCurrentContent("music-manage"); } - set_content(type: FrameContent) { - if(this._content_type === type) { - return; - } - - if(type === FrameContent.CHANNEL_CHAT) { - this.showChannelConversations(); - } else if(type === FrameContent.PRIVATE_CHAT) { - this.showPrivateConversations(); - } else { - this.header.setState({ state: "none" }); - this.clearSideBar(); - this._content_type = FrameContent.NONE; - } + clearSideBar() { + this.setCurrentContent("none"); } } \ No newline at end of file diff --git a/shared/js/ui/frames/side/AbstractConversationRenderer.tsx b/shared/js/ui/frames/side/AbstractConversationRenderer.tsx index e4bd599e..3b517410 100644 --- a/shared/js/ui/frames/side/AbstractConversationRenderer.tsx +++ b/shared/js/ui/frames/side/AbstractConversationRenderer.tsx @@ -895,10 +895,12 @@ export const ConversationPanel = React.memo((props: { events: Registry { chatEnabled.current = event.state === "normal" && event.sendEnabled; updateChatBox(); }); + props.events.reactUse("notify_send_enabled", event => { if(event.chatId !== currentChat.current.id) return; diff --git a/shared/js/ui/frames/side/ClientInfoController.ts b/shared/js/ui/frames/side/ClientInfoController.ts index 8251ce79..3ab6ae46 100644 --- a/shared/js/ui/frames/side/ClientInfoController.ts +++ b/shared/js/ui/frames/side/ClientInfoController.ts @@ -3,20 +3,22 @@ import {ClientEntry, ClientType, LocalClientEntry} from "tc-shared/tree/Client"; import { ClientForumInfo, ClientGroupInfo, - ClientInfoEvents, + ClientInfoEvents, ClientInfoType, ClientStatusInfo, ClientVersionInfo } from "tc-shared/ui/frames/side/ClientInfoDefinitions"; -import * as ReactDOM from "react-dom"; -import {ClientInfoRenderer} from "tc-shared/ui/frames/side/ClientInfoRenderer"; import {Registry} from "tc-shared/events"; -import * as React from "react"; import * as i18nc from "../../../i18n/country"; import {openClientInfo} from "tc-shared/ui/modal/ModalClientInfo"; type CurrentClientInfo = { + type: ClientInfoType; name: string, + uniqueId: string, + databaseId: number, + clientId: number, + description: string, joinTimestamp: number, leaveTimestamp: number, @@ -29,12 +31,19 @@ type CurrentClientInfo = { version: ClientVersionInfo } +export interface ClientInfoControllerEvents { + notify_client_changed: { + newClient: ClientEntry | undefined + } +} + export class ClientInfoController { + readonly events: Registry; + private readonly connection: ConnectionHandler; private readonly listenerConnection: (() => void)[]; private readonly uiEvents: Registry; - private readonly htmlContainer: HTMLDivElement; private listenerClient: (() => void)[]; private currentClient: ClientEntry | undefined; @@ -42,6 +51,7 @@ export class ClientInfoController { constructor(connection: ConnectionHandler) { this.connection = connection; + this.events = new Registry(); this.uiEvents = new Registry(); this.uiEvents.enableDebug("client-info"); @@ -49,17 +59,6 @@ export class ClientInfoController { this.listenerClient = []; this.initialize(); - - this.htmlContainer = document.createElement("div"); - this.htmlContainer.style.display = "flex"; - this.htmlContainer.style.flexDirection = "column"; - this.htmlContainer.style.justifyContent = "strech"; - this.htmlContainer.style.height = "100%"; - ReactDOM.render(React.createElement(ClientInfoRenderer, { events: this.uiEvents }), this.htmlContainer); - } - - getHtmlTag() : HTMLDivElement { - return this.htmlContainer; } private initialize() { @@ -89,6 +88,7 @@ export class ClientInfoController { } this.currentClientStatus.leaveTimestamp = Date.now() / 1000; + this.currentClientStatus.clientId = 0; this.currentClient = undefined; this.unregisterClientEvents(); this.sendOnline(); @@ -102,6 +102,7 @@ export class ClientInfoController { } })) + this.uiEvents.on("query_client", () => this.sendClient()); this.uiEvents.on("query_client_name", () => this.sendClientName()); this.uiEvents.on("query_client_description", () => this.sendClientDescription()); this.uiEvents.on("query_channel_group", () => this.sendChannelGroup()); @@ -228,7 +229,12 @@ export class ClientInfoController { private initializeClientInfo(client: ClientEntry) { this.currentClientStatus = { + type: client instanceof LocalClientEntry ? "self" : client.properties.client_type === ClientType.CLIENT_QUERY ? "query" : "voice", name: client.properties.client_nickname, + databaseId: client.properties.client_database_id, + uniqueId: client.properties.client_unique_identifier, + clientId: client.clientId(), + description: client.properties.client_description, channelGroup: client.properties.client_channel_group_id, serverGroups: client.assignedServerGroupIds(), @@ -250,8 +256,6 @@ export class ClientInfoController { } destroy() { - ReactDOM.unmountComponentAtNode(this.htmlContainer); - this.listenerClient.forEach(callback => callback()); this.listenerClient = []; @@ -266,25 +270,14 @@ export class ClientInfoController { this.unregisterClientEvents(); this.currentClient = client; + this.currentClientStatus = undefined; if(this.currentClient) { this.currentClient.updateClientVariables().then(undefined); this.registerClientEvents(this.currentClient); this.initializeClientInfo(this.currentClient); - this.uiEvents.fire("notify_client", { - info: { - handlerId: this.connection.handlerId, - type: client instanceof LocalClientEntry ? "self" : client.properties.client_type === ClientType.CLIENT_QUERY ? "query" : "voice", - clientDatabaseId: client.properties.client_database_id, - clientId: client.clientId(), - clientUniqueId: client.properties.client_unique_identifier - } - }); - } else { - this.currentClientStatus = undefined; - this.uiEvents.fire("notify_client", { - info: undefined - }); } + this.sendClient(); + this.events.fire("notify_client_changed", { newClient: client }); } getClient() : ClientEntry | undefined { @@ -316,6 +309,24 @@ export class ClientInfoController { } } + private sendClient() { + if(this.currentClientStatus) { + this.uiEvents.fire_react("notify_client", { + info: { + handlerId: this.connection.handlerId, + type: this.currentClientStatus.type, + clientDatabaseId: this.currentClientStatus.databaseId, + clientId: this.currentClientStatus.clientId, + clientUniqueId: this.currentClientStatus.uniqueId + } + }); + } else { + this.uiEvents.fire_react("notify_client", { + info: undefined + }); + } + } + private sendChannelGroup() { if(typeof this.currentClientStatus === "undefined") { this.uiEvents.fire_react("notify_channel_group", { group: undefined }); diff --git a/shared/js/ui/frames/side/ClientInfoDefinitions.ts b/shared/js/ui/frames/side/ClientInfoDefinitions.ts index b31ef0df..1a0b31c8 100644 --- a/shared/js/ui/frames/side/ClientInfoDefinitions.ts +++ b/shared/js/ui/frames/side/ClientInfoDefinitions.ts @@ -61,6 +61,7 @@ export interface ClientInfoEvents { action_show_full_info: {}, action_edit_avatar: {}, + query_client: {}, query_channel_group: {}, query_server_groups: {}, query_client_name: {}, @@ -83,7 +84,6 @@ export interface ClientInfoEvents { notify_version: { version: ClientVersionInfo }, notify_forum: { forum: ClientForumInfo }, - /* reset all fields into "loading" state */ notify_client: { info: ClientInfoInfo | undefined } diff --git a/shared/js/ui/frames/side/ClientInfoRenderer.tsx b/shared/js/ui/frames/side/ClientInfoRenderer.tsx index 26a3e4c6..354fc110 100644 --- a/shared/js/ui/frames/side/ClientInfoRenderer.tsx +++ b/shared/js/ui/frames/side/ClientInfoRenderer.tsx @@ -439,7 +439,10 @@ const ServerGroupRenderer = () => { const ClientInfoProvider = () => { const events = useContext(EventsContext); - const [ client, setClient ] = useState({ type: "none", contextHash: guid() }); + const [ client, setClient ] = useState(() => { + events.fire("query_client"); + return { type: "none", contextHash: guid() }; + }); events.reactUse("notify_client", event => { if(event.info) { setClient({ diff --git a/shared/js/ui/frames/side/HeaderController.ts b/shared/js/ui/frames/side/HeaderController.ts index 32e45879..5b89dc8a 100644 --- a/shared/js/ui/frames/side/HeaderController.ts +++ b/shared/js/ui/frames/side/HeaderController.ts @@ -1,12 +1,8 @@ import {ConnectionHandler} from "tc-shared/ConnectionHandler"; -import * as ReactDOM from "react-dom"; -import {SideHeaderRenderer} from "./HeaderRenderer"; -import * as React from "react"; -import {SideHeaderEvents, SideHeaderState} from "tc-shared/ui/frames/side/HeaderDefinitions"; -import * as _ from "lodash"; +import {SideHeaderEvents} from "tc-shared/ui/frames/side/HeaderDefinitions"; import {Registry} from "tc-shared/events"; import {ChannelEntry, ChannelProperties} from "tc-shared/tree/Channel"; -import {ClientEntry, LocalClientEntry} from "tc-shared/tree/Client"; +import {LocalClientEntry} from "tc-shared/tree/Client"; import {openMusicManage} from "tc-shared/ui/modal/ModalMusicManage"; const ChannelInfoUpdateProperties: (keyof ChannelProperties)[] = [ @@ -21,9 +17,8 @@ const ChannelInfoUpdateProperties: (keyof ChannelProperties)[] = [ "channel_maxfamilyclients" ]; -/* TODO: Remove the ping interval handler. It's currently still there since the clients are not emiting the event yet */ +/* TODO: Remove the ping interval handler. It's currently still there since the clients are not emitting the event yet */ export class SideHeader { - private readonly htmlTag: HTMLDivElement; private readonly uiEvents: Registry; private connection: ConnectionHandler; @@ -32,7 +27,6 @@ export class SideHeader { private listenerVoiceChannel: (() => void)[]; private listenerTextChannel: (() => void)[]; - private currentState: SideHeaderState; private currentVoiceChannel: ChannelEntry; private currentTextChannel: ChannelEntry; @@ -44,13 +38,6 @@ export class SideHeader { this.listenerVoiceChannel = []; this.listenerTextChannel = []; - this.htmlTag = document.createElement("div"); - this.htmlTag.style.display = "flex"; - this.htmlTag.style.flexDirection = "column"; - this.htmlTag.style.flexShrink = "0"; - this.htmlTag.style.flexGrow = "0"; - - ReactDOM.render(React.createElement(SideHeaderRenderer, { events: this.uiEvents }), this.htmlTag); this.initialize(); } @@ -75,8 +62,9 @@ export class SideHeader { openMusicManage(this.connection, bot); }); - this.uiEvents.on("action_bot_manage", () => this.connection.side_bar.music_info().events.fire("action_song_add")); + this.uiEvents.on("action_bot_add_song", () => this.connection.side_bar.music_info().events.fire("action_song_add")); + this.uiEvents.on("query_client_info_own_client", () => this.sendClientInfoOwnClient()); this.uiEvents.on("query_current_channel_state", event => this.sendChannelState(event.mode)); this.uiEvents.on("query_private_conversations", () => this.sendPrivateConversationInfo()); this.uiEvents.on("query_ping", () => this.sendPing()); @@ -110,6 +98,7 @@ export class SideHeader { this.listenerConnection.push(this.connection.serverConnection.events.on("notify_ping_updated", () => this.sendPing())); this.listenerConnection.push(this.connection.getPrivateConversations().events.on("notify_unread_count_changed", () => this.sendPrivateConversationInfo())); this.listenerConnection.push(this.connection.getPrivateConversations().events.on(["notify_conversation_destroyed", "notify_conversation_destroyed"], () => this.sendPrivateConversationInfo())); + this.listenerConnection.push(this.connection.side_bar.getClientInfo().events.on("notify_client_changed", () => this.sendClientInfoOwnClient())); } setConnectionHandler(connection: ConnectionHandler) { @@ -123,23 +112,18 @@ export class SideHeader { this.connection = connection; if(connection) { this.initializeConnection(); - /* TODO: Update state! */ - } else { - this.setState({ state: "none" }); } + this.sendPing(); + this.sendPrivateConversationInfo(); + this.sendChannelState("voice"); + this.sendChannelState("text"); } getConnectionHandler() : ConnectionHandler | undefined { return this.connection; } - getHtmlTag() : HTMLDivElement { - return this.htmlTag; - } - destroy() { - ReactDOM.unmountComponentAtNode(this.htmlTag); - this.listenerConnection.forEach(callback => callback()); this.listenerConnection = []; @@ -153,15 +137,6 @@ export class SideHeader { this.pingUpdateInterval = undefined; } - setState(state: SideHeaderState) { - if(_.isEqual(this.currentState, state)) { - return; - } - - this.currentState = state; - this.uiEvents.fire_react("notify_header_state", { state: state }); - } - private sendChannelState(mode: "voice" | "text") { const channel = mode === "voice" ? this.currentVoiceChannel : this.currentTextChannel; if(channel) { @@ -258,12 +233,29 @@ export class SideHeader { } private sendPrivateConversationInfo() { - const conversations = this.connection.getPrivateConversations(); - this.uiEvents.fire_react("notify_private_conversations", { - info: { - open: conversations.getConversations().length, - unread: conversations.getUnreadCount() - } - }); + if(this.connection) { + const conversations = this.connection.getPrivateConversations(); + this.uiEvents.fire_react("notify_private_conversations", { + info: { + open: conversations.getConversations().length, + unread: conversations.getUnreadCount() + } + }); + } else { + this.uiEvents.fire_react("notify_private_conversations", { + info: { + open: 0, + unread: 0 + } + }); + } + } + + private sendClientInfoOwnClient() { + if(this.connection) { + this.uiEvents.fire_react("notify_client_info_own_client", { isOwnClient: this.connection.side_bar.getClientInfo().getClient() instanceof LocalClientEntry }); + } else { + this.uiEvents.fire_react("notify_client_info_own_client", { isOwnClient: false }); + } } } \ No newline at end of file diff --git a/shared/js/ui/frames/side/HeaderDefinitions.ts b/shared/js/ui/frames/side/HeaderDefinitions.ts index 638e1293..f7e98c3b 100644 --- a/shared/js/ui/frames/side/HeaderDefinitions.ts +++ b/shared/js/ui/frames/side/HeaderDefinitions.ts @@ -12,7 +12,6 @@ export type SideHeaderStateConversation = { export type SideHeaderStateClient = { state: "client", - ownClient: boolean } export type SideHeaderStateMusicBot = { @@ -46,12 +45,10 @@ export interface SideHeaderEvents { action_open_conversation: {}, query_current_channel_state: { mode: "voice" | "text" }, - query_ping: {}, query_private_conversations: {}, + query_client_info_own_client: {}, + query_ping: {}, - notify_header_state: { - state: SideHeaderState - }, notify_current_channel_state: { mode: "voice" | "text", state: SideHeaderChannelState @@ -61,5 +58,8 @@ export interface SideHeaderEvents { }, notify_private_conversations: { info: PrivateConversationInfo + }, + notify_client_info_own_client: { + isOwnClient: boolean } } \ No newline at end of file diff --git a/shared/js/ui/frames/side/HeaderRenderer.tsx b/shared/js/ui/frames/side/HeaderRenderer.tsx index 645129f3..21a87b2a 100644 --- a/shared/js/ui/frames/side/HeaderRenderer.tsx +++ b/shared/js/ui/frames/side/HeaderRenderer.tsx @@ -236,11 +236,19 @@ const BlockBottomLeft = () => { } const BlockBottomRight = () => { + const events = useContext(EventsContext); const state = useContext(StateContext); + const [ ownClient, setOwnClient ] = useState(() => { + events.fire("query_client_info_own_client"); + return false; + }); + + events.reactUse("notify_client_info_own_client", event => setOwnClient(event.isOwnClient)); + switch (state.state) { case "client": - if(state.ownClient) { + if(ownClient) { return null; } else { return ; @@ -258,19 +266,16 @@ const BlockBottomRight = () => { } } -export const SideHeaderRenderer = (props: { events: Registry }) => { - const [ state, setState ] = useState({ state: "none" }); - props.events.reactUse("notify_header_state", event => setState(event.state)); - +export const SideHeaderRenderer = React.memo((props: { events: Registry, state: SideHeaderState }) => { return ( - +
-
+
@@ -278,4 +283,4 @@ export const SideHeaderRenderer = (props: { events: Registry } ); -} \ No newline at end of file +}) \ No newline at end of file diff --git a/shared/js/ui/frames/side/PrivateConversationRenderer.tsx b/shared/js/ui/frames/side/PrivateConversationRenderer.tsx index 77fffcaf..ad013c0f 100644 --- a/shared/js/ui/frames/side/PrivateConversationRenderer.tsx +++ b/shared/js/ui/frames/side/PrivateConversationRenderer.tsx @@ -215,12 +215,12 @@ const OpenConversationsPanel = React.memo(() => { }); -export const PrivateConversationsPanel = (props: { events: Registry, handler: ConnectionHandler }) => ( - +export const PrivateConversationsPanel = (props: { events: Registry, handlerId: string }) => ( + - +