Added a basic notification system (Settings UI needs to be developed)
parent
ba7e618c19
commit
234e6d66b0
|
@ -1,4 +1,8 @@
|
|||
# Changelog:
|
||||
* **21.07.20**
|
||||
- Added the enchanted server log system
|
||||
- Recoded the server log with react
|
||||
|
||||
* **20.07.20**
|
||||
- Some general project cleanup
|
||||
- Heavily improved the IPC internal API
|
||||
|
|
|
@ -5,8 +5,6 @@ import {GroupManager} from "tc-shared/permission/GroupManager";
|
|||
import {ServerSettings, Settings, settings, StaticSettings} from "tc-shared/settings";
|
||||
import {Sound, SoundManager} from "tc-shared/sound/Sounds";
|
||||
import {LocalClientEntry} from "tc-shared/ui/client";
|
||||
import * as server_log from "tc-shared/ui/frames/server_log";
|
||||
import {ServerLog} from "tc-shared/ui/frames/server_log";
|
||||
import {ConnectionProfile, default_profile, find_profile} from "tc-shared/profiles/ConnectionProfile";
|
||||
import {ServerAddress} from "tc-shared/ui/server";
|
||||
import * as log from "tc-shared/log";
|
||||
|
@ -35,6 +33,8 @@ import {FileTransferState, TransferProvider} from "tc-shared/file/Transfer";
|
|||
import {traj} from "tc-shared/i18n/localize";
|
||||
import {md5} from "tc-shared/crypto/md5";
|
||||
import {guid} from "tc-shared/crypto/uid";
|
||||
import {ServerEventLog} from "tc-shared/ui/frames/log/ServerEventLog";
|
||||
import {EventType} from "tc-shared/ui/frames/log/Definitions";
|
||||
|
||||
export enum DisconnectReason {
|
||||
HANDLER_DESTROYED,
|
||||
|
@ -172,7 +172,7 @@ export class ConnectionHandler {
|
|||
};
|
||||
|
||||
invoke_resized_on_activate: boolean = false;
|
||||
log: ServerLog;
|
||||
log: ServerEventLog;
|
||||
|
||||
constructor() {
|
||||
this.handlerId = guid();
|
||||
|
@ -188,7 +188,7 @@ export class ConnectionHandler {
|
|||
this.fileManager = new FileManager(this);
|
||||
this.permissions = new PermissionManager(this);
|
||||
|
||||
this.log = new ServerLog(this);
|
||||
this.log = new ServerEventLog(this);
|
||||
this.side_bar = new Frame(this);
|
||||
this.sound = new SoundManager(this);
|
||||
this.hostbanner = new Hostbanner(this);
|
||||
|
@ -261,7 +261,7 @@ export class ConnectionHandler {
|
|||
}
|
||||
}
|
||||
log.info(LogCategory.CLIENT, tr("Start connection to %s:%d"), server_address.host, server_address.port);
|
||||
this.log.log(server_log.Type.CONNECTION_BEGIN, {
|
||||
this.log.log(EventType.CONNECTION_BEGIN, {
|
||||
address: {
|
||||
server_hostname: server_address.host,
|
||||
server_port: server_address.port
|
||||
|
@ -294,7 +294,7 @@ export class ConnectionHandler {
|
|||
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;
|
||||
this.log.log(server_log.Type.CONNECTION_HOSTNAME_RESOLVE, {});
|
||||
this.log.log(EventType.CONNECTION_HOSTNAME_RESOLVE, {});
|
||||
try {
|
||||
const resolved = await dns.resolve_address(server_address, { timeout: 5000 }) || {} as any;
|
||||
if(id != this._connect_initialize_id)
|
||||
|
@ -302,7 +302,7 @@ export class ConnectionHandler {
|
|||
|
||||
server_address.host = typeof(resolved.target_ip) === "string" ? resolved.target_ip : server_address.host;
|
||||
server_address.port = typeof(resolved.target_port) === "number" ? resolved.target_port : server_address.port;
|
||||
this.log.log(server_log.Type.CONNECTION_HOSTNAME_RESOLVED, {
|
||||
this.log.log(EventType.CONNECTION_HOSTNAME_RESOLVED, {
|
||||
address: {
|
||||
server_port: server_address.port,
|
||||
server_hostname: server_address.host
|
||||
|
@ -339,7 +339,7 @@ export class ConnectionHandler {
|
|||
log.warn(LogCategory.CLIENT, tr("Failed to successfully disconnect from server: {}"), error);
|
||||
}
|
||||
this.sound.play(Sound.CONNECTION_DISCONNECTED);
|
||||
this.log.log(server_log.Type.DISCONNECTED, {});
|
||||
this.log.log(EventType.DISCONNECTED, {});
|
||||
}
|
||||
|
||||
getClient() : LocalClientEntry { return this._local_client; }
|
||||
|
@ -362,7 +362,12 @@ export class ConnectionHandler {
|
|||
this.connection_state = event.new_state;
|
||||
|
||||
log.info(LogCategory.CLIENT, tr("Client connected"));
|
||||
this.log.log(server_log.Type.CONNECTION_CONNECTED, {
|
||||
this.log.log(EventType.CONNECTION_CONNECTED, {
|
||||
serverAddress: {
|
||||
server_port: this.channelTree.server.remote_address.port,
|
||||
server_hostname: this.channelTree.server.remote_address.host
|
||||
},
|
||||
serverName: this.channelTree.server.properties.virtualserver_name,
|
||||
own_client: this.getClient().log_data()
|
||||
});
|
||||
this.sound.play(Sound.CONNECTION_CONNECTED);
|
||||
|
@ -484,12 +489,12 @@ export class ConnectionHandler {
|
|||
case DisconnectReason.HANDLER_DESTROYED:
|
||||
if(data) {
|
||||
this.sound.play(Sound.CONNECTION_DISCONNECTED);
|
||||
this.log.log(server_log.Type.DISCONNECTED, {});
|
||||
this.log.log(EventType.DISCONNECTED, {});
|
||||
}
|
||||
break;
|
||||
case DisconnectReason.DNS_FAILED:
|
||||
log.error(LogCategory.CLIENT, tr("Failed to resolve hostname: %o"), data);
|
||||
this.log.log(server_log.Type.CONNECTION_HOSTNAME_RESOLVE_ERROR, {
|
||||
this.log.log(EventType.CONNECTION_HOSTNAME_RESOLVE_ERROR, {
|
||||
message: data as any
|
||||
});
|
||||
this.sound.play(Sound.CONNECTION_REFUSED);
|
||||
|
@ -497,7 +502,10 @@ export class ConnectionHandler {
|
|||
case DisconnectReason.CONNECT_FAILURE:
|
||||
if(this._reconnect_attempt) {
|
||||
auto_reconnect = true;
|
||||
this.log.log(server_log.Type.CONNECTION_FAILED, {});
|
||||
this.log.log(EventType.CONNECTION_FAILED, { serverAddress: {
|
||||
server_port: this.channelTree.server.remote_address.port,
|
||||
server_hostname: this.channelTree.server.remote_address.host
|
||||
} });
|
||||
break;
|
||||
}
|
||||
if(data)
|
||||
|
@ -572,7 +580,7 @@ export class ConnectionHandler {
|
|||
|
||||
break;
|
||||
case DisconnectReason.SERVER_CLOSED:
|
||||
this.log.log(server_log.Type.SERVER_CLOSED, {message: data.reasonmsg});
|
||||
this.log.log(EventType.SERVER_CLOSED, {message: data.reasonmsg});
|
||||
|
||||
createErrorModal(
|
||||
tr("Server closed"),
|
||||
|
@ -584,7 +592,7 @@ export class ConnectionHandler {
|
|||
auto_reconnect = true;
|
||||
break;
|
||||
case DisconnectReason.SERVER_REQUIRES_PASSWORD:
|
||||
this.log.log(server_log.Type.SERVER_REQUIRES_PASSWORD, {});
|
||||
this.log.log(EventType.SERVER_REQUIRES_PASSWORD, {});
|
||||
|
||||
createInputModal(tr("Server password"), tr("Enter server password:"), password => password.length != 0, password => {
|
||||
if(!(typeof password === "string")) return;
|
||||
|
@ -625,7 +633,7 @@ export class ConnectionHandler {
|
|||
this.sound.play(Sound.CONNECTION_BANNED);
|
||||
break;
|
||||
case DisconnectReason.CLIENT_BANNED:
|
||||
this.log.log(server_log.Type.SERVER_BANNED, {
|
||||
this.log.log(EventType.SERVER_BANNED, {
|
||||
invoker: {
|
||||
client_name: data["invokername"],
|
||||
client_id: parseInt(data["invokerid"]),
|
||||
|
@ -655,7 +663,7 @@ export class ConnectionHandler {
|
|||
log.info(LogCategory.NETWORKING, tr("Allowed to auto reconnect but cant reconnect because we dont have any information left..."));
|
||||
return;
|
||||
}
|
||||
this.log.log(server_log.Type.RECONNECT_SCHEDULED, {timeout: 5000});
|
||||
this.log.log(EventType.RECONNECT_SCHEDULED, {timeout: 5000});
|
||||
|
||||
log.info(LogCategory.NETWORKING, tr("Allowed to auto reconnect. Reconnecting in 5000ms"));
|
||||
const server_address = this.serverConnection.remote_address();
|
||||
|
@ -663,7 +671,7 @@ export class ConnectionHandler {
|
|||
|
||||
this._reconnect_timer = setTimeout(() => {
|
||||
this._reconnect_timer = undefined;
|
||||
this.log.log(server_log.Type.RECONNECT_EXECUTE, {});
|
||||
this.log.log(EventType.RECONNECT_EXECUTE, {});
|
||||
log.info(LogCategory.NETWORKING, tr("Reconnecting..."));
|
||||
|
||||
this.startConnection(server_address.host + ":" + server_address.port, profile, false, Object.assign(this.reconnect_properties(profile), {auto_reconnect_attempt: true}));
|
||||
|
@ -675,7 +683,7 @@ export class ConnectionHandler {
|
|||
|
||||
cancel_reconnect(log_event: boolean) {
|
||||
if(this._reconnect_timer) {
|
||||
if(log_event) this.log.log(server_log.Type.RECONNECT_CANCELED, {});
|
||||
if(log_event) this.log.log(EventType.RECONNECT_CANCELED, {});
|
||||
clearTimeout(this._reconnect_timer);
|
||||
this._reconnect_timer = undefined;
|
||||
}
|
||||
|
@ -734,7 +742,7 @@ export class ConnectionHandler {
|
|||
if(Object.keys(property_update).length > 0) {
|
||||
this.serverConnection.send_command("clientupdate", property_update).catch(error => {
|
||||
log.warn(LogCategory.GENERAL, tr("Failed to update client audio hardware properties. Error: %o"), error);
|
||||
this.log.log(server_log.Type.ERROR_CUSTOM, {message: tr("Failed to update audio hardware properties.")});
|
||||
this.log.log(EventType.ERROR_CUSTOM, {message: tr("Failed to update audio hardware properties.")});
|
||||
|
||||
/* Update these properties anyways (for case the server fails to handle the command) */
|
||||
const updates = [];
|
||||
|
@ -816,7 +824,7 @@ export class ConnectionHandler {
|
|||
client_output_hardware: this.client_status.sound_playback_supported
|
||||
}).catch(error => {
|
||||
log.warn(LogCategory.GENERAL, tr("Failed to sync handler state with server. Error: %o"), error);
|
||||
this.log.log(server_log.Type.ERROR_CUSTOM, {message: tr("Failed to sync handler state with server.")});
|
||||
this.log.log(EventType.ERROR_CUSTOM, {message: tr("Failed to sync handler state with server.")});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1039,7 +1047,7 @@ export class ConnectionHandler {
|
|||
client_away_message: typeof(this.client_status.away) === "string" ? this.client_status.away : "",
|
||||
}).catch(error => {
|
||||
log.warn(LogCategory.GENERAL, tr("Failed to update away status. Error: %o"), error);
|
||||
this.log.log(server_log.Type.ERROR_CUSTOM, {message: tr("Failed to update away status.")});
|
||||
this.log.log(EventType.ERROR_CUSTOM, {message: tr("Failed to update away status.")});
|
||||
});
|
||||
|
||||
this.event_registry.fire("notify_state_updated", {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import * as server_log from "tc-shared/ui/frames/server_log";
|
||||
import {AbstractServerConnection, CommandOptions, ServerCommand} from "tc-shared/connection/ConnectionBase";
|
||||
import {Sound} from "tc-shared/sound/Sounds";
|
||||
import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
|
@ -23,6 +22,7 @@ import {batch_updates, BatchUpdateType, flush_batched_updates} from "tc-shared/u
|
|||
import {OutOfViewClient} from "tc-shared/ui/frames/side/PrivateConversationManager";
|
||||
import {renderBBCodeAsJQuery} from "tc-shared/text/bbcode";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {EventClient, EventType} from "tc-shared/ui/frames/log/Definitions";
|
||||
|
||||
export class ServerConnectionCommandBoss extends AbstractCommandHandlerBoss {
|
||||
constructor(connection: AbstractServerConnection) {
|
||||
|
@ -85,7 +85,7 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
this["notifyplaylistsongloaded"] = this.handleNotifyPlaylistSongLoaded;
|
||||
}
|
||||
|
||||
private loggable_invoker(unique_id, client_id, name) : server_log.base.Client | undefined {
|
||||
private loggable_invoker(unique_id, client_id, name) : EventClient | undefined {
|
||||
const id = parseInt(client_id);
|
||||
if(typeof(client_id) === "undefined" || Number.isNaN(id))
|
||||
return undefined;
|
||||
|
@ -116,18 +116,18 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
if(res.id == ErrorID.PERMISSION_ERROR) { //Permission error
|
||||
const permission = this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number);
|
||||
res.message = tr("Insufficient client permissions. Failed on permission ") + (permission ? permission.name : "unknown");
|
||||
this.connection_handler.log.log(server_log.Type.ERROR_PERMISSION, {
|
||||
this.connection_handler.log.log(EventType.ERROR_PERMISSION, {
|
||||
permission: this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number)
|
||||
});
|
||||
this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS);
|
||||
} else if(res.id != ErrorID.EMPTY_RESULT) {
|
||||
this.connection_handler.log.log(server_log.Type.ERROR_CUSTOM, {
|
||||
this.connection_handler.log.log(EventType.ERROR_CUSTOM, {
|
||||
message: res.extra_message.length == 0 ? res.message : res.extra_message
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if(typeof(ex) === "string") {
|
||||
this.connection_handler.log.log(server_log.Type.CONNECTION_COMMAND_ERROR, {error: ex});
|
||||
this.connection_handler.log.log(EventType.CONNECTION_COMMAND_ERROR, {error: ex});
|
||||
} else {
|
||||
log.error(LogCategory.NETWORKING, tr("Invalid promise result type: %s. Result: %o"), typeof (ex), ex);
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
if(properties.virtualserver_hostmessage_mode == 1) {
|
||||
/* show in log */
|
||||
if(properties.virtualserver_hostmessage)
|
||||
this.connection_handler.log.log(server_log.Type.SERVER_HOST_MESSAGE, {
|
||||
this.connection_handler.log.log(EventType.SERVER_HOST_MESSAGE, {
|
||||
message: properties.virtualserver_hostmessage
|
||||
});
|
||||
} else {
|
||||
|
@ -227,7 +227,7 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
if(properties.virtualserver_hostmessage_mode == 3) {
|
||||
/* first let the client initialize his stuff */
|
||||
setTimeout(() => {
|
||||
this.connection_handler.log.log(server_log.Type.SERVER_HOST_MESSAGE_DISCONNECT, {
|
||||
this.connection_handler.log.log(EventType.SERVER_HOST_MESSAGE_DISCONNECT, {
|
||||
message: properties.virtualserver_welcomemessage
|
||||
});
|
||||
|
||||
|
@ -241,7 +241,7 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
|
||||
/* welcome message */
|
||||
if(properties.virtualserver_welcomemessage) {
|
||||
this.connection_handler.log.log(server_log.Type.SERVER_WELCOME_MESSAGE, {
|
||||
this.connection_handler.log.log(EventType.SERVER_WELCOME_MESSAGE, {
|
||||
message: properties.virtualserver_welcomemessage
|
||||
});
|
||||
}
|
||||
|
@ -452,14 +452,13 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
|
||||
if(this.connection_handler.areQueriesShown() || client.properties.client_type != ClientType.CLIENT_QUERY) {
|
||||
const own_channel = this.connection.client.getClient().currentChannel();
|
||||
this.connection_handler.log.log(server_log.Type.CLIENT_VIEW_ENTER, {
|
||||
this.connection_handler.log.log(channel == own_channel ? EventType.CLIENT_VIEW_ENTER_OWN_CHANNEL : EventType.CLIENT_VIEW_ENTER, {
|
||||
channel_from: old_channel ? old_channel.log_data() : undefined,
|
||||
channel_to: channel ? channel.log_data() : undefined,
|
||||
client: client.log_data(),
|
||||
invoker: this.loggable_invoker(invokeruid, invokerid, invokername),
|
||||
message:reason_msg,
|
||||
reason: parseInt(reason_id),
|
||||
own_channel: channel == own_channel
|
||||
});
|
||||
|
||||
if(reason_id == ViewReasonId.VREASON_USER_ACTION) {
|
||||
|
@ -543,7 +542,7 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
let channel_to = tree.findChannel(targetChannelId);
|
||||
|
||||
const is_own_channel = channel_from == own_channel;
|
||||
this.connection_handler.log.log(server_log.Type.CLIENT_VIEW_LEAVE, {
|
||||
this.connection_handler.log.log(is_own_channel ? EventType.CLIENT_VIEW_LEAVE_OWN_CHANNEL : EventType.CLIENT_VIEW_LEAVE, {
|
||||
channel_from: channel_from ? channel_from.log_data() : undefined,
|
||||
channel_to: channel_to ? channel_to.log_data() : undefined,
|
||||
client: client.log_data(),
|
||||
|
@ -551,7 +550,6 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
message: entry["reasonmsg"],
|
||||
reason: parseInt(entry["reasonid"]),
|
||||
ban_time: parseInt(entry["bantime"]),
|
||||
own_channel: is_own_channel
|
||||
});
|
||||
|
||||
if(is_own_channel) {
|
||||
|
@ -629,7 +627,8 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
}
|
||||
|
||||
const own_channel = this.connection.client.getClient().currentChannel();
|
||||
this.connection_handler.log.log(server_log.Type.CLIENT_VIEW_MOVE, {
|
||||
const event = self ? EventType.CLIENT_VIEW_MOVE_OWN : (channel_from == own_channel || channel_to == own_channel ? EventType.CLIENT_VIEW_MOVE_OWN_CHANNEL : EventType.CLIENT_VIEW_MOVE);
|
||||
this.connection_handler.log.log(event, {
|
||||
channel_from: channel_from ? {
|
||||
channel_id: channel_from.channelId,
|
||||
channel_name: channel_from.channelName()
|
||||
|
@ -770,8 +769,24 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
});
|
||||
if(targetIsOwn) {
|
||||
this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5});
|
||||
this.connection_handler.log.log(EventType.PRIVATE_MESSAGE_RECEIVED, {
|
||||
message: json["msg"],
|
||||
sender: {
|
||||
client_unique_id: json["invokeruid"],
|
||||
client_name: json["invokername"],
|
||||
client_id: parseInt(json["invokerid"])
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5});
|
||||
this.connection_handler.log.log(EventType.PRIVATE_MESSAGE_SEND, {
|
||||
message: json["msg"],
|
||||
target: {
|
||||
client_unique_id: json["invokeruid"],
|
||||
client_name: json["invokername"],
|
||||
client_id: parseInt(json["invokerid"])
|
||||
}
|
||||
});
|
||||
}
|
||||
this.connection_handler.side_bar.info_frame().update_chat_counter();
|
||||
} else if(mode == 2) {
|
||||
|
@ -798,7 +813,11 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
message: json["msg"]
|
||||
}, invoker instanceof LocalClientEntry);
|
||||
} else if(mode == 3) {
|
||||
this.connection_handler.log.log(server_log.Type.GLOBAL_MESSAGE, {
|
||||
const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"]));
|
||||
const conversations = this.connection_handler.side_bar.channel_conversations();
|
||||
|
||||
this.connection_handler.log.log(EventType.GLOBAL_MESSAGE, {
|
||||
isOwnMessage: invoker instanceof LocalClientEntry,
|
||||
message: json["msg"],
|
||||
sender: {
|
||||
client_unique_id: json["invokeruid"],
|
||||
|
@ -807,9 +826,6 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
}
|
||||
});
|
||||
|
||||
const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"]));
|
||||
const conversations = this.connection_handler.side_bar.channel_conversations();
|
||||
|
||||
if(!(invoker instanceof LocalClientEntry))
|
||||
this.connection_handler.channelTree.server.setUnread(true);
|
||||
|
||||
|
@ -921,6 +937,10 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
unique_id: json["invokeruid"]
|
||||
}, json["msg"]);
|
||||
|
||||
this.connection_handler.log.log(EventType.CLIENT_POKE_RECEIVED, {
|
||||
sender: this.loggable_invoker(json["invokeruid"], json["invokerid"], json["invokername"]),
|
||||
message: json["msg"]
|
||||
});
|
||||
this.connection_handler.sound.play(Sound.USER_POKED_SELF);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,17 @@ loader.register_task(Stage.LOADED, {
|
|||
priority: 10,
|
||||
function: async () => {
|
||||
console.error("Spawning video popup");
|
||||
spawnVideoPopout();
|
||||
//spawnVideoPopout();
|
||||
|
||||
Notification.requestPermission().then(permission => {
|
||||
if(permission === "denied")
|
||||
return;
|
||||
|
||||
const notification = new Notification("Hello World", {
|
||||
body: "This is a simple test notification - " + Math.random(),
|
||||
renotify: true,
|
||||
tag: "xx"
|
||||
});
|
||||
})
|
||||
}
|
||||
});
|
|
@ -4,7 +4,15 @@ import {Registry} from "tc-shared/events";
|
|||
export interface ClientGlobalControlEvents {
|
||||
/* open a basic window */
|
||||
action_open_window: {
|
||||
window: "bookmark-manage" | "query-manage" | "query-create" | "ban-list" | "permissions" | "token-list" | "token-use" | "settings",
|
||||
window:
|
||||
"bookmark-manage" |
|
||||
"query-manage" |
|
||||
"query-create" |
|
||||
"ban-list" |
|
||||
"permissions" |
|
||||
"token-list" |
|
||||
"token-use" |
|
||||
"settings",
|
||||
connection?: ConnectionHandler
|
||||
},
|
||||
|
||||
|
|
|
@ -536,6 +536,13 @@ export class Settings extends StaticSettings {
|
|||
}
|
||||
};
|
||||
|
||||
static readonly FN_EVENTS_NOTIFICATION_ENABLED: (event: string) => SettingsKey<boolean> = event => {
|
||||
return {
|
||||
key: "notification_" + event + "_enabled",
|
||||
valueType: "boolean"
|
||||
}
|
||||
};
|
||||
|
||||
static readonly KEYS = (() => {
|
||||
const result = [];
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {XBBCodeRenderer} from "vendor/xbbcode/react";
|
||||
import * as React from "react";
|
||||
import {rendererHTML, rendererReact} from "tc-shared/text/bbcode/renderer";
|
||||
import {rendererHTML, rendererReact, rendererText} from "tc-shared/text/bbcode/renderer";
|
||||
import {parse as parseBBCode} from "vendor/xbbcode/parser";
|
||||
import {fixupJQueryUrlTags} from "tc-shared/text/bbcode/url";
|
||||
import {fixupJQueryImageTags} from "tc-shared/text/bbcode/image";
|
||||
|
@ -101,3 +101,7 @@ export function renderBBCodeAsJQuery(message: string, settings: BBCodeRenderOpti
|
|||
|
||||
return [container.contents() as JQuery];
|
||||
}
|
||||
|
||||
export function renderBBCodeAsText(message: string) {
|
||||
return parseBBCode(message, { tag_whitelist: allowedBBCodes }).map(e => rendererText.render(e)).join("");
|
||||
}
|
|
@ -11,7 +11,6 @@ import {createErrorModal, createInfoModal, createInputModal} from "tc-shared/ui/
|
|||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import * as htmltags from "./htmltags";
|
||||
import {hashPassword} from "tc-shared/utils/helpers";
|
||||
import * as server_log from "tc-shared/ui/frames/server_log";
|
||||
import {openChannelInfo} from "tc-shared/ui/modal/ModalChannelInfo";
|
||||
import {createChannelModal} from "tc-shared/ui/modal/ModalCreateChannel";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
|
@ -22,6 +21,7 @@ import {ChannelTreeEntry, ChannelTreeEntryEvents} from "tc-shared/ui/TreeEntry";
|
|||
import {ChannelEntryView as ChannelEntryView} from "./tree/Channel";
|
||||
import {spawnFileTransferModal} from "tc-shared/ui/modal/transfer/ModalFileTransfer";
|
||||
import {ViewReasonId} from "tc-shared/ConnectionHandler";
|
||||
import {EventChannelData} from "tc-shared/ui/frames/log/Definitions";
|
||||
|
||||
export enum ChannelType {
|
||||
PERMANENT,
|
||||
|
@ -409,7 +409,8 @@ export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
|
|||
type: contextmenu.MenuEntryType.ENTRY,
|
||||
icon_class: "client-channel_switch",
|
||||
name: bold(tr("Switch to channel")),
|
||||
callback: () => this.joinChannel()
|
||||
callback: () => this.joinChannel(),
|
||||
visible: this !== this.channelTree.client.getClient()?.currentChannel()
|
||||
},{
|
||||
type: contextmenu.MenuEntryType.ENTRY,
|
||||
icon_class: "client-filetransfer",
|
||||
|
@ -766,7 +767,7 @@ export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
|
|||
this.channelTree.client.settings.changeServer(Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE(this.channelId), mode);
|
||||
}
|
||||
|
||||
log_data() : server_log.base.Channel {
|
||||
log_data() : EventChannelData {
|
||||
return {
|
||||
channel_name: this.channelName(),
|
||||
channel_id: this.channelId
|
||||
|
|
|
@ -9,7 +9,6 @@ import {Group, GroupManager, GroupTarget, GroupType} from "tc-shared/permission/
|
|||
import PermissionType from "tc-shared/permission/PermissionType";
|
||||
import {createErrorModal, createInputModal} from "tc-shared/ui/elements/Modal";
|
||||
import * as htmltags from "tc-shared/ui/htmltags";
|
||||
import * as server_log from "tc-shared/ui/frames/server_log";
|
||||
import {CommandResult, PlaylistSong} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {ChannelEntry} from "tc-shared/ui/channel";
|
||||
import {ConnectionHandler, ViewReasonId} from "tc-shared/ConnectionHandler";
|
||||
|
@ -27,6 +26,7 @@ import * as React from "react";
|
|||
import {ChannelTreeEntry, ChannelTreeEntryEvents} from "tc-shared/ui/TreeEntry";
|
||||
import {spawnClientVolumeChange, spawnMusicBotVolumeChange} from "tc-shared/ui/modal/ModalChangeVolumeNew";
|
||||
import {spawnPermissionEditorModal} from "tc-shared/ui/modal/permission/ModalPermissionEditor";
|
||||
import {EventClient, EventType} from "tc-shared/ui/frames/log/Definitions";
|
||||
|
||||
export enum ClientType {
|
||||
CLIENT_VOICE,
|
||||
|
@ -535,13 +535,15 @@ export class ClientEntry extends ChannelTreeEntry<ClientEvents> {
|
|||
callback: () => {
|
||||
createInputModal(tr("Poke client"), tr("Poke message:<br>"), text => true, result => {
|
||||
if(typeof(result) === "string") {
|
||||
//TODO tr
|
||||
console.log("Poking client " + this.clientNickName() + " with message " + result);
|
||||
this.channelTree.client.serverConnection.send_command("clientpoke", {
|
||||
clid: this.clientId(),
|
||||
msg: result
|
||||
}).then(() => {
|
||||
this.channelTree.client.log.log(EventType.CLIENT_POKE_SEND, {
|
||||
target: this.log_data(),
|
||||
message: result
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
}, { width: 400, maxLength: 512 }).open();
|
||||
}
|
||||
|
@ -742,8 +744,7 @@ export class ClientEntry extends ChannelTreeEntry<ClientEvents> {
|
|||
if(variable.key == "client_nickname") {
|
||||
if(variable.value !== old_value && typeof(old_value) === "string") {
|
||||
if(!(this instanceof LocalClientEntry)) { /* own changes will be logged somewhere else */
|
||||
this.channelTree.client.log.log(server_log.Type.CLIENT_NICKNAME_CHANGED, {
|
||||
own_client: false,
|
||||
this.channelTree.client.log.log(EventType.CLIENT_NICKNAME_CHANGED, {
|
||||
client: this.log_data(),
|
||||
new_name: variable.value,
|
||||
old_name: old_value
|
||||
|
@ -873,7 +874,7 @@ export class ClientEntry extends ChannelTreeEntry<ClientEvents> {
|
|||
}
|
||||
}
|
||||
|
||||
log_data() : server_log.base.Client {
|
||||
log_data() : EventClient {
|
||||
return {
|
||||
client_unique_id: this.properties.client_unique_identifier,
|
||||
client_name: this.clientNickName(),
|
||||
|
@ -980,16 +981,15 @@ export class LocalClientEntry extends ClientEntry {
|
|||
this.updateVariables({ key: "client_nickname", value: new_name }); /* change it locally */
|
||||
return this.handle.serverConnection.command_helper.updateClient("client_nickname", new_name).then((e) => {
|
||||
settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, new_name);
|
||||
this.channelTree.client.log.log(server_log.Type.CLIENT_NICKNAME_CHANGED, {
|
||||
this.channelTree.client.log.log(EventType.CLIENT_NICKNAME_CHANGED_OWN, {
|
||||
client: this.log_data(),
|
||||
old_name: old_name,
|
||||
new_name: new_name,
|
||||
own_client: true
|
||||
});
|
||||
return true;
|
||||
}).catch((e: CommandResult) => {
|
||||
this.updateVariables({ key: "client_nickname", value: old_name }); /* change it back */
|
||||
this.channelTree.client.log.log(server_log.Type.CLIENT_NICKNAME_CHANGE_FAILED, {
|
||||
this.channelTree.client.log.log(EventType.CLIENT_NICKNAME_CHANGE_FAILED, {
|
||||
reason: e.extra_message
|
||||
});
|
||||
return false;
|
||||
|
|
|
@ -95,8 +95,13 @@ export function formatMessage(pattern: string, ...objects: any[]) : JQuery[] {
|
|||
}
|
||||
|
||||
export function formatMessageString(pattern: string, ...args: string[]) : string {
|
||||
return parseMessageWithArguments(pattern, args.length).map(e => typeof e === "string" ? e : args[e]).join("");
|
||||
}
|
||||
|
||||
export function parseMessageWithArguments(pattern: string, argumentCount: number) : (string | number)[] {
|
||||
let begin = 0, found = 0;
|
||||
|
||||
let unspecifiedIndex = 0;
|
||||
let result: string[] = [];
|
||||
do {
|
||||
found = pattern.indexOf('{', found);
|
||||
|
@ -111,27 +116,42 @@ export function formatMessageString(pattern: string, ...args: string[]) : string
|
|||
continue;
|
||||
}
|
||||
|
||||
result.push(pattern.substr(begin, found - begin)); //Append the text
|
||||
result.push(pattern.substring(begin, found)); //Append the text
|
||||
|
||||
let offset = 0;
|
||||
let number;
|
||||
while ("0123456789".includes(pattern[found + 1 + offset])) offset++;
|
||||
number = parseInt(offset > 0 ? pattern.substr(found + 1, offset) : "0");
|
||||
while ("0123456789".includes(pattern[found + 1 + offset]))
|
||||
offset++;
|
||||
|
||||
if(offset > 0) {
|
||||
number = parseInt(pattern.substr(found + 1, offset));
|
||||
} else {
|
||||
number = unspecifiedIndex++;
|
||||
}
|
||||
|
||||
if(pattern[found + offset + 1] != '}') {
|
||||
found++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(args.length < number)
|
||||
if(argumentCount < number) {
|
||||
log.warn(LogCategory.GENERAL, tr("Message to format contains invalid index (%o)"), number);
|
||||
|
||||
result.push(args[number]);
|
||||
result.push("{" + offset.toString() + "}");
|
||||
} else {
|
||||
result.push(number);
|
||||
}
|
||||
|
||||
found = found + 1 + offset;
|
||||
begin = found + 1;
|
||||
} while(found++);
|
||||
|
||||
return result.join("");
|
||||
return result.reduce((prev, element) => {
|
||||
if(typeof element === "string" && typeof prev.last() === "string")
|
||||
prev.push(prev.pop() + element);
|
||||
else
|
||||
prev.push(element);
|
||||
return prev;
|
||||
}, []);
|
||||
}
|
||||
|
||||
export namespace network {
|
||||
|
|
|
@ -112,7 +112,7 @@ export class ConnectionManager {
|
|||
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.html_tag());
|
||||
this._container_log_server.append(handler.log.getHTMLTag());
|
||||
|
||||
if(handler.invoke_resized_on_activate)
|
||||
handler.resize_elements();
|
||||
|
|
|
@ -0,0 +1,321 @@
|
|||
import {PermissionInfo} from "tc-shared/permission/PermissionManager";
|
||||
import {ViewReasonId} from "tc-shared/ConnectionHandler";
|
||||
import * as React from "react";
|
||||
import {ServerEventLog} from "tc-shared/ui/frames/log/ServerEventLog";
|
||||
|
||||
export enum EventType {
|
||||
CONNECTION_BEGIN = "connection.begin",
|
||||
CONNECTION_HOSTNAME_RESOLVE = "connection.hostname.resolve",
|
||||
CONNECTION_HOSTNAME_RESOLVE_ERROR = "connection.hostname.resolve.error",
|
||||
CONNECTION_HOSTNAME_RESOLVED = "connection.hostname.resolved",
|
||||
CONNECTION_LOGIN = "connection.login",
|
||||
CONNECTION_CONNECTED = "connection.connected",
|
||||
CONNECTION_FAILED = "connection.failed",
|
||||
|
||||
DISCONNECTED = "disconnected",
|
||||
|
||||
CONNECTION_VOICE_SETUP_FAILED = "connection.voice.setup.failed",
|
||||
CONNECTION_COMMAND_ERROR = "connection.command.error",
|
||||
|
||||
GLOBAL_MESSAGE = "global.message",
|
||||
|
||||
SERVER_WELCOME_MESSAGE = "server.welcome.message",
|
||||
SERVER_HOST_MESSAGE = "server.host.message",
|
||||
SERVER_HOST_MESSAGE_DISCONNECT = "server.host.message.disconnect",
|
||||
|
||||
SERVER_CLOSED = "server.closed",
|
||||
SERVER_BANNED = "server.banned",
|
||||
SERVER_REQUIRES_PASSWORD = "server.requires.password",
|
||||
|
||||
CLIENT_VIEW_ENTER = "client.view.enter",
|
||||
CLIENT_VIEW_LEAVE = "client.view.leave",
|
||||
CLIENT_VIEW_MOVE = "client.view.move",
|
||||
|
||||
CLIENT_VIEW_ENTER_OWN_CHANNEL = "client.view.enter.own.channel",
|
||||
CLIENT_VIEW_LEAVE_OWN_CHANNEL = "client.view.leave.own.channel",
|
||||
CLIENT_VIEW_MOVE_OWN_CHANNEL = "client.view.move.own.channel",
|
||||
|
||||
CLIENT_VIEW_MOVE_OWN = "client.view.move.own",
|
||||
|
||||
CLIENT_NICKNAME_CHANGED = "client.nickname.changed",
|
||||
CLIENT_NICKNAME_CHANGED_OWN = "client.nickname.changed.own",
|
||||
CLIENT_NICKNAME_CHANGE_FAILED = "client.nickname.change.failed",
|
||||
|
||||
CLIENT_SERVER_GROUP_ADD = "client.server.group.add",
|
||||
CLIENT_SERVER_GROUP_REMOVE = "client.server.group.remove",
|
||||
CLIENT_CHANNEL_GROUP_CHANGE = "client.channel.group.change",
|
||||
|
||||
PRIVATE_MESSAGE_RECEIVED = "private.message.received",
|
||||
PRIVATE_MESSAGE_SEND = "private.message.send",
|
||||
|
||||
CHANNEL_CREATE = "channel.create",
|
||||
CHANNEL_DELETE = "channel.delete",
|
||||
|
||||
CHANNEL_CREATE_OWN = "channel.create.own",
|
||||
CHANNEL_DELETE_OWN = "channel.delete.own",
|
||||
|
||||
ERROR_CUSTOM = "error.custom",
|
||||
ERROR_PERMISSION = "error.permission",
|
||||
|
||||
CLIENT_POKE_RECEIVED = "client.poke.received",
|
||||
CLIENT_POKE_SEND = "client.poke.send",
|
||||
|
||||
RECONNECT_SCHEDULED = "reconnect.scheduled",
|
||||
RECONNECT_EXECUTE = "reconnect.execute",
|
||||
RECONNECT_CANCELED = "reconnect.canceled"
|
||||
}
|
||||
|
||||
export type EventClient = {
|
||||
client_unique_id: string;
|
||||
client_name: string;
|
||||
client_id: number;
|
||||
}
|
||||
export type EventChannelData = {
|
||||
channel_id: number;
|
||||
channel_name: string;
|
||||
}
|
||||
export type EventServerData = {
|
||||
server_name: string;
|
||||
server_unique_id: string;
|
||||
}
|
||||
export type EventServerAddress = {
|
||||
server_hostname: string;
|
||||
server_port: number;
|
||||
}
|
||||
|
||||
export namespace event {
|
||||
export type EventGlobalMessage = {
|
||||
isOwnMessage: boolean;
|
||||
sender: EventClient;
|
||||
message: string;
|
||||
}
|
||||
export type EventConnectBegin = {
|
||||
address: EventServerAddress;
|
||||
client_nickname: string;
|
||||
}
|
||||
export type EventErrorCustom = {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type EventReconnectScheduled = {
|
||||
timeout: number;
|
||||
}
|
||||
|
||||
export type EventReconnectCanceled = { }
|
||||
export type EventReconnectExecute = { }
|
||||
|
||||
export type EventErrorPermission = {
|
||||
permission: PermissionInfo;
|
||||
}
|
||||
|
||||
export type EventWelcomeMessage = {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type EventHostMessageDisconnect = {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type EventClientMove = {
|
||||
channel_from?: EventChannelData;
|
||||
channel_from_own: boolean;
|
||||
|
||||
channel_to?: EventChannelData;
|
||||
channel_to_own: boolean;
|
||||
|
||||
client: EventClient;
|
||||
client_own: boolean;
|
||||
|
||||
invoker?: EventClient;
|
||||
|
||||
message?: string;
|
||||
reason: ViewReasonId;
|
||||
}
|
||||
|
||||
export type EventClientEnter = {
|
||||
channel_from?: EventChannelData;
|
||||
channel_to?: EventChannelData;
|
||||
|
||||
client: EventClient;
|
||||
invoker?: EventClient;
|
||||
|
||||
message?: string;
|
||||
|
||||
reason: ViewReasonId;
|
||||
ban_time?: number;
|
||||
}
|
||||
|
||||
export type EventClientLeave = {
|
||||
channel_from?: EventChannelData;
|
||||
channel_to?: EventChannelData;
|
||||
|
||||
client: EventClient;
|
||||
invoker?: EventClient;
|
||||
|
||||
message?: string;
|
||||
|
||||
reason: ViewReasonId;
|
||||
ban_time?: number;
|
||||
}
|
||||
|
||||
export type EventChannelCreate = {
|
||||
creator: EventClient;
|
||||
channel: EventChannelData;
|
||||
}
|
||||
|
||||
export type EventChannelDelete = {
|
||||
deleter: EventClient;
|
||||
channel: EventChannelData;
|
||||
}
|
||||
|
||||
export type EventConnectionConnected = {
|
||||
serverName: string,
|
||||
serverAddress: EventServerAddress,
|
||||
own_client: EventClient;
|
||||
}
|
||||
export type EventConnectionFailed = {
|
||||
serverAddress: EventServerAddress
|
||||
}
|
||||
export type EventConnectionLogin = {}
|
||||
export type EventConnectionHostnameResolve = {};
|
||||
export type EventConnectionHostnameResolved = {
|
||||
address: EventServerAddress;
|
||||
}
|
||||
export type EventConnectionHostnameResolveError = {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type EventConnectionVoiceSetupFailed = {
|
||||
reason: string;
|
||||
reconnect_delay: number; /* if less or equal to 0 reconnect is prohibited */
|
||||
}
|
||||
|
||||
export type EventConnectionCommandError = {
|
||||
error: any;
|
||||
}
|
||||
|
||||
export type EventClientNicknameChanged = {
|
||||
client: EventClient;
|
||||
|
||||
old_name: string;
|
||||
new_name: string;
|
||||
}
|
||||
|
||||
export type EventClientNicknameChangeFailed = {
|
||||
reason: string;
|
||||
}
|
||||
|
||||
export type EventServerClosed = {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type EventServerRequiresPassword = {}
|
||||
|
||||
export type EventServerBanned = {
|
||||
message: string;
|
||||
time: number;
|
||||
|
||||
invoker: EventClient;
|
||||
}
|
||||
|
||||
export type EventClientPokeReceived = {
|
||||
sender: EventClient,
|
||||
message: string
|
||||
}
|
||||
|
||||
export type EventClientPokeSend = {
|
||||
target: EventClient,
|
||||
message: string
|
||||
}
|
||||
|
||||
export type EventPrivateMessageSend = {
|
||||
target: EventClient,
|
||||
message: string
|
||||
}
|
||||
|
||||
export type EventPrivateMessageReceived = {
|
||||
sender: EventClient,
|
||||
message: string
|
||||
}
|
||||
}
|
||||
|
||||
export type LogMessage = {
|
||||
type: EventType;
|
||||
uniqueId: string;
|
||||
timestamp: number;
|
||||
data: any;
|
||||
}
|
||||
|
||||
export interface TypeInfo {
|
||||
"connection.begin" : event.EventConnectBegin;
|
||||
"global.message": event.EventGlobalMessage;
|
||||
|
||||
"error.custom": event.EventErrorCustom;
|
||||
"error.permission": event.EventErrorPermission;
|
||||
|
||||
"connection.hostname.resolved": event.EventConnectionHostnameResolved;
|
||||
"connection.hostname.resolve": event.EventConnectionHostnameResolve;
|
||||
"connection.hostname.resolve.error": event.EventConnectionHostnameResolveError;
|
||||
"connection.failed": event.EventConnectionFailed;
|
||||
"connection.login": event.EventConnectionLogin;
|
||||
"connection.connected": event.EventConnectionConnected;
|
||||
"connection.voice.setup.failed": event.EventConnectionVoiceSetupFailed;
|
||||
"connection.command.error": event.EventConnectionCommandError;
|
||||
|
||||
"reconnect.scheduled": event.EventReconnectScheduled;
|
||||
"reconnect.canceled": event.EventReconnectCanceled;
|
||||
"reconnect.execute": event.EventReconnectExecute;
|
||||
|
||||
"server.welcome.message": event.EventWelcomeMessage;
|
||||
"server.host.message": event.EventWelcomeMessage;
|
||||
"server.host.message.disconnect": event.EventHostMessageDisconnect;
|
||||
|
||||
"server.closed": event.EventServerClosed;
|
||||
"server.requires.password": event.EventServerRequiresPassword;
|
||||
"server.banned": event.EventServerBanned;
|
||||
|
||||
"client.view.enter": event.EventClientEnter;
|
||||
"client.view.move": event.EventClientMove;
|
||||
"client.view.leave": event.EventClientLeave;
|
||||
|
||||
"client.view.enter.own.channel": event.EventClientEnter;
|
||||
"client.view.move.own.channel": event.EventClientMove;
|
||||
"client.view.leave.own.channel": event.EventClientLeave;
|
||||
|
||||
"client.view.move.own": event.EventClientMove;
|
||||
|
||||
"client.nickname.change.failed": event.EventClientNicknameChangeFailed,
|
||||
"client.nickname.changed": event.EventClientNicknameChanged,
|
||||
"client.nickname.changed.own": event.EventClientNicknameChanged
|
||||
|
||||
"channel.create": event.EventChannelCreate;
|
||||
"channel.delete": event.EventChannelDelete;
|
||||
|
||||
"channel.create.own": event.EventChannelCreate;
|
||||
"channel.delete.own": event.EventChannelDelete;
|
||||
|
||||
"client.poke.received": event.EventClientPokeReceived,
|
||||
"client.poke.send": event.EventClientPokeSend,
|
||||
|
||||
|
||||
"private.message.received": event.EventPrivateMessageReceived,
|
||||
"private.message.send": event.EventPrivateMessageSend,
|
||||
|
||||
"disconnected": any;
|
||||
}
|
||||
|
||||
export interface EventDispatcher<EventType extends keyof TypeInfo> {
|
||||
log(data: TypeInfo[EventType], logger: ServerEventLog) : React.ReactNode;
|
||||
notify(data: TypeInfo[EventType], logger: ServerEventLog);
|
||||
sound(data: TypeInfo[EventType], logger: ServerEventLog);
|
||||
}
|
||||
|
||||
export interface ServerLogUIEvents {
|
||||
"query_log": {},
|
||||
"notify_log": {
|
||||
log: LogMessage[]
|
||||
},
|
||||
"notify_log_add": {
|
||||
event: LogMessage
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
.clientEntry, .channelEntry {
|
||||
color: var(--server-log-tree-entry);
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
|
@ -0,0 +1,607 @@
|
|||
import {ViewReasonId} from "tc-shared/ConnectionHandler";
|
||||
import {EventChannelData, EventClient, EventType, TypeInfo} from "tc-shared/ui/frames/log/Definitions";
|
||||
import * as React from "react";
|
||||
import {Translatable, VariadicTranslatable} from "tc-shared/ui/react-elements/i18n";
|
||||
import {formatDate} from "tc-shared/MessageFormatter";
|
||||
import {BBCodeRenderer} from "tc-shared/text/bbcode";
|
||||
import {format_time} from "tc-shared/ui/frames/chat";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
|
||||
const cssStyle = require("./DispatcherLog.scss");
|
||||
const cssStyleRenderer = require("./Renderer.scss");
|
||||
|
||||
export type DispatcherLog<T extends keyof TypeInfo> = (data: TypeInfo[T], handlerId: string, eventType: T) => React.ReactNode;
|
||||
|
||||
const dispatchers: {[key: string]: DispatcherLog<any>} = { };
|
||||
function registerDispatcher<T extends keyof TypeInfo>(key: T, builder: DispatcherLog<T>) {
|
||||
dispatchers[key] = builder;
|
||||
}
|
||||
|
||||
export function findLogDispatcher<T extends keyof TypeInfo>(type: T) : DispatcherLog<T> {
|
||||
return dispatchers[type];
|
||||
}
|
||||
|
||||
export function getRegisteredLogDispatchers() : TypeInfo[] {
|
||||
return Object.keys(dispatchers) as any;
|
||||
}
|
||||
|
||||
/* TODO: Enable context menu */
|
||||
const ClientRenderer = (props: { client: EventClient, handlerId: string, braces?: boolean }) => (
|
||||
<div className={cssStyle.clientEntry}>
|
||||
{props.client.client_name}
|
||||
</div>
|
||||
);
|
||||
|
||||
/* TODO: Enable context menu */
|
||||
const ChannelRenderer = (props: { channel: EventChannelData, handlerId: string, braces?: boolean }) => (
|
||||
<div className={cssStyle.channelEntry}>
|
||||
{props.channel.channel_name}
|
||||
</div>
|
||||
);
|
||||
|
||||
registerDispatcher(EventType.ERROR_CUSTOM, data => <div className={cssStyleRenderer.errorMessage}>{data.message}</div>);
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_BEGIN, data => (
|
||||
<VariadicTranslatable text={"Connecting to {0}{1}"}>
|
||||
<>{data.address.server_hostname}</>
|
||||
<>{data.address.server_port == 9987 ? "" : (":" + data.address.server_port)}</>
|
||||
</VariadicTranslatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_HOSTNAME_RESOLVE, () => (
|
||||
<Translatable>Resolving hostname</Translatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_HOSTNAME_RESOLVED, data => (
|
||||
<VariadicTranslatable text={"Hostname resolved successfully to {0}:{1}"}>
|
||||
<>{data.address.server_hostname}</>
|
||||
<>{data.address.server_port}</>
|
||||
</VariadicTranslatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_HOSTNAME_RESOLVE_ERROR, data => (
|
||||
<VariadicTranslatable text={"Failed to resolve hostname. Connecting to given hostname. Error: {0}"}>
|
||||
<>{data.message}</>
|
||||
</VariadicTranslatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_LOGIN, () => (
|
||||
<Translatable>Logging in...</Translatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_FAILED, () => (
|
||||
<Translatable>Connect failed.</Translatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_CONNECTED, (data,handlerId) => (
|
||||
<VariadicTranslatable text={"Connected as {0}"}>
|
||||
<ClientRenderer client={data.own_client} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_VOICE_SETUP_FAILED, (data) => (
|
||||
<VariadicTranslatable text={"Failed to setup voice bridge: {0}. Allow reconnect: {1}"}>
|
||||
<>{data.reason}</>
|
||||
{data.reconnect_delay > 0 ? <Translatable>Yes</Translatable> : <Translatable>No</Translatable>}
|
||||
</VariadicTranslatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.ERROR_PERMISSION, data => (
|
||||
<div className={cssStyleRenderer.errorMessage}>
|
||||
<VariadicTranslatable text={"Insufficient client permissions. Failed on permission {0}"}>
|
||||
<>{data.permission ? data.permission.name : <Translatable>unknown</Translatable>}</>
|
||||
</VariadicTranslatable>
|
||||
</div>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.CLIENT_VIEW_ENTER, (data, handlerId) => {
|
||||
switch (data.reason) {
|
||||
case ViewReasonId.VREASON_USER_ACTION:
|
||||
if(data.channel_from) {
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} appeared from {1} to {2}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} appeared to channel {1}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
}
|
||||
|
||||
case ViewReasonId.VREASON_MOVED:
|
||||
if(data.channel_from) {
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} appeared from {1} to {2}, moved by {3}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} appeared to {1}, moved by {2}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
}
|
||||
|
||||
case ViewReasonId.VREASON_CHANNEL_KICK:
|
||||
if(data.channel_from) {
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} appeared from {1} to {2}, kicked by {3}{4}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
<>{data.message ? " (" + data.message + ")" : ""}</>
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} appeared to {1}, kicked by {2}{3}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
<>{data.message ? " (" + data.message + ")" : ""}</>
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
}
|
||||
|
||||
case ViewReasonId.VREASON_SYSTEM:
|
||||
return undefined;
|
||||
|
||||
default:
|
||||
return (
|
||||
<div className={cssStyleRenderer.errorMessage}>
|
||||
<VariadicTranslatable text={"Having user enter event with invalid reason: {0}"}>
|
||||
<>{data.reason}</>
|
||||
</VariadicTranslatable>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.CLIENT_VIEW_ENTER_OWN_CHANNEL, (data, handlerId) => {
|
||||
switch (data.reason) {
|
||||
case ViewReasonId.VREASON_USER_ACTION:
|
||||
if(data.channel_from) {
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} appeared from {1} to your channel {2}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} appeared to your channel {1}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
}
|
||||
|
||||
case ViewReasonId.VREASON_MOVED:
|
||||
if(data.channel_from) {
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} appeared from {1} to your channel {2}, moved by {3}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} appeared to your channel {1}, moved by {2}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
}
|
||||
|
||||
case ViewReasonId.VREASON_CHANNEL_KICK:
|
||||
if(data.channel_from) {
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} appeared from {1} to your channel {2}, kicked by {3}{4}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
<>{data.message ? " (" + data.message + ")" : ""}</>
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} appeared to your channel {1}, kicked by {2}{3}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
<>{data.message ? " (" + data.message + ")" : ""}</>
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
}
|
||||
|
||||
case ViewReasonId.VREASON_SYSTEM:
|
||||
return undefined;
|
||||
|
||||
default:
|
||||
return (
|
||||
<div className={cssStyleRenderer.errorMessage}>
|
||||
<VariadicTranslatable text={"Having user enter your channel event with invalid reason: {0}"}>
|
||||
<>{data.reason}</>
|
||||
</VariadicTranslatable>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.CLIENT_VIEW_MOVE, (data, handlerId) => {
|
||||
switch (data.reason) {
|
||||
case ViewReasonId.VREASON_MOVED:
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} was moved from channel {1} to {2} by {3}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
|
||||
case ViewReasonId.VREASON_USER_ACTION:
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} switched from channel {1} to {2}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
|
||||
case ViewReasonId.VREASON_CHANNEL_KICK:
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} got kicked from channel {1} to {2} by {3}{4}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
<>{data.message ? " (" + data.message + ")" : ""}</>
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<div className={cssStyleRenderer.errorMessage}>
|
||||
<VariadicTranslatable text={"Having user move event with invalid reason: {0}"}>
|
||||
<>{data.reason}</>
|
||||
</VariadicTranslatable>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.CLIENT_VIEW_MOVE_OWN_CHANNEL, findLogDispatcher(EventType.CLIENT_VIEW_MOVE));
|
||||
|
||||
registerDispatcher(EventType.CLIENT_VIEW_MOVE_OWN, (data, handlerId) => {
|
||||
switch (data.reason) {
|
||||
case ViewReasonId.VREASON_MOVED:
|
||||
return (
|
||||
<VariadicTranslatable text={"You have been moved by {3} from channel {1} to {2}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
|
||||
case ViewReasonId.VREASON_USER_ACTION:
|
||||
return (
|
||||
<VariadicTranslatable text={"You switched from {1} to {2}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
|
||||
case ViewReasonId.VREASON_CHANNEL_KICK:
|
||||
return (
|
||||
<VariadicTranslatable text={"You got kicked out of the channel {1} to channel {2} by {3}{4}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
<>{data.message ? " (" + data.message + ")" : ""}</>
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<div className={cssStyleRenderer.errorMessage}>
|
||||
<VariadicTranslatable text={"Having own move event with invalid reason: {0}"}>
|
||||
<>{data.reason}</>
|
||||
</VariadicTranslatable>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.CLIENT_VIEW_LEAVE, (data, handlerId) => {
|
||||
switch (data.reason) {
|
||||
case ViewReasonId.VREASON_USER_ACTION:
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} disappeared from {1} to {2}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
|
||||
case ViewReasonId.VREASON_SERVER_LEFT:
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} left the server{1}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<>{data.message ? " (" + data.message + ")" : ""}</>
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
|
||||
case ViewReasonId.VREASON_SERVER_KICK:
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} was kicked from the server by {1}.{2}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
<>{data.message ? " (" + data.message + ")" : ""}</>
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
|
||||
case ViewReasonId.VREASON_CHANNEL_KICK:
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} was kicked from channel {1} by {2}.{3}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
<>{data.message ? " (" + data.message + ")" : ""}</>
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
|
||||
case ViewReasonId.VREASON_BAN:
|
||||
let duration = <Translatable>permanently</Translatable>;
|
||||
if(data.ban_time)
|
||||
duration = <VariadicTranslatable text={"for"}><>{" " + formatDate(data.ban_time)}</></VariadicTranslatable>;
|
||||
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} was banned {1} by {2}.{3}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
{duration}
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
<>{data.message ? " (" + data.message + ")" : ""}</>
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
|
||||
case ViewReasonId.VREASON_TIMEOUT:
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} timed out{1}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<>{data.message ? " (" + data.message + ")" : ""}</>
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
|
||||
case ViewReasonId.VREASON_MOVED:
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} disappeared from {1} to {2}, moved by {3}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<div className={cssStyleRenderer.errorMessage}>
|
||||
<VariadicTranslatable text={"Having client leave event with invalid reason: {0}"}>
|
||||
<>{data.reason}</>
|
||||
</VariadicTranslatable>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.CLIENT_VIEW_LEAVE_OWN_CHANNEL, (data, handlerId) => {
|
||||
switch (data.reason) {
|
||||
case ViewReasonId.VREASON_USER_ACTION:
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} disappeared from your channel {1} to {2}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
|
||||
case ViewReasonId.VREASON_MOVED:
|
||||
return (
|
||||
<VariadicTranslatable text={"{0} disappeared from your channel {1} to {2}, moved by {3}"}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_from} handlerId={handlerId} />
|
||||
<ChannelRenderer channel={data.channel_to} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
|
||||
default:
|
||||
return findLogDispatcher(EventType.CLIENT_VIEW_LEAVE)(data, handlerId, EventType.CLIENT_VIEW_LEAVE);
|
||||
}
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.SERVER_WELCOME_MESSAGE,data => (
|
||||
<BBCodeRenderer message={"[color=green]" + data.message + "[/color]"} settings={{convertSingleUrls: false}} />
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.SERVER_HOST_MESSAGE,data => (
|
||||
<BBCodeRenderer message={"[color=green]" + data.message + "[/color]"} settings={{convertSingleUrls: false}} />
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.SERVER_HOST_MESSAGE_DISCONNECT,data => (
|
||||
<BBCodeRenderer message={"[color=red]" + data.message + "[/color]"} settings={{convertSingleUrls: false}} />
|
||||
));
|
||||
|
||||
|
||||
registerDispatcher(EventType.CLIENT_NICKNAME_CHANGED,(data, handlerId) => (
|
||||
<VariadicTranslatable text={"{0} changed his nickname from \"{1}\" to \"{2}\""}>
|
||||
<ClientRenderer client={data.client} handlerId={handlerId} />
|
||||
<>{data.old_name}</>
|
||||
<>{data.new_name}</>
|
||||
</VariadicTranslatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.CLIENT_NICKNAME_CHANGED_OWN,() => (
|
||||
<Translatable>Nickname successfully changed.</Translatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.CLIENT_NICKNAME_CHANGE_FAILED,(data) => (
|
||||
<VariadicTranslatable text={"Failed to change nickname: {0}"}>
|
||||
<>{data.reason}</>
|
||||
</VariadicTranslatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.GLOBAL_MESSAGE, () => undefined);
|
||||
|
||||
|
||||
registerDispatcher(EventType.DISCONNECTED,() => (
|
||||
<Translatable>Disconnected from server</Translatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.RECONNECT_SCHEDULED,data => (
|
||||
<VariadicTranslatable text={"Reconnecting in {0}."}>
|
||||
<>{format_time(data.timeout, tr("now"))}</>
|
||||
</VariadicTranslatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.RECONNECT_CANCELED,() => (
|
||||
<Translatable>Reconnect canceled.</Translatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.RECONNECT_CANCELED,() => (
|
||||
<Translatable>Reconnecting...</Translatable>
|
||||
));
|
||||
|
||||
|
||||
registerDispatcher(EventType.SERVER_BANNED,(data, handlerId) => {
|
||||
const time = data.time === 0 ? <Translatable>ever</Translatable> : <>{format_time(data.time * 1000, tr("one second"))}</>;
|
||||
const reason = data.message ? <> <Translatable>Reason:</Translatable> {data.message}</> : undefined;
|
||||
|
||||
if(data.invoker.client_id > 0)
|
||||
return (
|
||||
<div className={cssStyleRenderer.errorMessage}>
|
||||
<VariadicTranslatable text={"You've been banned from the server by {0} for {1}.{2}"}>
|
||||
<ClientRenderer client={data.invoker} handlerId={handlerId} />
|
||||
{time}
|
||||
{reason}
|
||||
</VariadicTranslatable>
|
||||
</div>
|
||||
);
|
||||
else
|
||||
|
||||
return (
|
||||
<div className={cssStyleRenderer.errorMessage}>
|
||||
<VariadicTranslatable text={"You've been banned from the server for {0}.{1}"}>
|
||||
{time}
|
||||
{reason}
|
||||
</VariadicTranslatable>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.SERVER_REQUIRES_PASSWORD,() => (
|
||||
<Translatable>Server requires a password to connect.</Translatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.SERVER_CLOSED,data => {
|
||||
if(data.message)
|
||||
return (
|
||||
<VariadicTranslatable text={"Server has been closed ({})."}>
|
||||
<>{data.message}</>
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
return <Translatable>Server has been closed.</Translatable>;
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_COMMAND_ERROR,data => {
|
||||
let message;
|
||||
if(typeof data.error === "string")
|
||||
message = data.error;
|
||||
else if(data.error instanceof CommandResult)
|
||||
message = data.error.formattedMessage();
|
||||
else
|
||||
message = data.error + "";
|
||||
|
||||
return (
|
||||
<div className={cssStyleRenderer.errorMessage}>
|
||||
<VariadicTranslatable text={"Command execution resulted in: {}"}>
|
||||
<>{message}</>
|
||||
</VariadicTranslatable>
|
||||
</div>
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
registerDispatcher(EventType.CHANNEL_CREATE,(data, handlerId) => (
|
||||
<VariadicTranslatable text={"Channel {} has been created by {}."}>
|
||||
<ChannelRenderer channel={data.channel} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.creator} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.CHANNEL_CREATE_OWN,(data, handlerId) => (
|
||||
<VariadicTranslatable text={"Channel {} has been created."}>
|
||||
<ChannelRenderer channel={data.channel} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.CHANNEL_DELETE,(data, handlerId) => (
|
||||
<VariadicTranslatable text={"Channel {} has been deleted by {}."}>
|
||||
<ChannelRenderer channel={data.channel} handlerId={handlerId} />
|
||||
<ClientRenderer client={data.deleter} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.CHANNEL_DELETE_OWN,(data, handlerId) => (
|
||||
<VariadicTranslatable text={"Channel {} has been deleted."}>
|
||||
<ChannelRenderer channel={data.channel} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
));
|
||||
|
||||
|
||||
registerDispatcher(EventType.CLIENT_POKE_SEND,(data, handlerId) => (
|
||||
<VariadicTranslatable text={"You poked {}."}>
|
||||
<ClientRenderer client={data.target} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
));
|
||||
|
||||
registerDispatcher(EventType.CLIENT_POKE_RECEIVED,(data, handlerId) => {
|
||||
if(data.message) {
|
||||
return (
|
||||
<VariadicTranslatable text={"You received a poke from {}: {}"}>
|
||||
<ClientRenderer client={data.sender} handlerId={handlerId} />
|
||||
<BBCodeRenderer message={data.message} settings={{ convertSingleUrls: false }} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<VariadicTranslatable text={"You received a poke from {}."}>
|
||||
<ClientRenderer client={data.sender} handlerId={handlerId} />
|
||||
</VariadicTranslatable>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.PRIVATE_MESSAGE_RECEIVED, () => undefined);
|
||||
registerDispatcher(EventType.PRIVATE_MESSAGE_SEND, () => undefined);
|
|
@ -0,0 +1,480 @@
|
|||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
import * as log from "../../../log";
|
||||
import {LogCategory} from "../../../log";
|
||||
import {EventClient, EventServerAddress, EventType, TypeInfo} from "tc-shared/ui/frames/log/Definitions";
|
||||
import {server_connections} from "tc-shared/ui/frames/connection_handlers";
|
||||
import {renderBBCodeAsText} from "tc-shared/text/bbcode";
|
||||
import {format_time} from "tc-shared/ui/frames/chat";
|
||||
import {ViewReasonId} from "tc-shared/ConnectionHandler";
|
||||
import {findLogDispatcher} from "tc-shared/ui/frames/log/DispatcherLog";
|
||||
import {formatDate} from "tc-shared/MessageFormatter";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
|
||||
export type DispatcherLog<T extends keyof TypeInfo> = (data: TypeInfo[T], handlerId: string, eventType: T) => void;
|
||||
|
||||
const notificationDefaultStatus = {};
|
||||
notificationDefaultStatus[EventType.CLIENT_POKE_RECEIVED] = true;
|
||||
notificationDefaultStatus[EventType.SERVER_BANNED] = true;
|
||||
notificationDefaultStatus[EventType.SERVER_CLOSED] = true;
|
||||
notificationDefaultStatus[EventType.SERVER_HOST_MESSAGE_DISCONNECT] = true;
|
||||
notificationDefaultStatus[EventType.GLOBAL_MESSAGE] = true;
|
||||
notificationDefaultStatus[EventType.CONNECTION_FAILED] = true;
|
||||
//notificationDefaultStatus[EventType.PRIVATE_MESSAGE_RECEIVED] = true;
|
||||
|
||||
let windowFocused = false;
|
||||
|
||||
document.addEventListener("focusin", () => windowFocused = true);
|
||||
document.addEventListener("focusout", () => windowFocused = false);
|
||||
|
||||
const dispatchers: {[key: string]: DispatcherLog<any>} = { };
|
||||
function registerDispatcher<T extends keyof TypeInfo>(key: T, builder: DispatcherLog<T>) {
|
||||
dispatchers[key] = builder;
|
||||
}
|
||||
|
||||
export function findNotificationDispatcher<T extends keyof TypeInfo>(type: T) : DispatcherLog<T> {
|
||||
if(!isNotificationEnabled(type as any))
|
||||
return undefined;
|
||||
return dispatchers[type];
|
||||
}
|
||||
|
||||
export function getRegisteredNotificationDispatchers() : TypeInfo[] {
|
||||
return Object.keys(dispatchers) as any;
|
||||
}
|
||||
|
||||
export function isNotificationEnabled(type: EventType) {
|
||||
return settings.global(Settings.FN_EVENTS_NOTIFICATION_ENABLED(type), notificationDefaultStatus[type as any] || false);
|
||||
}
|
||||
|
||||
const kDefaultIcon = "img/teaspeak_cup_animated.png";
|
||||
|
||||
async function resolveAvatarUrl(client: EventClient, handlerId: string) {
|
||||
const connection = server_connections.findConnection(handlerId);
|
||||
const avatar = connection.fileManager.avatars.resolveClientAvatar({ clientUniqueId: client.client_unique_id, id: client.client_id });
|
||||
await avatar.awaitLoaded();
|
||||
return avatar.getAvatarUrl();
|
||||
}
|
||||
|
||||
async function resolveServerIconUrl(handlerId: string) {
|
||||
const connection = server_connections.findConnection(handlerId);
|
||||
|
||||
if(connection.channelTree.server.properties.virtualserver_icon_id) {
|
||||
const icon = connection.fileManager.icons.load_icon(connection.channelTree.server.properties.virtualserver_icon_id);
|
||||
await icon.await_loading();
|
||||
return icon.loaded_url;
|
||||
}
|
||||
|
||||
return kDefaultIcon;
|
||||
}
|
||||
|
||||
function spawnNotification(title: string, options: NotificationOptions) {
|
||||
if(!options.icon)
|
||||
options.icon = kDefaultIcon;
|
||||
|
||||
if('Notification' in window) {
|
||||
try {
|
||||
new Notification(title, options);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function spawnServerNotification(handlerId: string, options: NotificationOptions) {
|
||||
resolveServerIconUrl(handlerId).then(iconUrl => {
|
||||
const connection = server_connections.findConnection(handlerId);
|
||||
if(!connection) return;
|
||||
|
||||
options.icon = iconUrl;
|
||||
spawnNotification(connection.channelTree.server.properties.virtualserver_name, options);
|
||||
});
|
||||
}
|
||||
|
||||
function spawnClientNotification(handlerId: string, client: EventClient, options: NotificationOptions) {
|
||||
resolveAvatarUrl(client, handlerId).then(avatarUrl => {
|
||||
const connection = server_connections.findConnection(handlerId);
|
||||
if(!connection) return;
|
||||
|
||||
options.icon = avatarUrl;
|
||||
spawnNotification(connection.channelTree.server.properties.virtualserver_name, options);
|
||||
});
|
||||
}
|
||||
|
||||
const formatServerAddress = (address: EventServerAddress) => address.server_hostname + (address.server_port === 9987 ? "" : ":" + address.server_port);
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_BEGIN, data => {
|
||||
spawnNotification(tr("Connecting..."), {
|
||||
body: tra("Connecting to {}", formatServerAddress(data.address))
|
||||
});
|
||||
});
|
||||
|
||||
/* Snipped CONNECTION_HOSTNAME_RESOLVE */
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_HOSTNAME_RESOLVED, data => {
|
||||
spawnNotification(tr("Hostname resolved"), {
|
||||
body: tra("Hostname resolved successfully to {}", formatServerAddress(data.address))
|
||||
});
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_HOSTNAME_RESOLVE_ERROR, data => {
|
||||
spawnNotification(tr("Connect failed"), {
|
||||
body: tra("Failed to resolve hostname.\nConnecting to given hostname.\nError: {0}", data.message)
|
||||
});
|
||||
});
|
||||
|
||||
/* Snipped CONNECTION_LOGIN */
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_CONNECTED, data => {
|
||||
spawnNotification(tra("Connected to {}", formatServerAddress(data.serverAddress)), {
|
||||
body: tra("You connected as {}", data.own_client.client_name)
|
||||
});
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_FAILED, data => {
|
||||
spawnNotification(tra("Connection to {} failed", formatServerAddress(data.serverAddress)), {
|
||||
body: tra("Failed to connect to {}.", formatServerAddress(data.serverAddress))
|
||||
});
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.DISCONNECTED, () => {
|
||||
spawnNotification(tra("You disconnected from the server"), { });
|
||||
});
|
||||
|
||||
|
||||
/* snipped RECONNECT_SCHEDULED */
|
||||
/* snipped RECONNECT_EXECUTE */
|
||||
/* snipped RECONNECT_CANCELED */
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_VOICE_SETUP_FAILED, (data, handlerId) => {
|
||||
spawnServerNotification(handlerId, {
|
||||
body: tra("Failed to setup voice bridge: {0}. Allow reconnect: {1}", data.reason, data.reconnect_delay > 0 ? tr("Yes") : tr("No"))
|
||||
});
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.CONNECTION_COMMAND_ERROR, (data, handlerId) => {
|
||||
spawnServerNotification(handlerId, {
|
||||
body: tra("Command execution resulted in an error.")
|
||||
});
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.SERVER_WELCOME_MESSAGE, (data, handlerId) => {
|
||||
spawnServerNotification(handlerId, {
|
||||
body: tra("Welcome message:\n{}", data.message)
|
||||
});
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.SERVER_HOST_MESSAGE, (data, handlerId) => {
|
||||
spawnServerNotification(handlerId, {
|
||||
body: tra("Host message:\n{}", data.message)
|
||||
});
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.SERVER_HOST_MESSAGE_DISCONNECT, (data) => {
|
||||
spawnNotification(tr("Connection to server denied"), {
|
||||
body: tra("Server message:\n{}", data.message)
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
registerDispatcher(EventType.SERVER_CLOSED, (data, handlerId) => {
|
||||
spawnServerNotification(handlerId, {
|
||||
body: data.message ? tra("Server has been closed ({})", data.message) : tr("Server has been closed")
|
||||
});
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.SERVER_BANNED, (data, handlerId) => {
|
||||
const time = data.time === 0 ? "ever" : format_time(data.time * 1000, tr("one second"));
|
||||
const reason = data.message ? " Reason: " + data.message : "";
|
||||
|
||||
spawnServerNotification(handlerId, {
|
||||
body: data.invoker.client_id > 0 ? tra("You've been banned from the server by {0} for {1}.{2}", data.invoker.client_name, time, reason) :
|
||||
tra("You've been banned from the server for {0}.{1}", time, reason)
|
||||
});
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.SERVER_REQUIRES_PASSWORD, () => {
|
||||
spawnNotification(tra("Failed to connect to the server"), {
|
||||
body: tr("Server requires a password to connect.")
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
registerDispatcher(EventType.CLIENT_VIEW_ENTER, (data, handlerId) => {
|
||||
let message;
|
||||
|
||||
switch (data.reason) {
|
||||
case ViewReasonId.VREASON_USER_ACTION:
|
||||
if(data.channel_from) {
|
||||
message = tra("{0} appeared from {1} to {2}", data.client.client_name, data.channel_from.channel_name, data.channel_to.channel_name);
|
||||
} else {
|
||||
message = tra("{0} appeared to channel {1}", data.client.client_name, data.channel_to.channel_name);
|
||||
}
|
||||
break;
|
||||
|
||||
case ViewReasonId.VREASON_MOVED:
|
||||
if(data.channel_from) {
|
||||
message = tra("{0} appeared from {1} to {2}, moved by {3}", data.client.client_name, data.channel_from.channel_name, data.channel_to.channel_name, data.invoker.client_name);
|
||||
} else {
|
||||
message = tra("{0} appeared to {1}, moved by {2}", data.client.client_name, data.channel_to.channel_name, data.invoker.client_name);
|
||||
}
|
||||
break;
|
||||
|
||||
case ViewReasonId.VREASON_CHANNEL_KICK:
|
||||
if(data.channel_from) {
|
||||
message = tra("{0} appeared from {1} to {2}, kicked by {3}{4}", data.client.client_name, data.channel_from.channel_name, data.channel_to.channel_name, data.invoker.client_name, data.message ? " (" + data.message + ")" : "");
|
||||
} else {
|
||||
message = tra("{0} appeared to {1}, kicked by {2}{3}", data.client.client_name, data.channel_to.channel_name, data.invoker.client_name, data.message ? " (" + data.message + ")" : "");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
spawnClientNotification(handlerId, data.client, {
|
||||
body: message
|
||||
});
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.CLIENT_VIEW_ENTER_OWN_CHANNEL, (data, handlerId) => {
|
||||
let message;
|
||||
|
||||
switch (data.reason) {
|
||||
case ViewReasonId.VREASON_USER_ACTION:
|
||||
if(data.channel_from) {
|
||||
message = tra("{0} appeared from {1} to your channel {2}", data.client.client_name, data.channel_from.channel_name, data.channel_to.channel_name);
|
||||
} else {
|
||||
message = tra("{0} appeared to your channel {1}", data.client.client_name, data.channel_to.channel_name);
|
||||
}
|
||||
break;
|
||||
|
||||
case ViewReasonId.VREASON_MOVED:
|
||||
if(data.channel_from) {
|
||||
message = tra("{0} appeared from {1} to your channel {2}, moved by {3}", data.client.client_name, data.channel_from.channel_name, data.channel_to.channel_name, data.invoker.client_name);
|
||||
} else {
|
||||
message = tra("{0} appeared to your channel {1}, moved by {2}", data.client.client_name, data.channel_to.channel_name, data.invoker.client_name);
|
||||
}
|
||||
break;
|
||||
|
||||
case ViewReasonId.VREASON_CHANNEL_KICK:
|
||||
if(data.channel_from) {
|
||||
message = tra("{0} appeared from {1} to your channel {2}, kicked by {3}{4}", data.client.client_name, data.channel_from.channel_name, data.channel_to.channel_name, data.invoker.client_name, data.message ? " (" + data.message + ")" : "");
|
||||
} else {
|
||||
message = tra("{0} appeared to your channel {1}, kicked by {2}{3}", data.client.client_name, data.channel_to.channel_name, data.invoker.client_name, data.message ? " (" + data.message + ")" : "");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
spawnClientNotification(handlerId, data.client, {
|
||||
body: message
|
||||
});
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.CLIENT_VIEW_MOVE, (data, handlerId) => {
|
||||
let message;
|
||||
|
||||
switch (data.reason) {
|
||||
case ViewReasonId.VREASON_MOVED:
|
||||
message = tra("{0} was moved from channel {1} to {2} by {3}", data.client.client_name, data.channel_from.channel_name, data.channel_to.channel_name, data.invoker.client_name);
|
||||
break;
|
||||
|
||||
case ViewReasonId.VREASON_USER_ACTION:
|
||||
message = tra("{0} switched from channel {1} to {2}", data.client.client_name, data.channel_from.channel_name, data.channel_to.channel_name);
|
||||
break;
|
||||
|
||||
case ViewReasonId.VREASON_CHANNEL_KICK:
|
||||
message = tra("{0} got kicked from channel {1} to {2} by {3}{4}", data.client.client_name, data.channel_from.channel_name, data.channel_to.channel_name, data.invoker.client_name, data.message ? " (" + data.message + ")" : "");
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
spawnClientNotification(handlerId, data.client, {
|
||||
body: message
|
||||
});
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.CLIENT_VIEW_MOVE_OWN_CHANNEL, findLogDispatcher(EventType.CLIENT_VIEW_MOVE));
|
||||
|
||||
registerDispatcher(EventType.CLIENT_VIEW_MOVE_OWN, (data, handlerId) => {
|
||||
let message;
|
||||
|
||||
switch (data.reason) {
|
||||
case ViewReasonId.VREASON_MOVED:
|
||||
message = tra("You have been moved by {3} from channel {1} to {2}", data.client.client_name, data.channel_from.channel_name, data.channel_to.channel_name, data.invoker.client_name);
|
||||
break;
|
||||
|
||||
case ViewReasonId.VREASON_USER_ACTION:
|
||||
/* no need to notify here */
|
||||
return;
|
||||
|
||||
case ViewReasonId.VREASON_CHANNEL_KICK:
|
||||
message = tra("You got kicked out of the channel {1} to channel {2} by {3}{4}", data.client.client_name, data.channel_from.channel_name, data.channel_to.channel_name, data.invoker.client_name, data.message ? " (" + data.message + ")" : "");
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
spawnClientNotification(handlerId, data.client, {
|
||||
body: message
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
registerDispatcher(EventType.CLIENT_VIEW_LEAVE, (data, handlerId) => {
|
||||
let message;
|
||||
|
||||
switch (data.reason) {
|
||||
case ViewReasonId.VREASON_USER_ACTION:
|
||||
message = tra("{0} disappeared from {1} to {2}", data.client.client_name, data.channel_from.channel_name, data.channel_to.channel_name);
|
||||
break;
|
||||
|
||||
case ViewReasonId.VREASON_SERVER_LEFT:
|
||||
message = tra("{0} left the server{1}", data.client.client_name, data.message ? " (" + data.message + ")" : "");
|
||||
break;
|
||||
|
||||
case ViewReasonId.VREASON_SERVER_KICK:
|
||||
message = tra("{0} was kicked from the server by {1}.{2}", data.client, data.invoker.client_name, data.message ? " (" + data.message + ")" : "");
|
||||
break;
|
||||
|
||||
case ViewReasonId.VREASON_CHANNEL_KICK:
|
||||
message = tra("{0} was kicked from channel {1} by {2}.{3}", data.client, data.channel_from.channel_name, data.invoker.client_name, data.message ? " (" + data.message + ")" : "");
|
||||
break;
|
||||
|
||||
case ViewReasonId.VREASON_BAN:
|
||||
let duration = "permanently";
|
||||
if(data.ban_time)
|
||||
duration = tr("for") + " " + formatDate(data.ban_time);
|
||||
|
||||
message = tra("{0} was banned {1} by {2}.{3}", data.client.client_name, duration, data.invoker.client_name, data.message ? " (" + data.message + ")" : "");
|
||||
break;
|
||||
|
||||
case ViewReasonId.VREASON_TIMEOUT:
|
||||
message = tra("{0} timed out{1}", data.client.client_name, data.message ? " (" + data.message + ")" : "");
|
||||
break;
|
||||
|
||||
case ViewReasonId.VREASON_MOVED:
|
||||
message = tra("{0} disappeared from {1} to {2}, moved by {3}", data.client.client_name, data.channel_from.channel_name, data.channel_to.channel_name, data.invoker.client_name);
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
spawnClientNotification(handlerId, data.client, {
|
||||
body: message
|
||||
});
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.CLIENT_VIEW_LEAVE_OWN_CHANNEL, (data, handlerId) => {
|
||||
let message;
|
||||
|
||||
switch (data.reason) {
|
||||
case ViewReasonId.VREASON_USER_ACTION:
|
||||
message = tra("{0} disappeared from your channel {1} to {2}", data.client.client_name, data.channel_from.channel_name, data.channel_to.channel_name);
|
||||
break;
|
||||
|
||||
case ViewReasonId.VREASON_MOVED:
|
||||
message = tra("{0} disappeared from your channel {1} to {2}, moved by {3}", data.client.client_name, data.channel_from.channel_name, data.channel_to.channel_name, data.invoker.client_name);
|
||||
break;
|
||||
|
||||
default:
|
||||
return findLogDispatcher(EventType.CLIENT_VIEW_LEAVE)(data, handlerId, EventType.CLIENT_VIEW_LEAVE);
|
||||
}
|
||||
|
||||
spawnClientNotification(handlerId, data.client, {
|
||||
body: message
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
registerDispatcher(EventType.CLIENT_NICKNAME_CHANGED, (data, handlerId) => {
|
||||
spawnClientNotification(handlerId, data.client, {
|
||||
body: tra("{0} changed his nickname from \"{1}\" to \"{2}\"", data.client.client_name, data.old_name, data.new_name)
|
||||
});
|
||||
});
|
||||
|
||||
/* snipped CLIENT_NICKNAME_CHANGED_OWN */
|
||||
/* snipped CLIENT_NICKNAME_CHANGE_FAILED */
|
||||
|
||||
registerDispatcher(EventType.CHANNEL_CREATE, (data, handlerId) => {
|
||||
spawnServerNotification(handlerId, {
|
||||
body: tra("Channel {} has been created by {}.", data.channel.channel_name, data.creator.client_name)
|
||||
});
|
||||
});
|
||||
|
||||
registerDispatcher(EventType.CHANNEL_DELETE, (data, handlerId) => {
|
||||
spawnServerNotification(handlerId, {
|
||||
body: tra("Channel {} has been deleted by {}.", data.channel.channel_name, data.deleter.client_name)
|
||||
});
|
||||
});
|
||||
|
||||
/* snipped CHANNEL_CREATE_OWN */
|
||||
/* snipped CHANNEL_DELETE_OWN */
|
||||
|
||||
/* snipped ERROR_CUSTOM */
|
||||
/* snipped ERROR_PERMISSION */
|
||||
|
||||
/* TODO!
|
||||
CLIENT_SERVER_GROUP_ADD = "client.server.group.add",
|
||||
CLIENT_SERVER_GROUP_REMOVE = "client.server.group.remove",
|
||||
CLIENT_CHANNEL_GROUP_CHANGE = "client.channel.group.change",
|
||||
*/
|
||||
|
||||
registerDispatcher(EventType.CLIENT_POKE_RECEIVED, (data, handlerId) => {
|
||||
resolveAvatarUrl(data.sender, handlerId).then(avatarUrl => {
|
||||
const connection = server_connections.findConnection(handlerId);
|
||||
if(!connection) return;
|
||||
|
||||
new Notification(connection.channelTree.server.properties.virtualserver_name, {
|
||||
body: tr("You've peen poked by") + " " + data.sender.client_name + (data.message ? ":\n" + renderBBCodeAsText(data.message) : ""),
|
||||
icon: avatarUrl
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/* snipped CLIENT_POKE_SEND */
|
||||
|
||||
registerDispatcher(EventType.GLOBAL_MESSAGE, (data, handlerId) => {
|
||||
if(windowFocused)
|
||||
return;
|
||||
|
||||
spawnServerNotification(handlerId, {
|
||||
body: tra("{} send a server message: {}", data.sender.client_name, renderBBCodeAsText(data.message)),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
registerDispatcher(EventType.PRIVATE_MESSAGE_RECEIVED, (data, handlerId) => {
|
||||
if(windowFocused)
|
||||
return;
|
||||
|
||||
spawnClientNotification(handlerId, data.sender, {
|
||||
body: tra("Private message from {}: {}", data.sender.client_name, renderBBCodeAsText(data.message)),
|
||||
});
|
||||
});
|
||||
|
||||
/* snipped PRIVATE_MESSAGE_SEND */
|
||||
|
||||
loader.register_task(Stage.LOADED, {
|
||||
function: async () => {
|
||||
if(!('Notification' in window))
|
||||
return;
|
||||
|
||||
if(Notification.permission === "granted")
|
||||
return;
|
||||
|
||||
Notification.requestPermission().then(result => {
|
||||
log.info(LogCategory.GENERAL, tr("Notification permission request resulted in %s"), result);
|
||||
}).catch(error => {
|
||||
log.warn(LogCategory.GENERAL, tr("Failed to execute notification permission request: %O"), error);
|
||||
});
|
||||
},
|
||||
name: "Request notifications",
|
||||
priority: 1
|
||||
});
|
|
@ -0,0 +1,55 @@
|
|||
@import "../../../../css/static/mixin";
|
||||
|
||||
.htmlTag {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
flex-shrink: 1;
|
||||
min-height: 2em;
|
||||
}
|
||||
|
||||
.logContainer {
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
||||
min-height: 2em;
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
@include chat-scrollbar-vertical();
|
||||
}
|
||||
|
||||
.logEntry {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
color: var(--server-log-text);
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
|
||||
display: block;
|
||||
|
||||
> *, .errorMessage > * {
|
||||
display: inline-block;
|
||||
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.errorMessage {
|
||||
color: var(--server-log-error);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
import {LogMessage, ServerLogUIEvents} from "tc-shared/ui/frames/log/Definitions";
|
||||
import {VariadicTranslatable} from "tc-shared/ui/react-elements/i18n";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {useEffect, useRef, useState} from "react";
|
||||
import * as React from "react";
|
||||
import {findLogDispatcher} from "tc-shared/ui/frames/log/DispatcherLog";
|
||||
|
||||
const cssStyle = require("./Renderer.scss");
|
||||
|
||||
const LogFallbackDispatcher = (_unused, __unused, eventType) => (
|
||||
<div className={cssStyle.errorMessage}>
|
||||
<VariadicTranslatable text={"Missing log entry builder for {0}"}>
|
||||
<>{eventType}</>
|
||||
</VariadicTranslatable>
|
||||
</div>
|
||||
);
|
||||
|
||||
const LogEntryRenderer = React.memo((props: { entry: LogMessage, handlerId: string }) => {
|
||||
const dispatcher = findLogDispatcher(props.entry.type as any) || LogFallbackDispatcher;
|
||||
const rendered = dispatcher(props.entry.data, props.handlerId, props.entry.type);
|
||||
|
||||
if(!rendered) /* hide message */
|
||||
return null;
|
||||
|
||||
const date = new Date(props.entry.timestamp);
|
||||
return (
|
||||
<div className={cssStyle.logEntry}>
|
||||
<div className={cssStyle.timestamp}><{
|
||||
("0" + date.getHours()).substr(-2) + ":" +
|
||||
("0" + date.getMinutes()).substr(-2) + ":" +
|
||||
("0" + date.getSeconds()).substr(-2)
|
||||
}></div>
|
||||
{rendered}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const ServerLogRenderer = (props: { events: Registry<ServerLogUIEvents>, handlerId: string }) => {
|
||||
const [ logs, setLogs ] = useState<LogMessage[] | "loading">(() => {
|
||||
props.events.fire_async("query_log");
|
||||
return "loading";
|
||||
});
|
||||
|
||||
const [ revision, setRevision ] = useState(0);
|
||||
|
||||
const refContainer = useRef<HTMLDivElement>();
|
||||
const scrollOffset = useRef<number | "bottom">("bottom");
|
||||
|
||||
props.events.reactUse("notify_log", event => {
|
||||
const logs = event.log.slice(0);
|
||||
logs.splice(0, Math.max(0, logs.length - 100));
|
||||
logs.sort((a, b) => a.timestamp - b.timestamp);
|
||||
setLogs(logs);
|
||||
});
|
||||
|
||||
props.events.reactUse("notify_log_add", event => {
|
||||
if(logs === "loading")
|
||||
return;
|
||||
|
||||
logs.push(event.event);
|
||||
logs.splice(0, Math.max(0, logs.length - 100));
|
||||
logs.sort((a, b) => a.timestamp - b.timestamp);
|
||||
setRevision(revision + 1);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const id = requestAnimationFrame(() => {
|
||||
if(!refContainer.current)
|
||||
return;
|
||||
|
||||
refContainer.current.scrollTop = scrollOffset.current === "bottom" ? refContainer.current.scrollHeight : scrollOffset.current;
|
||||
});
|
||||
return () => cancelAnimationFrame(id);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={cssStyle.logContainer} ref={refContainer} onScroll={event => {
|
||||
const target = event.target as HTMLDivElement;
|
||||
|
||||
const top = target.scrollTop;
|
||||
const total = target.scrollHeight - target.clientHeight;
|
||||
const shouldFollow = top + 50 > total;
|
||||
|
||||
scrollOffset.current = shouldFollow ? "bottom" : top;
|
||||
}}>
|
||||
{logs === "loading" ? null : logs.map(e => <LogEntryRenderer key={e.uniqueId} entry={e} handlerId={props.handlerId} />)}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,65 @@
|
|||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import * as React from "react";
|
||||
import {LogMessage, ServerLogUIEvents, TypeInfo} from "tc-shared/ui/frames/log/Definitions";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import {ServerLogRenderer} from "tc-shared/ui/frames/log/Renderer";
|
||||
import {findNotificationDispatcher} from "tc-shared/ui/frames/log/DispatcherNotifications";
|
||||
|
||||
const cssStyle = require("./Renderer.scss");
|
||||
|
||||
let uniqueLogEventId = 0;
|
||||
export class ServerEventLog {
|
||||
private readonly connection: ConnectionHandler;
|
||||
private readonly uiEvents: Registry<ServerLogUIEvents>;
|
||||
private htmlTag: HTMLDivElement;
|
||||
|
||||
private maxHistoryLength: number = 100;
|
||||
|
||||
private eventLog: LogMessage[] = [];
|
||||
|
||||
constructor(connection: ConnectionHandler) {
|
||||
this.connection = connection;
|
||||
this.uiEvents = new Registry<ServerLogUIEvents>();
|
||||
this.htmlTag = document.createElement("div");
|
||||
this.htmlTag.classList.add(cssStyle.htmlTag);
|
||||
|
||||
this.uiEvents.on("query_log", () => {
|
||||
this.uiEvents.fire_async("notify_log", { log: this.eventLog });
|
||||
});
|
||||
|
||||
ReactDOM.render(<ServerLogRenderer events={this.uiEvents} handlerId={this.connection.handlerId} />, this.htmlTag);
|
||||
}
|
||||
|
||||
log<T extends keyof TypeInfo>(type: T, data: TypeInfo[T]) {
|
||||
const event = {
|
||||
data: data,
|
||||
timestamp: Date.now(),
|
||||
type: type as any,
|
||||
uniqueId: "log-" + Date.now() + "-" + (++uniqueLogEventId)
|
||||
};
|
||||
|
||||
this.eventLog.push(event);
|
||||
while(this.eventLog.length > this.maxHistoryLength)
|
||||
this.eventLog.pop_front();
|
||||
|
||||
this.uiEvents.fire_async("notify_log_add", { event: event });
|
||||
|
||||
const notification = findNotificationDispatcher(type);
|
||||
if(notification) notification(data, this.connection.handlerId, type);
|
||||
}
|
||||
|
||||
getHTMLTag() {
|
||||
return this.htmlTag;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if(this.htmlTag) {
|
||||
ReactDOM.unmountComponentAtNode(this.htmlTag);
|
||||
this.htmlTag?.remove();
|
||||
this.htmlTag = undefined;
|
||||
}
|
||||
|
||||
this.eventLog = undefined;
|
||||
}
|
||||
}
|
|
@ -1,611 +0,0 @@
|
|||
import {tra, traj} from "tc-shared/i18n/localize";
|
||||
import {PermissionInfo} from "tc-shared/permission/PermissionManager";
|
||||
import {ConnectionHandler, ViewReasonId} from "tc-shared/ConnectionHandler";
|
||||
import * as htmltags from "tc-shared/ui/htmltags";
|
||||
import {format_time, formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {formatDate} from "tc-shared/MessageFormatter";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {BBCodeRenderOptions, renderBBCodeAsJQuery} from "tc-shared/text/bbcode";
|
||||
|
||||
export enum Type {
|
||||
CONNECTION_BEGIN = "connection_begin",
|
||||
CONNECTION_HOSTNAME_RESOLVE = "connection_hostname_resolve",
|
||||
CONNECTION_HOSTNAME_RESOLVE_ERROR = "connection_hostname_resolve_error",
|
||||
CONNECTION_HOSTNAME_RESOLVED = "connection_hostname_resolved",
|
||||
CONNECTION_LOGIN = "connection_login",
|
||||
CONNECTION_CONNECTED = "connection_connected",
|
||||
CONNECTION_FAILED = "connection_failed",
|
||||
|
||||
DISCONNECTED = "disconnected",
|
||||
|
||||
CONNECTION_VOICE_SETUP_FAILED = "connection_voice_setup_failed",
|
||||
CONNECTION_COMMAND_ERROR = "connection_command_error",
|
||||
|
||||
GLOBAL_MESSAGE = "global_message",
|
||||
|
||||
SERVER_WELCOME_MESSAGE = "server_welcome_message",
|
||||
SERVER_HOST_MESSAGE = "server_host_message",
|
||||
SERVER_HOST_MESSAGE_DISCONNECT = "server_host_message_disconnect",
|
||||
|
||||
SERVER_CLOSED = "server_closed",
|
||||
SERVER_BANNED = "server_banned",
|
||||
SERVER_REQUIRES_PASSWORD = "server_requires_password",
|
||||
|
||||
CLIENT_VIEW_ENTER = "client_view_enter",
|
||||
CLIENT_VIEW_LEAVE = "client_view_leave",
|
||||
CLIENT_VIEW_MOVE = "client_view_move",
|
||||
|
||||
CLIENT_NICKNAME_CHANGED = "client_nickname_changed",
|
||||
CLIENT_NICKNAME_CHANGE_FAILED = "client_nickname_change_failed",
|
||||
|
||||
CLIENT_SERVER_GROUP_ADD = "client_server_group_add",
|
||||
CLIENT_SERVER_GROUP_REMOVE = "client_server_group_remove",
|
||||
CLIENT_CHANNEL_GROUP_CHANGE = "client_channel_group_change",
|
||||
|
||||
CHANNEL_CREATE = "channel_create",
|
||||
CHANNEL_DELETE = "channel_delete",
|
||||
|
||||
ERROR_CUSTOM = "error_custom",
|
||||
ERROR_PERMISSION = "error_permission",
|
||||
|
||||
RECONNECT_SCHEDULED = "reconnect_scheduled",
|
||||
RECONNECT_EXECUTE = "reconnect_execute",
|
||||
RECONNECT_CANCELED = "reconnect_canceled"
|
||||
}
|
||||
|
||||
export namespace base {
|
||||
export type Client = {
|
||||
client_unique_id: string;
|
||||
client_name: string;
|
||||
client_id: number;
|
||||
}
|
||||
export type Channel = {
|
||||
channel_id: number;
|
||||
channel_name: string;
|
||||
}
|
||||
export type Server = {
|
||||
server_name: string;
|
||||
server_unique_id: string;
|
||||
}
|
||||
export type ServerAddress = {
|
||||
server_hostname: string;
|
||||
server_port: number;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace event {
|
||||
export type GlobalMessage = {
|
||||
sender: base.Client;
|
||||
message: string;
|
||||
}
|
||||
export type ConnectBegin = {
|
||||
address: base.ServerAddress;
|
||||
client_nickname: string;
|
||||
}
|
||||
export type ErrorCustom = {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type ReconnectScheduled = {
|
||||
timeout: number;
|
||||
}
|
||||
|
||||
export type ReconnectCanceled = { }
|
||||
export type ReconnectExecute = { }
|
||||
|
||||
export type ErrorPermission = {
|
||||
permission: PermissionInfo;
|
||||
}
|
||||
|
||||
export type WelcomeMessage = {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type HostMessageDisconnect = {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type ClientMove = {
|
||||
channel_from?: base.Channel;
|
||||
channel_from_own: boolean;
|
||||
|
||||
channel_to?: base.Channel;
|
||||
channel_to_own: boolean;
|
||||
|
||||
client: base.Client;
|
||||
client_own: boolean;
|
||||
|
||||
invoker?: base.Client;
|
||||
|
||||
message?: string;
|
||||
reason: ViewReasonId;
|
||||
}
|
||||
|
||||
export type ClientEnter = {
|
||||
channel_from?: base.Channel;
|
||||
channel_to?: base.Channel;
|
||||
|
||||
client: base.Client;
|
||||
invoker?: base.Client;
|
||||
|
||||
message?: string;
|
||||
own_channel: boolean;
|
||||
|
||||
reason: ViewReasonId;
|
||||
ban_time?: number;
|
||||
}
|
||||
|
||||
export type ClientLeave = {
|
||||
channel_from?: base.Channel;
|
||||
channel_to?: base.Channel;
|
||||
|
||||
client: base.Client;
|
||||
invoker?: base.Client;
|
||||
|
||||
message?: string;
|
||||
own_channel: boolean;
|
||||
|
||||
reason: ViewReasonId;
|
||||
ban_time?: number;
|
||||
}
|
||||
|
||||
export type ChannelCreate = {
|
||||
creator: base.Client;
|
||||
channel: base.Channel;
|
||||
|
||||
own_action: boolean;
|
||||
}
|
||||
|
||||
export type ChannelDelete = {
|
||||
deleter: base.Client;
|
||||
channel: base.Channel;
|
||||
|
||||
own_action: boolean;
|
||||
}
|
||||
|
||||
export type ConnectionConnected = {
|
||||
own_client: base.Client;
|
||||
}
|
||||
export type ConnectionFailed = {};
|
||||
export type ConnectionLogin = {}
|
||||
export type ConnectionHostnameResolve = {};
|
||||
export type ConnectionHostnameResolved = {
|
||||
address: base.ServerAddress;
|
||||
}
|
||||
export type ConnectionHostnameResolveError = {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type ConnectionVoiceSetupFailed = {
|
||||
reason: string;
|
||||
reconnect_delay: number; /* if less or equal to 0 reconnect is prohibited */
|
||||
}
|
||||
|
||||
export type ConnectionCommandError = {
|
||||
error: any;
|
||||
}
|
||||
|
||||
export type ClientNicknameChanged = {
|
||||
own_client: boolean;
|
||||
|
||||
client: base.Client;
|
||||
|
||||
old_name: string;
|
||||
new_name: string;
|
||||
}
|
||||
|
||||
export type ClientNicknameChangeFailed = {
|
||||
reason: string;
|
||||
}
|
||||
|
||||
export type ServerClosed = {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type ServerRequiresPassword = {}
|
||||
|
||||
export type ServerBanned = {
|
||||
message: string;
|
||||
time: number;
|
||||
|
||||
invoker: base.Client;
|
||||
}
|
||||
}
|
||||
|
||||
export type LogMessage = {
|
||||
type: Type;
|
||||
timestamp: number;
|
||||
data: any;
|
||||
}
|
||||
|
||||
export interface TypeInfo {
|
||||
"connection_begin" : event.ConnectBegin;
|
||||
"global_message": event.GlobalMessage;
|
||||
|
||||
"error_custom": event.ErrorCustom;
|
||||
"error_permission": event.ErrorPermission;
|
||||
|
||||
"connection_hostname_resolved": event.ConnectionHostnameResolved;
|
||||
"connection_hostname_resolve": event.ConnectionHostnameResolve;
|
||||
"connection_hostname_resolve_error": event.ConnectionHostnameResolveError;
|
||||
"connection_failed": event.ConnectionFailed;
|
||||
"connection_login": event.ConnectionLogin;
|
||||
"connection_connected": event.ConnectionConnected;
|
||||
"connection_voice_setup_failed": event.ConnectionVoiceSetupFailed;
|
||||
"connection_command_error": event.ConnectionCommandError;
|
||||
|
||||
"reconnect_scheduled": event.ReconnectScheduled;
|
||||
"reconnect_canceled": event.ReconnectCanceled;
|
||||
"reconnect_execute": event.ReconnectExecute;
|
||||
|
||||
"server_welcome_message": event.WelcomeMessage;
|
||||
"server_host_message": event.WelcomeMessage;
|
||||
"server_host_message_disconnect": event.HostMessageDisconnect;
|
||||
|
||||
"server_closed": event.ServerClosed;
|
||||
"server_requires_password": event.ServerRequiresPassword;
|
||||
"server_banned": event.ServerBanned;
|
||||
|
||||
"client_view_enter": event.ClientEnter;
|
||||
"client_view_move": event.ClientMove;
|
||||
"client_view_leave": event.ClientLeave;
|
||||
|
||||
"client_nickname_change_failed": event.ClientNicknameChangeFailed,
|
||||
"client_nickname_changed": event.ClientNicknameChanged,
|
||||
|
||||
"channel_create": event.ChannelCreate;
|
||||
"channel_delete": event.ChannelDelete;
|
||||
|
||||
"disconnected": any;
|
||||
}
|
||||
|
||||
export type MessageBuilderOptions = {};
|
||||
export type MessageBuilder<T extends keyof TypeInfo> = (data: TypeInfo[T], options: MessageBuilderOptions) => JQuery[] | string | undefined;
|
||||
|
||||
export const MessageBuilders: {[key: string]: MessageBuilder<any>} = {
|
||||
"error_custom": (data: event.ErrorCustom) => {
|
||||
return [$.spawn("div").addClass("log-error").text(data.message)]
|
||||
}
|
||||
};
|
||||
function register_message_builder<T extends keyof TypeInfo>(key: T, builder: MessageBuilder<T>) {
|
||||
MessageBuilders[key] = builder;
|
||||
}
|
||||
|
||||
export class ServerLog {
|
||||
private readonly handle: ConnectionHandler;
|
||||
private history_length: number = 100;
|
||||
|
||||
private _log: LogMessage[] = [];
|
||||
private _html_tag: JQuery;
|
||||
private _log_container: JQuery;
|
||||
private auto_follow: boolean; /* automatic scroll to bottom */
|
||||
private _ignore_event: number; /* after auto scroll we've triggered the scroll event. We want to prevent this so we capture the next event */
|
||||
|
||||
constructor(handle: ConnectionHandler) {
|
||||
this.handle = handle;
|
||||
this.auto_follow = true;
|
||||
|
||||
this._html_tag = $.spawn("div").addClass("container-log");
|
||||
this._log_container = $.spawn("div").addClass("container-messages");
|
||||
this._log_container.appendTo(this._html_tag);
|
||||
|
||||
this._html_tag.on('scroll', event => {
|
||||
if(Date.now() - this._ignore_event < 100) {
|
||||
this._ignore_event = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
this.auto_follow = (this._html_tag[0].scrollTop + this._html_tag[0].clientHeight + this._html_tag[0].clientHeight * .125) > this._html_tag[0].scrollHeight;
|
||||
});
|
||||
}
|
||||
|
||||
log<T extends keyof TypeInfo>(type: T, data: TypeInfo[T]) {
|
||||
const event = {
|
||||
data: data,
|
||||
timestamp: Date.now(),
|
||||
type: type as any
|
||||
};
|
||||
|
||||
this._log.push(event);
|
||||
while(this._log.length > this.history_length)
|
||||
this._log.pop_front();
|
||||
|
||||
this.append_log(event);
|
||||
}
|
||||
|
||||
html_tag() : JQuery {
|
||||
return this._html_tag;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._html_tag && this._html_tag.remove();
|
||||
this._html_tag = undefined;
|
||||
this._log_container = undefined;
|
||||
|
||||
this._log = undefined;
|
||||
}
|
||||
|
||||
private _scroll_task: number;
|
||||
|
||||
private append_log(message: LogMessage) {
|
||||
let container = $.spawn("div").addClass("log-message");
|
||||
|
||||
/* build timestamp */
|
||||
{
|
||||
const num = number => ('00' + number).substr(-2);
|
||||
const date = new Date(message.timestamp);
|
||||
$.spawn("div")
|
||||
.addClass("timestamp")
|
||||
.text("<" + num(date.getHours()) + ":" + num(date.getMinutes()) + ":" + num(date.getSeconds()) + ">")
|
||||
.appendTo(container);
|
||||
}
|
||||
|
||||
/* build message data */
|
||||
{
|
||||
const builder = MessageBuilders[message.type];
|
||||
if(!builder) {
|
||||
formatMessage(tr("missing log message builder {0}!"), message.type).forEach(e => e.addClass("log-error").appendTo(container));
|
||||
} else {
|
||||
const elements = builder(message.data, {});
|
||||
if(!elements || elements.length == 0)
|
||||
return; /* discard message */
|
||||
container.append(...elements);
|
||||
}
|
||||
}
|
||||
this._ignore_event = Date.now();
|
||||
this._log_container.append(container);
|
||||
|
||||
/* max history messages! */
|
||||
const messages = this._log_container.children();
|
||||
let index = 0;
|
||||
while(messages.length - index > this.history_length)
|
||||
index++;
|
||||
const hide_elements = messages.filter(idx => idx < index);
|
||||
hide_elements.hide(250, () => hide_elements.remove());
|
||||
|
||||
if(this.auto_follow) {
|
||||
clearTimeout(this._scroll_task);
|
||||
|
||||
/* do not enforce a recalculate style here */
|
||||
this._scroll_task = setTimeout(() => {
|
||||
this._html_tag.scrollTop(this._html_tag[0].scrollHeight);
|
||||
this._scroll_task = 0;
|
||||
}, 5) as any;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* impl of the parsers */
|
||||
const client_tag = (client: base.Client, braces?: boolean) => htmltags.generate_client_object({
|
||||
client_unique_id: client.client_unique_id,
|
||||
client_id: client.client_id,
|
||||
client_name: client.client_name,
|
||||
add_braces: braces
|
||||
});
|
||||
const channel_tag = (channel: base.Channel, braces?: boolean) => htmltags.generate_channel_object({
|
||||
channel_display_name: channel.channel_name,
|
||||
channel_name: channel.channel_name,
|
||||
channel_id: channel.channel_id,
|
||||
add_braces: braces
|
||||
});
|
||||
|
||||
MessageBuilders["connection_begin"] = (data: event.ConnectBegin) => {
|
||||
return formatMessage(tr("Connecting to {0}{1}"), data.address.server_hostname, data.address.server_port == 9987 ? "" : (":" + data.address.server_port));
|
||||
};
|
||||
|
||||
MessageBuilders["connection_hostname_resolve"] = (data: event.ConnectionHostnameResolve) => formatMessage(tr("Resolving hostname"));
|
||||
MessageBuilders["connection_hostname_resolved"] = (data: event.ConnectionHostnameResolved) => formatMessage(tr("Hostname resolved successfully to {0}:{1}"), data.address.server_hostname, data.address.server_port);
|
||||
MessageBuilders["connection_hostname_resolve_error"] = (data: event.ConnectionHostnameResolveError) => formatMessage(tr("Failed to resolve hostname. Connecting to given hostname. Error: {0}"), data.message);
|
||||
|
||||
MessageBuilders["connection_login"] = () => formatMessage(tr("Logging in..."));
|
||||
MessageBuilders["connection_failed"] = () => formatMessage(tr("Connect failed."));
|
||||
MessageBuilders["connection_connected"] = (data: event.ConnectionConnected) => formatMessage(tr("Connected as {0}"), client_tag(data.own_client, true));
|
||||
|
||||
MessageBuilders["connection_voice_setup_failed"] = (data: event.ConnectionVoiceSetupFailed) => {
|
||||
return formatMessage(tr("Failed to setup voice bridge: {0}. Allow reconnect: {1}"), data.reason, data.reconnect_delay > 0 ? tr("yes") : tr("no"));
|
||||
};
|
||||
|
||||
MessageBuilders["error_permission"] = (data: event.ErrorPermission) => {
|
||||
return formatMessage(tr("Insufficient client permissions. Failed on permission {0}"), data.permission ? data.permission.name : "unknown").map(e => e.addClass("log-error"));
|
||||
};
|
||||
|
||||
MessageBuilders["client_view_enter"] = (data: event.ClientEnter) => {
|
||||
if(data.reason == ViewReasonId.VREASON_SYSTEM) {
|
||||
return undefined;
|
||||
} if(data.reason == ViewReasonId.VREASON_USER_ACTION) {
|
||||
/* client appeared */
|
||||
if(data.channel_from) {
|
||||
return formatMessage(data.own_channel ? tr("{0} appeared from {1} to your {2}") : tr("{0} appeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to));
|
||||
} else {
|
||||
return formatMessage(data.own_channel ? tr("{0} connected to your channel {1}") : tr("{0} connected to channel {1}"), client_tag(data.client), channel_tag(data.channel_to));
|
||||
}
|
||||
} else if(data.reason == ViewReasonId.VREASON_MOVED) {
|
||||
if(data.channel_from) {
|
||||
return formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, moved by {3}") : tr("{0} appeared from {1} to {2}, moved by {3}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_from),
|
||||
channel_tag(data.channel_to),
|
||||
client_tag(data.invoker)
|
||||
);
|
||||
} else {
|
||||
return formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, moved by {2}") : tr("{0} appeared to {1}, moved by {2}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_to),
|
||||
client_tag(data.invoker)
|
||||
);
|
||||
}
|
||||
} else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) {
|
||||
if(data.channel_from) {
|
||||
return formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, kicked by {3}{4}") : tr("{0} appeared from {1} to {2}, kicked by {3}{4}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_from),
|
||||
channel_tag(data.channel_to),
|
||||
client_tag(data.invoker),
|
||||
data.message ? (" (" + data.message + ")") : ""
|
||||
);
|
||||
} else {
|
||||
return formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, kicked by {2}{3}") : tr("{0} appeared to {1}, kicked by {2}{3}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_to),
|
||||
client_tag(data.invoker),
|
||||
data.message ? (" (" + data.message + ")") : ""
|
||||
);
|
||||
}
|
||||
}
|
||||
return [$.spawn("div").addClass("log-error").text("Invalid view enter reason id (" + data.message + ")")];
|
||||
};
|
||||
|
||||
MessageBuilders["client_view_move"] = (data: event.ClientMove) => {
|
||||
if(data.reason == ViewReasonId.VREASON_MOVED) {
|
||||
return formatMessage(data.client_own ? tr("You was moved by {3} from channel {1} to {2}") : tr("{0} was moved from channel {1} to {2} by {3}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_from),
|
||||
channel_tag(data.channel_to),
|
||||
client_tag(data.invoker)
|
||||
);
|
||||
} else if(data.reason == ViewReasonId.VREASON_USER_ACTION) {
|
||||
return formatMessage(data.client_own ? tr("You switched from channel {1} to {2}") : tr("{0} switched from channel {1} to {2}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_from),
|
||||
channel_tag(data.channel_to)
|
||||
);
|
||||
} else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) {
|
||||
return formatMessage(data.client_own ? tr("You got kicked out of the channel {1} to channel {2} by {3}{4}") : tr("{0} got kicked from channel {1} to {2} by {3}{4}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_from),
|
||||
channel_tag(data.channel_to),
|
||||
client_tag(data.invoker),
|
||||
data.message ? (" (" + data.message + ")") : ""
|
||||
);
|
||||
}
|
||||
return [$.spawn("div").addClass("log-error").text("Invalid view move reason id (" + data.reason + ")")];
|
||||
};
|
||||
|
||||
MessageBuilders["client_view_leave"] = (data: event.ClientLeave) => {
|
||||
if(data.reason == ViewReasonId.VREASON_USER_ACTION) {
|
||||
return formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}") : tr("{0} disappeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to));
|
||||
} else if(data.reason == ViewReasonId.VREASON_SERVER_LEFT) {
|
||||
return formatMessage(tr("{0} left the server{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : "");
|
||||
} else if(data.reason == ViewReasonId.VREASON_SERVER_KICK) {
|
||||
return formatMessage(tr("{0} was kicked from the server by {1}.{2}"), client_tag(data.client), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : "");
|
||||
} else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) {
|
||||
return formatMessage(data.own_channel ? tr("{0} was kicked from your channel by {2}.{3}") : tr("{0} was kicked from channel {1} by {2}.{3}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_from),
|
||||
client_tag(data.invoker),
|
||||
data.message ? (" (" + data.message + ")") : ""
|
||||
);
|
||||
} else if(data.reason == ViewReasonId.VREASON_BAN) {
|
||||
let duration = "permanently";
|
||||
if(data.ban_time)
|
||||
duration = "for " + formatDate(data.ban_time);
|
||||
return formatMessage(tr("{0} was banned {1} by {2}.{3}"),
|
||||
client_tag(data.client),
|
||||
duration,
|
||||
client_tag(data.invoker),
|
||||
data.message ? (" (" + data.message + ")") : ""
|
||||
);
|
||||
} else if(data.reason == ViewReasonId.VREASON_TIMEOUT) {
|
||||
return formatMessage(tr("{0} timed out{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : "");
|
||||
} else if(data.reason == ViewReasonId.VREASON_MOVED) {
|
||||
return formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}, moved by {3}") : tr("{0} disappeared from {1} to {2}, moved by {3}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker));
|
||||
}
|
||||
|
||||
return [$.spawn("div").addClass("log-error").text("Invalid view leave reason id (" + data.reason + ")")];
|
||||
};
|
||||
|
||||
const bbcodeRenderOptions: BBCodeRenderOptions = {
|
||||
convertSingleUrls: false
|
||||
};
|
||||
|
||||
MessageBuilders["server_welcome_message"] = (data: event.WelcomeMessage) => {
|
||||
return renderBBCodeAsJQuery("[color=green]" + data.message + "[/color]", bbcodeRenderOptions);
|
||||
};
|
||||
|
||||
MessageBuilders["server_host_message"] = (data: event.WelcomeMessage) => {
|
||||
return renderBBCodeAsJQuery("[color=green]" + data.message + "[/color]", bbcodeRenderOptions);
|
||||
};
|
||||
|
||||
MessageBuilders["client_nickname_changed"] = (data: event.ClientNicknameChanged) => {
|
||||
if(data.own_client) {
|
||||
return tra("Nickname successfully changed.");
|
||||
} else {
|
||||
return tra("{0} changed his nickname from \"{1}\" to \"{2}\"", client_tag(data.client), data.old_name, data.new_name);
|
||||
}
|
||||
};
|
||||
|
||||
register_message_builder("client_nickname_change_failed", (data) => {
|
||||
return tra("Failed to change own client name: {}", data.reason);
|
||||
});
|
||||
|
||||
MessageBuilders["global_message"] = () => {
|
||||
return []; /* we do not show global messages within log */
|
||||
};
|
||||
|
||||
MessageBuilders["disconnected"] = () => formatMessage(tr("Disconnected from server"));
|
||||
|
||||
MessageBuilders["reconnect_scheduled"] = (data: event.ReconnectScheduled) => {
|
||||
return tra("Reconnecting in {0}.", format_time(data.timeout, tr("now")))
|
||||
};
|
||||
|
||||
MessageBuilders["reconnect_canceled"] = () => {
|
||||
return tra("Canceled reconnect.")
|
||||
};
|
||||
|
||||
MessageBuilders["reconnect_execute"] = () => {
|
||||
return tra("Reconnecting...")
|
||||
};
|
||||
|
||||
MessageBuilders["server_banned"] = (data: event.ServerBanned) => {
|
||||
let result: JQuery[];
|
||||
|
||||
const time = data.time == 0 ? tr("ever") : format_time(data.time * 1000, tr("one second"));
|
||||
if(data.invoker.client_id > 0) {
|
||||
if(data.message)
|
||||
result = traj("You've been banned from the server by {0} for {1}. Reason: {2}", client_tag(data.invoker), time, data.message);
|
||||
else
|
||||
result = traj("You've been banned from the server by {0} for {1}.", client_tag(data.invoker), time);
|
||||
} else {
|
||||
if(data.message)
|
||||
result = traj("You've been banned from the server for {0}. Reason: {1}", time, data.message);
|
||||
else
|
||||
result = traj("You've been banned from the server for {0}.", time);
|
||||
}
|
||||
|
||||
return result.map(e => e.addClass("log-error"));
|
||||
};
|
||||
|
||||
register_message_builder("server_host_message_disconnect", (data) => {
|
||||
return tra(data.message);
|
||||
});
|
||||
|
||||
register_message_builder("server_requires_password", () => {
|
||||
return tra("Server requires a password to connect.");
|
||||
});
|
||||
|
||||
register_message_builder("server_closed", (data) => {
|
||||
return data.message ? tra("Server has been closed ({}).", data.message) : tra("Server has been closed.");
|
||||
});
|
||||
|
||||
register_message_builder("connection_command_error", (data) => {
|
||||
let error_message;
|
||||
if(typeof data.error === "string")
|
||||
error_message = data.error;
|
||||
else if(data.error instanceof CommandResult)
|
||||
error_message = data.error.extra_message || data.error.message;
|
||||
else
|
||||
error_message = data.error + "";
|
||||
return tra("Command execution resulted in: {}", error_message);
|
||||
});
|
||||
|
||||
register_message_builder("channel_create", (data) => {
|
||||
if(data.own_action)
|
||||
return tra("Channel {} has been created.", channel_tag(data.channel));
|
||||
return tra("Channel {} has been created by {}.", channel_tag(data.channel), client_tag(data.creator));
|
||||
});
|
||||
|
||||
register_message_builder("channel_delete", (data) => {
|
||||
if(data.own_action)
|
||||
return tra("Channel {} has been deleted.", channel_tag(data.channel));
|
||||
return tra("Channel {] has been deleted by {}.", channel_tag(data.channel), client_tag(data.deleter));
|
||||
});
|
|
@ -1,4 +1,6 @@
|
|||
import * as React from "react";
|
||||
import {parseMessageWithArguments} from "tc-shared/ui/frames/chat";
|
||||
import {cloneElement} from "react";
|
||||
|
||||
let instances = [];
|
||||
export class Translatable extends React.Component<{ message: string, children?: never } | { children: string }, { translated: string }> {
|
||||
|
@ -29,6 +31,29 @@ export class Translatable extends React.Component<{ message: string, children?:
|
|||
}
|
||||
}
|
||||
|
||||
export const VariadicTranslatable = (props: { text: string, children?: React.ReactElement[] | React.ReactElement }) => {
|
||||
const args = Array.isArray(props.children) ? props.children : [props.children];
|
||||
const argsUseCount = [...new Array(args.length)].map(() => 0);
|
||||
|
||||
const translated = /* @tr-ignore */ tr(props.text);
|
||||
|
||||
return (<>
|
||||
{
|
||||
parseMessageWithArguments(translated, args.length).map(e => {
|
||||
if(typeof e === "string")
|
||||
return e;
|
||||
|
||||
let element = args[e];
|
||||
if(argsUseCount[e])
|
||||
element = cloneElement(element);
|
||||
argsUseCount[e]++;
|
||||
|
||||
return <React.Fragment key={"argument-" + e + "-" + argsUseCount[e]}>{element}</React.Fragment>;
|
||||
})
|
||||
}
|
||||
</>);
|
||||
};
|
||||
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
|
|
@ -7,7 +7,6 @@ import {PermissionType} from "tc-shared/permission/PermissionType";
|
|||
import {KeyCode, SpecialKey} from "tc-shared/PPTListener";
|
||||
import {Sound} from "tc-shared/sound/Sounds";
|
||||
import {Group} from "tc-shared/permission/GroupManager";
|
||||
import * as server_log from "tc-shared/ui/frames/server_log";
|
||||
import {ServerAddress, ServerEntry} from "tc-shared/ui/server";
|
||||
import {ChannelEntry, ChannelProperties, ChannelSubscribeMode} from "tc-shared/ui/channel";
|
||||
import {ClientEntry, LocalClientEntry, MusicClientEntry} from "tc-shared/ui/client";
|
||||
|
@ -27,6 +26,7 @@ import {formatMessage} from "tc-shared/ui/frames/chat";
|
|||
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
|
||||
import {tra} from "tc-shared/i18n/localize";
|
||||
import {TreeEntryMove} from "tc-shared/ui/tree/TreeEntryMove";
|
||||
import {EventType} from "tc-shared/ui/frames/log/Definitions";
|
||||
|
||||
export interface ChannelTreeEvents {
|
||||
action_select_entries: {
|
||||
|
@ -898,10 +898,9 @@ export class ChannelTree {
|
|||
|
||||
return new Promise<ChannelEntry>(resolve => { resolve(channel); })
|
||||
}).then(channel => {
|
||||
this.client.log.log(server_log.Type.CHANNEL_CREATE, {
|
||||
this.client.log.log(EventType.CHANNEL_CREATE_OWN, {
|
||||
channel: channel.log_data(),
|
||||
creator: this.client.getClient().log_data(),
|
||||
own_action: true
|
||||
});
|
||||
this.client.sound.play(Sound.CHANNEL_CREATED);
|
||||
});
|
||||
|
|
|
@ -16,21 +16,5 @@ const NumberRenderer = (props: { events: Registry<VideoViewerEvents> }) => {
|
|||
export function spawnVideoPopout() {
|
||||
const registry = new Registry<VideoViewerEvents>();
|
||||
const modalController = spawnExternalModal("video-viewer", registry, {});
|
||||
modalController.open().then(() => {
|
||||
const url = URL.createObjectURL(new Blob(["Hello World"], { type: "plain/text" }));
|
||||
registry.fire("notify_data_url", { url: url });
|
||||
});
|
||||
|
||||
spawnReactModal(class extends Modal {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
title() {
|
||||
return "Hello World";
|
||||
}
|
||||
renderBody() {
|
||||
return <h1>Hello World: <NumberRenderer events={registry} /></h1>;
|
||||
}
|
||||
}).show();
|
||||
modalController.open();
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
.container {
|
||||
background: #19191b;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
min-height: 10em;
|
||||
min-width: 20em;
|
||||
|
||||
padding-left: 4em;
|
||||
}
|
|
@ -4,7 +4,9 @@ import * as React from "react";
|
|||
import {Registry} from "tc-shared/events";
|
||||
import {VideoViewerEvents} from "./Definitions";
|
||||
import {LoadingDots} from "tc-shared/ui/react-elements/LoadingDots";
|
||||
import {Slider} from "tc-shared/ui/react-elements/Slider";
|
||||
import ReactPlayer from 'react-player'
|
||||
|
||||
const cssStyle = require("./Renderer.scss");
|
||||
|
||||
class ModalVideoPopout extends AbstractModal {
|
||||
readonly events: Registry<VideoViewerEvents>;
|
||||
|
@ -28,8 +30,31 @@ class ModalVideoPopout extends AbstractModal {
|
|||
}
|
||||
|
||||
renderBody(): React.ReactElement {
|
||||
return <div style={{ padding: "10em" }}>
|
||||
<Slider value={100} minValue={0} maxValue={100} stepSize={1} onInput={value => this.events.fire("notify_value", { value: value })} />
|
||||
return <div className={cssStyle.container} >
|
||||
<ReactPlayer
|
||||
url={"https://www.youtube.com/watch?v=u_TuibFg-GA"}
|
||||
height={"100%"}
|
||||
width={"100%"}
|
||||
|
||||
onError={(error, data, hlsInstance, hlsGlobal) => console.log("onError(%o, %o, %o, %o)", error, data, hlsInstance, hlsGlobal)}
|
||||
onBuffer={() => console.log("onBuffer()")}
|
||||
onBufferEnd={() => console.log("onBufferEnd()")}
|
||||
onDisablePIP={() => console.log("onDisabledPIP()")}
|
||||
onEnablePIP={() => console.log("onEnablePIP()")}
|
||||
onDuration={duration => console.log("onDuration(%o)", duration)}
|
||||
onEnded={() => console.log("onEnded()")}
|
||||
onPause={() => console.log("onPause()")}
|
||||
onPlay={() => console.log("onPlay()")}
|
||||
onProgress={state => console.log("onProgress(%o)", state)}
|
||||
onReady={() => console.log("onReady()")}
|
||||
onSeek={seconds => console.log("onSeek(%o)", seconds)}
|
||||
onStart={() => console.log("onStart()")}
|
||||
|
||||
controls={true}
|
||||
|
||||
loop={false}
|
||||
light={false}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,9 @@ import * as log from "tc-shared/log";
|
|||
import {LogCategory} from "tc-shared/log";
|
||||
import {Regex} from "tc-shared/ui/modal/ModalConnect";
|
||||
import {AbstractCommandHandlerBoss} from "tc-shared/connection/AbstractCommandHandler";
|
||||
import * as elog from "tc-shared/ui/frames/server_log";
|
||||
import {VoiceConnection} from "../voice/VoiceHandler";
|
||||
import AbstractVoiceConnection = voice.AbstractVoiceConnection;
|
||||
import {EventType} from "tc-shared/ui/frames/log/Definitions";
|
||||
|
||||
class ReturnListener<T> {
|
||||
resolve: (value?: T | PromiseLike<T>) => void;
|
||||
|
@ -290,7 +290,7 @@ export class ServerConnection extends AbstractServerConnection {
|
|||
|
||||
private start_handshake() {
|
||||
this.updateConnectionState(ConnectionState.INITIALISING);
|
||||
this.client.log.log(elog.Type.CONNECTION_LOGIN, {});
|
||||
this.client.log.log(EventType.CONNECTION_LOGIN, {});
|
||||
this._handshakeHandler.initialize();
|
||||
this._handshakeHandler.startHandshake();
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import * as log from "tc-shared/log";
|
|||
import {LogCategory} from "tc-shared/log";
|
||||
import * as loader from "tc-loader";
|
||||
import * as aplayer from "../audio/player";
|
||||
import * as elog from "tc-shared/ui/frames/server_log";
|
||||
import {BasicCodec} from "../codec/BasicCodec";
|
||||
import {CodecType} from "../codec/Codec";
|
||||
import {createErrorModal} from "tc-shared/ui/elements/Modal";
|
||||
|
@ -16,6 +15,7 @@ import {CallbackInputConsumer, InputConsumerType, NodeInputConsumer} from "tc-sh
|
|||
import AbstractVoiceConnection = voice.AbstractVoiceConnection;
|
||||
import VoiceClient = voice.VoiceClient;
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {EventType} from "tc-shared/ui/frames/log/Definitions";
|
||||
|
||||
export namespace codec {
|
||||
class CacheEntry {
|
||||
|
@ -508,7 +508,7 @@ export class VoiceConnection extends AbstractVoiceConnection {
|
|||
} else if(json["request"] == "status") {
|
||||
if(json["state"] == "failed") {
|
||||
const chandler = this.connection.client;
|
||||
chandler.log.log(elog.Type.CONNECTION_VOICE_SETUP_FAILED, {
|
||||
chandler.log.log(EventType.CONNECTION_VOICE_SETUP_FAILED, {
|
||||
reason: json["reason"],
|
||||
reconnect_delay: json["allow_reconnect"] ? 1 : 0
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue