Globally rendering the app via React
parent
0dbf991e36
commit
2211da243d
|
@ -3,6 +3,7 @@
|
|||
|
||||
/* FIXME: Resolve variable usage! */
|
||||
html:root {
|
||||
--side-background: #353535;
|
||||
--side-info-background: #2e2e2e;
|
||||
--side-info-shadow: rgba(0, 0, 0, 0.25);
|
||||
--side-info-title: #8b8b8b;
|
||||
|
|
|
@ -7,8 +7,6 @@ $animation_length: .5s;
|
|||
html:root {
|
||||
--app-background: #1e1e1e;
|
||||
|
||||
--control-bar-background: #454545;
|
||||
|
||||
--chat-background: #353535;
|
||||
--channel-tree-background: #353535;
|
||||
--server-log-background: #353535;
|
||||
|
@ -20,201 +18,16 @@ html:root {
|
|||
--channel-chat-seperator-selected: #707070;
|
||||
}
|
||||
|
||||
.app {
|
||||
min-width: 600px;
|
||||
min-height: 330px;
|
||||
|
||||
padding: 5px;
|
||||
|
||||
.container-app-main {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
min-height: 500px;
|
||||
margin-top: 5px;
|
||||
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
|
||||
.container-channel-chat {
|
||||
height: 80%; /* "default" settings */
|
||||
width: 100%;
|
||||
|
||||
min-height: 27em; /* fits with the music bot interface */
|
||||
min-width: 100px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
& > * {
|
||||
height: 100%;
|
||||
min-height: 250px;
|
||||
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
> .container-channel-tree {
|
||||
width: 50%; /* "default" settings */
|
||||
height: 100%;
|
||||
|
||||
background: var(--channel-tree-background);
|
||||
min-width: 200px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
min-height: 100px;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
> .hostbanner {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
max-height: 9em; /* same size as the info pannel */
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
> .channel-tree {
|
||||
min-height: 5em;
|
||||
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
.channel-tree-container {
|
||||
height: 100%;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .container-chat {
|
||||
width: 50%; /* "default" settings */
|
||||
height: 100%;
|
||||
|
||||
background: var(--chat-background);
|
||||
min-width: 350px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
> .container-bottom {
|
||||
height: 20%;
|
||||
|
||||
min-height: 1.5em;
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
> .container-server-log {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
border-radius: 5px 5px 0 0;
|
||||
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
|
||||
background: var(--server-log-background);
|
||||
}
|
||||
|
||||
> .container-footer {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
height: 1.5em;
|
||||
|
||||
background: var(--footer-background);
|
||||
color: var(--footer-text);
|
||||
|
||||
border-radius: 0 0 5px 5px;
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
padding-top: 2px;
|
||||
|
||||
-webkit-box-shadow: inset 0 2px 5px 0 rgba(0,0,0,0.125);
|
||||
-moz-box-shadow: inset 0 2px 5px 0 rgba(0,0,0,0.125);
|
||||
box-shadow: inset 0 2px 5px 0 rgba(0,0,0,0.125);
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
> * {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
> span {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
||||
> a {
|
||||
margin-right: .5em;
|
||||
}
|
||||
}
|
||||
|
||||
a[href], a[href]:visited {
|
||||
color: var(--footer-text)!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-control-bar {
|
||||
z-index: 200;
|
||||
|
||||
flex-shrink: 0;
|
||||
|
||||
border-radius: 5px;
|
||||
|
||||
height: 2em;
|
||||
width: 100%;
|
||||
|
||||
background-color: var(--control-bar-background);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.hide-small {
|
||||
.hide-small {
|
||||
opacity: 1;
|
||||
transition: opacity $animation_length linear;
|
||||
}
|
||||
}
|
||||
|
||||
.show-small {
|
||||
.show-small {
|
||||
display: none;
|
||||
|
||||
opacity: 0;
|
||||
transition: opacity $animation_length linear;
|
||||
}
|
||||
}
|
||||
|
||||
.app-container {
|
||||
|
|
|
@ -7,36 +7,6 @@
|
|||
</head>
|
||||
<body>
|
||||
<script class="jsrender-template" id="tmpl_main" type="text/html">
|
||||
<div class="app-container">
|
||||
<div class="app">
|
||||
<!-- navigation bar -->
|
||||
<div class="container-control-bar">
|
||||
<div id="control_bar" class="control_bar">
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-connection-handlers" id="connection-handler-list"></div>
|
||||
<div class="container-app-main">
|
||||
<div class="container-channel-video" id="channel-video"></div>
|
||||
<div class="container-channel-chat">
|
||||
<!-- Channel tree -->
|
||||
<div class="container-channel-tree">
|
||||
<div class="hostbanner" id="hostbanner"></div>
|
||||
<div class="channel-tree" id="channelTree"></div>
|
||||
</div>
|
||||
|
||||
<div class="container-seperator vertical" seperator-id="seperator-channel-chat"></div>
|
||||
<!-- Chat window -->
|
||||
<div class="container-chat" id="chat"></div>
|
||||
</div>
|
||||
<div class="container-seperator horizontal" seperator-id="seperator-main-log"></div>
|
||||
<div class="container-bottom">
|
||||
<div class="container-server-log" id="server-log"></div>
|
||||
<div class="container-footer" id="container-footer">
|
||||
</div>
|
||||
</div> <!-- Selection info -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="contextMenu" class="context-menu"></div>
|
||||
<div class="overlay-image-preview hidden" id="overlay-image-preview">
|
||||
<div class="container-menu-bar">
|
||||
|
|
|
@ -133,7 +133,7 @@ export interface ConnectParameters {
|
|||
export class ConnectionHandler {
|
||||
readonly handlerId: string;
|
||||
|
||||
private readonly event_registry: Registry<ConnectionEvents>;
|
||||
private readonly events_: Registry<ConnectionEvents>;
|
||||
channelTree: ChannelTree;
|
||||
|
||||
connection_state: ConnectionState = ConnectionState.UNCONNECTED;
|
||||
|
@ -159,13 +159,13 @@ export class ConnectionHandler {
|
|||
|
||||
private clientInfoManager: SelectedClientInfo;
|
||||
|
||||
private _clientId: number = 0;
|
||||
private localClientId: number = 0;
|
||||
private localClient: LocalClientEntry;
|
||||
|
||||
private _reconnect_timer: number;
|
||||
private _reconnect_attempt: boolean = false;
|
||||
private autoReconnectTimer: number;
|
||||
private autoReconnectAttempt: boolean = false;
|
||||
|
||||
private _connect_initialize_id: number = 1;
|
||||
private connectAttemptId: number = 1;
|
||||
private echoTestRunning = false;
|
||||
|
||||
private pluginCmdRegistry: PluginCmdRegistry;
|
||||
|
@ -188,8 +188,8 @@ export class ConnectionHandler {
|
|||
|
||||
constructor() {
|
||||
this.handlerId = guid();
|
||||
this.event_registry = new Registry<ConnectionEvents>();
|
||||
this.event_registry.enableDebug("connection-handler");
|
||||
this.events_ = new Registry<ConnectionEvents>();
|
||||
this.events_.enableDebug("connection-handler");
|
||||
|
||||
this.settings = new ServerSettings();
|
||||
|
||||
|
@ -225,10 +225,10 @@ export class ConnectionHandler {
|
|||
this.localClient = new LocalClientEntry(this);
|
||||
this.localClient.channelTree = this.channelTree;
|
||||
|
||||
this.event_registry.register_handler(this);
|
||||
this.events_.register_handler(this);
|
||||
this.pluginCmdRegistry.registerHandler(new W2GPluginCmdHandler());
|
||||
|
||||
this.events().fire("notify_handler_initialized");
|
||||
this.events_.fire("notify_handler_initialized");
|
||||
}
|
||||
|
||||
initialize_client_state(source?: ConnectionHandler) {
|
||||
|
@ -242,12 +242,12 @@ export class ConnectionHandler {
|
|||
}
|
||||
|
||||
events() : Registry<ConnectionEvents> {
|
||||
return this.event_registry;
|
||||
return this.events_;
|
||||
}
|
||||
|
||||
async startConnection(addr: string, profile: ConnectionProfile, user_action: boolean, parameters: ConnectParameters) {
|
||||
this.cancel_reconnect(false);
|
||||
this._reconnect_attempt = parameters.auto_reconnect_attempt || false;
|
||||
this.autoReconnectAttempt = parameters.auto_reconnect_attempt || false;
|
||||
this.handleDisconnect(DisconnectReason.REQUESTED);
|
||||
|
||||
let server_address: ServerAddress = {
|
||||
|
@ -298,11 +298,11 @@ export class ConnectionHandler {
|
|||
if(server_address.host === "localhost") {
|
||||
server_address.host = "127.0.0.1";
|
||||
} else if(dns.supported() && !server_address.host.match(Regex.IP_V4) && !server_address.host.match(Regex.IP_V6)) {
|
||||
const id = ++this._connect_initialize_id;
|
||||
const id = ++this.connectAttemptId;
|
||||
this.log.log("connection.hostname.resolve", {});
|
||||
try {
|
||||
const resolved = await dns.resolve_address(server_address, { timeout: 5000 }) || {} as any;
|
||||
if(id != this._connect_initialize_id)
|
||||
if(id != this.connectAttemptId)
|
||||
return; /* we're old */
|
||||
|
||||
server_address.host = typeof(resolved.target_ip) === "string" ? resolved.target_ip : server_address.host;
|
||||
|
@ -314,7 +314,7 @@ export class ConnectionHandler {
|
|||
}
|
||||
});
|
||||
} catch(error) {
|
||||
if(id != this._connect_initialize_id)
|
||||
if(id != this.connectAttemptId)
|
||||
return; /* we're old */
|
||||
|
||||
this.handleDisconnect(DisconnectReason.DNS_FAILED, error);
|
||||
|
@ -348,7 +348,7 @@ export class ConnectionHandler {
|
|||
}
|
||||
|
||||
getClient() : LocalClientEntry { return this.localClient; }
|
||||
getClientId() { return this._clientId; }
|
||||
getClientId() { return this.localClientId; }
|
||||
|
||||
getPrivateConversations() : PrivateConversationManager {
|
||||
return this.privateConversations;
|
||||
|
@ -371,7 +371,7 @@ export class ConnectionHandler {
|
|||
}
|
||||
|
||||
initializeLocalClient(clientId: number, acceptedName: string) {
|
||||
this._clientId = clientId;
|
||||
this.localClientId = clientId;
|
||||
this.localClient["_clientId"] = clientId;
|
||||
|
||||
this.channelTree.registerClient(this.localClient);
|
||||
|
@ -477,7 +477,7 @@ export class ConnectionHandler {
|
|||
|
||||
private _certificate_modal: Modal;
|
||||
handleDisconnect(type: DisconnectReason, data: any = {}) {
|
||||
this._connect_initialize_id++;
|
||||
this.connectAttemptId++;
|
||||
|
||||
let auto_reconnect = false;
|
||||
switch (type) {
|
||||
|
@ -498,7 +498,7 @@ export class ConnectionHandler {
|
|||
this.sound.play(Sound.CONNECTION_REFUSED);
|
||||
break;
|
||||
case DisconnectReason.CONNECT_FAILURE:
|
||||
if(this._reconnect_attempt) {
|
||||
if(this.autoReconnectAttempt) {
|
||||
auto_reconnect = true;
|
||||
break;
|
||||
}
|
||||
|
@ -574,7 +574,7 @@ export class ConnectionHandler {
|
|||
break;
|
||||
case DisconnectReason.CONNECTION_CLOSED:
|
||||
log.error(LogCategory.CLIENT, tr("Lost connection to remote server!"));
|
||||
if(!this._reconnect_attempt) {
|
||||
if(!this.autoReconnectAttempt) {
|
||||
createErrorModal(
|
||||
tr("Connection closed"),
|
||||
tr("The connection was closed by remote host")
|
||||
|
@ -683,8 +683,8 @@ export class ConnectionHandler {
|
|||
const server_address = this.serverConnection.remote_address();
|
||||
const profile = this.serverConnection.handshake_handler().profile;
|
||||
|
||||
this._reconnect_timer = setTimeout(() => {
|
||||
this._reconnect_timer = undefined;
|
||||
this.autoReconnectTimer = setTimeout(() => {
|
||||
this.autoReconnectTimer = undefined;
|
||||
this.log.log("reconnect.execute", {});
|
||||
log.info(LogCategory.NETWORKING, tr("Reconnecting..."));
|
||||
|
||||
|
@ -696,16 +696,16 @@ export class ConnectionHandler {
|
|||
}
|
||||
|
||||
cancel_reconnect(log_event: boolean) {
|
||||
if(this._reconnect_timer) {
|
||||
if(this.autoReconnectTimer) {
|
||||
if(log_event) this.log.log("reconnect.canceled", {});
|
||||
clearTimeout(this._reconnect_timer);
|
||||
this._reconnect_timer = undefined;
|
||||
clearTimeout(this.autoReconnectTimer);
|
||||
this.autoReconnectTimer = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private on_connection_state_changed(old_state: ConnectionState, new_state: ConnectionState) {
|
||||
console.log("From %s to %s", ConnectionState[old_state], ConnectionState[new_state]);
|
||||
this.event_registry.fire("notify_connection_state_changed", {
|
||||
this.events_.fire("notify_connection_state_changed", {
|
||||
oldState: old_state,
|
||||
newState: new_state
|
||||
});
|
||||
|
@ -805,14 +805,14 @@ export class ConnectionHandler {
|
|||
if(shouldRecord || this.echoTestRunning) {
|
||||
if(this.getInputHardwareState() !== InputHardwareState.START_FAILED) {
|
||||
this.startVoiceRecorder(Date.now() - this._last_record_error_popup > 10 * 1000).then(() => {
|
||||
this.event_registry.fire("notify_state_updated", { state: "microphone" });
|
||||
this.events_.fire("notify_state_updated", { state: "microphone" });
|
||||
});
|
||||
}
|
||||
} else {
|
||||
currentInput.stop().catch(error => {
|
||||
logWarn(LogCategory.AUDIO, tr("Failed to stop the microphone input recorder: %o"), error);
|
||||
}).then(() => {
|
||||
this.event_registry.fire("notify_state_updated", { state: "microphone" });
|
||||
this.events_.fire("notify_state_updated", { state: "microphone" });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1017,7 +1017,7 @@ export class ConnectionHandler {
|
|||
}
|
||||
|
||||
destroy() {
|
||||
this.event_registry.unregister_handler(this);
|
||||
this.events_.unregister_handler(this);
|
||||
this.cancel_reconnect(true);
|
||||
|
||||
this.pluginCmdRegistry?.destroy();
|
||||
|
@ -1093,7 +1093,7 @@ export class ConnectionHandler {
|
|||
this.sound.play(muted ? Sound.MICROPHONE_MUTED : Sound.MICROPHONE_ACTIVATED);
|
||||
}
|
||||
this.update_voice_status();
|
||||
this.event_registry.fire("notify_state_updated", { state: "microphone" });
|
||||
this.events_.fire("notify_state_updated", { state: "microphone" });
|
||||
}
|
||||
toggleMicrophone() { this.setMicrophoneMuted(!this.isMicrophoneMuted()); }
|
||||
|
||||
|
@ -1104,7 +1104,7 @@ export class ConnectionHandler {
|
|||
if(this.client_status.output_muted === muted) return;
|
||||
if(muted && !dontPlaySound) this.sound.play(Sound.SOUND_MUTED); /* play the sound *before* we're setting the muted state */
|
||||
this.client_status.output_muted = muted;
|
||||
this.event_registry.fire("notify_state_updated", { state: "speaker" });
|
||||
this.events_.fire("notify_state_updated", { state: "speaker" });
|
||||
if(!muted && !dontPlaySound) this.sound.play(Sound.SOUND_ACTIVATED); /* play the sound *after* we're setting we've unmuted the sound */
|
||||
this.update_voice_status();
|
||||
this.serverConnection.getVoiceConnection().stopAllVoiceReplays();
|
||||
|
@ -1129,7 +1129,7 @@ export class ConnectionHandler {
|
|||
} else {
|
||||
this.channelTree.unsubscribe_all_channels();
|
||||
}
|
||||
this.event_registry.fire("notify_state_updated", { state: "subscribe" });
|
||||
this.events_.fire("notify_state_updated", { state: "subscribe" });
|
||||
}
|
||||
|
||||
isSubscribeToAllChannels() : boolean { return this.client_status.channel_subscribe_all; }
|
||||
|
@ -1156,7 +1156,7 @@ export class ConnectionHandler {
|
|||
this.log.log("error.custom", {message: tr("Failed to update away status.")});
|
||||
});
|
||||
|
||||
this.event_registry.fire("notify_state_updated", {
|
||||
this.events_.fire("notify_state_updated", {
|
||||
state: "away"
|
||||
});
|
||||
}
|
||||
|
@ -1168,7 +1168,7 @@ export class ConnectionHandler {
|
|||
this.client_status.queries_visible = flag;
|
||||
this.channelTree.toggle_server_queries(flag);
|
||||
|
||||
this.event_registry.fire("notify_state_updated", {
|
||||
this.events_.fire("notify_state_updated", {
|
||||
state: "query"
|
||||
});
|
||||
}
|
||||
|
@ -1183,7 +1183,7 @@ export class ConnectionHandler {
|
|||
return;
|
||||
|
||||
this.inputHardwareState = state;
|
||||
this.event_registry.fire("notify_state_updated", { state: "microphone" });
|
||||
this.events_.fire("notify_state_updated", { state: "microphone" });
|
||||
}
|
||||
|
||||
hasOutputHardware() : boolean { return true; }
|
||||
|
|
|
@ -10,6 +10,8 @@ import {ServerEventLogController} from "tc-shared/ui/frames/log/Controller";
|
|||
import {ServerLogFrame} from "tc-shared/ui/frames/log/Renderer";
|
||||
import {HostBannerController} from "tc-shared/ui/frames/HostBannerController";
|
||||
import {HostBanner} from "tc-shared/ui/frames/HostBannerRenderer";
|
||||
import {ChannelTreeView} from "tc-shared/ui/tree/RendererView";
|
||||
import {ChannelTreeRenderer} from "tc-shared/ui/tree/Renderer";
|
||||
|
||||
export let server_connections: ConnectionManager;
|
||||
|
||||
|
@ -34,15 +36,16 @@ export class ConnectionManager {
|
|||
private connection_handlers: ConnectionHandler[] = [];
|
||||
private active_handler: ConnectionHandler | undefined;
|
||||
|
||||
private _container_channel_tree: JQuery;
|
||||
private containerChannelVideo: ReplaceableContainer;
|
||||
private containerFooter: HTMLDivElement;
|
||||
private containerServerLog: HTMLDivElement;
|
||||
private containerHostBanner: HTMLDivElement;
|
||||
private containerChannelTree: HTMLDivElement;
|
||||
|
||||
private sideBarController: SideBarController;
|
||||
private serverLogController: ServerEventLogController;
|
||||
private hostBannerController: HostBannerController;
|
||||
/* FIXME: Move these controller out! */
|
||||
sideBarController: SideBarController;
|
||||
serverLogController: ServerEventLogController;
|
||||
hostBannerController: HostBannerController;
|
||||
|
||||
constructor() {
|
||||
this.event_registry = new Registry<ConnectionManagerEvents>();
|
||||
|
@ -56,13 +59,14 @@ export class ConnectionManager {
|
|||
this.containerServerLog = document.getElementById("server-log") as HTMLDivElement;
|
||||
this.containerFooter = document.getElementById("container-footer") as HTMLDivElement;
|
||||
this.containerHostBanner = document.getElementById("hostbanner") as HTMLDivElement;
|
||||
this._container_channel_tree = $("#channelTree");
|
||||
this.containerChannelTree = document.getElementById("channelTree") as HTMLDivElement;
|
||||
|
||||
this.sideBarController.renderInto(document.getElementById("chat") as HTMLDivElement);
|
||||
this.set_active_connection(undefined);
|
||||
}
|
||||
|
||||
initializeReactComponents() {
|
||||
return;
|
||||
ReactDOM.render(React.createElement(FooterRenderer), this.containerFooter);
|
||||
ReactDOM.render(React.createElement(ServerLogFrame, { events: this.serverLogController.events }), this.containerServerLog);
|
||||
ReactDOM.render(React.createElement(HostBanner, { events: this.hostBannerController.uiEvents }), this.containerHostBanner);
|
||||
|
@ -129,12 +133,15 @@ export class ConnectionManager {
|
|||
this.serverLogController.setConnectionHandler(handler);
|
||||
this.hostBannerController.setConnectionHandler(handler);
|
||||
|
||||
this._container_channel_tree.children().detach();
|
||||
/*
|
||||
this.containerChannelVideo.replaceWith(handler?.video_frame.getContainer());
|
||||
|
||||
if(handler) {
|
||||
this._container_channel_tree.append(handler.channelTree.tag_tree());
|
||||
ReactDOM.render(React.createElement(ChannelTreeRenderer, { handlerId: handler.handlerId, events: handler.channelTree.mainTreeUiEvents }), this.containerChannelTree);
|
||||
} else {
|
||||
ReactDOM.render(undefined, this.containerChannelTree);
|
||||
}
|
||||
*/
|
||||
|
||||
const old_handler = this.active_handler;
|
||||
this.active_handler = handler;
|
||||
|
|
|
@ -47,15 +47,11 @@ import "./connection/rtc/Connection";
|
|||
import "./connection/rtc/video/Connection";
|
||||
import "./video/VideoSource";
|
||||
import "./media/Video";
|
||||
import "./ui/AppController";
|
||||
|
||||
import {defaultConnectProfile, findConnectProfile} from "tc-shared/profiles/ConnectionProfile";
|
||||
import {server_connections} from "tc-shared/ConnectionManager";
|
||||
import {initializeConnectionUIList} from "tc-shared/ui/frames/connection-handler-list/Controller";
|
||||
import ContextMenuEvent = JQuery.ContextMenuEvent;
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {ControlBarEvents} from "tc-shared/ui/frames/control-bar/Definitions";
|
||||
import {ControlBar2} from "tc-shared/ui/frames/control-bar/Renderer";
|
||||
import {initializeControlBarController} from "tc-shared/ui/frames/control-bar/Controller";
|
||||
|
||||
let preventWelcomeUI = false;
|
||||
async function initialize() {
|
||||
|
@ -71,15 +67,7 @@ async function initialize() {
|
|||
}
|
||||
|
||||
async function initialize_app() {
|
||||
initializeConnectionUIList();
|
||||
|
||||
global_ev_handler.initialize(global_client_actions);
|
||||
{
|
||||
const events = new Registry<ControlBarEvents>()
|
||||
initializeControlBarController(events, "main");
|
||||
ReactDOM.render(<ControlBar2 events={events} />, $(".container-control-bar")[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "settings init",
|
||||
|
@ -88,8 +76,9 @@ async function initialize_app() {
|
|||
});
|
||||
*/
|
||||
|
||||
if(!aplayer.initialize())
|
||||
if(!aplayer.initialize()) {
|
||||
console.warn(tr("Failed to initialize audio controller!"));
|
||||
}
|
||||
|
||||
aplayer.on_ready(() => {
|
||||
if(aplayer.set_master_volume)
|
||||
|
|
|
@ -20,13 +20,14 @@ import {spawnBanClient} from "tc-shared/ui/modal/ModalBanClient";
|
|||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
|
||||
import {tr, tra} from "tc-shared/i18n/localize";
|
||||
import {renderChannelTree} from "tc-shared/ui/tree/Controller";
|
||||
import {initializeChannelTreeUiEvents, renderChannelTree} from "tc-shared/ui/tree/Controller";
|
||||
import {ChannelTreePopoutController} from "tc-shared/ui/tree/popout/Controller";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import {ClientIcon} from "svg-sprites/client-icons";
|
||||
|
||||
import "./EntryTagsHandler";
|
||||
import {spawnChannelEditNew} from "tc-shared/ui/modal/channel-edit/Controller";
|
||||
import {ChannelTreeUIEvents} from "tc-shared/ui/tree/Definitions";
|
||||
|
||||
export interface ChannelTreeEvents {
|
||||
/* general tree notified */
|
||||
|
@ -96,7 +97,11 @@ export class ChannelTree {
|
|||
|
||||
readonly popoutController: ChannelTreePopoutController;
|
||||
|
||||
private readonly tagContainer: JQuery;
|
||||
/*
|
||||
* We're constantly keeping the UI controller used (IDK yet how fast event attachment/detachment is)
|
||||
* The main background is to speed up server tab switching.
|
||||
*/
|
||||
mainTreeUiEvents: Registry<ChannelTreeUIEvents>;
|
||||
|
||||
private selectedEntry: ChannelTreeEntry<any> | undefined;
|
||||
private showQueries: boolean;
|
||||
|
@ -112,8 +117,7 @@ export class ChannelTree {
|
|||
this.server = new ServerEntry(this, "undefined", undefined);
|
||||
this.popoutController = new ChannelTreePopoutController(this);
|
||||
|
||||
this.tagContainer = $.spawn("div").addClass("channel-tree-container");
|
||||
renderChannelTree(this, this.tagContainer[0], { popoutButton: true });
|
||||
this.mainTreeUiEvents = initializeChannelTreeUiEvents(this, { popoutButton: true });
|
||||
|
||||
this.events.on("notify_channel_list_received", () => {
|
||||
if(!this.selectedEntry) {
|
||||
|
@ -124,10 +128,6 @@ export class ChannelTree {
|
|||
this.reset();
|
||||
}
|
||||
|
||||
tag_tree() : HTMLDivElement {
|
||||
return this.tagContainer[0] as HTMLDivElement;
|
||||
}
|
||||
|
||||
channelsOrdered() : ChannelEntry[] {
|
||||
const result = [];
|
||||
|
||||
|
@ -195,7 +195,9 @@ export class ChannelTree {
|
|||
}
|
||||
|
||||
destroy() {
|
||||
ReactDOM.unmountComponentAtNode(this.tagContainer[0]);
|
||||
this.mainTreeUiEvents?.fire("notify_destroy");
|
||||
this.mainTreeUiEvents?.destroy();
|
||||
this.mainTreeUiEvents = undefined;
|
||||
|
||||
if(this.server) {
|
||||
this.server.destroy();
|
||||
|
@ -207,7 +209,6 @@ export class ChannelTree {
|
|||
this.channelLast = undefined;
|
||||
|
||||
this.popoutController.destroy();
|
||||
this.tagContainer.remove();
|
||||
this.events.destroy();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {ControlBarEvents} from "tc-shared/ui/frames/control-bar/Definitions";
|
||||
import {initializeControlBarController} from "tc-shared/ui/frames/control-bar/Controller";
|
||||
import {TeaAppMainView} from "tc-shared/ui/AppRenderer";
|
||||
import {ConnectionListUIEvents} from "tc-shared/ui/frames/connection-handler-list/Definitions";
|
||||
import {initializeConnectionListController} from "tc-shared/ui/frames/connection-handler-list/Controller";
|
||||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
import {server_connections} from "tc-shared/ConnectionManager";
|
||||
import {AppUiEvents} from "tc-shared/ui/AppDefinitions";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
|
||||
export class AppController {
|
||||
private uiEvents: Registry<AppUiEvents>;
|
||||
|
||||
private listener: (() => void)[];
|
||||
|
||||
private currentConnection: ConnectionHandler;
|
||||
private listenerConnection: (() => void)[];
|
||||
|
||||
private container: HTMLDivElement;
|
||||
private controlBarEvents: Registry<ControlBarEvents>;
|
||||
private connectionListEvents: Registry<ConnectionListUIEvents>;
|
||||
|
||||
constructor() {
|
||||
this.uiEvents = new Registry<AppUiEvents>();
|
||||
this.uiEvents.on("query_channel_tree", () => this.notifyChannelTree());
|
||||
|
||||
this.listener = [];
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.listener?.forEach(callback => callback());
|
||||
this.listener = [];
|
||||
|
||||
ReactDOM.unmountComponentAtNode(this.container);
|
||||
this.container.remove();
|
||||
this.container = undefined;
|
||||
|
||||
this.controlBarEvents?.fire("notify_destroy");
|
||||
this.controlBarEvents?.destroy();
|
||||
this.controlBarEvents = undefined;
|
||||
|
||||
this.connectionListEvents?.fire("notify_destroy");
|
||||
this.connectionListEvents?.destroy();
|
||||
this.connectionListEvents = undefined;
|
||||
|
||||
this.uiEvents?.destroy();
|
||||
this.uiEvents = undefined;
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.listener = [];
|
||||
|
||||
this.container = document.createElement("div");
|
||||
this.container.classList.add("app-container");
|
||||
document.body.append(this.container);
|
||||
|
||||
this.controlBarEvents = new Registry<ControlBarEvents>()
|
||||
initializeControlBarController(this.controlBarEvents, "main");
|
||||
|
||||
this.connectionListEvents = new Registry<ConnectionListUIEvents>();
|
||||
initializeConnectionListController(this.connectionListEvents);
|
||||
|
||||
this.listener.push(server_connections.events().on("notify_active_handler_changed", event => this.setConnectionHandler(event.newHandler)));
|
||||
this.setConnectionHandler(server_connections.active_connection());
|
||||
}
|
||||
|
||||
setConnectionHandler(connection: ConnectionHandler) {
|
||||
if(this.currentConnection === connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.listenerConnection?.forEach(callback => callback());
|
||||
this.listenerConnection = [];
|
||||
this.currentConnection = connection;
|
||||
|
||||
this.notifyChannelTree();
|
||||
}
|
||||
|
||||
renderApp() {
|
||||
ReactDOM.render(React.createElement(TeaAppMainView, {
|
||||
controlBar: this.controlBarEvents,
|
||||
connectionList: this.connectionListEvents,
|
||||
sidebar: server_connections.getSidebarController().uiEvents,
|
||||
sidebarHeader: server_connections.getSidebarController().getHeaderController().uiEvents,
|
||||
log: server_connections.serverLogController.events,
|
||||
events: this.uiEvents,
|
||||
hostBanner: server_connections.hostBannerController.uiEvents
|
||||
}), this.container);
|
||||
}
|
||||
|
||||
private notifyChannelTree() {
|
||||
this.uiEvents.fire_react("notify_channel_tree", {
|
||||
handlerId: this.currentConnection?.handlerId,
|
||||
events: this.currentConnection?.channelTree.mainTreeUiEvents
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let appViewController: AppController;
|
||||
loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "app view",
|
||||
function: async () => {
|
||||
appViewController = new AppController();
|
||||
appViewController.initialize();
|
||||
appViewController.renderApp();
|
||||
|
||||
(window as any).AppController = AppController;
|
||||
(window as any).appViewController = appViewController;
|
||||
},
|
||||
priority: 0
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
import {Registry} from "tc-shared/events";
|
||||
import {ChannelTreeUIEvents} from "tc-shared/ui/tree/Definitions";
|
||||
|
||||
export interface AppUiEvents {
|
||||
query_channel_tree: {},
|
||||
|
||||
notify_channel_tree: {
|
||||
events: Registry<ChannelTreeUIEvents> | undefined,
|
||||
handlerId: string
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
@import "../../css/static/properties";
|
||||
@import "../../css/static/mixin";
|
||||
|
||||
.app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
padding: 5px;
|
||||
|
||||
/* TODO: Move this into the control bar? */
|
||||
.controlBar {
|
||||
z-index: 200;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
border-radius: 5px;
|
||||
|
||||
height: 2em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.channelTreeAndSidebar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
margin-top: 5px;
|
||||
min-height: 27em;
|
||||
}
|
||||
|
||||
.channelTree {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
background: var(--channel-tree-background);
|
||||
min-width: 200px;
|
||||
min-height: 100px;
|
||||
|
||||
overflow: hidden;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.sideBar {
|
||||
min-width: 350px;
|
||||
}
|
||||
|
||||
.containerLog {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
import * as React from "react";
|
||||
import {ControlBar2} from "tc-shared/ui/frames/control-bar/Renderer";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {ControlBarEvents} from "tc-shared/ui/frames/control-bar/Definitions";
|
||||
import {ConnectionListUIEvents} from "tc-shared/ui/frames/connection-handler-list/Definitions";
|
||||
import {ConnectionHandlerList} from "tc-shared/ui/frames/connection-handler-list/Renderer";
|
||||
import {ErrorBoundary} from "tc-shared/ui/react-elements/ErrorBoundary";
|
||||
import {ContextDivider} from "tc-shared/ui/react-elements/ContextDivider";
|
||||
import {SideBarRenderer} from "tc-shared/ui/frames/SideBarRenderer";
|
||||
import {SideBarEvents} from "tc-shared/ui/frames/SideBarDefinitions";
|
||||
import {SideHeaderEvents} from "tc-shared/ui/frames/side/HeaderDefinitions";
|
||||
import {ServerLogFrame} from "tc-shared/ui/frames/log/Renderer";
|
||||
import {ServerEventLogUiEvents} from "tc-shared/ui/frames/log/Definitions";
|
||||
import {FooterRenderer} from "tc-shared/ui/frames/footer/Renderer";
|
||||
import {HostBanner} from "tc-shared/ui/frames/HostBannerRenderer";
|
||||
import {HostBannerUiEvents} from "tc-shared/ui/frames/HostBannerDefinitions";
|
||||
import {AppUiEvents} from "tc-shared/ui/AppDefinitions";
|
||||
import {useState} from "react";
|
||||
import {ChannelTreeRenderer} from "tc-shared/ui/tree/Renderer";
|
||||
import {ChannelTreeUIEvents} from "tc-shared/ui/tree/Definitions";
|
||||
|
||||
const cssStyle = require("./AppRenderer.scss");
|
||||
|
||||
/*
|
||||
<div class="app-container">
|
||||
<div class="app">
|
||||
<!-- navigation bar -->
|
||||
<div class="container-control-bar">
|
||||
<div id="control_bar" class="control_bar">
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-connection-handlers" id="connection-handler-list"></div>
|
||||
<div class="container-app-main">
|
||||
<div class="container-channel-video" id="channel-video"></div>
|
||||
<div class="container-channel-chat">
|
||||
<!-- Channel tree -->
|
||||
<div class="container-channel-tree">
|
||||
<div class="hostbanner" id="hostbanner"></div>
|
||||
<div class="channel-tree" id="channelTree"></div>
|
||||
</div>
|
||||
|
||||
<div class="container-seperator vertical" seperator-id="seperator-channel-chat"></div>
|
||||
<!-- Chat window -->
|
||||
<div class="container-chat" id="chat"></div>
|
||||
</div>
|
||||
<div class="container-seperator horizontal" seperator-id="seperator-main-log"></div>
|
||||
<div class="container-bottom">
|
||||
<div class="container-server-log" id="server-log"></div>
|
||||
<div class="container-footer" id="container-footer">
|
||||
</div>
|
||||
</div> <!-- Selection info -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
*/
|
||||
|
||||
const ChannelTree = React.memo((props: { events: Registry<AppUiEvents> }) => {
|
||||
const [ data, setData ] = useState<{ events: Registry<ChannelTreeUIEvents>, handlerId: string }>(() => {
|
||||
props.events.fire("query_channel_tree");
|
||||
return undefined;
|
||||
});
|
||||
|
||||
props.events.reactUse("notify_channel_tree", event => {
|
||||
setData({ events: event.events, handlerId: event.handlerId });
|
||||
}, undefined, []);
|
||||
|
||||
if(!data?.events) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <ChannelTreeRenderer handlerId={data.handlerId} events={data.events} />;
|
||||
});
|
||||
|
||||
export const TeaAppMainView = (props: {
|
||||
events: Registry<AppUiEvents>
|
||||
controlBar: Registry<ControlBarEvents>,
|
||||
connectionList: Registry<ConnectionListUIEvents>,
|
||||
sidebar: Registry<SideBarEvents>,
|
||||
sidebarHeader: Registry<SideHeaderEvents>,
|
||||
log: Registry<ServerEventLogUiEvents>,
|
||||
hostBanner: Registry<HostBannerUiEvents>
|
||||
}) => {
|
||||
return (
|
||||
<div className={cssStyle.app}>
|
||||
<ErrorBoundary>
|
||||
<ControlBar2 events={props.controlBar} className={cssStyle.controlBar} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<ConnectionHandlerList events={props.connectionList} />
|
||||
</ErrorBoundary>
|
||||
{/* TODO: The video! */}
|
||||
|
||||
<div className={cssStyle.channelTreeAndSidebar}>
|
||||
<div className={cssStyle.channelTree}>
|
||||
<ErrorBoundary>
|
||||
<HostBanner events={props.hostBanner} />
|
||||
<ChannelTree events={props.events} />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
<ContextDivider id={"channel-chat"} direction={"horizontal"} defaultValue={25} />
|
||||
<SideBarRenderer events={props.sidebar} eventsHeader={props.sidebarHeader} className={cssStyle.sideBar} />
|
||||
</div>
|
||||
<ContextDivider id={"main-log"} direction={"vertical"} defaultValue={75} />
|
||||
<ErrorBoundary>
|
||||
<div className={cssStyle.containerLog}>
|
||||
<ServerLogFrame events={props.log} />
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
<FooterRenderer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* ConnectionHandlerList */
|
|
@ -13,6 +13,8 @@ html:root {
|
|||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
flex-shrink: 0;
|
||||
|
||||
.withBackground {
|
||||
background-color: var(--hostbanner-background);
|
||||
border-top-left-radius: 5px;
|
||||
|
|
|
@ -2,9 +2,6 @@ import {ConnectionHandler} from "../../ConnectionHandler";
|
|||
import {PrivateConversationController} from "./side/PrivateConversationController";
|
||||
import {ClientInfoController} from "tc-shared/ui/frames/side/ClientInfoController";
|
||||
import {SideHeaderController} 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";
|
||||
import {LogCategory, logWarn} from "tc-shared/log";
|
||||
|
@ -12,7 +9,7 @@ import {ChannelBarController} from "tc-shared/ui/frames/side/ChannelBarControlle
|
|||
import {MusicBotController} from "tc-shared/ui/frames/side/MusicBotController";
|
||||
|
||||
export class SideBarController {
|
||||
private readonly uiEvents: Registry<SideBarEvents>;
|
||||
readonly uiEvents: Registry<SideBarEvents>;
|
||||
|
||||
private currentConnection: ConnectionHandler;
|
||||
private listenerConnection: (() => void)[];
|
||||
|
@ -77,16 +74,22 @@ export class SideBarController {
|
|||
}
|
||||
|
||||
renderInto(container: HTMLDivElement) {
|
||||
/*
|
||||
ReactDOM.render(React.createElement(SideBarRenderer, {
|
||||
events: this.uiEvents,
|
||||
eventsHeader: this.header["uiEvents"],
|
||||
}), container);
|
||||
*/
|
||||
}
|
||||
|
||||
getMusicController() : MusicBotController {
|
||||
return this.musicPanel;
|
||||
}
|
||||
|
||||
getHeaderController() : SideHeaderController {
|
||||
return this.header;
|
||||
}
|
||||
|
||||
private sendContent() {
|
||||
if(this.currentConnection) {
|
||||
this.uiEvents.fire("notify_content", { content: this.currentConnection.getSideBar().getSideBarContent() });
|
||||
|
|
|
@ -34,4 +34,6 @@
|
|||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
background: var(--side-background);
|
||||
}
|
|
@ -169,7 +169,8 @@ const SideBarHeader = (props: { type: SideBarType, eventsHeader: Registry<SideHe
|
|||
|
||||
export const SideBarRenderer = (props: {
|
||||
events: Registry<SideBarEvents>,
|
||||
eventsHeader: Registry<SideHeaderEvents>
|
||||
eventsHeader: Registry<SideHeaderEvents>,
|
||||
className?: string
|
||||
}) => {
|
||||
const [ content, setContent ] = useState<SideBarType>(() => {
|
||||
props.events.fire("query_content");
|
||||
|
@ -179,7 +180,7 @@ export const SideBarRenderer = (props: {
|
|||
|
||||
return (
|
||||
<EventContent.Provider value={props.events}>
|
||||
<div className={cssStyle.container}>
|
||||
<div className={cssStyle.container + " " + props.className}>
|
||||
<ErrorBoundary>
|
||||
<SideBarHeader eventsHeader={props.eventsHeader} type={content} />
|
||||
</ErrorBoundary>
|
||||
|
|
|
@ -1,23 +1,11 @@
|
|||
import {Registry} from "tc-shared/events";
|
||||
import {ConnectionListUIEvents, HandlerConnectionState} from "tc-shared/ui/frames/connection-handler-list/Definitions";
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import {ConnectionHandlerList} from "tc-shared/ui/frames/connection-handler-list/Renderer";
|
||||
import {server_connections} from "tc-shared/ConnectionManager";
|
||||
import {LogCategory, logWarn} from "tc-shared/log";
|
||||
import {ConnectionState} from "tc-shared/ConnectionHandler";
|
||||
import { tr } from "tc-shared/i18n/localize";
|
||||
|
||||
export function initializeConnectionUIList() {
|
||||
const container = document.getElementById("connection-handler-list");
|
||||
const events = new Registry<ConnectionListUIEvents>();
|
||||
//events.enableDebug("Handler-List");
|
||||
initializeController(events);
|
||||
|
||||
ReactDOM.render(React.createElement(ConnectionHandlerList, { events: events }), container);
|
||||
}
|
||||
|
||||
function initializeController(events: Registry<ConnectionListUIEvents>) {
|
||||
export function initializeConnectionListController(events: Registry<ConnectionListUIEvents>) {
|
||||
let registeredHandlerEvents: {[key: string]:(() => void)[]} = {};
|
||||
|
||||
events.on("notify_destroy", () => {
|
||||
|
|
|
@ -467,7 +467,7 @@ export const ControlBar2 = (props: { events: Registry<ControlBarEvents>, classNa
|
|||
return (
|
||||
<Events.Provider value={props.events}>
|
||||
<ModeContext.Provider value={mode}>
|
||||
<div className={cssStyle.controlBar + " " + cssStyle["mode-" + mode]}>
|
||||
<div className={cssStyle.controlBar + " " + cssStyle["mode-" + mode] + " " + props.className}>
|
||||
{items}
|
||||
</div>
|
||||
</ModeContext.Provider>
|
||||
|
|
|
@ -11,12 +11,29 @@
|
|||
@include user-select(none);
|
||||
width: 100%;
|
||||
|
||||
.version {
|
||||
margin-right: .5em;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
height: 1.5em;
|
||||
|
||||
background: var(--footer-background);
|
||||
color: var(--footer-text);
|
||||
|
||||
border-radius: 0 0 5px 5px;
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
padding-top: 2px;
|
||||
|
||||
-webkit-box-shadow: inset 0 2px 5px 0 rgba(0,0,0,0.125);
|
||||
-moz-box-shadow: inset 0 2px 5px 0 rgba(0,0,0,0.125);
|
||||
box-shadow: inset 0 2px 5px 0 rgba(0,0,0,0.125);
|
||||
|
||||
> * {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.source {
|
||||
display: inline-block;
|
||||
a[href], a[href]:visited {
|
||||
color: var(--footer-text)!important;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ const VersionsRenderer = () => (
|
|||
</React.Fragment>
|
||||
);
|
||||
|
||||
/* FIXME: Outsource this! */
|
||||
const RtcStatus = () => {
|
||||
const statusController = useMemo(() => new StatusController(new Registry<ConnectionStatusEvents>()), []);
|
||||
statusController.setConnectionHandler(server_connections.active_connection());
|
||||
|
|
|
@ -10,14 +10,21 @@
|
|||
}
|
||||
|
||||
.logContainer {
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
|
||||
background: var(--server-log-background);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
|
||||
min-height: 1em;
|
||||
width: 100%;
|
||||
|
||||
border-radius: 5px 5px 0 0;
|
||||
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
|
|
@ -21,7 +21,7 @@ const ChannelInfoUpdateProperties: (keyof ChannelProperties)[] = [
|
|||
|
||||
/* TODO: Remove the ping interval handler. It's currently still there since the clients are not emitting the event yet */
|
||||
export class SideHeaderController {
|
||||
private readonly uiEvents: Registry<SideHeaderEvents>;
|
||||
readonly uiEvents: Registry<SideHeaderEvents>;
|
||||
|
||||
private connection: ConnectionHandler;
|
||||
|
||||
|
|
|
@ -25,10 +25,15 @@ export interface ChannelTreeRendererOptions {
|
|||
popoutButton: boolean;
|
||||
}
|
||||
|
||||
export function renderChannelTree(channelTree: ChannelTree, target: HTMLElement, options: ChannelTreeRendererOptions) {
|
||||
export function initializeChannelTreeUiEvents(channelTree: ChannelTree, options: ChannelTreeRendererOptions) : Registry<ChannelTreeUIEvents> {
|
||||
const events = new Registry<ChannelTreeUIEvents>();
|
||||
events.enableDebug("channel-tree-view");
|
||||
initializeChannelTreeController(events, channelTree, options);
|
||||
return events;
|
||||
}
|
||||
|
||||
export function renderChannelTree(channelTree: ChannelTree, target: HTMLElement, options: ChannelTreeRendererOptions) {
|
||||
const events = initializeChannelTreeUiEvents(channelTree, options);
|
||||
|
||||
ReactDOM.render(<ChannelTreeRenderer handlerId={channelTree.client.handlerId} events={events} />, target);
|
||||
|
||||
|
@ -89,7 +94,6 @@ class ChannelTreeController {
|
|||
|
||||
/* the key here is the unique entry id! */
|
||||
private eventListeners: {[key: number]: (() => void)[]} = {};
|
||||
private channelTreeInitialized = false;
|
||||
|
||||
private readonly connectionStateListener;
|
||||
private readonly voiceConnectionStateListener;
|
||||
|
@ -135,7 +139,7 @@ class ChannelTreeController {
|
|||
|
||||
private handleConnectionStateChanged(event: ConnectionEvents["notify_connection_state_changed"]) {
|
||||
if(event.newState !== ConnectionState.CONNECTED) {
|
||||
this.channelTreeInitialized = false;
|
||||
this.channelTree.channelsInitialized = false;
|
||||
this.sendChannelTreeEntries();
|
||||
}
|
||||
this.sendServerStatus(this.channelTree.server);
|
||||
|
@ -146,7 +150,7 @@ class ChannelTreeController {
|
|||
return;
|
||||
}
|
||||
|
||||
if(!this.channelTreeInitialized) {
|
||||
if(!this.channelTree.channelsInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -154,7 +158,7 @@ class ChannelTreeController {
|
|||
}
|
||||
|
||||
private handleGroupsUpdated(event: GroupManagerEvents["notify_groups_updated"]) {
|
||||
if(!this.channelTreeInitialized) {
|
||||
if(!this.channelTree.channelsInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -176,7 +180,7 @@ class ChannelTreeController {
|
|||
}
|
||||
|
||||
private handleGroupsReceived() {
|
||||
if(!this.channelTreeInitialized) {
|
||||
if(!this.channelTree.channelsInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -186,13 +190,13 @@ class ChannelTreeController {
|
|||
|
||||
/* general channel tree event handlers */
|
||||
@EventHandler<ChannelTreeEvents>("notify_popout_state_changed")
|
||||
private handlePoputStateChanged() {
|
||||
private handlePopoutStateChanged() {
|
||||
this.sendPopoutState();
|
||||
}
|
||||
|
||||
@EventHandler<ChannelTreeEvents>("notify_channel_list_received")
|
||||
private handleChannelListReceived() {
|
||||
this.channelTreeInitialized = true;
|
||||
this.channelTree.channelsInitialized = true;
|
||||
this.channelTree.channels.forEach(channel => this.initializeChannelEvents(channel));
|
||||
this.channelTree.clients.forEach(channel => this.initializeClientEvents(channel));
|
||||
this.sendChannelTreeEntries();
|
||||
|
@ -201,14 +205,14 @@ class ChannelTreeController {
|
|||
|
||||
@EventHandler<ChannelTreeEvents>("notify_channel_created")
|
||||
private handleChannelCreated(event: ChannelTreeEvents["notify_channel_created"]) {
|
||||
if(!this.channelTreeInitialized) { return; }
|
||||
if(!this.channelTree.channelsInitialized) { return; }
|
||||
this.initializeChannelEvents(event.channel);
|
||||
this.sendChannelTreeEntries();
|
||||
}
|
||||
|
||||
@EventHandler<ChannelTreeEvents>("notify_channel_moved")
|
||||
private handleChannelMoved(event: ChannelTreeEvents["notify_channel_moved"]) {
|
||||
if(!this.channelTreeInitialized) { return; }
|
||||
if(!this.channelTree.channelsInitialized) { return; }
|
||||
this.sendChannelTreeEntries();
|
||||
|
||||
if(event.previousParent && !event.previousParent.child_channel_head) {
|
||||
|
@ -223,14 +227,14 @@ class ChannelTreeController {
|
|||
|
||||
@EventHandler<ChannelTreeEvents>("notify_channel_deleted")
|
||||
private handleChannelDeleted(event: ChannelTreeEvents["notify_channel_deleted"]) {
|
||||
if(!this.channelTreeInitialized) { return; }
|
||||
if(!this.channelTree.channelsInitialized) { return; }
|
||||
this.finalizeEvents(event.channel);
|
||||
this.sendChannelTreeEntries();
|
||||
}
|
||||
|
||||
@EventHandler<ChannelTreeEvents>("notify_client_enter_view")
|
||||
private handleClientEnter(event: ChannelTreeEvents["notify_client_enter_view"]) {
|
||||
if(!this.channelTreeInitialized) { return; }
|
||||
if(!this.channelTree.channelsInitialized) { return; }
|
||||
|
||||
this.initializeClientEvents(event.client);
|
||||
this.sendChannelInfo(event.targetChannel);
|
||||
|
@ -240,7 +244,7 @@ class ChannelTreeController {
|
|||
|
||||
@EventHandler<ChannelTreeEvents>("notify_client_leave_view")
|
||||
private handleClientLeave(event: ChannelTreeEvents["notify_client_leave_view"]) {
|
||||
if(!this.channelTreeInitialized) { return; }
|
||||
if(!this.channelTree.channelsInitialized) { return; }
|
||||
|
||||
this.finalizeEvents(event.client);
|
||||
this.sendChannelInfo(event.sourceChannel);
|
||||
|
@ -250,7 +254,7 @@ class ChannelTreeController {
|
|||
|
||||
@EventHandler<ChannelTreeEvents>("notify_client_moved")
|
||||
private handleClientMoved(event: ChannelTreeEvents["notify_client_moved"]) {
|
||||
if(!this.channelTreeInitialized) { return; }
|
||||
if(!this.channelTree.channelsInitialized) { return; }
|
||||
|
||||
this.sendChannelInfo(event.oldChannel);
|
||||
this.sendChannelStatusIcon(event.oldChannel);
|
||||
|
@ -264,7 +268,7 @@ class ChannelTreeController {
|
|||
|
||||
@EventHandler<ChannelTreeEvents>("notify_selected_entry_changed")
|
||||
private handleSelectedEntryChanged(_event: ChannelTreeEvents["notify_selected_entry_changed"]) {
|
||||
if(!this.channelTreeInitialized) { return; }
|
||||
if(!this.channelTree.channelsInitialized) { return; }
|
||||
|
||||
this.sendSelectedEntry();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue