A lot of updated related to the menu bar and the basic event handler system
parent
cccd780724
commit
f7d6671457
|
@ -1,4 +1,10 @@
|
|||
# Changelog:
|
||||
* **09.03.20**
|
||||
- Using React for the client control bar
|
||||
- Saving last away state and message
|
||||
- Saving last query show state
|
||||
- Removing the hostbutton when we're disconnected from the server
|
||||
|
||||
* **04.03.20**
|
||||
- Implemented the new music bot playlist song list
|
||||
- Implemented the missing server log message builders
|
||||
|
|
|
@ -2,15 +2,14 @@ import {ChannelTree} from "tc-shared/ui/view";
|
|||
import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase";
|
||||
import {PermissionManager} from "tc-shared/permission/PermissionManager";
|
||||
import {GroupManager} from "tc-shared/permission/GroupManager";
|
||||
import {ServerSettings, Settings, StaticSettings} from "tc-shared/settings";
|
||||
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 {ServerLog} from "tc-shared/ui/frames/server_log";
|
||||
import * as server_log 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";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import * as server_log from "tc-shared/ui/frames/server_log";
|
||||
import {createErrorModal, createInfoModal, createInputModal, Modal} from "tc-shared/ui/elements/Modal";
|
||||
import {hashPassword} from "tc-shared/utils/helpers";
|
||||
import {HandshakeHandler} from "tc-shared/connection/HandshakeHandler";
|
||||
|
@ -31,7 +30,8 @@ import {spawnAvatarUpload} from "tc-shared/ui/modal/ModalAvatar";
|
|||
import * as connection from "tc-backend/connection";
|
||||
import * as dns from "tc-backend/dns";
|
||||
import * as top_menu from "tc-shared/ui/frames/MenuBar";
|
||||
import {control_bar_instance} from "tc-shared/ui/frames/control-bar";
|
||||
import {EventHandler, Registry} from "tc-shared/events";
|
||||
import {ServerLog} from "tc-shared/ui/frames/server_log";
|
||||
|
||||
export enum DisconnectReason {
|
||||
HANDLER_DESTROYED,
|
||||
|
@ -54,11 +54,25 @@ export enum DisconnectReason {
|
|||
}
|
||||
|
||||
export enum ConnectionState {
|
||||
UNCONNECTED,
|
||||
CONNECTING,
|
||||
INITIALISING,
|
||||
CONNECTED,
|
||||
DISCONNECTING
|
||||
UNCONNECTED, /* no connection is currenting running */
|
||||
CONNECTING, /* we try to establish a connection to the target server */
|
||||
INITIALISING, /* we're setting up the connection encryption */
|
||||
AUTHENTICATING, /* we're authenticating ourself so we get a unique ID */
|
||||
CONNECTED, /* we're connected to the server. Server init has been done, may not everything is initialized */
|
||||
DISCONNECTING/* we're curently disconnecting from the server and awaiting disconnect acknowledge */
|
||||
}
|
||||
|
||||
export namespace ConnectionState {
|
||||
export function socket_connected(state: ConnectionState) {
|
||||
switch (state) {
|
||||
case ConnectionState.CONNECTED:
|
||||
case ConnectionState.AUTHENTICATING:
|
||||
//case ConnectionState.INITIALISING: /* its not yet possible to send any data */
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export enum ViewReasonId {
|
||||
|
@ -76,7 +90,7 @@ export enum ViewReasonId {
|
|||
VREASON_SERVER_SHUTDOWN = 11
|
||||
}
|
||||
|
||||
export interface VoiceStatus {
|
||||
export interface LocalClientStatus {
|
||||
input_hardware: boolean;
|
||||
input_muted: boolean;
|
||||
output_muted: boolean;
|
||||
|
@ -106,6 +120,7 @@ export interface ConnectParameters {
|
|||
|
||||
declare const native_client;
|
||||
export class ConnectionHandler {
|
||||
private readonly event_registry: Registry<ConnectionEvents>;
|
||||
channelTree: ChannelTree;
|
||||
|
||||
serverConnection: AbstractServerConnection;
|
||||
|
@ -132,7 +147,7 @@ export class ConnectionHandler {
|
|||
|
||||
private _connect_initialize_id: number = 1;
|
||||
|
||||
client_status: VoiceStatus = {
|
||||
private client_status: LocalClientStatus = {
|
||||
input_hardware: false,
|
||||
input_muted: false,
|
||||
output_muted: false,
|
||||
|
@ -150,6 +165,9 @@ export class ConnectionHandler {
|
|||
log: ServerLog;
|
||||
|
||||
constructor() {
|
||||
this.event_registry = new Registry<ConnectionEvents>();
|
||||
this.event_registry.enable_debug("connection-handler");
|
||||
|
||||
this.settings = new ServerSettings();
|
||||
|
||||
this.log = new ServerLog(this);
|
||||
|
@ -178,14 +196,30 @@ export class ConnectionHandler {
|
|||
if(event.isDefaultPrevented())
|
||||
return;
|
||||
|
||||
server_connections.set_active_connection_handler(this);
|
||||
server_connections.set_active_connection(this);
|
||||
});
|
||||
this.tag_connection_handler.find(".button-close").on('click', event => {
|
||||
server_connections.destroy_server_connection_handler(this);
|
||||
server_connections.destroy_server_connection(this);
|
||||
event.preventDefault();
|
||||
});
|
||||
this.tab_set_name(tr("Not connected"));
|
||||
}
|
||||
|
||||
this.event_registry.register_handler(this);
|
||||
}
|
||||
|
||||
initialize_client_state(source?: ConnectionHandler) {
|
||||
this.client_status.input_muted = source ? source.client_status.input_muted : settings.global(Settings.KEY_CLIENT_STATE_MICROPHONE_MUTED);
|
||||
this.client_status.output_muted = source ? source.client_status.output_muted : settings.global(Settings.KEY_CLIENT_STATE_SPEAKER_MUTED);
|
||||
this.update_voice_status();
|
||||
|
||||
this.setSubscribeToAllChannels(source ? source.client_status.channel_subscribe_all : settings.global(Settings.KEY_CLIENT_STATE_SUBSCRIBE_ALL_CHANNELS));
|
||||
this.setAway_(source ? source.client_status.away : (settings.global(Settings.KEY_CLIENT_STATE_AWAY) ? settings.global(Settings.KEY_CLIENT_AWAY_MESSAGE) : true), false);
|
||||
this.setQueriesShown(source ? source.client_status.queries_visible : settings.global(Settings.KEY_CLIENT_STATE_QUERY_SHOWN));
|
||||
}
|
||||
|
||||
events() : Registry<ConnectionEvents> {
|
||||
return this.event_registry;
|
||||
}
|
||||
|
||||
tab_set_name(name: string) {
|
||||
|
@ -193,8 +227,6 @@ export class ConnectionHandler {
|
|||
this.tag_connection_handler.find(".server-name").text(name);
|
||||
}
|
||||
|
||||
setup() { }
|
||||
|
||||
async startConnection(addr: string, profile: ConnectionProfile, user_action: boolean, parameters: ConnectParameters) {
|
||||
this.tab_set_name(tr("Connecting"));
|
||||
this.cancel_reconnect(false);
|
||||
|
@ -283,6 +315,17 @@ export class ConnectionHandler {
|
|||
}, 50);
|
||||
}
|
||||
|
||||
async disconnectFromServer(reason?: string) {
|
||||
this.cancel_reconnect(true);
|
||||
this.handleDisconnect(DisconnectReason.REQUESTED); //TODO message?
|
||||
try {
|
||||
await this.serverConnection.disconnect();
|
||||
} catch (error) {
|
||||
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, {});
|
||||
}
|
||||
|
||||
getClient() : LocalClientEntry { return this._local_client; }
|
||||
getClientId() { return this._clientId; }
|
||||
|
@ -299,16 +342,20 @@ export class ConnectionHandler {
|
|||
getServerConnection() : AbstractServerConnection { return this.serverConnection; }
|
||||
|
||||
|
||||
/**
|
||||
* LISTENER
|
||||
*/
|
||||
onConnected() {
|
||||
@EventHandler<ConnectionEvents>("notify_connection_state_changed")
|
||||
private handleConnectionConnected(event: ConnectionEvents["notify_connection_state_changed"]) {
|
||||
if(event.new_state !== ConnectionState.CONNECTED) return;
|
||||
log.info(LogCategory.CLIENT, tr("Client connected"));
|
||||
this.log.log(server_log.Type.CONNECTION_CONNECTED, {
|
||||
own_client: this.getClient().log_data()
|
||||
});
|
||||
this.sound.play(Sound.CONNECTION_CONNECTED);
|
||||
|
||||
this.permissions.requestPermissionList();
|
||||
if(this.groups.serverGroups.length == 0)
|
||||
this.groups.requestGroups();
|
||||
|
||||
this.initialize_server_settings();
|
||||
this.settings.setServer(this.channelTree.server.properties.virtualserver_unique_identifier);
|
||||
|
||||
/* apply the server settings */
|
||||
if(this.client_status.channel_subscribe_all)
|
||||
|
@ -327,27 +374,6 @@ export class ConnectionHandler {
|
|||
*/
|
||||
}
|
||||
|
||||
private initialize_server_settings() {
|
||||
let update_control = false;
|
||||
this.settings.setServer(this.channelTree.server.properties.virtualserver_unique_identifier);
|
||||
{
|
||||
const flag_subscribe = this.settings.server(Settings.KEY_CONTROL_CHANNEL_SUBSCRIBE_ALL, true);
|
||||
if(this.client_status.channel_subscribe_all != flag_subscribe) {
|
||||
this.client_status.channel_subscribe_all = flag_subscribe;
|
||||
update_control = true;
|
||||
}
|
||||
}
|
||||
{
|
||||
const flag_query = this.settings.server(Settings.KEY_CONTROL_SHOW_QUERIES, false);
|
||||
if(this.client_status.queries_visible != flag_query) {
|
||||
this.client_status.queries_visible = flag_query;
|
||||
update_control = true;
|
||||
}
|
||||
}
|
||||
|
||||
control_bar_instance()?.events().fire("server_updated", { category: "settings-initialized", handler: this });
|
||||
}
|
||||
|
||||
get connected() : boolean {
|
||||
return this.serverConnection && this.serverConnection.connected();
|
||||
}
|
||||
|
@ -606,7 +632,6 @@ export class ConnectionHandler {
|
|||
if(this.serverConnection)
|
||||
this.serverConnection.disconnect();
|
||||
|
||||
this.on_connection_state_changed(); /* really required to call? */
|
||||
this.side_bar.private_conversations().clear_client_ids();
|
||||
this.hostbanner.update();
|
||||
|
||||
|
@ -639,12 +664,16 @@ export class ConnectionHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private on_connection_state_changed() {
|
||||
control_bar_instance()?.events().fire("server_updated", { category: "connection-state", handler: this });
|
||||
private on_connection_state_changed(old_state: ConnectionState, new_state: ConnectionState) {
|
||||
this.event_registry.fire("notify_connection_state_changed", {
|
||||
old_state: old_state,
|
||||
new_state: new_state
|
||||
});
|
||||
}
|
||||
|
||||
private _last_record_error_popup: number;
|
||||
update_voice_status(targetChannel?: ChannelEntry) {
|
||||
//TODO: Simplify this
|
||||
if(!this._local_client) return; /* we've been destroyed */
|
||||
|
||||
targetChannel = targetChannel || this.getClient().currentChannel();
|
||||
|
@ -748,9 +777,14 @@ export class ConnectionHandler {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
control_bar_instance()?.events().fire("server_updated", { category: "audio", handler: this });
|
||||
top_menu.update_state(); //TODO: Only run "small" update?
|
||||
//TODO: Only trigger events for stuff which has been updated
|
||||
this.event_registry.fire("notify_state_updated", {
|
||||
state: "microphone"
|
||||
});
|
||||
this.event_registry.fire("notify_state_updated", {
|
||||
state: "speaker"
|
||||
});
|
||||
top_menu.update_state(); //TODO: Top-Menu should register their listener
|
||||
}
|
||||
|
||||
sync_status_with_server() {
|
||||
|
@ -768,35 +802,13 @@ export class ConnectionHandler {
|
|||
});
|
||||
}
|
||||
|
||||
set_away_status(state: boolean | string, update_control_bar: boolean) {
|
||||
if(this.client_status.away === state)
|
||||
return;
|
||||
|
||||
if(state) {
|
||||
this.sound.play(Sound.AWAY_ACTIVATED);
|
||||
} else {
|
||||
this.sound.play(Sound.AWAY_DEACTIVATED);
|
||||
}
|
||||
|
||||
this.client_status.away = state;
|
||||
this.serverConnection.send_command("clientupdate", {
|
||||
client_away: typeof(this.client_status.away) === "string" || this.client_status.away,
|
||||
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.")});
|
||||
});
|
||||
|
||||
if(update_control_bar)
|
||||
control_bar_instance()?.events().fire("server_updated", { category: "away-status", handler: this });
|
||||
}
|
||||
|
||||
resize_elements() {
|
||||
this.channelTree.handle_resized();
|
||||
this.invoke_resized_on_activate = false;
|
||||
}
|
||||
|
||||
acquire_recorder(voice_recoder: RecorderProfile, update_control_bar: boolean) {
|
||||
/* TODO: If the voice connection hasn't been set upped cache the target recorder */
|
||||
const vconnection = this.serverConnection.voice_connection();
|
||||
(vconnection ? vconnection.acquire_voice_recorder(voice_recoder) : Promise.resolve()).catch(error => {
|
||||
log.warn(LogCategory.VOICE, tr("Failed to acquire recorder (%o)"), error);
|
||||
|
@ -805,6 +817,8 @@ export class ConnectionHandler {
|
|||
});
|
||||
}
|
||||
|
||||
getVoiceRecorder() :RecorderProfile | undefined { return this.serverConnection?.voice_connection()?.voice_recorder(); }
|
||||
|
||||
reconnect_properties(profile?: ConnectionProfile) : ConnectParameters {
|
||||
const name = (this.getClient() ? this.getClient().clientNickName() : "") ||
|
||||
(this.serverConnection && this.serverConnection.handshake_handler() ? this.serverConnection.handshake_handler().parameters.nickname : "") ||
|
||||
|
@ -913,6 +927,7 @@ export class ConnectionHandler {
|
|||
}
|
||||
|
||||
destroy() {
|
||||
this.event_registry.unregister_handler(this);
|
||||
this.cancel_reconnect(true);
|
||||
|
||||
this.tag_connection_handler && this.tag_connection_handler.remove();
|
||||
|
@ -954,4 +969,107 @@ export class ConnectionHandler {
|
|||
this.sound = undefined;
|
||||
this._local_client = undefined;
|
||||
}
|
||||
|
||||
/* state changing methods */
|
||||
setMicrophoneMuted(muted: boolean) {
|
||||
if(this.client_status.input_muted === muted) return;
|
||||
this.client_status.input_muted = muted;
|
||||
this.sound.play(muted ? Sound.MICROPHONE_MUTED : Sound.MICROPHONE_ACTIVATED);
|
||||
this.update_voice_status();
|
||||
}
|
||||
|
||||
isMicrophoneMuted() { return this.client_status.input_muted; }
|
||||
|
||||
/*
|
||||
* Returns whatever the client is able to talk or not. Reasons for returning true could be:
|
||||
* - Channel codec isn't supported
|
||||
* - No recorder has been acquired
|
||||
* - Voice bridge hasn't been set upped yet
|
||||
*/
|
||||
isMicrophoneDisabled() { return !this.client_status.input_hardware; }
|
||||
|
||||
setSpeakerMuted(muted: boolean) {
|
||||
if(this.client_status.output_muted === muted) return;
|
||||
if(muted) this.sound.play(Sound.SOUND_MUTED); /* play the sound *before* we're setting the muted state */
|
||||
this.client_status.output_muted = muted;
|
||||
if(!muted) this.sound.play(Sound.SOUND_ACTIVATED); /* play the sound *after* we're setting we've unmuted the sound */
|
||||
this.update_voice_status();
|
||||
}
|
||||
|
||||
isSpeakerMuted() { return this.client_status.output_muted; }
|
||||
|
||||
/*
|
||||
* Returns whatever the client is able to playback sound (voice). Reasons for returning true could be:
|
||||
* - Channel codec isn't supported
|
||||
* - Voice bridge hasn't been set upped yet
|
||||
*/
|
||||
//TODO: This currently returns false
|
||||
isSpeakerDisabled() { return false; }
|
||||
|
||||
setSubscribeToAllChannels(flag: boolean) {
|
||||
if(this.client_status.channel_subscribe_all === flag) return;
|
||||
this.client_status.channel_subscribe_all = flag;
|
||||
if(flag)
|
||||
this.channelTree.subscribe_all_channels();
|
||||
else
|
||||
this.channelTree.unsubscribe_all_channels();
|
||||
this.event_registry.fire("notify_state_updated", { state: "subscribe" });
|
||||
}
|
||||
|
||||
isSubscribeToAllChannels() { return this.client_status.channel_subscribe_all; }
|
||||
|
||||
setAway(state: boolean | string) {
|
||||
this.setAway_(state, true);
|
||||
}
|
||||
|
||||
private setAway_(state: boolean | string, play_sound: boolean) {
|
||||
if(this.client_status.away === state)
|
||||
return;
|
||||
|
||||
const was_away = this.isAway();
|
||||
const will_away = typeof state === "boolean" ? state : true;
|
||||
if(was_away != will_away && play_sound)
|
||||
this.sound.play(will_away ? Sound.AWAY_ACTIVATED : Sound.AWAY_DEACTIVATED);
|
||||
|
||||
this.client_status.away = state;
|
||||
this.serverConnection.send_command("clientupdate", {
|
||||
client_away: typeof(this.client_status.away) === "string" || this.client_status.away,
|
||||
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.event_registry.fire("notify_state_updated", {
|
||||
state: "away"
|
||||
});
|
||||
}
|
||||
|
||||
isAway() : boolean { return typeof this.client_status.away !== "boolean" || this.client_status.away; }
|
||||
|
||||
setQueriesShown(flag: boolean) {
|
||||
if(this.client_status.queries_visible === flag) return;
|
||||
this.client_status.queries_visible = flag;
|
||||
this.channelTree.toggle_server_queries(flag);
|
||||
|
||||
this.event_registry.fire("notify_state_updated", {
|
||||
state: "query"
|
||||
});
|
||||
}
|
||||
|
||||
areQueriesShown() {
|
||||
return this.client_status.queries_visible;
|
||||
}
|
||||
}
|
||||
|
||||
export type ConnectionStateUpdateType = "microphone" | "speaker" | "away" | "subscribe" | "query";
|
||||
export interface ConnectionEvents {
|
||||
notify_state_updated: {
|
||||
state: ConnectionStateUpdateType;
|
||||
}
|
||||
|
||||
notify_connection_state_changed: {
|
||||
old_state: ConnectionState,
|
||||
new_state: ConnectionState
|
||||
}
|
||||
}
|
|
@ -5,14 +5,15 @@ import {createErrorModal, createInfoModal, createInputModal} from "tc-shared/ui/
|
|||
import {default_profile, find_profile} from "tc-shared/profiles/ConnectionProfile";
|
||||
import {server_connections} from "tc-shared/ui/frames/connection_handlers";
|
||||
import {spawnConnectModal} from "tc-shared/ui/modal/ModalConnect";
|
||||
import {control_bar} from "tc-shared/ui/frames/ControlBar";
|
||||
import * as top_menu from "./ui/frames/MenuBar";
|
||||
import {control_bar_instance} from "tc-shared/ui/frames/control-bar";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
|
||||
export const boorkmak_connect = (mark: Bookmark, new_tab?: boolean) => {
|
||||
const profile = find_profile(mark.connect_profile) || default_profile();
|
||||
if(profile.valid()) {
|
||||
const connection = (typeof(new_tab) !== "boolean" || !new_tab) ? server_connections.active_connection_handler() : server_connections.spawn_server_connection_handler();
|
||||
server_connections.set_active_connection_handler(connection);
|
||||
const connection = (typeof(new_tab) !== "boolean" || !new_tab) ? server_connections.active_connection() : server_connections.spawn_server_connection();
|
||||
server_connections.set_active_connection(connection);
|
||||
connection.startConnection(
|
||||
mark.server_properties.server_address + ":" + mark.server_properties.server_port,
|
||||
profile,
|
||||
|
@ -232,23 +233,23 @@ export function delete_bookmark(bookmark: Bookmark | DirectoryBookmark) {
|
|||
delete_bookmark_recursive(bookmarks(), bookmark)
|
||||
}
|
||||
|
||||
export function add_current_server() {
|
||||
const ch = server_connections.active_connection_handler();
|
||||
if(ch && ch.connected) {
|
||||
const ce = ch.getClient();
|
||||
export function add_server_to_bookmarks(server: ConnectionHandler) {
|
||||
if(server && server.connected) {
|
||||
const ce = server.getClient();
|
||||
const name = ce ? ce.clientNickName() : undefined;
|
||||
createInputModal(tr("Enter bookmarks name"), tr("Please enter the bookmarks name:<br>"), text => text.length > 0, result => {
|
||||
if(result) {
|
||||
const bookmark = create_bookmark(result as string, bookmarks(), {
|
||||
server_port: ch.serverConnection.remote_address().port,
|
||||
server_address: ch.serverConnection.remote_address().host,
|
||||
server_port: server.serverConnection.remote_address().port,
|
||||
server_address: server.serverConnection.remote_address().host,
|
||||
|
||||
server_password: "",
|
||||
server_password_hash: ""
|
||||
}, name);
|
||||
save_bookmark(bookmark);
|
||||
|
||||
control_bar.update_bookmarks();
|
||||
control_bar_instance().events().fire("update_state", { state: "bookmarks" });
|
||||
//control_bar.update_bookmarks();
|
||||
top_menu.rebuild_bookmarks();
|
||||
|
||||
createInfoModal(tr("Server added"), tr("Server has been successfully added to your bookmarks.")).open();
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
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 {AbstractServerConnection, CommandOptions, ServerCommand} from "tc-shared/connection/ConnectionBase";
|
||||
import {Sound} from "tc-shared/sound/Sounds";
|
||||
import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {createErrorModal, createInfoModal, createInputModal, createModal} from "tc-shared/ui/elements/Modal";
|
||||
import {
|
||||
ClientConnectionInfo,
|
||||
|
@ -16,7 +14,7 @@ import {
|
|||
SongInfo
|
||||
} from "tc-shared/ui/client";
|
||||
import {ChannelEntry} from "tc-shared/ui/channel";
|
||||
import {ConnectionHandler, DisconnectReason, ViewReasonId} from "tc-shared/ConnectionHandler";
|
||||
import {ConnectionHandler, ConnectionState, DisconnectReason, ViewReasonId} from "tc-shared/ConnectionHandler";
|
||||
import {bbcode_chat, formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {server_connections} from "tc-shared/ui/frames/connection_handlers";
|
||||
import {spawnPoke} from "tc-shared/ui/modal/ModalPoke";
|
||||
|
@ -247,7 +245,7 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
if(properties.virtualserver_ask_for_privilegekey) {
|
||||
createInputModal(tr("Use a privilege key"), tr("This is a newly created server for which administrator privileges have not yet been claimed.<br>Please enter the \"privilege key\" that was automatically generated when this server was created to gain administrator permissions."), message => message.length > 0, result => {
|
||||
if(!result) return;
|
||||
const scon = server_connections.active_connection_handler();
|
||||
const scon = server_connections.active_connection();
|
||||
|
||||
if(scon.serverConnection.connected)
|
||||
scon.serverConnection.send_command("tokenuse", {
|
||||
|
@ -260,11 +258,7 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
}, { field_placeholder: tr("Enter Privilege Key") }).open();
|
||||
}
|
||||
|
||||
this.connection_handler.log.log(server_log.Type.CONNECTION_CONNECTED, {
|
||||
own_client: this.connection_handler.getClient().log_data()
|
||||
});
|
||||
this.connection_handler.sound.play(Sound.CONNECTION_CONNECTED);
|
||||
this.connection.client.onConnected();
|
||||
this.connection.updateConnectionState(ConnectionState.CONNECTED);
|
||||
}
|
||||
|
||||
handleNotifyServerConnectionInfo(json) {
|
||||
|
|
|
@ -47,6 +47,9 @@ export abstract class AbstractServerConnection {
|
|||
abstract remote_address() : ServerAddress; /* only valid when connected */
|
||||
abstract handshake_handler() : HandshakeHandler; /* only valid when connected */
|
||||
|
||||
//FIXME: Remove this this is currently only some kind of hack
|
||||
abstract updateConnectionState(state: ConnectionState);
|
||||
|
||||
abstract ping() : {
|
||||
native: number,
|
||||
javascript?: number
|
||||
|
|
|
@ -110,6 +110,7 @@ export class Registry<Events> {
|
|||
fire<T extends keyof Events>(event_type: T, data?: Events[T]) {
|
||||
if(this.debug_prefix) console.log("[%s] Trigger event: %s", this.debug_prefix, event_type);
|
||||
|
||||
if(typeof data === "object" && 'type' in data) throw tr("The keyword 'type' is reserved for the event type and should not be passed as argument");
|
||||
const event = Object.assign(typeof data === "undefined" ? SingletonEvent.instance : data, {
|
||||
type: event_type,
|
||||
as: function () { return this; }
|
||||
|
@ -130,7 +131,7 @@ export class Registry<Events> {
|
|||
invoke_count++;
|
||||
}
|
||||
if(invoke_count === 0) {
|
||||
console.warn("Event handler (%s) triggered event %s which has no consumers.", this.debug_prefix, event_type);
|
||||
console.warn(tr("Event handler (%s) triggered event %s which has no consumers."), this.debug_prefix, event_type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,6 +155,7 @@ export class Registry<Events> {
|
|||
let registered_events = {};
|
||||
for(const function_name of Object.getOwnPropertyNames(proto)) {
|
||||
if(function_name === "constructor") continue;
|
||||
if(typeof proto[function_name] !== "function") continue;
|
||||
if(typeof proto[function_name][event_annotation_key] !== "object") continue;
|
||||
|
||||
const event_data = proto[function_name][event_annotation_key];
|
||||
|
|
|
@ -7,7 +7,7 @@ import {server_connections} from "tc-shared/ui/frames/connection_handlers";
|
|||
import {createErrorModal, createInfoModal, createInputModal} from "tc-shared/ui/elements/Modal";
|
||||
import {default_recorder} from "tc-shared/voice/RecorderProfile";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import {add_current_server} from "tc-shared/bookmarks";
|
||||
import {add_server_to_bookmarks} from "tc-shared/bookmarks";
|
||||
import {spawnConnectModal} from "tc-shared/ui/modal/ModalConnect";
|
||||
import PermissionType from "tc-shared/permission/PermissionType";
|
||||
import {spawnQueryCreate} from "tc-shared/ui/modal/ModalQuery";
|
||||
|
@ -17,6 +17,7 @@ import {formatMessage} from "tc-shared/ui/frames/chat";
|
|||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {spawnSettingsModal} from "tc-shared/ui/modal/ModalSettings";
|
||||
|
||||
/*
|
||||
function initialize_sounds(event_registry: Registry<ClientGlobalControlEvents>) {
|
||||
{
|
||||
let microphone_muted = undefined;
|
||||
|
@ -38,102 +39,16 @@ function initialize_sounds(event_registry: Registry<ClientGlobalControlEvents>)
|
|||
}
|
||||
}
|
||||
|
||||
function load_default_states() {
|
||||
this.event_registry.fire("action_toggle_speaker", { state: settings.static_global(Settings.KEY_CONTROL_MUTE_OUTPUT, false) });
|
||||
this.event_registry.fire("action_toggle_microphone", { state: settings.static_global(Settings.KEY_CONTROL_MUTE_INPUT, false) });
|
||||
export function load_default_states(event_registry: Registry<ClientGlobalControlEvents>) {
|
||||
event_registry.fire("action_toggle_speaker", { state: settings.static_global(Settings.KEY_CONTROL_MUTE_OUTPUT, false) });
|
||||
event_registry.fire("action_toggle_microphone", { state: settings.static_global(Settings.KEY_CONTROL_MUTE_INPUT, false) });
|
||||
}
|
||||
*/
|
||||
|
||||
export function initialize(event_registry: Registry<ClientGlobalControlEvents>) {
|
||||
let current_connection_handler: ConnectionHandler | undefined;
|
||||
event_registry.on("action_set_active_connection_handler", event => { current_connection_handler = event.handler; });
|
||||
|
||||
initialize_sounds(event_registry);
|
||||
|
||||
/* away state handler */
|
||||
event_registry.on("action_set_away", event => {
|
||||
const set_away = message => {
|
||||
for(const connection of event.globally ? server_connections.server_connection_handlers() : [server_connections.active_connection_handler()]) {
|
||||
if(!connection) continue;
|
||||
|
||||
connection.set_away_status(typeof message === "string" && !!message ? message : true, false);
|
||||
}
|
||||
control_bar_instance()?.events()?.fire("update_state", { state: "away" });
|
||||
};
|
||||
|
||||
if(event.prompt_reason) {
|
||||
createInputModal(tr("Set away message"), tr("Please enter your away message"), () => true, message => {
|
||||
if(typeof(message) === "string")
|
||||
set_away(message);
|
||||
}).open();
|
||||
} else {
|
||||
set_away(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
event_registry.on("action_disable_away", event => {
|
||||
for(const connection of event.globally ? server_connections.server_connection_handlers() : [server_connections.active_connection_handler()]) {
|
||||
if(!connection) continue;
|
||||
|
||||
connection.set_away_status(false, false);
|
||||
}
|
||||
|
||||
control_bar_instance()?.events()?.fire("update_state", { state: "away" });
|
||||
});
|
||||
|
||||
|
||||
event_registry.on("action_toggle_microphone", event => {
|
||||
/* just update the last changed value */
|
||||
settings.changeGlobal(Settings.KEY_CONTROL_MUTE_INPUT, !event.state);
|
||||
|
||||
if(current_connection_handler) {
|
||||
current_connection_handler.client_status.input_muted = !event.state;
|
||||
if(!current_connection_handler.client_status.input_hardware)
|
||||
current_connection_handler.acquire_recorder(default_recorder, true); /* acquire_recorder already updates the voice status */
|
||||
else
|
||||
current_connection_handler.update_voice_status(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
event_registry.on("action_toggle_speaker", event => {
|
||||
/* just update the last changed value */
|
||||
settings.changeGlobal(Settings.KEY_CONTROL_MUTE_OUTPUT, !event.state);
|
||||
|
||||
if(!current_connection_handler) return;
|
||||
|
||||
current_connection_handler.client_status.output_muted = !event.state;
|
||||
current_connection_handler.update_voice_status(undefined);
|
||||
});
|
||||
|
||||
event_registry.on("action_set_channel_subscribe_mode", event => {
|
||||
if(!current_connection_handler) return;
|
||||
|
||||
current_connection_handler.client_status.channel_subscribe_all = event.subscribe;
|
||||
if(event.subscribe)
|
||||
current_connection_handler.channelTree.subscribe_all_channels();
|
||||
else
|
||||
current_connection_handler.channelTree.unsubscribe_all_channels(true);
|
||||
current_connection_handler.settings.changeServer(Settings.KEY_CONTROL_CHANNEL_SUBSCRIBE_ALL, event.subscribe);
|
||||
});
|
||||
|
||||
event_registry.on("action_toggle_query", event => {
|
||||
if(!current_connection_handler) return;
|
||||
|
||||
current_connection_handler.client_status.queries_visible = event.shown;
|
||||
current_connection_handler.channelTree.toggle_server_queries(event.shown);
|
||||
current_connection_handler.settings.changeServer(Settings.KEY_CONTROL_SHOW_QUERIES, event.shown);
|
||||
});
|
||||
|
||||
event_registry.on("action_add_current_server_to_bookmarks", () => add_current_server());
|
||||
|
||||
event_registry.on("action_open_connect", event => {
|
||||
current_connection_handler?.cancel_reconnect(true);
|
||||
spawnConnectModal({
|
||||
default_connect_new_tab: event.new_tab
|
||||
}, {
|
||||
url: "ts.TeaSpeak.de",
|
||||
enforce: false
|
||||
});
|
||||
});
|
||||
server_connections.events().on("notify_active_handler_changed", event => current_connection_handler = event.new_handler);
|
||||
//initialize_sounds(event_registry);
|
||||
|
||||
event_registry.on("action_open_window", event => {
|
||||
const handle_import_error = error => {
|
||||
|
@ -233,5 +148,9 @@ export function initialize(event_registry: Registry<ClientGlobalControlEvents>)
|
|||
}
|
||||
});
|
||||
|
||||
load_default_states();
|
||||
event_registry.on("action_open_window_connect", event => {
|
||||
spawnConnectModal({
|
||||
default_connect_new_tab: event.new_tab
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,49 +1,17 @@
|
|||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {Registry} from "tc-shared/events";
|
||||
|
||||
export interface ClientGlobalControlEvents {
|
||||
action_set_channel_subscribe_mode: {
|
||||
subscribe: boolean
|
||||
},
|
||||
action_disconnect: {
|
||||
globally: boolean
|
||||
},
|
||||
action_open_connect: {
|
||||
new_tab: boolean
|
||||
},
|
||||
|
||||
action_toggle_microphone: {
|
||||
state: boolean
|
||||
},
|
||||
|
||||
action_toggle_speaker: {
|
||||
state: boolean
|
||||
},
|
||||
|
||||
action_disable_away: {
|
||||
globally: boolean
|
||||
},
|
||||
action_set_away: {
|
||||
globally: boolean;
|
||||
prompt_reason: boolean;
|
||||
},
|
||||
|
||||
action_toggle_query: {
|
||||
shown: boolean
|
||||
},
|
||||
|
||||
/* open a basic window */
|
||||
action_open_window: {
|
||||
window: "bookmark-manage" | "query-manage" | "query-create" | "ban-list" | "permissions" | "token-list" | "token-use" | "settings",
|
||||
connection?: ConnectionHandler
|
||||
},
|
||||
|
||||
action_add_current_server_to_bookmarks: {},
|
||||
action_set_active_connection_handler: {
|
||||
handler?: ConnectionHandler
|
||||
},
|
||||
|
||||
|
||||
//TODO
|
||||
notify_microphone_state_changed: {
|
||||
state: boolean
|
||||
/* some more specific window openings */
|
||||
action_open_window_connect: {
|
||||
new_tab: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const global_client_actions = new Registry<ClientGlobalControlEvents>();
|
|
@ -15,7 +15,7 @@ import * as stats from "./stats";
|
|||
import * as fidentity from "./profiles/identities/TeaForumIdentity";
|
||||
import {default_recorder, RecorderProfile, set_default_recorder} from "tc-shared/voice/RecorderProfile";
|
||||
import * as cmanager from "tc-shared/ui/frames/connection_handlers";
|
||||
import {server_connections, ServerConnectionManager} from "tc-shared/ui/frames/connection_handlers";
|
||||
import {server_connections, ConnectionManager} from "tc-shared/ui/frames/connection_handlers";
|
||||
import {spawnConnectModal} from "tc-shared/ui/modal/ModalConnect";
|
||||
import * as top_menu from "./ui/frames/MenuBar";
|
||||
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
|
||||
|
@ -28,8 +28,9 @@ import * as ppt from "tc-backend/ppt";
|
|||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import * as cbar from "./ui/frames/control-bar";
|
||||
import * as global_ev_handler from "./events/ClientGlobalControlHandler";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {ClientGlobalControlEvents} from "tc-shared/events/GlobalEvents";
|
||||
import {ClientGlobalControlEvents, global_client_actions} from "tc-shared/events/GlobalEvents";
|
||||
|
||||
/* required import for init */
|
||||
require("./proto").initialize();
|
||||
|
@ -52,14 +53,14 @@ function setup_close() {
|
|||
profiles.save();
|
||||
|
||||
if(!settings.static(Settings.KEY_DISABLE_UNLOAD_DIALOG, false)) {
|
||||
const active_connections = server_connections.server_connection_handlers().filter(e => e.connected);
|
||||
const active_connections = server_connections.all_connections().filter(e => e.connected);
|
||||
if(active_connections.length == 0) return;
|
||||
|
||||
if(!native_client) {
|
||||
event.returnValue = "Are you really sure?<br>You're still connected!";
|
||||
} else {
|
||||
const do_exit = () => {
|
||||
const dp = server_connections.server_connection_handlers().map(e => {
|
||||
const dp = server_connections.all_connections().map(e => {
|
||||
if(e.serverConnection.connected())
|
||||
return e.serverConnection.disconnect(tr("client closed"));
|
||||
return Promise.resolve();
|
||||
|
@ -146,8 +147,6 @@ async function initialize() {
|
|||
bipc.setup();
|
||||
}
|
||||
|
||||
export let client_control_events: Registry<ClientGlobalControlEvents>;
|
||||
|
||||
async function initialize_app() {
|
||||
try { //Initialize main template
|
||||
const main = $("#tmpl_main").renderTag({
|
||||
|
@ -161,16 +160,22 @@ async function initialize_app() {
|
|||
loader.critical_error(tr("Failed to setup main page!"));
|
||||
return;
|
||||
}
|
||||
|
||||
client_control_events = new Registry<ClientGlobalControlEvents>();
|
||||
cmanager.initialize();
|
||||
global_ev_handler.initialize(global_client_actions);
|
||||
{
|
||||
const bar = (
|
||||
<cbar.ControlBar ref={cbar.react_reference()} multiSession={true} />
|
||||
);
|
||||
|
||||
ReactDOM.render(bar, $(".container-control-bar")[0]);
|
||||
cbar.control_bar_instance().load_default_states();
|
||||
}
|
||||
/*
|
||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "settings init",
|
||||
priority: 10,
|
||||
function: async () => global_ev_handler.load_default_states(client_control_events)
|
||||
});
|
||||
*/
|
||||
|
||||
if(!aplayer.initialize())
|
||||
console.warn(tr("Failed to initialize audio controller!"));
|
||||
|
@ -266,7 +271,7 @@ export function handle_connect_request(properties: bipc.connect.ConnectRequestDa
|
|||
hashed: password_hashed
|
||||
} : undefined
|
||||
});
|
||||
server_connections.set_active_connection_handler(connection);
|
||||
server_connections.set_active_connection(connection);
|
||||
} else {
|
||||
spawnConnectModal({},{
|
||||
url: properties.address,
|
||||
|
@ -311,12 +316,9 @@ function main() {
|
|||
|
||||
top_menu.initialize();
|
||||
|
||||
cmanager.initialize(new ServerConnectionManager($("#connection-handlers")));
|
||||
control_bar.control_bar.initialise(); /* before connection handler to allow property apply */
|
||||
|
||||
const initial_handler = server_connections.spawn_server_connection_handler();
|
||||
const initial_handler = server_connections.spawn_server_connection();
|
||||
initial_handler.acquire_recorder(default_recorder, false);
|
||||
control_bar.control_bar.set_connection_handler(initial_handler);
|
||||
cmanager.server_connections.set_active_connection(initial_handler);
|
||||
/** Setup the XF forum identity **/
|
||||
fidentity.update_forum();
|
||||
|
||||
|
@ -328,9 +330,9 @@ function main() {
|
|||
if(_resize_timeout)
|
||||
clearTimeout(_resize_timeout);
|
||||
_resize_timeout = setTimeout(() => {
|
||||
for(const connection of server_connections.server_connection_handlers())
|
||||
for(const connection of server_connections.all_connections())
|
||||
connection.invoke_resized_on_activate = true;
|
||||
const active_connection = server_connections.active_connection_handler();
|
||||
const active_connection = server_connections.active_connection();
|
||||
if(active_connection)
|
||||
active_connection.resize_elements();
|
||||
$(".window-resize-listener").trigger('resize');
|
||||
|
@ -346,13 +348,13 @@ function main() {
|
|||
log.info(LogCategory.STATISTICS, tr("Received user count update: %o"), status);
|
||||
});
|
||||
|
||||
server_connections.set_active_connection_handler(server_connections.server_connection_handlers()[0]);
|
||||
server_connections.set_active_connection(server_connections.all_connections()[0]);
|
||||
|
||||
|
||||
(window as any).test_upload = (message?: string) => {
|
||||
message = message || "Hello World";
|
||||
|
||||
const connection = server_connections.active_connection_handler();
|
||||
const connection = server_connections.active_connection();
|
||||
connection.fileManager.upload_file({
|
||||
size: message.length,
|
||||
overwrite: true,
|
||||
|
@ -376,7 +378,7 @@ function main() {
|
|||
|
||||
/* schedule it a bit later then the main because the main function is still within the loader */
|
||||
setTimeout(() => {
|
||||
const connection = server_connections.active_connection_handler();
|
||||
const connection = server_connections.active_connection();
|
||||
/*
|
||||
Modals.createChannelModal(connection, undefined, undefined, connection.permissions, (cb, perms) => {
|
||||
|
||||
|
@ -512,7 +514,7 @@ const task_connect_handler: loader.Task = {
|
|||
|
||||
loader.register_task(loader.Stage.LOADED, {
|
||||
priority: 0,
|
||||
function: async () => handle_connect_request(connect_data, server_connections.active_connection_handler() || server_connections.spawn_server_connection_handler()),
|
||||
function: async () => handle_connect_request(connect_data, server_connections.active_connection() || server_connections.spawn_server_connection()),
|
||||
name: tr("default url connect")
|
||||
});
|
||||
}
|
||||
|
@ -523,7 +525,7 @@ const task_connect_handler: loader.Task = {
|
|||
};
|
||||
|
||||
chandler.callback_execute = data => {
|
||||
handle_connect_request(data, server_connections.spawn_server_connection_handler());
|
||||
handle_connect_request(data, server_connections.spawn_server_connection());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -184,18 +184,34 @@ export class Settings extends StaticSettings {
|
|||
description: 'Triggers a loading error at the end of the loading process.'
|
||||
};
|
||||
|
||||
/* Control bar */
|
||||
static readonly KEY_CONTROL_MUTE_INPUT: SettingsKey<boolean> = {
|
||||
key: 'mute_input'
|
||||
/* Default client states */
|
||||
static readonly KEY_CLIENT_STATE_MICROPHONE_MUTED: SettingsKey<boolean> = {
|
||||
key: 'client_state_microphone_muted',
|
||||
default_value: false,
|
||||
fallback_keys: ["mute_input"]
|
||||
};
|
||||
static readonly KEY_CONTROL_MUTE_OUTPUT: SettingsKey<boolean> = {
|
||||
key: 'mute_output'
|
||||
static readonly KEY_CLIENT_STATE_SPEAKER_MUTED: SettingsKey<boolean> = {
|
||||
key: 'client_state_speaker_muted',
|
||||
default_value: false,
|
||||
fallback_keys: ["mute_output"]
|
||||
};
|
||||
static readonly KEY_CONTROL_SHOW_QUERIES: SettingsKey<boolean> = {
|
||||
key: 'show_server_queries'
|
||||
static readonly KEY_CLIENT_STATE_QUERY_SHOWN: SettingsKey<boolean> = {
|
||||
key: 'client_state_query_shown',
|
||||
default_value: false,
|
||||
fallback_keys: ["show_server_queries"]
|
||||
};
|
||||
static readonly KEY_CONTROL_CHANNEL_SUBSCRIBE_ALL: SettingsKey<boolean> = {
|
||||
key: 'channel_subscribe_all'
|
||||
static readonly KEY_CLIENT_STATE_SUBSCRIBE_ALL_CHANNELS: SettingsKey<boolean> = {
|
||||
key: 'client_state_subscribe_all_channels',
|
||||
default_value: true,
|
||||
fallback_keys: ["channel_subscribe_all"]
|
||||
};
|
||||
static readonly KEY_CLIENT_STATE_AWAY: SettingsKey<boolean> = {
|
||||
key: 'client_state_away',
|
||||
default_value: false
|
||||
};
|
||||
static readonly KEY_CLIENT_AWAY_MESSAGE: SettingsKey<string> = {
|
||||
key: 'client_away_message',
|
||||
default_value: ""
|
||||
};
|
||||
|
||||
/* Connect parameters */
|
||||
|
@ -367,6 +383,8 @@ export class Settings extends StaticSettings {
|
|||
|
||||
static initialize() {
|
||||
settings = new Settings();
|
||||
(window as any).settings = settings;
|
||||
(window as any).Settings = Settings;
|
||||
}
|
||||
|
||||
private cacheGlobal = {};
|
||||
|
@ -417,9 +435,7 @@ export class Settings extends StaticSettings {
|
|||
|
||||
changeGlobal<T>(key: string | SettingsKey<T>, value?: T){
|
||||
key = Settings.keyify(key);
|
||||
|
||||
|
||||
if(this.cacheGlobal[key.key] == value) return;
|
||||
if(this.cacheGlobal[key.key] === value) return;
|
||||
|
||||
this.updated = true;
|
||||
this.cacheGlobal[key.key] = StaticSettings.transformOtS(value);
|
||||
|
|
|
@ -12,6 +12,8 @@ export abstract class ReactComponentBase<Properties, State> extends React.Compon
|
|||
protected abstract default_state() : State;
|
||||
|
||||
updateState(updates: {[key in keyof State]?: State[key]}) {
|
||||
if(Object.keys(updates).findIndex(e => updates[e] !== this.state[e]) === -1)
|
||||
return; /* no state has been changed */
|
||||
this.setState(Object.assign(this.state, updates));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {Icon, IconManager} from "tc-shared/FileManager";
|
||||
import {spawnBookmarkModal} from "tc-shared/ui/modal/ModalBookmarks";
|
||||
import {
|
||||
add_current_server,
|
||||
add_server_to_bookmarks,
|
||||
Bookmark,
|
||||
bookmarks,
|
||||
BookmarkType,
|
||||
|
@ -23,7 +23,6 @@ import {spawnAbout} from "tc-shared/ui/modal/ModalAbout";
|
|||
import {server_connections} from "tc-shared/ui/frames/connection_handlers";
|
||||
import * as loader from "tc-loader";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import * as slog from "tc-shared/ui/frames/server_log";
|
||||
import {control_bar_instance} from "tc-shared/ui/frames/control-bar";
|
||||
|
||||
export interface HRItem { }
|
||||
|
@ -268,7 +267,7 @@ export function rebuild_bookmarks() {
|
|||
|
||||
_items_bookmark.add_current = _items_bookmark.root.append_item(tr("Add current server to bookmarks"));
|
||||
_items_bookmark.add_current.icon('client-bookmark_add');
|
||||
_items_bookmark.add_current.click(() => add_current_server());
|
||||
_items_bookmark.add_current.click(() => add_server_to_bookmarks(server_connections.active_connection()));
|
||||
_state_updater["bookmarks.ac"] = { item: _items_bookmark.add_current, conditions: [condition_connected]};
|
||||
}
|
||||
|
||||
|
@ -320,7 +319,7 @@ export function update_state() {
|
|||
}
|
||||
|
||||
const condition_connected = () => {
|
||||
const scon = server_connections ? server_connections.active_connection_handler() : undefined;
|
||||
const scon = server_connections ? server_connections.active_connection() : undefined;
|
||||
return scon && scon.connected;
|
||||
};
|
||||
|
||||
|
@ -341,13 +340,8 @@ export function initialize() {
|
|||
item.click(() => spawnConnectModal({}));
|
||||
|
||||
const do_disconnect = (handlers: ConnectionHandler[]) => {
|
||||
for(const handler of handlers) {
|
||||
handler.cancel_reconnect(true);
|
||||
handler.handleDisconnect(DisconnectReason.REQUESTED); //TODO message?
|
||||
server_connections.active_connection_handler().serverConnection.disconnect();
|
||||
handler.sound.play(Sound.CONNECTION_DISCONNECTED);
|
||||
handler.log.log(slog.Type.DISCONNECTED, {});
|
||||
}
|
||||
for(const handler of handlers)
|
||||
handler.disconnectFromServer();
|
||||
|
||||
control_bar_instance()?.events().fire("update_state", { state: "connect-state" });
|
||||
update_state();
|
||||
|
@ -356,7 +350,7 @@ export function initialize() {
|
|||
item.icon('client-disconnect');
|
||||
item.disabled(true);
|
||||
item.click(() => {
|
||||
const handler = server_connections.active_connection_handler();
|
||||
const handler = server_connections.active_connection();
|
||||
do_disconnect([handler]);
|
||||
});
|
||||
_state_updater["connection.dc"] = { item: item, conditions: [() => condition_connected()]};
|
||||
|
@ -364,10 +358,10 @@ export function initialize() {
|
|||
item = menu.append_item(tr("Disconnect from all servers"));
|
||||
item.icon('client-disconnect');
|
||||
item.click(() => {
|
||||
do_disconnect(server_connections.server_connection_handlers());
|
||||
do_disconnect(server_connections.all_connections());
|
||||
});
|
||||
_state_updater["connection.dca"] = { item: item, conditions: [], update_handler: (item) => {
|
||||
item.visible(server_connections && server_connections.server_connection_handlers().length > 1);
|
||||
item.visible(server_connections && server_connections.all_connections().length > 1);
|
||||
return true;
|
||||
}};
|
||||
|
||||
|
@ -394,35 +388,35 @@ export function initialize() {
|
|||
item = menu.append_item(tr("Server Groups"));
|
||||
item.icon("client-permission_server_groups");
|
||||
item.click(() => {
|
||||
spawnPermissionEdit(server_connections.active_connection_handler(), "sg").open();
|
||||
spawnPermissionEdit(server_connections.active_connection(), "sg").open();
|
||||
});
|
||||
_state_updater["permission.sg"] = { item: item, conditions: [condition_connected]};
|
||||
|
||||
item = menu.append_item(tr("Client Permissions"));
|
||||
item.icon("client-permission_client");
|
||||
item.click(() => {
|
||||
spawnPermissionEdit(server_connections.active_connection_handler(), "clp").open();
|
||||
spawnPermissionEdit(server_connections.active_connection(), "clp").open();
|
||||
});
|
||||
_state_updater["permission.clp"] = { item: item, conditions: [condition_connected]};
|
||||
|
||||
item = menu.append_item(tr("Channel Client Permissions"));
|
||||
item.icon("client-permission_client");
|
||||
item.click(() => {
|
||||
spawnPermissionEdit(server_connections.active_connection_handler(), "clchp").open();
|
||||
spawnPermissionEdit(server_connections.active_connection(), "clchp").open();
|
||||
});
|
||||
_state_updater["permission.chclp"] = { item: item, conditions: [condition_connected]};
|
||||
|
||||
item = menu.append_item(tr("Channel Groups"));
|
||||
item.icon("client-permission_channel");
|
||||
item.click(() => {
|
||||
spawnPermissionEdit(server_connections.active_connection_handler(), "cg").open();
|
||||
spawnPermissionEdit(server_connections.active_connection(), "cg").open();
|
||||
});
|
||||
_state_updater["permission.cg"] = { item: item, conditions: [condition_connected]};
|
||||
|
||||
item = menu.append_item(tr("Channel Permissions"));
|
||||
item.icon("client-permission_channel");
|
||||
item.click(() => {
|
||||
spawnPermissionEdit(server_connections.active_connection_handler(), "chp").open();
|
||||
spawnPermissionEdit(server_connections.active_connection(), "chp").open();
|
||||
});
|
||||
_state_updater["permission.cp"] = { item: item, conditions: [condition_connected]};
|
||||
|
||||
|
@ -440,7 +434,7 @@ export function initialize() {
|
|||
//TODO: Fixeme use one method for the control bar and here!
|
||||
createInputModal(tr("Use token"), tr("Please enter your token/privilege key"), message => message.length > 0, result => {
|
||||
if(!result) return;
|
||||
const scon = server_connections.active_connection_handler();
|
||||
const scon = server_connections.active_connection();
|
||||
|
||||
if(scon.serverConnection.connected)
|
||||
scon.serverConnection.send_command("tokenuse", {
|
||||
|
@ -476,7 +470,7 @@ export function initialize() {
|
|||
item = menu.append_item(tr("Ban List"));
|
||||
item.icon('client-ban_list');
|
||||
item.click(() => {
|
||||
const scon = server_connections.active_connection_handler();
|
||||
const scon = server_connections.active_connection();
|
||||
if(scon && scon.connected) {
|
||||
if(scon.permissions.neededPermission(PermissionType.B_CLIENT_BAN_LIST).granted(1)) {
|
||||
openBanList(scon);
|
||||
|
@ -493,7 +487,7 @@ export function initialize() {
|
|||
item = menu.append_item(tr("Query List"));
|
||||
item.icon('client-server_query');
|
||||
item.click(() => {
|
||||
const scon = server_connections.active_connection_handler();
|
||||
const scon = server_connections.active_connection();
|
||||
if(scon && scon.connected) {
|
||||
if(scon.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_LIST).granted(1) || scon.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_LIST_OWN).granted(1)) {
|
||||
spawnQueryManage(scon);
|
||||
|
@ -510,7 +504,7 @@ export function initialize() {
|
|||
item = menu.append_item(tr("Query Create"));
|
||||
item.icon('client-server_query');
|
||||
item.click(() => {
|
||||
const scon = server_connections.active_connection_handler();
|
||||
const scon = server_connections.active_connection();
|
||||
if(scon && scon.connected) {
|
||||
if(scon.permissions.neededPermission(PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN).granted(1) || scon.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CREATE).granted(1)) {
|
||||
spawnQueryCreate(scon);
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import {ConnectionHandler, DisconnectReason} from "tc-shared/ConnectionHandler";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import * as top_menu from "./MenuBar";
|
||||
import {control_bar_instance} from "tc-shared/ui/frames/control-bar";
|
||||
import {client_control_events} from "tc-shared/main";
|
||||
import {Registry} from "tc-shared/events";
|
||||
|
||||
export let server_connections: ServerConnectionManager;
|
||||
export function initialize(manager: ServerConnectionManager) {
|
||||
server_connections = manager;
|
||||
export let server_connections: ConnectionManager;
|
||||
export function initialize() {
|
||||
if(server_connections) throw tr("Connection manager has already been initialized");
|
||||
server_connections = new ConnectionManager($("#connection-handlers"));
|
||||
}
|
||||
export class ServerConnectionManager {
|
||||
export class ConnectionManager {
|
||||
private readonly event_registry: Registry<ConnectionManagerEvents>;
|
||||
private connection_handlers: ConnectionHandler[] = [];
|
||||
private active_handler: ConnectionHandler | undefined;
|
||||
|
||||
|
@ -23,7 +24,20 @@ export class ServerConnectionManager {
|
|||
private _tag_button_scoll_right: JQuery;
|
||||
private _tag_button_scoll_left: JQuery;
|
||||
|
||||
private default_server_state: {
|
||||
microphone_disabled: boolean,
|
||||
speaker_disabled: boolean,
|
||||
away: string | boolean
|
||||
} = {
|
||||
away: false,
|
||||
speaker_disabled: false,
|
||||
microphone_disabled: false
|
||||
};
|
||||
|
||||
constructor(tag: JQuery) {
|
||||
this.event_registry = new Registry<ConnectionManagerEvents>();
|
||||
this.event_registry.enable_debug("connection-manager");
|
||||
|
||||
this._tag = tag;
|
||||
|
||||
if(settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION, false))
|
||||
|
@ -42,23 +56,42 @@ export class ServerConnectionManager {
|
|||
this._container_hostbanner = $("#hostbanner");
|
||||
this._container_chat = $("#chat");
|
||||
|
||||
this.set_active_connection_handler(undefined);
|
||||
this.set_active_connection(undefined);
|
||||
}
|
||||
|
||||
spawn_server_connection_handler() : ConnectionHandler {
|
||||
events() : Registry<ConnectionManagerEvents> {
|
||||
return this.event_registry;
|
||||
}
|
||||
|
||||
spawn_server_connection() : ConnectionHandler {
|
||||
const handler = new ConnectionHandler();
|
||||
handler.initialize_client_state(this.active_handler);
|
||||
this.connection_handlers.push(handler);
|
||||
control_bar.update_button_away();
|
||||
control_bar.initialize_connection_handler_state(handler);
|
||||
|
||||
//FIXME: Load last status from last connection or via global variables!
|
||||
/*
|
||||
handler.set_away_status(this.default_server_state.away, false);
|
||||
handler.client_status.input_muted = this.default_server_state.microphone_disabled;
|
||||
handler.client_status.output_muted = this.default_server_state.speaker_disabled;
|
||||
if(!this.default_server_state.microphone_disabled)
|
||||
handler.acquire_recorder(default_recorder, true);
|
||||
*/
|
||||
|
||||
handler.tag_connection_handler.appendTo(this._tag_connection_entries);
|
||||
this._tag.toggleClass("shown", this.connection_handlers.length > 1);
|
||||
this._update_scroll();
|
||||
|
||||
this.event_registry.fire("notify_handler_created", { handler: handler });
|
||||
return handler;
|
||||
}
|
||||
|
||||
destroy_server_connection_handler(handler: ConnectionHandler) {
|
||||
this.connection_handlers.remove(handler);
|
||||
destroy_server_connection(handler: ConnectionHandler) {
|
||||
if(this.connection_handlers.length <= 1)
|
||||
throw "cannot deleted the last connection handler";
|
||||
|
||||
if(!this.connection_handlers.remove(handler))
|
||||
throw "unknown connection handler";
|
||||
|
||||
handler.tag_connection_handler.remove();
|
||||
this._update_scroll();
|
||||
this._tag.toggleClass("shown", this.connection_handlers.length > 1);
|
||||
|
@ -70,16 +103,22 @@ export class ServerConnectionManager {
|
|||
}
|
||||
|
||||
if(handler === this.active_handler)
|
||||
this.set_active_connection_handler(this.connection_handlers[0]);
|
||||
this.set_active_connection_(this.connection_handlers[0]);
|
||||
this.event_registry.fire("notify_handler_deleted", { handler: handler });
|
||||
|
||||
/* destroy all elements */
|
||||
handler.destroy();
|
||||
}
|
||||
|
||||
set_active_connection_handler(handler: ConnectionHandler) {
|
||||
set_active_connection(handler: ConnectionHandler) {
|
||||
if(handler && this.connection_handlers.indexOf(handler) == -1)
|
||||
throw "Handler hasn't been registered or is already obsolete!";
|
||||
if(handler === this.active_handler)
|
||||
return;
|
||||
this.set_active_connection_(handler);
|
||||
}
|
||||
|
||||
private set_active_connection_(handler: ConnectionHandler) {
|
||||
this._tag_connection_entries.find(".active").removeClass("active");
|
||||
this._container_channel_tree.children().detach();
|
||||
this._container_chat.children().detach();
|
||||
|
@ -97,16 +136,20 @@ export class ServerConnectionManager {
|
|||
if(handler.invoke_resized_on_activate)
|
||||
handler.resize_elements();
|
||||
}
|
||||
const old_handler = this.active_handler;
|
||||
this.active_handler = handler;
|
||||
client_control_events.fire("action_set_active_connection_handler", { handler: handler }); //FIXME: This even should set the new handler, not vice versa!
|
||||
top_menu.update_state();
|
||||
this.event_registry.fire("notify_active_handler_changed", {
|
||||
old_handler: old_handler,
|
||||
new_handler: handler
|
||||
});
|
||||
top_menu.update_state(); //FIXME: Top menu should listen to our events!
|
||||
}
|
||||
|
||||
active_connection_handler() : ConnectionHandler | undefined {
|
||||
active_connection() : ConnectionHandler | undefined {
|
||||
return this.active_handler;
|
||||
}
|
||||
|
||||
server_connection_handlers() : ConnectionHandler[] {
|
||||
all_connections() : ConnectionHandler[] {
|
||||
return this.connection_handlers;
|
||||
}
|
||||
|
||||
|
@ -140,4 +183,22 @@ export class ServerConnectionManager {
|
|||
this._tag_button_scoll_left.toggleClass("disabled", scroll <= 0);
|
||||
this._tag_button_scoll_right.toggleClass("disabled", scroll + this._tag_connection_entries.width() + 2 >= this._tag_connection_entries[0].scrollWidth);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ConnectionManagerEvents {
|
||||
notify_handler_created: {
|
||||
handler: ConnectionHandler
|
||||
},
|
||||
|
||||
/* This will also trigger when a connection gets deleted. So if you're just interested to connect event handler to the active connection,
|
||||
unregister them from the old handler and register them for the new handler every time */
|
||||
notify_active_handler_changed: {
|
||||
old_handler: ConnectionHandler | undefined,
|
||||
new_handler: ConnectionHandler | undefined
|
||||
},
|
||||
|
||||
/* Will never fire on an active connection handler! */
|
||||
notify_handler_deleted: {
|
||||
handler: ConnectionHandler
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
import {Registry} from "tc-shared/events";
|
||||
import {ControlBarEvents} from "tc-shared/ui/frames/control-bar/index";
|
||||
import {manager, Sound} from "tc-shared/sound/Sounds";
|
||||
|
||||
function initialize_sounds(event_registry: Registry<ControlBarEvents>) {
|
||||
{
|
||||
let microphone_muted = undefined;
|
||||
event_registry.on("update_microphone_state", event => {
|
||||
if(microphone_muted === event.muted) return;
|
||||
if(typeof microphone_muted !== "undefined")
|
||||
manager.play(event.muted ? Sound.MICROPHONE_MUTED : Sound.MICROPHONE_ACTIVATED);
|
||||
microphone_muted = event.muted;
|
||||
})
|
||||
}
|
||||
{
|
||||
let speakers_muted = undefined;
|
||||
event_registry.on("update_speaker_state", event => {
|
||||
if(speakers_muted === event.muted) return;
|
||||
if(typeof speakers_muted !== "undefined")
|
||||
manager.play(event.muted ? Sound.SOUND_MUTED : Sound.SOUND_ACTIVATED);
|
||||
speakers_muted = event.muted;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export = (event_registry: Registry<ControlBarEvents>) => {
|
||||
initialize_sounds(event_registry);
|
||||
};
|
||||
|
||||
//TODO: Left action handler!
|
|
@ -1,39 +1,54 @@
|
|||
@import "../../../../css/static/properties";
|
||||
@import "../../../../css/static/mixin";
|
||||
|
||||
$border_color_activated: rgba(255, 255, 255, .75);
|
||||
/* Variables */
|
||||
html:root {
|
||||
--menu-bar-button-background: #454545;
|
||||
--menu-bar-button-background-hover: #393c43;
|
||||
--menu-bar-button-background-activated: #2f3841;
|
||||
--menu-bar-button-background-activated-red: #412f2f;
|
||||
--menu-bar-button-background-activated-hover: #263340;
|
||||
--menu-bar-button-background-activated-red-hover: #402626;
|
||||
|
||||
--menu-bar-button-border: #454545;
|
||||
--menu-bar-button-border-hover: #4a4c55;
|
||||
--menu-bar-button-border-activated: #005fa1;
|
||||
--menu-bar-button-border-activated-red: #a10000;
|
||||
--menu-bar-button-border-activated-hover: #005fa1;
|
||||
--menu-bar-button-border-activated-red-hover: #a10000;
|
||||
}
|
||||
|
||||
/* border etc */
|
||||
.button, .dropdownArrow {
|
||||
text-align: center;
|
||||
|
||||
border: .05em solid rgba(0, 0, 0, 0);
|
||||
border: .05em solid var(--menu-bar-button-border);
|
||||
border-radius: $border_radius_small;
|
||||
|
||||
background-color: #454545;
|
||||
background-color: var(--menu-bar-button-background);
|
||||
|
||||
&:hover {
|
||||
background-color: #393c43;
|
||||
border-color: #4a4c55;
|
||||
/*box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19);*/
|
||||
background-color: var(--menu-bar-button-background-hover);
|
||||
border-color: var(--menu-bar-button-border-hover);
|
||||
/* box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19); */
|
||||
}
|
||||
|
||||
&.activated {
|
||||
background-color: #2f3841;
|
||||
border-color: #005fa1;
|
||||
background-color: var(--menu-bar-button-background-activated);
|
||||
border-color: var(--menu-bar-button-border-activated);
|
||||
|
||||
&:hover {
|
||||
background-color: #263340;
|
||||
border-color: #005fa1;
|
||||
background-color: var(--menu-bar-button-background-activated-hover);
|
||||
border-color: var(--menu-bar-button-border-activated-hover);
|
||||
}
|
||||
|
||||
&.theme-red {
|
||||
background-color: #412f2f;
|
||||
border-color: #a10000;
|
||||
background-color: var(--menu-bar-button-background-activated-red);
|
||||
border-color: var(--menu-bar-button-border-activated-red);
|
||||
|
||||
&:hover {
|
||||
background-color: #402626;
|
||||
border-color: #a10000;
|
||||
background-color: var(--menu-bar-button-background-activated-red-hover);
|
||||
border-color: var(--menu-bar-button-border-activated-red-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ export class Button extends ReactComponentBase<ButtonProperties, ButtonState> {
|
|||
}
|
||||
|
||||
private onClick() {
|
||||
const new_state = !this.state.switched;
|
||||
const new_state = !(this.state.switched || this.props.switched);
|
||||
const result = this.props.onToggle?.call(undefined, new_state);
|
||||
if(this.props.autoSwitch)
|
||||
this.updateState({ switched: typeof result === "boolean" ? result : new_state });
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
@import "../../../../css/static/properties";
|
||||
@import "../../../../css/static/mixin";
|
||||
|
||||
/* Variables */
|
||||
html:root {
|
||||
--menu-bar-background: #454545;
|
||||
}
|
||||
|
||||
/* max height is 2em */
|
||||
.controlBar {
|
||||
display: flex;
|
||||
|
@ -10,6 +15,7 @@
|
|||
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
background: var(--menu-bar-background);
|
||||
|
||||
/* tmp fix for ultra small devices */
|
||||
overflow-y: visible;
|
||||
|
|
|
@ -3,11 +3,12 @@ import {Button} from "./button";
|
|||
import {DropdownEntry} from "tc-shared/ui/frames/control-bar/dropdown";
|
||||
import {Translatable} from "tc-shared/ui/elements/i18n";
|
||||
import {ReactComponentBase} from "tc-shared/ui/elements/ReactComponentBase";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {ConnectionEvents, ConnectionHandler, ConnectionStateUpdateType} from "tc-shared/ConnectionHandler";
|
||||
import {Event, EventHandler, ReactEventHandler, Registry} from "tc-shared/events";
|
||||
import {server_connections} from "tc-shared/ui/frames/connection_handlers";
|
||||
import {ConnectionManagerEvents, server_connections} from "tc-shared/ui/frames/connection_handlers";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import {
|
||||
add_server_to_bookmarks,
|
||||
Bookmark,
|
||||
bookmarks,
|
||||
BookmarkType,
|
||||
|
@ -17,8 +18,9 @@ import {
|
|||
} from "tc-shared/bookmarks";
|
||||
import {IconManager} from "tc-shared/FileManager";
|
||||
import * as contextmenu from "tc-shared/ui/elements/ContextMenu";
|
||||
import {client_control_events} from "tc-shared/main";
|
||||
const register_actions = require("./actions");
|
||||
import {createInputModal} from "tc-shared/ui/elements/Modal";
|
||||
import {default_recorder} from "tc-shared/voice/RecorderProfile";
|
||||
import {global_client_actions} from "tc-shared/events/GlobalEvents";
|
||||
|
||||
const cssStyle = require("./index.scss");
|
||||
const cssButtonStyle = require("./button.scss");
|
||||
|
@ -29,7 +31,7 @@ export interface ConnectionState {
|
|||
}
|
||||
|
||||
@ReactEventHandler(obj => obj.props.event_registry)
|
||||
class ConnectButton extends ReactComponentBase<{ multiSession: boolean; event_registry: Registry<ControlBarEvents> }, ConnectionState> {
|
||||
class ConnectButton extends ReactComponentBase<{ multiSession: boolean; event_registry: Registry<InternalControlBarEvents> }, ConnectionState> {
|
||||
protected default_state(): ConnectionState {
|
||||
return {
|
||||
connected: false,
|
||||
|
@ -43,51 +45,51 @@ class ConnectButton extends ReactComponentBase<{ multiSession: boolean; event_re
|
|||
if(!this.state.connected) {
|
||||
subentries.push(
|
||||
<DropdownEntry key={"connect-server"} icon={"client-connect"} text={<Translatable message={"Connect to a server"} />}
|
||||
onClick={ () => client_control_events.fire("action_open_connect", { new_tab: false }) } />
|
||||
onClick={ () => global_client_actions.fire("action_open_window_connect", {new_tab: false }) } />
|
||||
);
|
||||
} else {
|
||||
subentries.push(
|
||||
<DropdownEntry key={"disconnect-current"} icon={"client-disconnect"} text={<Translatable message={"Disconnect from current server"} />}
|
||||
onClick={ () => client_control_events.fire("action_disconnect", { globally: false }) }/>
|
||||
<DropdownEntry key={"disconnect-current-a"} icon={"client-disconnect"} text={<Translatable message={"Disconnect from current server"} />}
|
||||
onClick={ () => this.props.event_registry.fire("action_disconnect", { globally: false }) }/>
|
||||
);
|
||||
}
|
||||
if(this.state.connectedAnywhere) {
|
||||
subentries.push(
|
||||
<DropdownEntry key={"disconnect-current"} icon={"client-disconnect"} text={<Translatable message={"Disconnect from all servers"} />}
|
||||
onClick={ () => client_control_events.fire("action_disconnect", { globally: true }) }/>
|
||||
<DropdownEntry key={"disconnect-current-b"} icon={"client-disconnect"} text={<Translatable message={"Disconnect from all servers"} />}
|
||||
onClick={ () => this.props.event_registry.fire("action_disconnect", { globally: true }) }/>
|
||||
);
|
||||
}
|
||||
subentries.push(
|
||||
<DropdownEntry key={"connect-new-tab"} icon={"client-connect"} text={<Translatable message={"Connect to a server in another tab"} />}
|
||||
onClick={ () => client_control_events.fire("action_open_connect", { new_tab: true }) } />
|
||||
onClick={ () => global_client_actions.fire("action_open_window_connect", { new_tab: true }) } />
|
||||
);
|
||||
}
|
||||
|
||||
if(!this.state.connected) {
|
||||
return (
|
||||
<Button colorTheme={"default"} autoSwitch={false} iconNormal={"client-connect"} tooltip={tr("Connect to a server")}
|
||||
onToggle={ () => client_control_events.fire("action_open_connect", { new_tab: false }) }>
|
||||
onToggle={ () => global_client_actions.fire("action_open_window_connect", { new_tab: false }) }>
|
||||
{subentries}
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Button colorTheme={"default"} autoSwitch={false} iconNormal={"client-disconnect"} tooltip={tr("Disconnect from server")}
|
||||
onToggle={ () => client_control_events.fire("action_disconnect", { globally: false }) }>
|
||||
onToggle={ () => this.props.event_registry.fire("action_disconnect", { globally: false }) }>
|
||||
{subentries}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>("update_connect_state")
|
||||
@EventHandler<InternalControlBarEvents>("update_connect_state")
|
||||
private handleStateUpdate(state: ConnectionState) {
|
||||
this.updateState(state);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactEventHandler(obj => obj.props.event_registry)
|
||||
class BookmarkButton extends ReactComponentBase<{ event_registry: Registry<ControlBarEvents> }, {}> {
|
||||
class BookmarkButton extends ReactComponentBase<{ event_registry: Registry<InternalControlBarEvents> }, {}> {
|
||||
private button_ref: React.RefObject<Button>;
|
||||
|
||||
protected initialize() {
|
||||
|
@ -105,8 +107,9 @@ class BookmarkButton extends ReactComponentBase<{ event_registry: Registry<Contr
|
|||
return (
|
||||
<Button ref={this.button_ref} dropdownButtonExtraClass={cssButtonStyle.buttonBookmarks} autoSwitch={false} iconNormal={"client-bookmark_manager"}>
|
||||
<DropdownEntry icon={"client-bookmark_manager"} text={<Translatable message={"Manage bookmarks"} />}
|
||||
onClick={() => client_control_events.fire("action_open_window", { window: "bookmark-manage" })} />
|
||||
<DropdownEntry icon={"client-bookmark_add"} text={<Translatable message={"Add current server to bookmarks"} />} />
|
||||
onClick={() => this.props.event_registry.fire("action_open_window", { window: "bookmark-manage" })} />
|
||||
<DropdownEntry icon={"client-bookmark_add"} text={<Translatable message={"Add current server to bookmarks"} />}
|
||||
onClick={() => this.props.event_registry.fire("action_add_current_server_to_bookmarks")} />
|
||||
{marks}
|
||||
</Button>
|
||||
)
|
||||
|
@ -160,7 +163,7 @@ class BookmarkButton extends ReactComponentBase<{ event_registry: Registry<Contr
|
|||
}));
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>("update_bookmarks")
|
||||
@EventHandler<InternalControlBarEvents>("update_bookmarks")
|
||||
private handleStateUpdate() {
|
||||
this.forceUpdate();
|
||||
}
|
||||
|
@ -173,7 +176,7 @@ export interface AwayState {
|
|||
}
|
||||
|
||||
@ReactEventHandler(obj => obj.props.event_registry)
|
||||
class AwayButton extends ReactComponentBase<{ event_registry: Registry<ControlBarEvents> }, AwayState> {
|
||||
class AwayButton extends ReactComponentBase<{ event_registry: Registry<InternalControlBarEvents> }, AwayState> {
|
||||
protected default_state(): AwayState {
|
||||
return {
|
||||
away: false,
|
||||
|
@ -186,35 +189,42 @@ class AwayButton extends ReactComponentBase<{ event_registry: Registry<ControlBa
|
|||
let dropdowns = [];
|
||||
if(this.state.away) {
|
||||
dropdowns.push(<DropdownEntry key={"cgo"} icon={"client-present"} text={<Translatable message={"Go online"} />}
|
||||
onClick={() => client_control_events.fire("action_disable_away", { globally: false })} />);
|
||||
onClick={() => this.props.event_registry.fire("action_disable_away", { globally: false })} />);
|
||||
} else {
|
||||
dropdowns.push(<DropdownEntry key={"sas"} icon={"client-away"} text={<Translatable message={"Set away on this server"} />}
|
||||
onClick={() => client_control_events.fire("action_set_away", { globally: false, prompt_reason: false })} />);
|
||||
onClick={() => this.props.event_registry.fire("action_set_away", { globally: false, prompt_reason: false })} />);
|
||||
}
|
||||
dropdowns.push(<DropdownEntry key={"sam"} icon={"client-away"} text={<Translatable message={"Set away message on this server"} />}
|
||||
onClick={() => client_control_events.fire("action_set_away", { globally: false, prompt_reason: true })} />);
|
||||
onClick={() => this.props.event_registry.fire("action_set_away", { globally: false, prompt_reason: true })} />);
|
||||
|
||||
dropdowns.push(<hr key={"-hr"} />);
|
||||
if(this.state.awayAnywhere) {
|
||||
dropdowns.push(<DropdownEntry key={"goa"} icon={"client-present"} text={<Translatable message={"Go online for all servers"} />}
|
||||
onClick={() => client_control_events.fire("action_disable_away", { globally: true })} />);
|
||||
onClick={() => this.props.event_registry.fire("action_disable_away", { globally: true })} />);
|
||||
}
|
||||
if(!this.state.awayAll) {
|
||||
dropdowns.push(<DropdownEntry key={"saa"} icon={"client-away"} text={<Translatable message={"Set away on all servers"} />}
|
||||
onClick={() => client_control_events.fire("action_set_away", { globally: true, prompt_reason: false })} />);
|
||||
onClick={() => this.props.event_registry.fire("action_set_away", { globally: true, prompt_reason: false })} />);
|
||||
}
|
||||
dropdowns.push(<DropdownEntry key={"sama"} icon={"client-away"} text={<Translatable message={"Set away message for all servers"} />}
|
||||
onClick={() => client_control_events.fire("action_set_away", { globally: true, prompt_reason: true })} />);
|
||||
onClick={() => this.props.event_registry.fire("action_set_away", { globally: true, prompt_reason: true })} />);
|
||||
|
||||
/* switchable because we're switching it manually */
|
||||
return (
|
||||
<Button autoSwitch={false} iconNormal={this.state.away ? "client-present" : "client-away"}>
|
||||
<Button autoSwitch={false} switched={this.state.away} iconNormal={this.state.away ? "client-present" : "client-away"} onToggle={this.handleButtonToggled.bind(this)}>
|
||||
{dropdowns}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>("update_away_state")
|
||||
private handleButtonToggled(state: boolean) {
|
||||
if(state)
|
||||
this.props.event_registry.fire("action_set_away", { globally: false, prompt_reason: false });
|
||||
else
|
||||
this.props.event_registry.fire("action_disable_away");
|
||||
}
|
||||
|
||||
@EventHandler<InternalControlBarEvents>("update_away_state")
|
||||
private handleStateUpdate(state: AwayState) {
|
||||
this.updateState(state);
|
||||
}
|
||||
|
@ -225,17 +235,17 @@ export interface ChannelSubscribeState {
|
|||
}
|
||||
|
||||
@ReactEventHandler(obj => obj.props.event_registry)
|
||||
class ChannelSubscribeButton extends ReactComponentBase<{ event_registry: Registry<ControlBarEvents> }, ChannelSubscribeState> {
|
||||
class ChannelSubscribeButton extends ReactComponentBase<{ event_registry: Registry<InternalControlBarEvents> }, ChannelSubscribeState> {
|
||||
protected default_state(): ChannelSubscribeState {
|
||||
return { subscribeEnabled: false };
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Button switched={this.state.subscribeEnabled} autoSwitch={false} iconNormal={"client-unsubscribe_from_all_channels"} iconSwitched={"client-subscribe_to_all_channels"}
|
||||
onToggle={flag => client_control_events.fire("action_set_channel_subscribe_mode", { subscribe: flag })}/>;
|
||||
onToggle={flag => this.props.event_registry.fire("action_set_subscribe", { subscribe: flag })}/>;
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>("update_subscribe_state")
|
||||
@EventHandler<InternalControlBarEvents>("update_subscribe_state")
|
||||
private handleStateUpdate(state: ChannelSubscribeState) {
|
||||
this.updateState(state);
|
||||
}
|
||||
|
@ -247,7 +257,7 @@ export interface MicrophoneState {
|
|||
}
|
||||
|
||||
@ReactEventHandler(obj => obj.props.event_registry)
|
||||
class MicrophoneButton extends ReactComponentBase<{ event_registry: Registry<ControlBarEvents> }, MicrophoneState> {
|
||||
class MicrophoneButton extends ReactComponentBase<{ event_registry: Registry<InternalControlBarEvents> }, MicrophoneState> {
|
||||
protected default_state(): MicrophoneState {
|
||||
return {
|
||||
enabled: false,
|
||||
|
@ -258,15 +268,15 @@ class MicrophoneButton extends ReactComponentBase<{ event_registry: Registry<Con
|
|||
render() {
|
||||
if(!this.state.enabled)
|
||||
return <Button autoSwitch={false} iconNormal={"client-activate_microphone"} tooltip={tr("Enable your microphone on this server")}
|
||||
onToggle={() => client_control_events.fire("action_toggle_microphone", { state: true })} />;
|
||||
onToggle={() => this.props.event_registry.fire("action_enable_microphone")} />;
|
||||
if(this.state.muted)
|
||||
return <Button switched={true} colorTheme={"red"} autoSwitch={false} iconNormal={"client-input_muted"} tooltip={tr("Unmute microphone")}
|
||||
onToggle={() => client_control_events.fire("action_toggle_microphone", { state: true })} />;
|
||||
onToggle={() => this.props.event_registry.fire("action_enable_microphone")} />;
|
||||
return <Button colorTheme={"red"} autoSwitch={false} iconNormal={"client-input_muted"} tooltip={tr("Mute microphone")}
|
||||
onToggle={() => client_control_events.fire("action_toggle_microphone", { state: false })} />;
|
||||
onToggle={() => this.props.event_registry.fire("action_disable_microphone")} />;
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>("update_microphone_state")
|
||||
@EventHandler<InternalControlBarEvents>("update_microphone_state")
|
||||
private handleStateUpdate(state: MicrophoneState) {
|
||||
this.updateState(state);
|
||||
}
|
||||
|
@ -277,7 +287,7 @@ export interface SpeakerState {
|
|||
}
|
||||
|
||||
@ReactEventHandler(obj => obj.props.event_registry)
|
||||
class SpeakerButton extends ReactComponentBase<{ event_registry: Registry<ControlBarEvents> }, SpeakerState> {
|
||||
class SpeakerButton extends ReactComponentBase<{ event_registry: Registry<InternalControlBarEvents> }, SpeakerState> {
|
||||
protected default_state(): SpeakerState {
|
||||
return {
|
||||
muted: false
|
||||
|
@ -287,12 +297,12 @@ class SpeakerButton extends ReactComponentBase<{ event_registry: Registry<Contro
|
|||
render() {
|
||||
if(this.state.muted)
|
||||
return <Button switched={true} colorTheme={"red"} autoSwitch={false} iconNormal={"client-output_muted"} tooltip={tr("Unmute headphones")}
|
||||
onToggle={() => client_control_events.fire("action_toggle_speaker", { state: true })}/>;
|
||||
onToggle={() => this.props.event_registry.fire("action_enable_speaker")}/>;
|
||||
return <Button colorTheme={"red"} autoSwitch={false} iconNormal={"client-output_muted"} tooltip={tr("Mute headphones")}
|
||||
onToggle={() => client_control_events.fire("action_toggle_speaker", { state: false })}/>;
|
||||
onToggle={() => this.props.event_registry.fire("action_disable_speaker")}/>;
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>("update_speaker_state")
|
||||
@EventHandler<InternalControlBarEvents>("update_speaker_state")
|
||||
private handleStateUpdate(state: SpeakerState) {
|
||||
this.updateState(state);
|
||||
}
|
||||
|
@ -303,7 +313,7 @@ export interface QueryState {
|
|||
}
|
||||
|
||||
@ReactEventHandler(obj => obj.props.event_registry)
|
||||
class QueryButton extends ReactComponentBase<{ event_registry: Registry<ControlBarEvents> }, QueryState> {
|
||||
class QueryButton extends ReactComponentBase<{ event_registry: Registry<InternalControlBarEvents> }, QueryState> {
|
||||
protected default_state() {
|
||||
return {
|
||||
queryShown: false
|
||||
|
@ -313,22 +323,22 @@ class QueryButton extends ReactComponentBase<{ event_registry: Registry<ControlB
|
|||
render() {
|
||||
let toggle;
|
||||
if(this.state.queryShown)
|
||||
toggle = <DropdownEntry icon={""} text={<Translatable message={"Hide server queries"} />}
|
||||
onClick={() => client_control_events.fire("action_toggle_query", { shown: false })}/>;
|
||||
toggle = <DropdownEntry icon={"client-toggle_server_query_clients"} text={<Translatable message={"Hide server queries"} />}
|
||||
onClick={() => this.props.event_registry.fire("action_toggle_query", { shown: false })}/>;
|
||||
else
|
||||
toggle = <DropdownEntry icon={"client-toggle_server_query_clients"} text={<Translatable message={"Show server queries"} />}
|
||||
onClick={() => client_control_events.fire("action_toggle_query", { shown: true })}/>;
|
||||
onClick={() => this.props.event_registry.fire("action_toggle_query", { shown: true })}/>;
|
||||
return (
|
||||
<Button switched={this.state.queryShown} autoSwitch={false} iconNormal={"client-server_query"}
|
||||
onToggle={flag => client_control_events.fire("action_toggle_query", { shown: flag })}>
|
||||
onToggle={flag => this.props.event_registry.fire("action_toggle_query", { shown: flag })}>
|
||||
{toggle}
|
||||
<DropdownEntry icon={"client-server_query"} text={<Translatable message={"Manage server queries"} />}
|
||||
onClick={() => client_control_events.fire("action_open_window", { window: "query-manage" })}/>
|
||||
onClick={() => this.props.event_registry.fire("action_open_window", { window: "query-manage" })}/>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>("update_query_state")
|
||||
@EventHandler<InternalControlBarEvents>("update_query_state")
|
||||
private handleStateUpdate(state: QueryState) {
|
||||
this.updateState(state);
|
||||
}
|
||||
|
@ -341,7 +351,7 @@ export interface HostButtonState {
|
|||
}
|
||||
|
||||
@ReactEventHandler(obj => obj.props.event_registry)
|
||||
class HostButton extends ReactComponentBase<{ event_registry: Registry<ControlBarEvents> }, HostButtonState> {
|
||||
class HostButton extends ReactComponentBase<{ event_registry: Registry<InternalControlBarEvents> }, HostButtonState> {
|
||||
protected default_state() {
|
||||
return {
|
||||
url: undefined,
|
||||
|
@ -370,7 +380,7 @@ class HostButton extends ReactComponentBase<{ event_registry: Registry<ControlBa
|
|||
event.preventDefault();
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>("update_host_button")
|
||||
@EventHandler<InternalControlBarEvents>("update_host_button")
|
||||
private handleStateUpdate(state: HostButtonState) {
|
||||
this.updateState(state);
|
||||
}
|
||||
|
@ -382,33 +392,25 @@ export interface ControlBarProperties {
|
|||
|
||||
@ReactEventHandler<ControlBar>(obj => obj.event_registry)
|
||||
export class ControlBar extends React.Component<ControlBarProperties, {}> {
|
||||
private readonly event_registry: Registry<ControlBarEvents>;
|
||||
private readonly event_registry: Registry<InternalControlBarEvents>;
|
||||
private connection: ConnectionHandler;
|
||||
private connection_handler_callbacks = {
|
||||
notify_state_updated: this.handleConnectionHandlerStateChange.bind(this),
|
||||
notify_connection_state_changed: this.handleConnectionHandlerConnectionStateChange.bind(this)
|
||||
};
|
||||
private connection_manager_callbacks = {
|
||||
active_handler_changed: this.handleActiveConnectionHandlerChanged.bind(this)
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.event_registry = new Registry<ControlBarEvents>();
|
||||
this.event_registry = new Registry<InternalControlBarEvents>();
|
||||
this.event_registry.enable_debug("control-bar");
|
||||
register_actions(this.event_registry);
|
||||
|
||||
initialize(this.event_registry);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
initialize_connection_handler_state(handler?: ConnectionHandler) {
|
||||
handler.client_status.output_muted = this._button_speakers === "muted";
|
||||
handler.client_status.input_muted = this._button_microphone === "muted";
|
||||
|
||||
handler.client_status.channel_subscribe_all = this._button_subscribe_all;
|
||||
handler.client_status.queries_visible = this._button_query_visible;
|
||||
}
|
||||
*/
|
||||
|
||||
events() : Registry<ControlBarEvents> { return this.event_registry; }
|
||||
events() : Registry<InternalControlBarEvents> { return this.event_registry; }
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
@ -428,22 +430,66 @@ export class ControlBar extends React.Component<ControlBarProperties, {}> {
|
|||
)
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>("set_connection_handler")
|
||||
private handleSetConnectionHandler(event: ControlBarEvents["set_connection_handler"]) {
|
||||
if(this.connection == event.handler) return;
|
||||
private handleActiveConnectionHandlerChanged(event: ConnectionManagerEvents["notify_active_handler_changed"]) {
|
||||
if(event.old_handler)
|
||||
this.unregisterConnectionHandlerEvents(event.old_handler);
|
||||
|
||||
this.connection = event.handler;
|
||||
this.connection = event.new_handler;
|
||||
if(event.new_handler)
|
||||
this.registerConnectionHandlerEvents(event.new_handler);
|
||||
|
||||
this.event_registry.fire("set_connection_handler", { handler: this.connection });
|
||||
this.event_registry.fire("update_state_all");
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateHostButton(event: Event<ControlBarEvents>) {
|
||||
if(event.type === "update_state")4
|
||||
if(event.as<"update_state">().state !== "host-button")
|
||||
private unregisterConnectionHandlerEvents(target: ConnectionHandler) {
|
||||
const events = target.events();
|
||||
events.off("notify_state_updated", this.connection_handler_callbacks.notify_state_updated);
|
||||
events.off("notify_connection_state_changed", this.connection_handler_callbacks.notify_connection_state_changed);
|
||||
}
|
||||
|
||||
private registerConnectionHandlerEvents(target: ConnectionHandler) {
|
||||
const events = target.events();
|
||||
events.on("notify_state_updated", this.connection_handler_callbacks.notify_state_updated);
|
||||
events.on("notify_connection_state_changed", this.connection_handler_callbacks.notify_connection_state_changed);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
console.error(server_connections.events());
|
||||
server_connections.events().on("notify_active_handler_changed", this.connection_manager_callbacks.active_handler_changed);
|
||||
this.event_registry.fire("set_connection_handler", { handler: server_connections.active_connection() });
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
server_connections.events().off("notify_active_handler_changed", this.connection_manager_callbacks.active_handler_changed);
|
||||
}
|
||||
|
||||
/* Active server connection handler events */
|
||||
private handleConnectionHandlerStateChange(event: ConnectionEvents["notify_state_updated"]) {
|
||||
const type_mapping: {[T in ConnectionStateUpdateType]:ControlStateUpdateType[]} = {
|
||||
"microphone": ["microphone"],
|
||||
"speaker": ["speaker"],
|
||||
"away": ["away"],
|
||||
"subscribe": ["subscribe-mode"],
|
||||
"query": ["query"]
|
||||
};
|
||||
for(const type of type_mapping[event.state] || [])
|
||||
this.event_registry.fire("update_state", { state: type });
|
||||
}
|
||||
|
||||
private handleConnectionHandlerConnectionStateChange(/* event: ConnectionEvents["notify_connection_state_changed"] */) {
|
||||
this.event_registry.fire("update_state", { state: "connect-state" });
|
||||
}
|
||||
|
||||
/* own update & state gathering events */
|
||||
@EventHandler<InternalControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateHostButton(event: Event<InternalControlBarEvents>) {
|
||||
if(event.type === "update_state")
|
||||
if(event.as<"update_state">().state !== "host-button" && event.as<"update_state">().state !== "connect-state")
|
||||
return;
|
||||
|
||||
const sprops = this.connection?.channelTree.server?.properties;
|
||||
if(!sprops || !sprops.virtualserver_hostbutton_gfx_url) {
|
||||
const server_props = this.connection?.channelTree.server?.properties;
|
||||
if(!this.connection?.connected || !server_props || !server_props.virtualserver_hostbutton_gfx_url) {
|
||||
this.event_registry.fire("update_host_button", {
|
||||
url: undefined,
|
||||
target_url: undefined,
|
||||
|
@ -453,88 +499,88 @@ export class ControlBar extends React.Component<ControlBarProperties, {}> {
|
|||
}
|
||||
|
||||
this.event_registry.fire("update_host_button", {
|
||||
url: sprops.virtualserver_hostbutton_gfx_url,
|
||||
target_url: sprops.virtualserver_hostbutton_url,
|
||||
title: sprops.virtualserver_hostbutton_tooltip
|
||||
url: server_props.virtualserver_hostbutton_gfx_url,
|
||||
target_url: server_props.virtualserver_hostbutton_url,
|
||||
title: server_props.virtualserver_hostbutton_tooltip
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateSubscribe(event: Event<ControlBarEvents>) {
|
||||
@EventHandler<InternalControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateSubscribe(event: Event<InternalControlBarEvents>) {
|
||||
if(event.type === "update_state")
|
||||
if(event.as<"update_state">().state !== "subscribe-mode")
|
||||
return;
|
||||
|
||||
this.event_registry.fire("update_subscribe_state", {
|
||||
subscribeEnabled: !!this.connection?.client_status.channel_subscribe_all
|
||||
subscribeEnabled: !!this.connection?.isSubscribeToAllChannels()
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateConnect(event: Event<ControlBarEvents>) {
|
||||
@EventHandler<InternalControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateConnect(event: Event<InternalControlBarEvents>) {
|
||||
if(event.type === "update_state")
|
||||
if(event.as<"update_state">().state !== "connect-state")
|
||||
return;
|
||||
|
||||
this.event_registry.fire("update_connect_state", {
|
||||
connectedAnywhere: server_connections.server_connection_handlers().findIndex(e => e.connected) !== -1,
|
||||
connectedAnywhere: server_connections.all_connections().findIndex(e => e.connected) !== -1,
|
||||
connected: !!this.connection?.connected
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateAway(event: Event<ControlBarEvents>) {
|
||||
@EventHandler<InternalControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateAway(event: Event<InternalControlBarEvents>) {
|
||||
if(event.type === "update_state")
|
||||
if(event.as<"update_state">().state !== "away")
|
||||
return;
|
||||
|
||||
const connections = server_connections.server_connection_handlers();
|
||||
const away_connections = server_connections.server_connection_handlers().filter(e => e.client_status.away);
|
||||
const connections = server_connections.all_connections();
|
||||
const away_connections = server_connections.all_connections().filter(e => e.isAway());
|
||||
|
||||
const away_status = this.connection?.client_status.away;
|
||||
const away_status = !!this.connection?.isAway();
|
||||
this.event_registry.fire("update_away_state", {
|
||||
awayAnywhere: away_connections.length > 0,
|
||||
away: typeof away_status === "string" ? true : !!away_status,
|
||||
away: away_status,
|
||||
awayAll: connections.length === away_connections.length
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateMicrophone(event: Event<ControlBarEvents>) {
|
||||
@EventHandler<InternalControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateMicrophone(event: Event<InternalControlBarEvents>) {
|
||||
if(event.type === "update_state")
|
||||
if(event.as<"update_state">().state !== "microphone")
|
||||
return;
|
||||
|
||||
this.event_registry.fire("update_microphone_state", {
|
||||
enabled: !!this.connection?.client_status.input_hardware,
|
||||
muted: this.connection?.client_status.input_muted
|
||||
enabled: !this.connection?.isMicrophoneDisabled(),
|
||||
muted: !!this.connection?.isMicrophoneMuted()
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateSpeaker(event: Event<ControlBarEvents>) {
|
||||
@EventHandler<InternalControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateSpeaker(event: Event<InternalControlBarEvents>) {
|
||||
if(event.type === "update_state")
|
||||
if(event.as<"update_state">().state !== "speaker")
|
||||
return;
|
||||
|
||||
this.event_registry.fire("update_speaker_state", {
|
||||
muted: this.connection?.client_status.output_muted
|
||||
muted: !!this.connection?.isSpeakerMuted()
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateQuery(event: Event<ControlBarEvents>) {
|
||||
@EventHandler<InternalControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateQuery(event: Event<InternalControlBarEvents>) {
|
||||
if(event.type === "update_state")
|
||||
if(event.as<"update_state">().state !== "query")
|
||||
return;
|
||||
|
||||
this.event_registry.fire("update_query_state", {
|
||||
queryShown: !!this.connection?.client_status.queries_visible
|
||||
queryShown: !!this.connection?.areQueriesShown()
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler<ControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateBookmarks(event: Event<ControlBarEvents>) {
|
||||
@EventHandler<InternalControlBarEvents>(["update_state_all", "update_state"])
|
||||
private updateStateBookmarks(event: Event<InternalControlBarEvents>) {
|
||||
if(event.type === "update_state")
|
||||
if(event.as<"update_state">().state !== "bookmarks")
|
||||
return;
|
||||
|
@ -549,7 +595,19 @@ export function control_bar_instance() : ControlBar | undefined {
|
|||
return react_reference_?.current;
|
||||
}
|
||||
|
||||
export type ControlStateUpdateType = "host-button" | "bookmarks" | "subscribe-mode" | "connect-state" | "away" | "microphone" | "speaker" | "query";
|
||||
export interface ControlBarEvents {
|
||||
update_state: {
|
||||
state: "host-button" | "bookmarks" | "subscribe-mode" | "connect-state" | "away" | "microphone" | "speaker" | "query"
|
||||
},
|
||||
|
||||
server_updated: {
|
||||
handler: ConnectionHandler,
|
||||
category: "audio" | "settings-initialized" | "connection-state" | "away-status" | "hostbanner"
|
||||
}
|
||||
}
|
||||
|
||||
export interface InternalControlBarEvents extends ControlBarEvents {
|
||||
/* update the UI */
|
||||
update_host_button: HostButtonState;
|
||||
update_subscribe_state: ChannelSubscribeState;
|
||||
|
@ -559,20 +617,131 @@ export interface ControlBarEvents {
|
|||
update_speaker_state: SpeakerState;
|
||||
update_query_state: QueryState;
|
||||
update_bookmarks: {},
|
||||
update_state: {
|
||||
state: "host-button" | "bookmarks" | "subscribe-mode" | "connect-state" | "away" | "microphone" | "speaker" | "query"
|
||||
},
|
||||
update_state_all: { },
|
||||
|
||||
/* trigger actions */
|
||||
set_connection_handler: {
|
||||
handler?: ConnectionHandler
|
||||
|
||||
/* UI-Actions */
|
||||
action_set_subscribe: { subscribe: boolean },
|
||||
action_disconnect: { globally: boolean },
|
||||
|
||||
action_enable_microphone: {}, /* enable/unmute microphone */
|
||||
action_disable_microphone: {},
|
||||
|
||||
action_enable_speaker: {},
|
||||
action_disable_speaker: {},
|
||||
|
||||
action_disable_away: {
|
||||
globally: boolean
|
||||
},
|
||||
action_set_away: {
|
||||
globally: boolean;
|
||||
prompt_reason: boolean;
|
||||
},
|
||||
|
||||
server_updated: {
|
||||
handler: ConnectionHandler,
|
||||
category: "audio" | "settings-initialized" | "connection-state" | "away-status" | "hostbanner"
|
||||
}
|
||||
action_toggle_query: {
|
||||
shown: boolean
|
||||
},
|
||||
|
||||
//settings-initialized: Update query and channel flags
|
||||
action_open_window: {
|
||||
window: "bookmark-manage" | "query-manage"
|
||||
},
|
||||
|
||||
action_add_current_server_to_bookmarks: {},
|
||||
|
||||
/* manly used for the action handler */
|
||||
set_connection_handler: {
|
||||
handler?: ConnectionHandler
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function initialize(event_registry: Registry<InternalControlBarEvents>) {
|
||||
let current_connection_handler: ConnectionHandler;
|
||||
|
||||
event_registry.on("set_connection_handler", event => current_connection_handler = event.handler);
|
||||
|
||||
event_registry.on("action_disconnect", event => {
|
||||
(event.globally ? server_connections.all_connections() : [server_connections.active_connection()]).filter(e => !!e).forEach(connection => {
|
||||
connection.disconnectFromServer();
|
||||
});
|
||||
});
|
||||
|
||||
event_registry.on("action_set_away", event => {
|
||||
const set_away = message => {
|
||||
const value = typeof message === "string" ? message : true;
|
||||
(event.globally ? server_connections.all_connections() : [server_connections.active_connection()]).filter(e => !!e).forEach(connection => {
|
||||
connection.setAway(value);
|
||||
});
|
||||
settings.changeGlobal(Settings.KEY_CLIENT_STATE_AWAY, true);
|
||||
settings.changeGlobal(Settings.KEY_CLIENT_AWAY_MESSAGE, typeof value === "boolean" ? "" : value);
|
||||
};
|
||||
|
||||
if(event.prompt_reason) {
|
||||
createInputModal(tr("Set away message"), tr("Please enter your away message"), () => true, message => {
|
||||
if(typeof(message) === "string")
|
||||
set_away(message);
|
||||
}).open();
|
||||
} else {
|
||||
set_away(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
event_registry.on("action_disable_away", event => {
|
||||
for(const connection of event.globally ? server_connections.all_connections() : [server_connections.active_connection()]) {
|
||||
if(!connection) continue;
|
||||
|
||||
connection.setAway(false);
|
||||
}
|
||||
|
||||
settings.changeGlobal(Settings.KEY_CLIENT_STATE_AWAY, false);
|
||||
});
|
||||
|
||||
|
||||
event_registry.on(["action_enable_microphone", "action_disable_microphone"], event => {
|
||||
const state = event.type === "action_enable_microphone";
|
||||
/* change the default global setting */
|
||||
settings.changeGlobal(Settings.KEY_CLIENT_STATE_MICROPHONE_MUTED, !state);
|
||||
|
||||
if(current_connection_handler) {
|
||||
current_connection_handler.setMicrophoneMuted(!state);
|
||||
if(!current_connection_handler.getVoiceRecorder())
|
||||
current_connection_handler.acquire_recorder(default_recorder, true); /* acquire_recorder already updates the voice status */
|
||||
}
|
||||
});
|
||||
|
||||
event_registry.on(["action_enable_speaker", "action_disable_speaker"], event => {
|
||||
const state = event.type === "action_enable_speaker";
|
||||
/* change the default global setting */
|
||||
settings.changeGlobal(Settings.KEY_CLIENT_STATE_SPEAKER_MUTED, !state);
|
||||
|
||||
current_connection_handler?.setSpeakerMuted(!state);
|
||||
});
|
||||
|
||||
event_registry.on("action_set_subscribe", event => {
|
||||
/* change the default global setting */
|
||||
settings.changeGlobal(Settings.KEY_CLIENT_STATE_SUBSCRIBE_ALL_CHANNELS, event.subscribe);
|
||||
|
||||
current_connection_handler?.setSubscribeToAllChannels(event.subscribe);
|
||||
});
|
||||
|
||||
event_registry.on("action_toggle_query", event => {
|
||||
/* change the default global setting */
|
||||
settings.changeGlobal(Settings.KEY_CLIENT_STATE_QUERY_SHOWN, event.shown);
|
||||
|
||||
current_connection_handler?.setQueriesShown(event.shown);
|
||||
});
|
||||
|
||||
event_registry.on("action_add_current_server_to_bookmarks", () => add_server_to_bookmarks(current_connection_handler));
|
||||
|
||||
event_registry.on("action_open_window", event => {
|
||||
switch (event.window) {
|
||||
case "bookmark-manage":
|
||||
global_client_actions.fire("action_open_window", { window: "bookmark-manage", connection: current_connection_handler });
|
||||
return;
|
||||
|
||||
case "query-manage":
|
||||
global_client_actions.fire("action_open_window", { window: "query-manage", connection: current_connection_handler });
|
||||
return;
|
||||
}
|
||||
})
|
||||
}
|
|
@ -139,7 +139,7 @@ export namespace callbacks {
|
|||
|
||||
let client: ClientEntry;
|
||||
|
||||
const current_connection = server_connections.active_connection_handler();
|
||||
const current_connection = server_connections.active_connection();
|
||||
if(current_connection && current_connection.channelTree) {
|
||||
if(!client && client_id) {
|
||||
client = current_connection.channelTree.findClient(client_id);
|
||||
|
@ -175,7 +175,7 @@ export namespace callbacks {
|
|||
export function callback_context_channel(element: JQuery) {
|
||||
const channel_id = parseInt(element.attr("channel-id") || "0");
|
||||
|
||||
const current_connection = server_connections.active_connection_handler();
|
||||
const current_connection = server_connections.active_connection();
|
||||
let channel: ChannelEntry;
|
||||
if(current_connection && current_connection.channelTree) {
|
||||
channel = current_connection.channelTree.findChannel(channel_id);
|
||||
|
|
|
@ -241,7 +241,7 @@ export function spawnConnectModal(options: {
|
|||
button_connect.on('click', event => {
|
||||
modal.close();
|
||||
|
||||
const connection = server_connections.active_connection_handler();
|
||||
const connection = server_connections.active_connection();
|
||||
if(connection) {
|
||||
connection.startConnection(
|
||||
current_connect_data ? current_connect_data.address.hostname + ":" + current_connect_data.address.port : server_address(),
|
||||
|
@ -259,8 +259,8 @@ export function spawnConnectModal(options: {
|
|||
button_connect_tab.on('click', event => {
|
||||
modal.close();
|
||||
|
||||
const connection = server_connections.spawn_server_connection_handler();
|
||||
server_connections.set_active_connection_handler(connection);
|
||||
const connection = server_connections.spawn_server_connection();
|
||||
server_connections.set_active_connection(connection);
|
||||
connection.startConnection(
|
||||
current_connect_data ? current_connect_data.address.hostname + ":" + current_connect_data.address.port : server_address(),
|
||||
selected_profile,
|
||||
|
|
|
@ -92,7 +92,7 @@ function settings_general_application(container: JQuery, modal: Modal) {
|
|||
const option = container.find(".option-hostbanner-background") as JQuery<HTMLInputElement>;
|
||||
option.on('change', event => {
|
||||
settings.changeGlobal(Settings.KEY_HOSTBANNER_BACKGROUND, option[0].checked);
|
||||
for(const sc of server_connections.server_connection_handlers())
|
||||
for(const sc of server_connections.all_connections())
|
||||
sc.hostbanner.update();
|
||||
}).prop("checked", settings.static_global(Settings.KEY_HOSTBANNER_BACKGROUND));
|
||||
}
|
||||
|
@ -384,7 +384,7 @@ function settings_general_chat(container: JQuery, modal: Modal) {
|
|||
}).prop("checked", settings.static_global(Settings.KEY_CHAT_COLORED_EMOJIES));
|
||||
}
|
||||
|
||||
const update_format_helper = () => server_connections.server_connection_handlers().map(e => e.side_bar).forEach(e => {
|
||||
const update_format_helper = () => server_connections.all_connections().map(e => e.side_bar).forEach(e => {
|
||||
e.private_conversations().update_input_format_helper();
|
||||
e.channel_conversations().update_input_format_helper();
|
||||
});
|
||||
|
|
|
@ -2,7 +2,8 @@ import {
|
|||
AbstractServerConnection,
|
||||
CommandOptionDefaults,
|
||||
CommandOptions,
|
||||
ConnectionStateListener, voice
|
||||
ConnectionStateListener,
|
||||
voice
|
||||
} from "tc-shared/connection/ConnectionBase";
|
||||
import {ConnectionHandler, ConnectionState, DisconnectReason} from "tc-shared/ConnectionHandler";
|
||||
import {ServerAddress} from "tc-shared/ui/server";
|
||||
|
@ -10,13 +11,13 @@ import {HandshakeHandler} from "tc-shared/connection/HandshakeHandler";
|
|||
import {ConnectionCommandHandler, ServerConnectionCommandBoss} from "tc-shared/connection/CommandHandler";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {settings, Settings} from "tc-shared/settings";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {Regex} from "tc-shared/ui/modal/ModalConnect";
|
||||
import AbstractVoiceConnection = voice.AbstractVoiceConnection;
|
||||
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;
|
||||
|
||||
class ReturnListener<T> {
|
||||
resolve: (value?: T | PromiseLike<T>) => void;
|
||||
|
@ -304,36 +305,38 @@ export class ServerConnection extends AbstractServerConnection {
|
|||
}
|
||||
|
||||
async disconnect(reason?: string) : Promise<void> {
|
||||
clearTimeout(this._connect_timeout_timer);
|
||||
this._connect_timeout_timer = undefined;
|
||||
this.updateConnectionState(ConnectionState.DISCONNECTING);
|
||||
try {
|
||||
clearTimeout(this._connect_timeout_timer);
|
||||
this._connect_timeout_timer = undefined;
|
||||
|
||||
clearTimeout(this._ping.thread_id);
|
||||
this._ping.thread_id = undefined;
|
||||
clearTimeout(this._ping.thread_id);
|
||||
this._ping.thread_id = undefined;
|
||||
|
||||
if(typeof(reason) === "string") {
|
||||
//TODO send disconnect reason
|
||||
}
|
||||
if(typeof(reason) === "string") {
|
||||
//TODO send disconnect reason
|
||||
}
|
||||
|
||||
|
||||
if(this._connectionState != ConnectionState.UNCONNECTED)
|
||||
if(this._voice_connection)
|
||||
this._voice_connection.drop_rtp_session();
|
||||
|
||||
|
||||
if(this._socket_connected) {
|
||||
this._socket_connected.close(3000 + 0xFF, tr("request disconnect"));
|
||||
this._socket_connected = undefined;
|
||||
}
|
||||
|
||||
|
||||
for(let future of this._retListener)
|
||||
future.reject(tr("Connection closed"));
|
||||
this._retListener = [];
|
||||
|
||||
this._connected = false;
|
||||
this._retCodeIdx = 0;
|
||||
} finally {
|
||||
this.updateConnectionState(ConnectionState.UNCONNECTED);
|
||||
|
||||
if(this._voice_connection)
|
||||
this._voice_connection.drop_rtp_session();
|
||||
|
||||
|
||||
if(this._socket_connected) {
|
||||
this._socket_connected.close(3000 + 0xFF, tr("request disconnect"));
|
||||
this._socket_connected = undefined;
|
||||
}
|
||||
|
||||
|
||||
for(let future of this._retListener)
|
||||
future.reject(tr("Connection closed"));
|
||||
this._retListener = [];
|
||||
|
||||
this._connected = false;
|
||||
this._retCodeIdx = 0;
|
||||
}
|
||||
|
||||
private handle_socket_message(data) {
|
||||
|
|
Loading…
Reference in New Issue