Fixed the legacy modal icon and bring it back to the front
This commit is contained in:
parent
b7b4c51904
commit
13feceb289
7 changed files with 159 additions and 33 deletions
|
@ -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++;
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -12,4 +12,13 @@ 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,
|
||||||
};
|
};
|
|
@ -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();
|
||||||
|
|
|
@ -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());
|
||||||
this.broadcastChannel.postMessage({
|
}
|
||||||
type: "bundled",
|
|
||||||
messages: this.enqueuedMessages
|
private sendEnqueuedMessages() {
|
||||||
});
|
if(!this.enqueuedMessages) {
|
||||||
this.enqueuedMessages = undefined;
|
return;
|
||||||
})
|
}
|
||||||
|
|
||||||
|
this.broadcastChannel.postMessage({
|
||||||
|
type: "bundled",
|
||||||
|
messages: this.enqueuedMessages
|
||||||
|
});
|
||||||
|
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());
|
||||||
this.broadcastChannel.postMessage({
|
}
|
||||||
type: "bundled",
|
|
||||||
messages: this.enqueuedMessages
|
private sendEnqueuedMessages() {
|
||||||
});
|
if(!this.enqueuedMessages) {
|
||||||
this.enqueuedMessages = undefined;
|
return;
|
||||||
})
|
}
|
||||||
|
|
||||||
|
this.broadcastChannel.postMessage({
|
||||||
|
type: "bundled",
|
||||||
|
messages: this.enqueuedMessages
|
||||||
|
});
|
||||||
|
this.enqueuedMessages = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue