Fixed the legacy modal icon and bring it back to the front

This commit is contained in:
WolverinDEV 2021-05-16 12:22:11 +02:00
parent b7b4c51904
commit 13feceb289
7 changed files with 159 additions and 33 deletions

View file

@ -39,6 +39,7 @@ import ipRegex from "ip-regex";
import * as htmltags from "./ui/htmltags"; import * as htmltags from "./ui/htmltags";
import {ServerSettings} from "tc-shared/ServerSettings"; import {ServerSettings} from "tc-shared/ServerSettings";
import {ignorePromise} from "tc-shared/proto"; import {ignorePromise} from "tc-shared/proto";
import {InvokerInfo} from "tc-shared/tree/ChannelDefinitions";
assertMainApplication(); assertMainApplication();
@ -68,6 +69,43 @@ export enum DisconnectReason {
UNKNOWN UNKNOWN
} }
export type ClientDisconnectInfo = {
reason: "handler-destroy" | "requested"
} | {
reason: "connection-closed" | "connection-ping-timeout"
} | {
reason: "connect-failure" | "connect-dns-fail" | "connect-identity-too-low"
} | {
reason: "connect-identity-unsupported",
unsupportedReason: "ts3-server"
} | {
/* Connect fail since client has been banned */
reason: "connect-banned",
message: string
} | {
/* Connection got closed because the client got kicked */
reason: "client-kicked",
message: string,
invoker: InvokerInfo
} | {
/* Connection got closed because the client got banned */
reason: "client-banned",
message: string,
length: number | 0,
invoker?: InvokerInfo,
} | {
reason: "server-shutdown",
message: string,
/* TODO: Do we have an invoker here? */
} | {
reason: "connect-host-message-disconnect",
message: string
} | {
reason: "generic-connection-error",
message: string,
allowReconnect: boolean
}
export enum ConnectionState { export enum ConnectionState {
UNCONNECTED, /* no connection is currenting running */ UNCONNECTED, /* no connection is currenting running */
CONNECTING, /* we try to establish a connection to the target server */ CONNECTING, /* we try to establish a connection to the target server */
@ -120,17 +158,6 @@ export interface LocalClientStatus {
queries_visible: boolean; queries_visible: boolean;
} }
export interface ConnectParametersOld {
nickname?: string;
channel?: {
target: string | number;
password?: string;
};
token?: string;
password?: {password: string, hashed: boolean};
auto_reconnect_attempt?: boolean;
}
export class ConnectionHandler { export class ConnectionHandler {
readonly handlerId: string; readonly handlerId: string;
@ -530,6 +557,15 @@ export class ConnectionHandler {
return tag; return tag;
} }
/**
* This method dispatches a connection disconnect.
* The method can be called out of every context and will properly terminate
* all resources related to the current connection.
*/
handleDisconnectNew(reason: ClientDisconnectInfo) {
/* TODO: */
}
private _certificate_modal: Modal; private _certificate_modal: Modal;
handleDisconnect(type: DisconnectReason, data: any = {}) { handleDisconnect(type: DisconnectReason, data: any = {}) {
this.connectAttemptId++; this.connectAttemptId++;

View file

@ -4,6 +4,7 @@ import {AbstractServerConnection} from "../connection/ConnectionBase";
import {DisconnectReason} from "../ConnectionHandler"; import {DisconnectReason} from "../ConnectionHandler";
import {ConnectParameters} from "tc-shared/ui/modal/connect/Controller"; import {ConnectParameters} from "tc-shared/ui/modal/connect/Controller";
import {getBackend} from "tc-shared/backend"; import {getBackend} from "tc-shared/backend";
import {ErrorCode} from "tc-shared/connection/ErrorCode";
export interface HandshakeIdentityHandler { export interface HandshakeIdentityHandler {
connection: AbstractServerConnection; connection: AbstractServerConnection;
@ -14,6 +15,18 @@ export interface HandshakeIdentityHandler {
fillClientInitData(data: any); fillClientInitData(data: any);
} }
export type ServerHandshakeError = {
reason: "identity-unsupported",
}
export type ServerHandshakeResult = {
status: "success",
/* TODO: May some other variables as well? */
} | {
status: "failed",
error: ServerHandshakeError
};
export class HandshakeHandler { export class HandshakeHandler {
private connection: AbstractServerConnection; private connection: AbstractServerConnection;
private handshakeImpl: HandshakeIdentityHandler; private handshakeImpl: HandshakeIdentityHandler;
@ -114,7 +127,7 @@ export class HandshakeHandler {
this.handshakeImpl.fillClientInitData(data); this.handshakeImpl.fillClientInitData(data);
this.connection.send_command("clientinit", data).catch(error => { this.connection.send_command("clientinit", data).catch(error => {
if(error instanceof CommandResult) { if(error instanceof CommandResult) {
if(error.id == 1028) { if(error.id == ErrorCode.SERVER_INVALID_PASSWORD) {
this.connection.client.handleDisconnect(DisconnectReason.SERVER_REQUIRES_PASSWORD); this.connection.client.handleDisconnect(DisconnectReason.SERVER_REQUIRES_PASSWORD);
} else if(error.id == 783 || error.id == 519) { } else if(error.id == 783 || error.id == 519) {
error.extra_message = isNaN(parseInt(error.extra_message)) ? "8" : error.extra_message; error.extra_message = isNaN(parseInt(error.extra_message)) ? "8" : error.extra_message;
@ -124,8 +137,9 @@ export class HandshakeHandler {
} else { } else {
this.connection.client.handleDisconnect(DisconnectReason.CLIENT_KICKED, error); this.connection.client.handleDisconnect(DisconnectReason.CLIENT_KICKED, error);
} }
} else } else {
this.connection.disconnect(); this.connection.disconnect();
}
}); });
} }
} }

View file

@ -71,8 +71,9 @@ export class ConnectionProfile {
spawnIdentityHandshakeHandler(connection: AbstractServerConnection): HandshakeIdentityHandler | undefined { spawnIdentityHandshakeHandler(connection: AbstractServerConnection): HandshakeIdentityHandler | undefined {
const identity = this.selectedIdentity(); const identity = this.selectedIdentity();
if (!identity) if (!identity) {
return undefined; return undefined;
}
return identity.spawn_identity_handshake_handler(connection); return identity.spawn_identity_handshake_handler(connection);
} }

View file

@ -808,6 +808,13 @@ export class Settings {
description: "Last used TeaSpeak Client version (TeaClient only)", description: "Last used TeaSpeak Client version (TeaClient only)",
} }
/* When using a higher number clients crash due to a bug in NodeJS */
static readonly KEY_IPC_EVENT_BUNDLE_MAX_SIZE: ValuedRegistryKey<number> = {
key: "ipc_event_bundle_max_size",
valueType: "number",
defaultValue: 0
}
static readonly FN_LOG_ENABLED: (category: string) => RegistryKey<boolean> = category => { static readonly FN_LOG_ENABLED: (category: string) => RegistryKey<boolean> = category => {
return { return {
key: "log." + category.toLowerCase() + ".enabled", key: "log." + category.toLowerCase() + ".enabled",

View file

@ -13,3 +13,12 @@ export type ChannelDescriptionResult = {
status: "error", status: "error",
message: string message: string
}; };
/**
* The invoker info the server sends with all notifies if required
*/
export type InvokerInfo = {
invokerName: string,
invokerUniqueId: string,
invokerId: number,
};

View file

@ -2,6 +2,7 @@ import * as loader from "tc-loader";
import {Stage} from "tc-loader"; import {Stage} from "tc-loader";
import $ from "jquery"; import $ from "jquery";
import {LogCategory, logError} from "tc-shared/log"; import {LogCategory, logError} from "tc-shared/log";
import ModalIcon from "../react-elements/modal/TeaCup.png";
export enum ElementType { export enum ElementType {
HEADER, HEADER,
@ -88,8 +89,6 @@ export class ModalProperties {
} }
template_properties?: any = {}; template_properties?: any = {};
trigger_tab: boolean = true;
full_size?: boolean = false;
} }
namespace modal { namespace modal {
@ -218,13 +217,14 @@ export class Modal {
modal_footer: footer, modal_footer: footer,
closeable: this.properties.closeable, closeable: this.properties.closeable,
full_size: this.properties.full_size full_size: false
}; };
if(this.properties.template_properties) if(this.properties.template_properties)
Object.assign(properties, this.properties.template_properties); Object.assign(properties, this.properties.template_properties);
const tag = template.renderTag(properties); const tag = template.renderTag(properties);
tag.find(".modal-header .container-icon img").attr("src", ModalIcon);
if(typeof(this.properties.width) !== "undefined" && typeof(this.properties.min_width) !== "undefined") if(typeof(this.properties.width) !== "undefined" && typeof(this.properties.min_width) !== "undefined")
tag.find(".modal-content") tag.find(".modal-content")
.css("min-width", this.properties.min_width) .css("min-width", this.properties.min_width)
@ -258,7 +258,7 @@ export class Modal {
_global_modal_last = this.htmlTag[0]; _global_modal_last = this.htmlTag[0];
this.shown = true; this.shown = true;
this.htmlTag.prependTo($("body")); this.htmlTag.appendTo($("body"));
_global_modal_count++; _global_modal_count++;
this.htmlTag.show(); this.htmlTag.show();

View file

@ -2,16 +2,43 @@ import {UiVariableConsumer, UiVariableMap, UiVariableProvider} from "tc-shared/u
import {guid} from "tc-shared/crypto/uid"; import {guid} from "tc-shared/crypto/uid";
import {LogCategory, logWarn} from "tc-shared/log"; import {LogCategory, logWarn} from "tc-shared/log";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import {Settings, settings} from "tc-shared/settings";
/*
* We need to globally bundle all IPC invoke events since
* calling setImmediate too often will cause a electron crash with
* "async hook stack has become corrupted (actual: 88, expected: 0)".
*
* WolverinDEV has never experience it by himself but @REDOSS had.
*/
let ipcInvokeCallbacks: (() => void)[];
function registerInvokeCallback(callback: () => void) {
if(Array.isArray(ipcInvokeCallbacks)) {
ipcInvokeCallbacks.push(callback);
} else {
ipcInvokeCallbacks = [ callback ];
setImmediate(() => {
const callbacks = ipcInvokeCallbacks;
ipcInvokeCallbacks = undefined;
for(const callback of callbacks) {
callback();
}
});
}
}
export class IpcUiVariableProvider<Variables extends UiVariableMap> extends UiVariableProvider<Variables> { export class IpcUiVariableProvider<Variables extends UiVariableMap> extends UiVariableProvider<Variables> {
readonly ipcChannelId: string; readonly ipcChannelId: string;
private broadcastChannel: BroadcastChannel; private readonly bundleMaxSize: number;
private broadcastChannel: BroadcastChannel;
private enqueuedMessages: any[]; private enqueuedMessages: any[];
constructor() { constructor() {
super(); super();
this.bundleMaxSize = settings.getValue(Settings.KEY_IPC_EVENT_BUNDLE_MAX_SIZE);
this.ipcChannelId = "teaspeak-ipc-vars-" + guid(); this.ipcChannelId = "teaspeak-ipc-vars-" + guid();
this.broadcastChannel = new BroadcastChannel(this.ipcChannelId); this.broadcastChannel = new BroadcastChannel(this.ipcChannelId);
this.broadcastChannel.onmessage = event => this.handleIpcMessage(event.data, event.source, event.origin); this.broadcastChannel.onmessage = event => this.handleIpcMessage(event.data, event.source, event.origin);
@ -96,19 +123,33 @@ export class IpcUiVariableProvider<Variables extends UiVariableMap> extends UiVa
* @private * @private
*/ */
private broadcastIpcMessage(message: any) { private broadcastIpcMessage(message: any) {
if(this.bundleMaxSize <= 0) {
this.broadcastChannel.postMessage(message);
return;
}
if(Array.isArray(this.enqueuedMessages)) { if(Array.isArray(this.enqueuedMessages)) {
this.enqueuedMessages.push(message); this.enqueuedMessages.push(message);
if(this.enqueuedMessages.length >= this.bundleMaxSize) {
this.sendEnqueuedMessages();
}
return; return;
} }
this.enqueuedMessages = [ message ]; this.enqueuedMessages = [ message ];
setImmediate(() => { registerInvokeCallback(() => this.sendEnqueuedMessages());
}
private sendEnqueuedMessages() {
if(!this.enqueuedMessages) {
return;
}
this.broadcastChannel.postMessage({ this.broadcastChannel.postMessage({
type: "bundled", type: "bundled",
messages: this.enqueuedMessages messages: this.enqueuedMessages
}); });
this.enqueuedMessages = undefined; this.enqueuedMessages = undefined;
})
} }
} }
@ -117,8 +158,11 @@ export type IpcVariableDescriptor<Variables extends UiVariableMap> = {
} }
let editTokenIndex = 0; let editTokenIndex = 0;
class IpcUiVariableConsumer<Variables extends UiVariableMap> extends UiVariableConsumer<Variables> { class IpcUiVariableConsumer<Variables extends UiVariableMap> extends UiVariableConsumer<Variables> {
readonly description: IpcVariableDescriptor<Variables>; readonly description: IpcVariableDescriptor<Variables>;
private readonly bundleMaxSize: number;
private broadcastChannel: BroadcastChannel; private broadcastChannel: BroadcastChannel;
private editListener: {[key: string]: { resolve: () => void, reject: (error) => void }}; private editListener: {[key: string]: { resolve: () => void, reject: (error) => void }};
@ -129,6 +173,7 @@ class IpcUiVariableConsumer<Variables extends UiVariableMap> extends UiVariableC
this.description = description; this.description = description;
this.editListener = {}; this.editListener = {};
this.bundleMaxSize = settings.getValue(Settings.KEY_IPC_EVENT_BUNDLE_MAX_SIZE);
this.broadcastChannel = new BroadcastChannel(this.description.ipcChannelId); this.broadcastChannel = new BroadcastChannel(this.description.ipcChannelId);
this.broadcastChannel.onmessage = event => this.handleIpcMessage(event.data, event.source); this.broadcastChannel.onmessage = event => this.handleIpcMessage(event.data, event.source);
} }
@ -207,19 +252,33 @@ class IpcUiVariableConsumer<Variables extends UiVariableMap> extends UiVariableC
* @private * @private
*/ */
private sendIpcMessage(message: any) { private sendIpcMessage(message: any) {
if(this.bundleMaxSize <= 0) {
this.broadcastChannel.postMessage(message);
return;
}
if(Array.isArray(this.enqueuedMessages)) { if(Array.isArray(this.enqueuedMessages)) {
this.enqueuedMessages.push(message); this.enqueuedMessages.push(message);
if(this.enqueuedMessages.length >= this.bundleMaxSize) {
this.sendEnqueuedMessages();
}
return; return;
} }
this.enqueuedMessages = [ message ]; this.enqueuedMessages = [ message ];
setImmediate(() => { registerInvokeCallback(() => this.sendEnqueuedMessages());
}
private sendEnqueuedMessages() {
if(!this.enqueuedMessages) {
return;
}
this.broadcastChannel.postMessage({ this.broadcastChannel.postMessage({
type: "bundled", type: "bundled",
messages: this.enqueuedMessages messages: this.enqueuedMessages
}); });
this.enqueuedMessages = undefined; this.enqueuedMessages = undefined;
})
} }
} }