Reworked the channel tree events
parent
3fb5ccf8cf
commit
3c70722bfd
|
@ -1,4 +1,8 @@
|
|||
# Changelog:
|
||||
* **16.09.20**
|
||||
- Updating group prefix/suffixes when the group naming mode changes
|
||||
- Added an client talk power indicator
|
||||
|
||||
* **25.09.20**
|
||||
- Update the translation files
|
||||
- Made the server tabs moveable
|
||||
|
|
|
@ -197,6 +197,7 @@ export class ConnectionHandler {
|
|||
this.serverConnection.getVoiceConnection().setWhisperSessionInitializer(this.initializeWhisperSession.bind(this));
|
||||
|
||||
this.serverFeatures = new ServerFeatures(this);
|
||||
this.groups = new GroupManager(this);
|
||||
|
||||
this.channelTree = new ChannelTree(this);
|
||||
this.fileManager = new FileManager(this);
|
||||
|
@ -209,7 +210,6 @@ export class ConnectionHandler {
|
|||
this.sound = new SoundManager(this);
|
||||
this.hostbanner = new Hostbanner(this);
|
||||
|
||||
this.groups = new GroupManager(this);
|
||||
this._local_client = new LocalClientEntry(this);
|
||||
|
||||
this.event_registry.register_handler(this);
|
||||
|
|
|
@ -315,7 +315,7 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
if(ignoreOrder) {
|
||||
for(let ch of tree.channels) {
|
||||
if(ch.properties.channel_order == channel.channelId) {
|
||||
tree.moveChannel(ch, channel, channel.parent); //Corrent the order :)
|
||||
tree.moveChannel(ch, channel, channel.parent, true); //Corrent the order :)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -324,7 +324,7 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
key: string,
|
||||
value: string
|
||||
}[] = [];
|
||||
for(let key in json) {
|
||||
for(let key of Object.keys(json)) {
|
||||
if(key === "cid") continue;
|
||||
if(key === "cpid") continue;
|
||||
if(key === "invokerid") continue;
|
||||
|
@ -692,7 +692,7 @@ export class ConnectionCommandHandler extends AbstractCommandHandler {
|
|||
return 0;
|
||||
}
|
||||
|
||||
tree.moveChannel(channel, prev, parent);
|
||||
tree.moveChannel(channel, prev, parent, true);
|
||||
}
|
||||
|
||||
handleNotifyChannelEdited(json) {
|
||||
|
|
|
@ -3,6 +3,7 @@ import {LogCategory} from "./log";
|
|||
import {guid} from "./crypto/uid";
|
||||
import * as React from "react";
|
||||
import {useEffect} from "react";
|
||||
import {unstable_batchedUpdates} from "react-dom";
|
||||
|
||||
export interface Event<Events, T = keyof Events> {
|
||||
readonly type: T;
|
||||
|
@ -39,6 +40,9 @@ export class Registry<Events extends { [key: string]: any } = { [key: string]: a
|
|||
private debugPrefix = undefined;
|
||||
private warnUnhandledEvents = false;
|
||||
|
||||
private pendingCallbacks: { type: any, data: any }[] = [];
|
||||
private pendingCallbacksTimeout: number = 0;
|
||||
|
||||
constructor() {
|
||||
this.registryUuid = "evreg_data_" + guid();
|
||||
}
|
||||
|
@ -200,11 +204,23 @@ export class Registry<Events extends { [key: string]: any } = { [key: string]: a
|
|||
}
|
||||
|
||||
fire_async<T extends keyof Events>(event_type: T, data?: Events[T], callback?: () => void) {
|
||||
/* TODO: Optimize, bundle them */
|
||||
setTimeout(() => {
|
||||
this.fire(event_type, data);
|
||||
if(typeof callback === "function")
|
||||
callback();
|
||||
if(!this.pendingCallbacksTimeout) {
|
||||
this.pendingCallbacksTimeout = setTimeout(() => this.invokeAsyncCallbacks());
|
||||
this.pendingCallbacks = [];
|
||||
}
|
||||
this.pendingCallbacks.push({ type: event_type, data: data });
|
||||
}
|
||||
|
||||
private invokeAsyncCallbacks() {
|
||||
const callbacks = this.pendingCallbacks;
|
||||
this.pendingCallbacksTimeout = 0;
|
||||
this.pendingCallbacks = undefined;
|
||||
|
||||
unstable_batchedUpdates(() => {
|
||||
let index = callbacks.length;
|
||||
while(index--) {
|
||||
this.fire(callbacks[index].type, callbacks[index].data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@ export class GroupPermissionRequest {
|
|||
promise: LaterPromise<PermissionValue[]>;
|
||||
}
|
||||
|
||||
export type GroupUpdate = { key: GroupProperty, group: Group, oldValue: any, newValue: any };
|
||||
|
||||
export interface GroupManagerEvents {
|
||||
notify_reset: {},
|
||||
notify_groups_created: {
|
||||
|
@ -42,7 +44,11 @@ export interface GroupManagerEvents {
|
|||
notify_groups_deleted: {
|
||||
groups: Group[],
|
||||
cause: "list-update" | "reset" | "user-action"
|
||||
}
|
||||
},
|
||||
notify_groups_updated: { updates: GroupUpdate[] },
|
||||
|
||||
/* will be fired when all server and channel groups have been received */
|
||||
notify_groups_received: {}
|
||||
}
|
||||
|
||||
export type GroupProperty = "name" | "icon" | "sort-id" | "save-db" | "name-mode";
|
||||
|
@ -82,41 +88,43 @@ export class Group {
|
|||
this.name = name;
|
||||
}
|
||||
|
||||
updatePropertiesFromGroupList(data: any) {
|
||||
const updates: GroupProperty[] = [];
|
||||
updatePropertiesFromGroupList(data: any) : GroupUpdate[] {
|
||||
const updates = [] as GroupUpdate[];
|
||||
|
||||
if(this.name !== data["name"]) {
|
||||
updates.push({ key: "name", group: this, oldValue: this.name, newValue: data["name"] });
|
||||
this.name = data["name"];
|
||||
updates.push("name");
|
||||
}
|
||||
|
||||
/* icon */
|
||||
let value = parseInt(data["iconid"]) >>> 0;
|
||||
if(value !== this.properties.iconid) {
|
||||
updates.push({ key: "icon", group: this, oldValue: this.properties.iconid, newValue: value });
|
||||
this.properties.iconid = value;
|
||||
updates.push("icon");
|
||||
}
|
||||
|
||||
value = parseInt(data["sortid"]);
|
||||
if(value !== this.properties.sortid) {
|
||||
updates.push({ key: "sort-id", group: this, oldValue: this.properties.sortid, newValue: value });
|
||||
this.properties.sortid = value;
|
||||
updates.push("sort-id");
|
||||
}
|
||||
|
||||
let flag = parseInt(data["savedb"]) >= 1;
|
||||
if(flag !== this.properties.savedb) {
|
||||
updates.push({ key: "save-db", group: this, oldValue: this.properties.savedb, newValue: flag });
|
||||
this.properties.savedb = flag;
|
||||
updates.push("save-db");
|
||||
}
|
||||
|
||||
value = parseInt(data["namemode"]);
|
||||
if(value !== this.properties.namemode) {
|
||||
updates.push({ key: "name-mode", group: this, oldValue: this.properties.namemode, newValue: flag });
|
||||
this.properties.namemode = value;
|
||||
updates.push("name-mode");
|
||||
}
|
||||
|
||||
if(updates.length > 0)
|
||||
this.events.fire("notify_properties_updated", { updated_properties: updates });
|
||||
if(updates.length > 0) {
|
||||
this.events.fire("notify_properties_updated", { updated_properties: updates.map(e => e.key) });
|
||||
}
|
||||
return updates;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,6 +155,7 @@ export class GroupManager extends AbstractCommandHandler {
|
|||
serverGroups: Group[] = [];
|
||||
channelGroups: Group[] = [];
|
||||
|
||||
private allGroupsReceived = false;
|
||||
private readonly connectionStateListener;
|
||||
private groupPermissionRequests: GroupPermissionRequest[] = [];
|
||||
|
||||
|
@ -155,8 +164,9 @@ export class GroupManager extends AbstractCommandHandler {
|
|||
this.connectionHandler = client;
|
||||
|
||||
this.connectionStateListener = (event: ConnectionEvents["notify_connection_state_changed"]) => {
|
||||
if(event.new_state === ConnectionState.DISCONNECTING || event.new_state === ConnectionState.UNCONNECTED || event.new_state === ConnectionState.CONNECTING)
|
||||
if(event.new_state === ConnectionState.DISCONNECTING || event.new_state === ConnectionState.UNCONNECTED || event.new_state === ConnectionState.CONNECTING) {
|
||||
this.reset();
|
||||
}
|
||||
};
|
||||
|
||||
client.serverConnection.command_handler_boss().register_handler(this);
|
||||
|
@ -174,15 +184,18 @@ export class GroupManager extends AbstractCommandHandler {
|
|||
}
|
||||
|
||||
reset() {
|
||||
if(this.serverGroups.length === 0 && this.channelGroups.length === 0)
|
||||
if(this.serverGroups.length === 0 && this.channelGroups.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug(LogCategory.PERMISSIONS, tr("Resetting server/channel groups"));
|
||||
this.serverGroups = [];
|
||||
this.channelGroups = [];
|
||||
this.allGroupsReceived = false;
|
||||
|
||||
for(const permission of this.groupPermissionRequests)
|
||||
for(const permission of this.groupPermissionRequests) {
|
||||
permission.promise.rejected(tr("Group manager reset"));
|
||||
}
|
||||
this.groupPermissionRequests = [];
|
||||
this.events.fire("notify_reset");
|
||||
}
|
||||
|
@ -215,9 +228,11 @@ export class GroupManager extends AbstractCommandHandler {
|
|||
}
|
||||
|
||||
findServerGroup(id: number) : Group | undefined {
|
||||
for(let group of this.serverGroups)
|
||||
if(group.id === id)
|
||||
for(let group of this.serverGroups) {
|
||||
if(group.id === id) {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -232,6 +247,7 @@ export class GroupManager extends AbstractCommandHandler {
|
|||
let groupList = target == GroupTarget.SERVER ? this.serverGroups : this.channelGroups;
|
||||
const deleteGroups = groupList.slice(0);
|
||||
const newGroups: Group[] = [];
|
||||
const groupUpdates: GroupUpdate[] = [];
|
||||
|
||||
const isInitialList = groupList.length === 0;
|
||||
for(const groupData of json) {
|
||||
|
@ -253,14 +269,15 @@ export class GroupManager extends AbstractCommandHandler {
|
|||
group = new Group(this, groupId, target, type, groupData["name"]);
|
||||
groupList.push(group);
|
||||
newGroups.push(group);
|
||||
group.updatePropertiesFromGroupList(groupData);
|
||||
} else {
|
||||
group = deleteGroups.splice(groupIndex, 1)[0];
|
||||
groupUpdates.push(...group.updatePropertiesFromGroupList(groupData));
|
||||
}
|
||||
|
||||
group.requiredMemberRemovePower = parseInt(groupData["n_member_removep"]);
|
||||
group.requiredMemberAddPower = parseInt(groupData["n_member_addp"]);
|
||||
group.requiredModifyPower = parseInt(groupData["n_modifyp"]);
|
||||
group.updatePropertiesFromGroupList(groupData);
|
||||
group.events.fire("notify_needed_powers_updated");
|
||||
}
|
||||
|
||||
|
@ -276,6 +293,13 @@ export class GroupManager extends AbstractCommandHandler {
|
|||
if(deleteGroups.length !== 0) {
|
||||
this.events.fire("notify_groups_deleted", { groups: deleteGroups, cause: "list-update" });
|
||||
}
|
||||
|
||||
this.events.fire("notify_groups_updated", { updates: groupUpdates });
|
||||
|
||||
if(!this.allGroupsReceived && this.serverGroups.length > 0 && this.channelGroups.length > 0) {
|
||||
this.allGroupsReceived = true;
|
||||
this.events.fire("notify_groups_received");
|
||||
}
|
||||
}
|
||||
|
||||
request_permissions(group: Group) : Promise<PermissionValue[]> {
|
||||
|
|
|
@ -15,14 +15,13 @@ import {openChannelInfo} from "../ui/modal/ModalChannelInfo";
|
|||
import {createChannelModal} from "../ui/modal/ModalCreateChannel";
|
||||
import {formatMessage} from "../ui/frames/chat";
|
||||
|
||||
import * as React from "react";
|
||||
import {Registry} from "../events";
|
||||
import {ChannelTreeEntry, ChannelTreeEntryEvents} from "./ChannelTreeEntry";
|
||||
import {ChannelEntryView as ChannelEntryView} from "../ui/tree/Channel";
|
||||
import {spawnFileTransferModal} from "../ui/modal/transfer/ModalFileTransfer";
|
||||
import {ViewReasonId} from "../ConnectionHandler";
|
||||
import {EventChannelData} from "../ui/frames/log/Definitions";
|
||||
import {ErrorCode} from "../connection/ErrorCode";
|
||||
import {ClientIcon} from "svg-sprites/client-icons";
|
||||
|
||||
export enum ChannelType {
|
||||
PERMANENT,
|
||||
|
@ -94,32 +93,28 @@ export interface ChannelEvents extends ChannelTreeEntryEvents {
|
|||
},
|
||||
notify_collapsed_state_changed: {
|
||||
collapsed: boolean
|
||||
},
|
||||
|
||||
notify_children_changed: {},
|
||||
notify_clients_changed: {}, /* will also be fired when clients haven been reordered */
|
||||
}
|
||||
}
|
||||
|
||||
export class ParsedChannelName {
|
||||
readonly original_name: string;
|
||||
alignment: "center" | "right" | "left" | "normal";
|
||||
repetitive: boolean;
|
||||
readonly originalName: string;
|
||||
alignment: "center" | "right" | "left" | "normal" | "repetitive";
|
||||
text: string; /* does not contain any alignment codes */
|
||||
|
||||
constructor(name: string, has_parent_channel: boolean) {
|
||||
this.original_name = name;
|
||||
this.parse(has_parent_channel);
|
||||
constructor(name: string, hasParentChannel: boolean) {
|
||||
this.originalName = name;
|
||||
this.parse(hasParentChannel);
|
||||
}
|
||||
|
||||
private parse(has_parent_channel: boolean) {
|
||||
this.alignment = "normal";
|
||||
|
||||
parse_type:
|
||||
if(!has_parent_channel && this.original_name.charAt(0) == '[') {
|
||||
let end = this.original_name.indexOf(']');
|
||||
if(!has_parent_channel && this.originalName.charAt(0) == '[') {
|
||||
let end = this.originalName.indexOf(']');
|
||||
if(end === -1) break parse_type;
|
||||
|
||||
let options = this.original_name.substr(1, end - 1);
|
||||
let options = this.originalName.substr(1, end - 1);
|
||||
if(options.indexOf("spacer") === -1) break parse_type;
|
||||
options = options.substr(0, options.indexOf("spacer"));
|
||||
|
||||
|
@ -139,17 +134,16 @@ export class ParsedChannelName {
|
|||
this.alignment = "center";
|
||||
break;
|
||||
case "*":
|
||||
this.alignment = "center";
|
||||
this.repetitive = true;
|
||||
this.alignment = "repetitive";
|
||||
break;
|
||||
default:
|
||||
break parse_type;
|
||||
}
|
||||
|
||||
this.text = this.original_name.substr(end + 1);
|
||||
this.text = this.originalName.substr(end + 1);
|
||||
}
|
||||
if(!this.text && this.alignment === "normal")
|
||||
this.text = this.original_name;
|
||||
this.text = this.originalName;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,7 +158,6 @@ export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
|
|||
child_channel_head?: ChannelEntry;
|
||||
|
||||
readonly events: Registry<ChannelEvents>;
|
||||
readonly view: React.RefObject<ChannelEntryView>;
|
||||
|
||||
parsed_channel_name: ParsedChannelName;
|
||||
|
||||
|
@ -184,13 +177,12 @@ export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
|
|||
private _subscribe_mode: ChannelSubscribeMode;
|
||||
|
||||
private client_list: ClientEntry[] = []; /* this list is sorted correctly! */
|
||||
private readonly client_property_listener;
|
||||
private readonly clientPropertyChangedListener;
|
||||
|
||||
constructor(channelId, channelName) {
|
||||
super();
|
||||
|
||||
this.events = new Registry<ChannelEvents>();
|
||||
this.view = React.createRef<ChannelEntryView>();
|
||||
|
||||
this.properties = new ChannelProperties();
|
||||
this.channelId = channelId;
|
||||
|
@ -199,9 +191,10 @@ export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
|
|||
|
||||
this.parsed_channel_name = new ParsedChannelName("undefined", false);
|
||||
|
||||
this.client_property_listener = (event: ClientEvents["notify_properties_updated"]) => {
|
||||
if(typeof event.updated_properties.client_nickname !== "undefined" || typeof event.updated_properties.client_talk_power !== "undefined")
|
||||
this.clientPropertyChangedListener = (event: ClientEvents["notify_properties_updated"]) => {
|
||||
if("client_nickname" in event.updated_properties || "client_talk_power" in event.updated_properties) {
|
||||
this.reorderClientList(true);
|
||||
}
|
||||
};
|
||||
|
||||
this.events.on("notify_properties_updated", event => {
|
||||
|
@ -262,20 +255,16 @@ export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
|
|||
}
|
||||
|
||||
registerClient(client: ClientEntry) {
|
||||
client.events.on("notify_properties_updated", this.client_property_listener);
|
||||
client.events.on("notify_properties_updated", this.clientPropertyChangedListener);
|
||||
this.client_list.push(client);
|
||||
this.reorderClientList(false);
|
||||
|
||||
this.events.fire("notify_clients_changed");
|
||||
}
|
||||
|
||||
unregisterClient(client: ClientEntry, no_event?: boolean) {
|
||||
client.events.off("notify_properties_updated", this.client_property_listener);
|
||||
if(!this.client_list.remove(client))
|
||||
unregisterClient(client: ClientEntry, noEvent?: boolean) {
|
||||
client.events.off("notify_properties_updated", this.clientPropertyChangedListener);
|
||||
if(!this.client_list.remove(client)) {
|
||||
log.warn(LogCategory.CHANNEL, tr("Unregistered unknown client from channel %s"), this.channelName());
|
||||
|
||||
if(!no_event)
|
||||
this.events.fire("notify_clients_changed");
|
||||
}
|
||||
}
|
||||
|
||||
private reorderClientList(fire_event: boolean) {
|
||||
|
@ -299,7 +288,7 @@ export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
|
|||
/* only fire if really something has changed ;) */
|
||||
for(let index = 0; index < this.client_list.length; index++) {
|
||||
if(this.client_list[index] !== original_list[index]) {
|
||||
this.events.fire("notify_clients_changed");
|
||||
this.channelTree.events.fire("notify_channel_client_order_changed", { channel: this });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -333,7 +322,7 @@ export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
|
|||
}, result);
|
||||
}
|
||||
|
||||
clients_ordered() : ClientEntry[] {
|
||||
channelClientsOrdered() : ClientEntry[] {
|
||||
return this.client_list;
|
||||
}
|
||||
|
||||
|
@ -595,7 +584,7 @@ export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
|
|||
info_update = true;
|
||||
} else if(key == "channel_order") {
|
||||
let order = this.channelTree.findChannel(this.properties.channel_order);
|
||||
this.channelTree.moveChannel(this, order, this.parent);
|
||||
this.channelTree.moveChannel(this, order, this.parent, true);
|
||||
} else if(key === "channel_icon_id") {
|
||||
this.properties.channel_icon_id = variable.value as any >>> 0; /* unsigned 32 bit number! */
|
||||
} else if(key == "channel_description") {
|
||||
|
@ -746,7 +735,6 @@ export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
|
|||
|
||||
this._flag_collapsed = flag;
|
||||
this.events.fire("notify_collapsed_state_changed", { collapsed: flag });
|
||||
this.view.current?.forceUpdate();
|
||||
this.channelTree.client.settings.changeServer(Settings.FN_SERVER_CHANNEL_COLLAPSED(this.channelId), flag);
|
||||
}
|
||||
|
||||
|
@ -780,4 +768,21 @@ export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
|
|||
channel_id: this.channelId
|
||||
}
|
||||
}
|
||||
|
||||
getStatusIcon() : ClientIcon | undefined {
|
||||
if(this.parsed_channel_name.alignment !== "normal") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const subscribed = this.flag_subscribed;
|
||||
if (this.properties.channel_flag_password === true && !this.cached_password()) {
|
||||
return subscribed ? ClientIcon.ChannelYellowSubscribed : ClientIcon.ChannelYellow;
|
||||
} else if (!this.properties.channel_flag_maxclients_unlimited && this.clients().length >= this.properties.channel_maxclients) {
|
||||
return subscribed ? ClientIcon.ChannelRedSubscribed : ClientIcon.ChannelRed;
|
||||
} else if (!this.properties.channel_flag_maxfamilyclients_unlimited && this.properties.channel_maxfamilyclients >= 0 && this.clients(true).length >= this.properties.channel_maxfamilyclients) {
|
||||
return subscribed ? ClientIcon.ChannelRedSubscribed : ClientIcon.ChannelRed;
|
||||
} else {
|
||||
return subscribed ? ClientIcon.ChannelGreenSubscribed : ClientIcon.ChannelGreen;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import * as contextmenu from "tc-shared/ui/elements/ContextMenu";
|
||||
import {MenuEntryType} from "tc-shared/ui/elements/ContextMenu";
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {LogCategory, logWarn} from "tc-shared/log";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import {PermissionType} from "tc-shared/permission/PermissionType";
|
||||
import {KeyCode, SpecialKey} from "tc-shared/PPTListener";
|
||||
|
@ -14,7 +14,6 @@ import {ChannelTreeEntry} from "./ChannelTreeEntry";
|
|||
import {ConnectionHandler, ViewReasonId} from "tc-shared/ConnectionHandler";
|
||||
import {createChannelModal} from "tc-shared/ui/modal/ModalCreateChannel";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {ChannelTreeView} from "tc-shared/ui/tree/View";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import * as React from "react";
|
||||
import * as ppt from "tc-backend/ppt";
|
||||
|
@ -25,8 +24,8 @@ import {spawnBanClient} from "tc-shared/ui/modal/ModalBanClient";
|
|||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
|
||||
import {tra} from "tc-shared/i18n/localize";
|
||||
import {TreeEntryMove} from "tc-shared/ui/tree/TreeEntryMove";
|
||||
import {EventType} from "tc-shared/ui/frames/log/Definitions";
|
||||
import {renderChannelTree} from "tc-shared/ui/tree/Controller";
|
||||
|
||||
export interface ChannelTreeEvents {
|
||||
action_select_entries: {
|
||||
|
@ -40,25 +39,19 @@ export interface ChannelTreeEvents {
|
|||
mode: "auto" | "exclusive" | "append" | "remove";
|
||||
},
|
||||
|
||||
notify_selection_changed: {},
|
||||
notify_root_channel_changed: {},
|
||||
/* general tree notified */
|
||||
notify_tree_reset: {},
|
||||
notify_selection_changed: {},
|
||||
notify_query_view_state_changed: { queries_shown: boolean },
|
||||
|
||||
notify_entry_move_begin: {},
|
||||
notify_entry_move_end: {},
|
||||
|
||||
notify_client_enter_view: {
|
||||
client: ClientEntry,
|
||||
reason: ViewReasonId,
|
||||
isServerJoin: boolean
|
||||
},
|
||||
notify_client_leave_view: {
|
||||
client: ClientEntry,
|
||||
reason: ViewReasonId,
|
||||
message?: string,
|
||||
isServerLeave: boolean
|
||||
},
|
||||
/* channel tree events */
|
||||
notify_channel_created: { channel: ChannelEntry },
|
||||
notify_channel_moved: { channel: ChannelEntry },
|
||||
notify_channel_deleted: { channel: ChannelEntry },
|
||||
notify_channel_client_order_changed: { channel: ChannelEntry },
|
||||
|
||||
notify_channel_updated: {
|
||||
channel: ChannelEntry,
|
||||
|
@ -67,6 +60,26 @@ export interface ChannelTreeEvents {
|
|||
},
|
||||
|
||||
notify_channel_list_received: {}
|
||||
|
||||
/* client events */
|
||||
notify_client_enter_view: {
|
||||
client: ClientEntry,
|
||||
reason: ViewReasonId,
|
||||
isServerJoin: boolean,
|
||||
targetChannel: ChannelEntry
|
||||
},
|
||||
notify_client_moved: {
|
||||
client: ClientEntry,
|
||||
oldChannel: ChannelEntry,
|
||||
newChannel: ChannelEntry
|
||||
}
|
||||
notify_client_leave_view: {
|
||||
client: ClientEntry,
|
||||
reason: ViewReasonId,
|
||||
message?: string,
|
||||
isServerLeave: boolean,
|
||||
sourceChannel: ChannelEntry
|
||||
}
|
||||
}
|
||||
|
||||
export class ChannelTreeEntrySelect {
|
||||
|
@ -198,8 +211,11 @@ export class ChannelTreeEntrySelect {
|
|||
console.warn("Received entry select event with unknown mode: %s", event.mode);
|
||||
}
|
||||
|
||||
/*
|
||||
TODO!
|
||||
if(this.selected_entries.length === 1)
|
||||
this.handle.view.current?.scrollEntryInView(this.selected_entries[0] as any);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,8 +228,8 @@ export class ChannelTree {
|
|||
channels: ChannelEntry[] = [];
|
||||
clients: ClientEntry[] = [];
|
||||
|
||||
readonly view: React.RefObject<ChannelTreeView>;
|
||||
readonly view_move: React.RefObject<TreeEntryMove>;
|
||||
//readonly view: React.RefObject<ChannelTreeView>;
|
||||
//readonly view_move: React.RefObject<TreeEntryMove>;
|
||||
readonly selection: ChannelTreeEntrySelect;
|
||||
|
||||
private readonly _tag_container: JQuery;
|
||||
|
@ -231,33 +247,38 @@ export class ChannelTree {
|
|||
this.events.enableDebug("channel-tree");
|
||||
|
||||
this.client = client;
|
||||
this.view = React.createRef();
|
||||
this.view_move = React.createRef();
|
||||
|
||||
this.server = new ServerEntry(this, "undefined", undefined);
|
||||
this.selection = new ChannelTreeEntrySelect(this);
|
||||
|
||||
this._tag_container = $.spawn("div").addClass("channel-tree-container");
|
||||
renderChannelTree(this, this._tag_container[0]);
|
||||
/*
|
||||
ReactDOM.render([
|
||||
<ChannelTreeView key={"tree"} onMoveStart={(a,b) => this.onChannelEntryMove(a, b)} tree={this} ref={this.view} />,
|
||||
<TreeEntryMove key={"move"} onMoveEnd={(point) => this.onMoveEnd(point.x, point.y)} ref={this.view_move} />
|
||||
], this._tag_container[0]);
|
||||
*/
|
||||
|
||||
this.reset();
|
||||
|
||||
if(!settings.static(Settings.KEY_DISABLE_CONTEXT_MENU, false)) {
|
||||
/*
|
||||
TODO: Move this into the channel tree renderer
|
||||
this._tag_container.on("contextmenu", (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const entry = this.view.current?.getEntryFromPoint(event.pageX, event.pageY);
|
||||
if(entry) {
|
||||
if(this.selection.is_multi_select())
|
||||
if(this.selection.is_multi_select()) {
|
||||
this.open_multiselect_context_menu(this.selection.selected_entries, event.pageX, event.pageY);
|
||||
}
|
||||
} else {
|
||||
this.selection.clear_selection();
|
||||
this.showContextMenu(event.pageX, event.pageY);
|
||||
}
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
this._listener_document_key = event => this.handle_key_press(event);
|
||||
|
@ -293,6 +314,25 @@ export class ChannelTree {
|
|||
return result;
|
||||
}
|
||||
|
||||
findEntryId(entryId: number) : ServerEntry | ChannelEntry | ClientEntry {
|
||||
/* TODO: Build a cache and don't iterate over everything */
|
||||
if(this.server.uniqueEntryId === entryId) {
|
||||
return this.server;
|
||||
}
|
||||
|
||||
const channelIndex = this.channels.findIndex(channel => channel.uniqueEntryId === entryId);
|
||||
if(channelIndex !== -1) {
|
||||
return this.channels[channelIndex];
|
||||
}
|
||||
|
||||
const clientIndex = this.clients.findIndex(client => client.uniqueEntryId === entryId);
|
||||
if(clientIndex !== -1) {
|
||||
return this.clients[clientIndex];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
ReactDOM.unmountComponentAtNode(this._tag_container[0]);
|
||||
|
||||
|
@ -320,7 +360,6 @@ export class ChannelTree {
|
|||
this.server.reset();
|
||||
this.server.remote_address = Object.assign({}, address);
|
||||
this.server.properties.virtualserver_name = serverName;
|
||||
this.events.fire("notify_root_channel_changed");
|
||||
}
|
||||
|
||||
rootChannel() : ChannelEntry[] {
|
||||
|
@ -338,19 +377,20 @@ export class ChannelTree {
|
|||
|
||||
batch_updates(BatchUpdateType.CHANNEL_TREE);
|
||||
try {
|
||||
if(!this.channels.remove(channel))
|
||||
if(!this.channels.remove(channel)) {
|
||||
log.warn(LogCategory.CHANNEL, tr("Deleting an unknown channel!"));
|
||||
}
|
||||
|
||||
channel.children(false).forEach(e => this.deleteChannel(e));
|
||||
if(channel.clients(false).length !== 0) {
|
||||
log.warn(LogCategory.CHANNEL, tr("Deleting a non empty channel! This could cause some errors."));
|
||||
for(const client of channel.clients(false))
|
||||
for(const client of channel.clients(false)) {
|
||||
this.deleteClient(client, { reason: ViewReasonId.VREASON_SYSTEM, serverLeave: false });
|
||||
}
|
||||
}
|
||||
|
||||
const is_root_tree = !channel.parent;
|
||||
this.unregisterChannelFromTree(channel);
|
||||
if(is_root_tree) this.events.fire("notify_root_channel_changed");
|
||||
this.events.fire("notify_channel_deleted", { channel: channel });
|
||||
} finally {
|
||||
flush_batched_updates(BatchUpdateType.CHANNEL_TREE);
|
||||
}
|
||||
|
@ -360,7 +400,8 @@ export class ChannelTree {
|
|||
channel.channelTree = this;
|
||||
this.channels.push(channel);
|
||||
|
||||
this.moveChannel(channel, previous, parent);
|
||||
this.moveChannel(channel, previous, parent, false);
|
||||
this.events.fire("notify_channel_created", { channel: channel });
|
||||
}
|
||||
|
||||
findChannel(channelId: number) : ChannelEntry | undefined {
|
||||
|
@ -379,63 +420,56 @@ export class ChannelTree {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
private unregisterChannelFromTree(channel: ChannelEntry, new_parent?: ChannelEntry) {
|
||||
let oldChannelParent;
|
||||
private unregisterChannelFromTree(channel: ChannelEntry) {
|
||||
if(channel.parent) {
|
||||
if(channel.parent.child_channel_head === channel)
|
||||
if(channel.parent.child_channel_head === channel) {
|
||||
channel.parent.child_channel_head = channel.channel_next;
|
||||
|
||||
/* We need only trigger this once.
|
||||
If the new parent is equal to the old one with applying the "new" parent this event will get triggered */
|
||||
oldChannelParent = channel.parent;
|
||||
}
|
||||
}
|
||||
|
||||
if(channel.channel_previous)
|
||||
if(channel.channel_previous) {
|
||||
channel.channel_previous.channel_next = channel.channel_next;
|
||||
}
|
||||
|
||||
if(channel.channel_next)
|
||||
if(channel.channel_next) {
|
||||
channel.channel_next.channel_previous = channel.channel_previous;
|
||||
}
|
||||
|
||||
if(channel === this.channel_last)
|
||||
if(channel === this.channel_last) {
|
||||
this.channel_last = channel.channel_previous;
|
||||
}
|
||||
|
||||
if(channel === this.channel_first)
|
||||
if(channel === this.channel_first) {
|
||||
this.channel_first = channel.channel_next;
|
||||
}
|
||||
|
||||
channel.channel_next = undefined;
|
||||
channel.channel_previous = undefined;
|
||||
channel.parent = undefined;
|
||||
|
||||
if(oldChannelParent && oldChannelParent !== new_parent)
|
||||
oldChannelParent.events.fire("notify_children_changed");
|
||||
}
|
||||
|
||||
moveChannel(channel: ChannelEntry, channel_previous: ChannelEntry, parent: ChannelEntry) {
|
||||
if(channel_previous != null && channel_previous.parent != parent) {
|
||||
console.error(tr("Invalid channel move (different parents! (%o|%o)"), channel_previous.parent, parent);
|
||||
moveChannel(channel: ChannelEntry, channelPrevious: ChannelEntry, parent: ChannelEntry, triggerMoveEvent: boolean) {
|
||||
if(channelPrevious != null && channelPrevious.parent != parent) {
|
||||
console.error(tr("Invalid channel move (different parents! (%o|%o)"), channelPrevious.parent, parent);
|
||||
return;
|
||||
}
|
||||
|
||||
let root_tree_updated = !channel.parent;
|
||||
this.unregisterChannelFromTree(channel, parent);
|
||||
channel.channel_previous = channel_previous;
|
||||
this.unregisterChannelFromTree(channel);
|
||||
channel.channel_previous = channelPrevious;
|
||||
channel.channel_next = undefined;
|
||||
channel.parent = parent;
|
||||
|
||||
if(channel_previous) {
|
||||
if(channel_previous == this.channel_last)
|
||||
if(channelPrevious) {
|
||||
if(channelPrevious == this.channel_last) {
|
||||
this.channel_last = channel;
|
||||
}
|
||||
|
||||
channel.channel_next = channel_previous.channel_next;
|
||||
channel_previous.channel_next = channel;
|
||||
channel.channel_next = channelPrevious.channel_next;
|
||||
channelPrevious.channel_next = channel;
|
||||
|
||||
if(channel.channel_next)
|
||||
if(channel.channel_next) {
|
||||
channel.channel_next.channel_previous = channel;
|
||||
|
||||
if(!channel.parent_channel())
|
||||
root_tree_updated = true;
|
||||
else
|
||||
channel.parent.events.fire("notify_children_changed");
|
||||
}
|
||||
} else {
|
||||
if(parent) {
|
||||
let children = parent.children();
|
||||
|
@ -446,7 +480,6 @@ export class ChannelTree {
|
|||
channel.channel_next = children[0];
|
||||
channel.channel_next.channel_previous = channel;
|
||||
}
|
||||
parent.events.fire("notify_children_changed");
|
||||
} else {
|
||||
channel.channel_next = this.channel_first;
|
||||
if(this.channel_first)
|
||||
|
@ -454,14 +487,9 @@ export class ChannelTree {
|
|||
|
||||
this.channel_first = channel;
|
||||
this.channel_last = this.channel_last || channel;
|
||||
root_tree_updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
//channel.update_family_index();
|
||||
//channel.children(true).forEach(e => e.update_family_index());
|
||||
//channel.clients(true).forEach(e => e.update_family_index());
|
||||
|
||||
if(channel.channel_previous == channel) { /* shall never happen */
|
||||
channel.channel_previous = undefined;
|
||||
debugger;
|
||||
|
@ -471,22 +499,23 @@ export class ChannelTree {
|
|||
debugger;
|
||||
}
|
||||
|
||||
if(root_tree_updated)
|
||||
this.events.fire("notify_root_channel_changed");
|
||||
if(triggerMoveEvent) {
|
||||
this.events.fire("notify_channel_moved", { channel: channel });
|
||||
}
|
||||
}
|
||||
|
||||
deleteClient(client: ClientEntry, reason: { reason: ViewReasonId, message?: string, serverLeave: boolean }) {
|
||||
const old_channel = client.currentChannel();
|
||||
old_channel?.unregisterClient(client);
|
||||
const oldChannel = client.currentChannel();
|
||||
oldChannel?.unregisterClient(client);
|
||||
this.clients.remove(client);
|
||||
|
||||
client.events.fire("notify_left_view", reason);
|
||||
if(old_channel) {
|
||||
this.events.fire("notify_client_leave_view", { client: client, message: reason.message, reason: reason.reason, isServerLeave: reason.serverLeave });
|
||||
this.client.side_bar.info_frame().update_channel_client_count(old_channel);
|
||||
if(oldChannel) {
|
||||
this.events.fire("notify_client_leave_view", { client: client, message: reason.message, reason: reason.reason, isServerLeave: reason.serverLeave, sourceChannel: oldChannel });
|
||||
this.client.side_bar.info_frame().update_channel_client_count(oldChannel);
|
||||
} else {
|
||||
logWarn(LogCategory.CHANNEL, tr("Deleting client %s from channel tree which hasn't a channel."), client.clientId());
|
||||
}
|
||||
|
||||
//FIXME: Trigger the notify_clients_changed event!
|
||||
const voice_connection = this.client.serverConnection.getVoiceConnection();
|
||||
if(client.getVoiceClient()) {
|
||||
const voiceClient = client.getVoiceClient();
|
||||
|
@ -530,7 +559,7 @@ export class ChannelTree {
|
|||
client["_channel"] = channel;
|
||||
channel.registerClient(client);
|
||||
|
||||
this.events.fire("notify_client_enter_view", { client: client, reason: reason.reason, isServerJoin: reason.isServerJoin });
|
||||
this.events.fire("notify_client_enter_view", { client: client, reason: reason.reason, isServerJoin: reason.isServerJoin, targetChannel: channel });
|
||||
return client;
|
||||
} finally {
|
||||
flush_batched_updates(BatchUpdateType.CHANNEL_TREE);
|
||||
|
@ -553,9 +582,7 @@ export class ChannelTree {
|
|||
this.client.side_bar.info_frame().update_channel_client_count(targetChannel);
|
||||
}
|
||||
|
||||
if(oldChannel && targetChannel) {
|
||||
client.events.fire("notify_client_moved", { oldChannel: oldChannel, newChannel: targetChannel });
|
||||
}
|
||||
this.events.fire("notify_client_moved", { oldChannel: oldChannel, newChannel: targetChannel, client: client });
|
||||
} finally {
|
||||
flush_batched_updates(BatchUpdateType.CHANNEL_TREE);
|
||||
}
|
||||
|
@ -916,7 +943,7 @@ export class ChannelTree {
|
|||
|
||||
private select_next_channel(channel: ChannelEntry, select_client: boolean) {
|
||||
if(select_client) {
|
||||
const clients = channel.clients_ordered();
|
||||
const clients = channel.channelClientsOrdered();
|
||||
if(clients.length > 0) {
|
||||
this.events.fire("action_select_entries", {
|
||||
mode: "exclusive",
|
||||
|
@ -974,7 +1001,7 @@ export class ChannelTree {
|
|||
if(siblings.length == 0) break;
|
||||
previous = siblings.last();
|
||||
}
|
||||
const clients = previous.clients_ordered();
|
||||
const clients = previous.channelClientsOrdered();
|
||||
if(clients.length > 0) {
|
||||
this.events.fire("action_select_entries", {
|
||||
mode: "exclusive",
|
||||
|
@ -990,7 +1017,7 @@ export class ChannelTree {
|
|||
}
|
||||
} else if(selected.hasParent()) {
|
||||
const channel = selected.parent_channel();
|
||||
const clients = channel.clients_ordered();
|
||||
const clients = channel.channelClientsOrdered();
|
||||
if(clients.length > 0) {
|
||||
this.events.fire("action_select_entries", {
|
||||
mode: "exclusive",
|
||||
|
@ -1012,7 +1039,7 @@ export class ChannelTree {
|
|||
}
|
||||
} else if(selected instanceof ClientEntry) {
|
||||
const channel = selected.currentChannel();
|
||||
const clients = channel.clients_ordered();
|
||||
const clients = channel.channelClientsOrdered();
|
||||
const index = clients.indexOf(selected);
|
||||
if(index > 0) {
|
||||
this.events.fire("action_select_entries", {
|
||||
|
@ -1034,7 +1061,7 @@ export class ChannelTree {
|
|||
this.select_next_channel(selected, true);
|
||||
} else if(selected instanceof ClientEntry){
|
||||
const channel = selected.currentChannel();
|
||||
const clients = channel.clients_ordered();
|
||||
const clients = channel.channelClientsOrdered();
|
||||
const index = clients.indexOf(selected);
|
||||
if(index + 1 < clients.length) {
|
||||
this.events.fire("action_select_entries", {
|
||||
|
@ -1131,6 +1158,7 @@ export class ChannelTree {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private onChannelEntryMove(start, current) {
|
||||
const move = this.view_move.current;
|
||||
if(!move) return;
|
||||
|
@ -1175,8 +1203,10 @@ export class ChannelTree {
|
|||
flush_batched_updates(BatchUpdateType.CHANNEL_TREE);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
isClientMoveActive() {
|
||||
return !!this.view_move.current?.isActive();
|
||||
//return !!this.view_move.current?.isActive();
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -5,12 +5,18 @@ export interface ChannelTreeEntryEvents {
|
|||
notify_unread_state_change: { unread: boolean }
|
||||
}
|
||||
|
||||
export class ChannelTreeEntry<Events extends ChannelTreeEntryEvents> {
|
||||
let treeEntryIdCounter = 0;
|
||||
export abstract class ChannelTreeEntry<Events extends ChannelTreeEntryEvents> {
|
||||
readonly events: Registry<Events>;
|
||||
readonly uniqueEntryId: number;
|
||||
|
||||
protected selected_: boolean = false;
|
||||
protected unread_: boolean = false;
|
||||
|
||||
protected constructor() {
|
||||
this.uniqueEntryId = treeEntryIdCounter++;
|
||||
}
|
||||
|
||||
/* called from the channel tree */
|
||||
protected onSelect(singleSelect: boolean) {
|
||||
if(this.selected_ === true) return;
|
||||
|
@ -36,4 +42,6 @@ export class ChannelTreeEntry<Events extends ChannelTreeEntryEvents> {
|
|||
this.events.fire("notify_unread_state_change", { unread: flag });
|
||||
}
|
||||
isUnread() { return this.unread_; }
|
||||
|
||||
abstract showContextMenu(pageX: number, pageY: number, on_close?);
|
||||
}
|
|
@ -19,8 +19,6 @@ import {spawnChangeLatency} from "../ui/modal/ModalChangeLatency";
|
|||
import {formatMessage} from "../ui/frames/chat";
|
||||
import {spawnYesNo} from "../ui/modal/ModalYesNo";
|
||||
import * as hex from "../crypto/hex";
|
||||
import {ClientEntry as ClientEntryView} from "../ui/tree/Client";
|
||||
import * as React from "react";
|
||||
import {ChannelTreeEntry, ChannelTreeEntryEvents} from "./ChannelTreeEntry";
|
||||
import {spawnClientVolumeChange, spawnMusicBotVolumeChange} from "../ui/modal/ModalChangeVolumeNew";
|
||||
import {spawnPermissionEditorModal} from "../ui/modal/permission/ModalPermissionEditor";
|
||||
|
@ -30,6 +28,7 @@ import {global_client_actions} from "../events/GlobalEvents";
|
|||
import {ClientIcon} from "svg-sprites/client-icons";
|
||||
import {VoiceClient} from "../voice/VoiceClient";
|
||||
import {VoicePlayerEvents, VoicePlayerState} from "../voice/VoicePlayer";
|
||||
import {ChannelTreeUIEvents} from "tc-shared/ui/tree/Definitions";
|
||||
|
||||
export enum ClientType {
|
||||
CLIENT_VOICE,
|
||||
|
@ -83,6 +82,10 @@ export class ClientProperties {
|
|||
client_total_bytes_downloaded: number = 0;
|
||||
|
||||
client_talk_power: number = 0;
|
||||
client_talk_request: number = 0;
|
||||
client_talk_request_msg: string = "";
|
||||
client_is_talker: boolean = false;
|
||||
|
||||
client_is_priority_speaker: boolean = false;
|
||||
}
|
||||
|
||||
|
@ -139,14 +142,6 @@ export class ClientConnectionInfo {
|
|||
}
|
||||
|
||||
export interface ClientEvents extends ChannelTreeEntryEvents {
|
||||
notify_enter_view: {},
|
||||
notify_client_moved: { oldChannel: ChannelEntry, newChannel: ChannelEntry }
|
||||
notify_left_view: {
|
||||
reason: ViewReasonId;
|
||||
message?: string;
|
||||
serverLeave: boolean;
|
||||
},
|
||||
|
||||
notify_properties_updated: {
|
||||
updated_properties: {[Key in keyof ClientProperties]: ClientProperties[Key]};
|
||||
client_properties: ClientProperties
|
||||
|
@ -183,7 +178,6 @@ const StatusIconUpdateKeys: (keyof ClientProperties)[] = [
|
|||
|
||||
export class ClientEntry extends ChannelTreeEntry<ClientEvents> {
|
||||
readonly events: Registry<ClientEvents>;
|
||||
readonly view: React.RefObject<ClientEntryView> = React.createRef<ClientEntryView>();
|
||||
channelTree: ChannelTree;
|
||||
|
||||
protected _clientId: number;
|
||||
|
@ -997,7 +991,7 @@ export class LocalClientEntry extends ClientEntry {
|
|||
tr("Change name") +
|
||||
(contextmenu.get_provider().html_format_enabled() ? "</b>" : ""),
|
||||
icon_class: "client-change_nickname",
|
||||
callback: () => this.openRename(),
|
||||
callback: () => this.openRenameModal(), /* FIXME: Pass the UI event registry */
|
||||
type: contextmenu.MenuEntryType.ENTRY
|
||||
}, {
|
||||
name: tr("Change description"),
|
||||
|
@ -1046,20 +1040,20 @@ export class LocalClientEntry extends ClientEntry {
|
|||
});
|
||||
}
|
||||
|
||||
openRename() : void {
|
||||
const view = this.channelTree.view.current;
|
||||
if(!view) return; //TODO: Fallback input modal
|
||||
view.scrollEntryInView(this, () => {
|
||||
const own_view = this.view.current;
|
||||
if(!own_view) {
|
||||
return; //TODO: Fallback input modal
|
||||
openRenameModal() {
|
||||
createInputModal(tr("Enter your new name"), tr("Enter your new client name"), text => text.length >= 3 && text.length <= 30, value => {
|
||||
if(value) {
|
||||
this.renameSelf(value as string).then(result => {
|
||||
if(!result) {
|
||||
createErrorModal(tr("Failed change nickname"), tr("Failed to change your client nickname")).open();
|
||||
}
|
||||
});
|
||||
}
|
||||
}).open();
|
||||
}
|
||||
|
||||
own_view.setState({
|
||||
rename: true,
|
||||
renameInitialName: this.properties.client_nickname
|
||||
});
|
||||
});
|
||||
openRename(events: Registry<ChannelTreeUIEvents>) : void {
|
||||
events.fire("notify_client_name_edit", { initialValue: this.clientNickName(), treeEntryId: this.uniqueEntryId });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,6 @@ import {spawnAvatarList} from "../ui/modal/ModalAvatarList";
|
|||
import {connection_log} from "../ui/modal/ModalConnect";
|
||||
import * as top_menu from "../ui/frames/MenuBar";
|
||||
import {control_bar_instance} from "../ui/frames/control-bar";
|
||||
import { ServerEntry as ServerEntryView } from "../ui/tree/Server";
|
||||
import * as React from "react";
|
||||
import {Registry} from "../events";
|
||||
import {ChannelTreeEntry, ChannelTreeEntryEvents} from "./ChannelTreeEntry";
|
||||
|
||||
|
@ -138,7 +136,6 @@ export class ServerEntry extends ChannelTreeEntry<ServerEvents> {
|
|||
properties: ServerProperties;
|
||||
|
||||
readonly events: Registry<ServerEvents>;
|
||||
readonly view: React.Ref<ServerEntryView>;
|
||||
|
||||
private info_request_promise: Promise<void> = undefined;
|
||||
private info_request_promise_resolve: any = undefined;
|
||||
|
@ -157,7 +154,6 @@ export class ServerEntry extends ChannelTreeEntry<ServerEvents> {
|
|||
super();
|
||||
|
||||
this.events = new Registry<ServerEvents>();
|
||||
this.view = React.createRef();
|
||||
|
||||
this.properties = new ServerProperties();
|
||||
this.channelTree = tree;
|
||||
|
@ -266,7 +262,7 @@ export class ServerEntry extends ChannelTreeEntry<ServerEvents> {
|
|||
];
|
||||
}
|
||||
|
||||
spawnContextMenu(x: number, y: number, on_close: () => void = () => {}) {
|
||||
showContextMenu(x: number, y: number, on_close: () => void = () => {}) {
|
||||
contextmenu.spawn_context_menu(x, y, ...this.contextMenuItems(),
|
||||
contextmenu.Entry.CLOSE(on_close)
|
||||
);
|
||||
|
|
|
@ -184,10 +184,13 @@ export class InfoFrame {
|
|||
}
|
||||
|
||||
update_channel_client_count(channel: ChannelEntry) {
|
||||
if(channel === this._channel_text)
|
||||
if(channel === this._channel_text) {
|
||||
this.update_channel_limit(channel, this._html_tag.find(".value-text-limit"));
|
||||
if(channel === this._channel_voice)
|
||||
}
|
||||
|
||||
if(channel === this._channel_voice) {
|
||||
this.update_channel_limit(channel, this._html_tag.find(".value-voice-limit"));
|
||||
}
|
||||
}
|
||||
|
||||
private update_channel_limit(channel: ChannelEntry, tag: JQuery) {
|
||||
|
|
|
@ -152,7 +152,7 @@ export namespace callbacks {
|
|||
|
||||
if(!client) {
|
||||
if(current_connection.channelTree.server.properties.virtualserver_unique_identifier === client_unique_id) {
|
||||
current_connection.channelTree.server.spawnContextMenu(mouse_coordinates.x, mouse_coordinates.y);
|
||||
current_connection.channelTree.server.showContextMenu(mouse_coordinates.x, mouse_coordinates.y);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,9 @@ import {WebModalRenderer} from "../../../ui/react-elements/external-modal/Popout
|
|||
import {ClientModalRenderer} from "../../../ui/react-elements/external-modal/PopoutRendererClient";
|
||||
import {setupJSRender} from "../../../ui/jsrender";
|
||||
|
||||
import "../../../file/RemoteAvatars";
|
||||
import "../../../file/RemoteIcons";
|
||||
|
||||
let modalRenderer: ModalRenderer;
|
||||
let modalInstance: AbstractModal;
|
||||
let modalClass: new <T>(events: Registry<T>, userData: any) => AbstractModal;
|
||||
|
|
|
@ -31,3 +31,8 @@ registerHandler({
|
|||
name: "css-editor",
|
||||
loadClass: async () => await import("tc-shared/ui/modal/css-editor/Renderer")
|
||||
});
|
||||
|
||||
registerHandler({
|
||||
name: "channel-tree",
|
||||
loadClass: async () => await import("tc-shared/ui/tree/RendererModal")
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue