Reworked the side bar algorithm. This should heavily improve the memory footprint especially on muti connection sessions
parent
c7a01ce79a
commit
3d02669d20
|
@ -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;
|
||||
|
|
|
@ -369,6 +369,13 @@ export class Settings extends StaticSettings {
|
|||
valueType: "boolean",
|
||||
};
|
||||
|
||||
static readonly KEY_CHAT_HIGHLIGHT_CODE: ValuedSettingsKey<boolean> = {
|
||||
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<boolean> = {
|
||||
key: 'chat_tag_urls',
|
||||
defaultValue: true,
|
||||
|
|
|
@ -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<any>) => {
|
||||
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<TagElement, React.ReactNode> {
|
||||
tags(): string | string[] {
|
||||
|
|
|
@ -524,7 +524,7 @@ export class ClientEntry extends ChannelTreeEntry<ClientEvents> {
|
|||
conversation.setActiveClientEntry(this);
|
||||
privateConversations.setSelectedConversation(conversation);
|
||||
sideBar.showPrivateConversations();
|
||||
sideBar.private_conversations().focusInput();
|
||||
sideBar.privateConversationsController().focusInput();
|
||||
}
|
||||
|
||||
showContextMenu(x: number, y: number, on_close: () => void = undefined) {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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<ConversationUIEvents>,
|
||||
handlerId: string
|
||||
},
|
||||
"private-chat": {
|
||||
events: Registry<PrivateConversationUIEvents>,
|
||||
handlerId: string
|
||||
},
|
||||
"client-info": {
|
||||
events: Registry<ClientInfoEvents>,
|
||||
},
|
||||
"music-manage": {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export type SideBarNotifyContentData<T extends SideBarType> = {
|
||||
content: T,
|
||||
data: SideBarTypeData[T]
|
||||
}
|
||||
|
||||
export interface SideBarEvents {
|
||||
query_content: {},
|
||||
query_content_data: { content: SideBarType },
|
||||
|
||||
notify_content: { content: SideBarType },
|
||||
notify_content_data: SideBarNotifyContentData<SideBarType>
|
||||
}
|
|
@ -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<Registry<SideBarEvents>>(undefined);
|
||||
|
||||
function useContentData<T extends SideBarType>(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 (
|
||||
<ConversationPanel
|
||||
key={"channel-chat"}
|
||||
events={contentData.events}
|
||||
handlerId={contentData.handlerId}
|
||||
messagesDeletable={true}
|
||||
noFirstMessageOverlay={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ContentRendererPrivateConversation = () => {
|
||||
const contentData = useContentData("private-chat");
|
||||
if(!contentData) { return null; }
|
||||
|
||||
return (
|
||||
<PrivateConversationsPanel
|
||||
events={contentData.events}
|
||||
handlerId={contentData.handlerId}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ContentRendererClientInfo = () => {
|
||||
const contentData = useContentData("client-info");
|
||||
if(!contentData) { return null; }
|
||||
|
||||
return (
|
||||
<ClientInfoRenderer
|
||||
events={contentData.events}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const SideBarFrame = (props: { type: SideBarType }) => {
|
||||
switch (props.type) {
|
||||
case "channel-chat":
|
||||
return <ContentRendererChannelConversation key={props.type} />;
|
||||
|
||||
case "private-chat":
|
||||
return <ContentRendererPrivateConversation key={props.type} />;
|
||||
|
||||
case "client-info":
|
||||
return <ContentRendererClientInfo key={props.type} />;
|
||||
|
||||
case "music-manage":
|
||||
/* TODO! */
|
||||
|
||||
case "none":
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const SideBarHeader = (props: { type: SideBarType, eventsHeader: Registry<SideHeaderEvents> }) => {
|
||||
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 <SideHeaderRenderer state={headerState} events={props.eventsHeader} />;
|
||||
}
|
||||
|
||||
export const SideBarRenderer = (props: {
|
||||
handlerId: string,
|
||||
events: Registry<SideBarEvents>,
|
||||
eventsHeader: Registry<SideHeaderEvents>
|
||||
}) => {
|
||||
const [ content, setContent ] = useState<SideBarType>(() => {
|
||||
props.events.fire("query_content");
|
||||
return "none";
|
||||
});
|
||||
props.events.reactUse("notify_content", event => setContent(event.content));
|
||||
|
||||
return (
|
||||
<EventContent.Provider value={props.events}>
|
||||
<div className={cssStyle.container}>
|
||||
<SideBarHeader eventsHeader={props.eventsHeader} type={content} />
|
||||
<div className={cssStyle.frameContainer}>
|
||||
<SideBarFrame type={content} />
|
||||
</div>
|
||||
</div>
|
||||
</EventContent.Provider>
|
||||
)
|
||||
};
|
|
@ -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<SideBarEvents>;
|
||||
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<SideBarEvents>();
|
||||
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");
|
||||
}
|
||||
}
|
|
@ -895,10 +895,12 @@ export const ConversationPanel = React.memo((props: { events: Registry<Conversat
|
|||
currentChat.current.id = event.chatId;
|
||||
updateChatBox();
|
||||
});
|
||||
|
||||
props.events.reactUse("notify_conversation_state", event => {
|
||||
chatEnabled.current = event.state === "normal" && event.sendEnabled;
|
||||
updateChatBox();
|
||||
});
|
||||
|
||||
props.events.reactUse("notify_send_enabled", event => {
|
||||
if(event.chatId !== currentChat.current.id)
|
||||
return;
|
||||
|
|
|
@ -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<ClientInfoControllerEvents>;
|
||||
|
||||
private readonly connection: ConnectionHandler;
|
||||
private readonly listenerConnection: (() => void)[];
|
||||
|
||||
private readonly uiEvents: Registry<ClientInfoEvents>;
|
||||
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<ClientInfoControllerEvents>();
|
||||
this.uiEvents = new Registry<ClientInfoEvents>();
|
||||
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 });
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -439,7 +439,10 @@ const ServerGroupRenderer = () => {
|
|||
const ClientInfoProvider = () => {
|
||||
const events = useContext(EventsContext);
|
||||
|
||||
const [ client, setClient ] = useState<OptionalClientInfoInfo>({ type: "none", contextHash: guid() });
|
||||
const [ client, setClient ] = useState<OptionalClientInfoInfo>(() => {
|
||||
events.fire("query_client");
|
||||
return { type: "none", contextHash: guid() };
|
||||
});
|
||||
events.reactUse("notify_client", event => {
|
||||
if(event.info) {
|
||||
setClient({
|
||||
|
|
|
@ -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<SideHeaderEvents>;
|
||||
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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 <BlockButtonOpenConversation key={"button-open-conversation"} />;
|
||||
|
@ -258,19 +266,16 @@ const BlockBottomRight = () => {
|
|||
}
|
||||
}
|
||||
|
||||
export const SideHeaderRenderer = (props: { events: Registry<SideHeaderEvents> }) => {
|
||||
const [ state, setState ] = useState<SideHeaderState>({ state: "none" });
|
||||
props.events.reactUse("notify_header_state", event => setState(event.state));
|
||||
|
||||
export const SideHeaderRenderer = React.memo((props: { events: Registry<SideHeaderEvents>, state: SideHeaderState }) => {
|
||||
return (
|
||||
<EventsContext.Provider value={props.events}>
|
||||
<StateContext.Provider value={state}>
|
||||
<StateContext.Provider value={props.state}>
|
||||
<div className={cssStyle.container}>
|
||||
<div className={cssStyle.lane}>
|
||||
<BlockTopLeft />
|
||||
<BlockTopRight />
|
||||
</div>
|
||||
<div className={cssStyle.lane + " " + (state.state === "music-bot" ? cssStyle.musicBotInfo : "")}>
|
||||
<div className={cssStyle.lane + " " + (props.state.state === "music-bot" ? cssStyle.musicBotInfo : "")}>
|
||||
<BlockBottomLeft />
|
||||
<BlockBottomRight />
|
||||
</div>
|
||||
|
@ -278,4 +283,4 @@ export const SideHeaderRenderer = (props: { events: Registry<SideHeaderEvents> }
|
|||
</StateContext.Provider>
|
||||
</EventsContext.Provider>
|
||||
);
|
||||
}
|
||||
})
|
|
@ -215,12 +215,12 @@ const OpenConversationsPanel = React.memo(() => {
|
|||
});
|
||||
|
||||
|
||||
export const PrivateConversationsPanel = (props: { events: Registry<PrivateConversationUIEvents>, handler: ConnectionHandler }) => (
|
||||
<HandlerIdContext.Provider value={props.handler.handlerId}>
|
||||
export const PrivateConversationsPanel = (props: { events: Registry<PrivateConversationUIEvents>, handlerId: string }) => (
|
||||
<HandlerIdContext.Provider value={props.handlerId}>
|
||||
<EventContext.Provider value={props.events}>
|
||||
<ContextDivider id={"seperator-conversation-list-messages"} direction={"horizontal"} defaultValue={25} separatorClassName={cssStyle.divider}>
|
||||
<OpenConversationsPanel />
|
||||
<ConversationPanel events={props.events as any} handlerId={props.handler.handlerId} noFirstMessageOverlay={true} messagesDeletable={false} />
|
||||
<ConversationPanel events={props.events as any} handlerId={props.handlerId} noFirstMessageOverlay={true} messagesDeletable={false} />
|
||||
</ContextDivider>
|
||||
</EventContext.Provider>
|
||||
</HandlerIdContext.Provider>
|
||||
|
|
Loading…
Reference in New Issue