2020-09-12 12:51:03 +00:00
|
|
|
import {ChannelTree} from "./ChannelTree";
|
|
|
|
import {ClientEntry, ClientEvents} from "./Client";
|
2020-09-12 13:49:20 +00:00
|
|
|
import * as log from "../log";
|
2021-01-10 16:36:57 +00:00
|
|
|
import {LogCategory, logInfo, LogType, logWarn} from "../log";
|
2020-09-12 13:49:20 +00:00
|
|
|
import {PermissionType} from "../permission/PermissionType";
|
|
|
|
import {settings, Settings} from "../settings";
|
|
|
|
import * as contextmenu from "../ui/elements/ContextMenu";
|
|
|
|
import {MenuEntryType} from "../ui/elements/ContextMenu";
|
|
|
|
import {Sound} from "../sound/Sounds";
|
|
|
|
import {createErrorModal, createInfoModal, createInputModal} from "../ui/elements/Modal";
|
|
|
|
import {CommandResult} from "../connection/ServerConnectionDeclaration";
|
2020-09-12 12:51:03 +00:00
|
|
|
import * as htmltags from "../ui/htmltags";
|
2020-09-12 13:49:20 +00:00
|
|
|
import {hashPassword} from "../utils/helpers";
|
|
|
|
import {openChannelInfo} from "../ui/modal/ModalChannelInfo";
|
|
|
|
import {formatMessage} from "../ui/frames/chat";
|
2020-03-30 11:44:18 +00:00
|
|
|
|
2020-09-12 13:49:20 +00:00
|
|
|
import {Registry} from "../events";
|
2020-09-12 12:51:03 +00:00
|
|
|
import {ChannelTreeEntry, ChannelTreeEntryEvents} from "./ChannelTreeEntry";
|
2020-09-12 13:49:20 +00:00
|
|
|
import {spawnFileTransferModal} from "../ui/modal/transfer/ModalFileTransfer";
|
|
|
|
import {ErrorCode} from "../connection/ErrorCode";
|
2020-09-26 19:34:46 +00:00
|
|
|
import {ClientIcon} from "svg-sprites/client-icons";
|
2020-11-29 13:42:02 +00:00
|
|
|
import { tr } from "tc-shared/i18n/localize";
|
2020-12-18 16:06:38 +00:00
|
|
|
import {EventChannelData} from "tc-shared/connectionlog/Definitions";
|
2020-12-22 12:32:56 +00:00
|
|
|
import {spawnChannelEditNew} from "tc-shared/ui/modal/channel-edit/Controller";
|
2021-02-19 22:05:48 +00:00
|
|
|
import {spawnInviteGenerator} from "tc-shared/ui/modal/invite/Controller";
|
2020-04-18 17:37:30 +00:00
|
|
|
|
2020-03-30 11:44:18 +00:00
|
|
|
export enum ChannelType {
|
2018-02-27 16:20:49 +00:00
|
|
|
PERMANENT,
|
|
|
|
SEMI_PERMANENT,
|
|
|
|
TEMPORARY
|
|
|
|
}
|
2020-03-30 11:44:18 +00:00
|
|
|
export namespace ChannelType {
|
2018-02-27 16:20:49 +00:00
|
|
|
export function normalize(mode: ChannelType) {
|
|
|
|
let value: string = ChannelType[mode];
|
|
|
|
value = value.toLowerCase();
|
|
|
|
return value[0].toUpperCase() + value.substr(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-30 11:44:18 +00:00
|
|
|
export enum ChannelSubscribeMode {
|
2019-03-17 11:15:39 +00:00
|
|
|
SUBSCRIBED,
|
|
|
|
UNSUBSCRIBED,
|
|
|
|
INHERITED
|
|
|
|
}
|
|
|
|
|
2020-12-09 13:22:22 +00:00
|
|
|
export enum ChannelConversationMode {
|
|
|
|
Public = 0,
|
|
|
|
Private = 1,
|
2020-12-18 16:06:38 +00:00
|
|
|
None = 2,
|
|
|
|
}
|
|
|
|
|
|
|
|
export enum ChannelSidebarMode {
|
|
|
|
Conversation = 0,
|
|
|
|
Description = 1,
|
|
|
|
FileTransfer = 2,
|
|
|
|
|
|
|
|
/* Only used within client side */
|
|
|
|
Unknown = 0xFF
|
2020-12-09 13:22:22 +00:00
|
|
|
}
|
|
|
|
|
2020-03-30 11:44:18 +00:00
|
|
|
export class ChannelProperties {
|
2018-04-16 18:38:35 +00:00
|
|
|
channel_order: number = 0;
|
|
|
|
channel_name: string = "";
|
2018-08-13 15:50:55 +00:00
|
|
|
channel_name_phonetic: string = "";
|
2018-04-16 18:38:35 +00:00
|
|
|
channel_topic: string = "";
|
|
|
|
|
|
|
|
channel_password: string = "";
|
|
|
|
|
|
|
|
channel_codec: number = 4;
|
2020-12-22 12:32:56 +00:00
|
|
|
channel_codec_quality: number = 6;
|
2018-04-16 18:38:35 +00:00
|
|
|
channel_codec_is_unencrypted: boolean = false;
|
|
|
|
|
|
|
|
channel_maxclients: number = -1;
|
|
|
|
channel_maxfamilyclients: number = -1;
|
|
|
|
|
2020-04-25 15:42:18 +00:00
|
|
|
channel_needed_talk_power: number = 0;
|
2018-04-16 18:38:35 +00:00
|
|
|
|
|
|
|
channel_flag_permanent: boolean = false;
|
|
|
|
channel_flag_semi_permanent: boolean = false;
|
|
|
|
channel_flag_default: boolean = false;
|
|
|
|
channel_flag_password: boolean = false;
|
2020-12-22 12:32:56 +00:00
|
|
|
channel_flag_maxclients_unlimited: boolean = true;
|
2018-04-16 18:38:35 +00:00
|
|
|
channel_flag_maxfamilyclients_inherited: boolean = false;
|
2020-12-22 12:32:56 +00:00
|
|
|
channel_flag_maxfamilyclients_unlimited: boolean = true;
|
2018-04-16 18:38:35 +00:00
|
|
|
|
2018-08-13 15:50:55 +00:00
|
|
|
channel_icon_id: number = 0;
|
|
|
|
channel_delete_delay: number = 0;
|
|
|
|
|
|
|
|
//Only after request
|
|
|
|
channel_description: string = "";
|
2019-08-21 08:00:01 +00:00
|
|
|
|
2020-12-18 16:06:38 +00:00
|
|
|
channel_conversation_mode: ChannelConversationMode = ChannelConversationMode.Public;
|
2019-09-18 23:25:57 +00:00
|
|
|
channel_conversation_history_length: number = -1;
|
2020-12-18 16:06:38 +00:00
|
|
|
|
|
|
|
channel_sidebar_mode: ChannelSidebarMode = ChannelSidebarMode.Unknown;
|
2018-04-16 18:38:35 +00:00
|
|
|
}
|
|
|
|
|
2020-04-18 17:37:30 +00:00
|
|
|
export interface ChannelEvents extends ChannelTreeEntryEvents {
|
|
|
|
notify_properties_updated: {
|
|
|
|
updated_properties: {[Key in keyof ChannelProperties]: ChannelProperties[Key]};
|
|
|
|
channel_properties: ChannelProperties
|
|
|
|
},
|
|
|
|
|
|
|
|
notify_cached_password_updated: {
|
|
|
|
reason: "channel-password-changed" | "password-miss-match" | "password-entered";
|
|
|
|
new_hash?: string;
|
|
|
|
},
|
|
|
|
|
|
|
|
notify_subscribe_state_changed: {
|
|
|
|
channel_subscribed: boolean
|
|
|
|
},
|
2020-04-18 19:26:44 +00:00
|
|
|
notify_collapsed_state_changed: {
|
|
|
|
collapsed: boolean
|
2020-12-18 16:06:38 +00:00
|
|
|
},
|
|
|
|
notify_description_changed: {}
|
2020-04-18 17:37:30 +00:00
|
|
|
}
|
|
|
|
|
2021-01-22 16:38:23 +00:00
|
|
|
export type ChannelNameAlignment = "center" | "right" | "left" | "normal" | "repetitive";
|
|
|
|
export class ChannelNameParser {
|
2020-09-26 19:34:46 +00:00
|
|
|
readonly originalName: string;
|
2021-01-22 16:38:23 +00:00
|
|
|
alignment: ChannelNameAlignment;
|
2020-04-18 17:37:30 +00:00
|
|
|
text: string; /* does not contain any alignment codes */
|
2021-01-22 16:38:23 +00:00
|
|
|
uniqueId: string;
|
2020-04-18 17:37:30 +00:00
|
|
|
|
2020-09-26 19:34:46 +00:00
|
|
|
constructor(name: string, hasParentChannel: boolean) {
|
|
|
|
this.originalName = name;
|
|
|
|
this.parse(hasParentChannel);
|
2020-04-18 17:37:30 +00:00
|
|
|
}
|
|
|
|
|
2021-01-22 16:38:23 +00:00
|
|
|
private parse(hasParentChannel: boolean) {
|
2020-04-18 17:37:30 +00:00
|
|
|
this.alignment = "normal";
|
2021-01-22 16:38:23 +00:00
|
|
|
if(this.originalName.length < 3) {
|
|
|
|
this.text = this.originalName;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-18 17:37:30 +00:00
|
|
|
|
2021-01-22 16:38:23 +00:00
|
|
|
parseType:
|
|
|
|
if(!hasParentChannel && this.originalName.charAt(0) == '[') {
|
2020-09-26 19:34:46 +00:00
|
|
|
let end = this.originalName.indexOf(']');
|
2021-01-22 16:38:23 +00:00
|
|
|
if(end === -1) {
|
|
|
|
break parseType;
|
|
|
|
}
|
2020-04-18 17:37:30 +00:00
|
|
|
|
2020-09-26 19:34:46 +00:00
|
|
|
let options = this.originalName.substr(1, end - 1);
|
2021-01-22 16:38:23 +00:00
|
|
|
const spacerIndex = options.indexOf("spacer");
|
|
|
|
if(spacerIndex === -1) break parseType;
|
|
|
|
this.uniqueId = options.substring(spacerIndex + 6);
|
|
|
|
options = options.substr(0, spacerIndex);
|
2020-04-18 17:37:30 +00:00
|
|
|
|
2021-01-22 16:38:23 +00:00
|
|
|
if(options.length == 0) {
|
2020-04-18 17:37:30 +00:00
|
|
|
options = "l";
|
2021-01-22 16:38:23 +00:00
|
|
|
} else if(options.length > 1) {
|
2020-04-18 17:37:30 +00:00
|
|
|
options = options[0];
|
2021-01-22 16:38:23 +00:00
|
|
|
}
|
2020-04-18 17:37:30 +00:00
|
|
|
|
|
|
|
switch (options) {
|
|
|
|
case "r":
|
|
|
|
this.alignment = "right";
|
|
|
|
break;
|
2021-01-22 16:38:23 +00:00
|
|
|
|
2020-04-18 17:37:30 +00:00
|
|
|
case "l":
|
2021-01-22 15:57:08 +00:00
|
|
|
this.alignment = "left";
|
2020-04-18 17:37:30 +00:00
|
|
|
break;
|
2021-01-22 16:38:23 +00:00
|
|
|
|
2020-04-18 17:37:30 +00:00
|
|
|
case "c":
|
|
|
|
this.alignment = "center";
|
|
|
|
break;
|
2021-01-22 16:38:23 +00:00
|
|
|
|
2020-04-18 17:37:30 +00:00
|
|
|
case "*":
|
2020-09-26 19:34:46 +00:00
|
|
|
this.alignment = "repetitive";
|
2020-04-18 17:37:30 +00:00
|
|
|
break;
|
2021-01-22 16:38:23 +00:00
|
|
|
|
2020-04-18 17:37:30 +00:00
|
|
|
default:
|
2021-01-22 16:38:23 +00:00
|
|
|
break parseType;
|
2020-04-18 17:37:30 +00:00
|
|
|
}
|
|
|
|
|
2020-09-26 19:34:46 +00:00
|
|
|
this.text = this.originalName.substr(end + 1);
|
2020-04-18 17:37:30 +00:00
|
|
|
}
|
2021-01-22 16:38:23 +00:00
|
|
|
|
|
|
|
if(!this.text && this.alignment === "normal") {
|
2020-09-26 19:34:46 +00:00
|
|
|
this.text = this.originalName;
|
2021-01-22 16:38:23 +00:00
|
|
|
}
|
2020-04-18 17:37:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
|
2018-02-27 16:20:49 +00:00
|
|
|
channelTree: ChannelTree;
|
|
|
|
channelId: number;
|
|
|
|
parent?: ChannelEntry;
|
2018-04-16 18:38:35 +00:00
|
|
|
properties: ChannelProperties = new ChannelProperties();
|
2018-02-27 16:20:49 +00:00
|
|
|
|
2018-12-02 13:12:23 +00:00
|
|
|
channel_previous?: ChannelEntry;
|
|
|
|
channel_next?: ChannelEntry;
|
2020-04-18 18:03:04 +00:00
|
|
|
child_channel_head?: ChannelEntry;
|
2018-12-02 13:12:23 +00:00
|
|
|
|
2020-04-18 17:37:30 +00:00
|
|
|
readonly events: Registry<ChannelEvents>;
|
|
|
|
|
2021-01-22 16:38:23 +00:00
|
|
|
parsed_channel_name: ChannelNameParser;
|
2020-04-18 17:37:30 +00:00
|
|
|
|
2018-11-03 23:39:29 +00:00
|
|
|
private _family_index: number = 0;
|
2018-04-16 18:38:35 +00:00
|
|
|
|
|
|
|
//HTML DOM elements
|
2019-08-21 08:00:01 +00:00
|
|
|
private _destroyed = false;
|
2018-04-16 18:38:35 +00:00
|
|
|
|
2020-06-10 16:13:56 +00:00
|
|
|
private cachedPasswordHash: string;
|
2020-12-18 16:06:38 +00:00
|
|
|
private channelDescriptionCached: boolean;
|
|
|
|
private channelDescriptionCallback: ((success: boolean) => void)[];
|
|
|
|
private channelDescriptionPromise: Promise<string>;
|
2018-02-27 16:20:49 +00:00
|
|
|
|
2020-12-04 12:36:34 +00:00
|
|
|
private collapsed: boolean;
|
|
|
|
private subscribed: boolean;
|
|
|
|
private subscriptionMode: ChannelSubscribeMode;
|
2019-03-17 11:15:39 +00:00
|
|
|
|
2020-04-18 17:37:30 +00:00
|
|
|
private client_list: ClientEntry[] = []; /* this list is sorted correctly! */
|
2020-09-26 19:34:46 +00:00
|
|
|
private readonly clientPropertyChangedListener;
|
2020-04-18 17:37:30 +00:00
|
|
|
|
2020-12-04 12:36:34 +00:00
|
|
|
constructor(channelTree: ChannelTree, channelId: number, channelName: string) {
|
2020-04-18 17:37:30 +00:00
|
|
|
super();
|
|
|
|
|
2020-12-04 12:36:34 +00:00
|
|
|
this.channelTree = channelTree;
|
2020-04-18 17:37:30 +00:00
|
|
|
this.events = new Registry<ChannelEvents>();
|
2020-09-26 19:34:46 +00:00
|
|
|
|
2020-12-18 18:18:01 +00:00
|
|
|
this.subscribed = false;
|
2018-04-16 18:38:35 +00:00
|
|
|
this.properties = new ChannelProperties();
|
2018-02-27 16:20:49 +00:00
|
|
|
this.channelId = channelId;
|
2019-02-17 15:08:10 +00:00
|
|
|
this.properties.channel_name = channelName;
|
2021-01-22 16:38:23 +00:00
|
|
|
this.parsed_channel_name = new ChannelNameParser(channelName, false);
|
2020-04-18 17:37:30 +00:00
|
|
|
|
2020-09-26 19:34:46 +00:00
|
|
|
this.clientPropertyChangedListener = (event: ClientEvents["notify_properties_updated"]) => {
|
|
|
|
if("client_nickname" in event.updated_properties || "client_talk_power" in event.updated_properties) {
|
2020-04-18 17:37:30 +00:00
|
|
|
this.reorderClientList(true);
|
2020-09-26 19:34:46 +00:00
|
|
|
}
|
2020-04-18 19:31:13 +00:00
|
|
|
};
|
2020-06-15 14:56:05 +00:00
|
|
|
|
|
|
|
this.events.on("notify_properties_updated", event => {
|
|
|
|
this.channelTree?.events.fire("notify_channel_updated", {
|
|
|
|
channel: this,
|
|
|
|
channelProperties: event.channel_properties,
|
|
|
|
updatedProperties: event.updated_properties
|
|
|
|
});
|
|
|
|
});
|
2020-12-04 12:36:34 +00:00
|
|
|
|
2021-01-10 15:13:15 +00:00
|
|
|
this.collapsed = this.channelTree.client.settings.getValue(Settings.FN_SERVER_CHANNEL_COLLAPSED(this.channelId));
|
2021-01-10 16:36:57 +00:00
|
|
|
this.subscriptionMode = this.channelTree.client.settings.getValue(Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE(this.channelId), ChannelSubscribeMode.INHERITED);
|
2020-12-18 16:06:38 +00:00
|
|
|
|
|
|
|
this.channelDescriptionCached = false;
|
|
|
|
this.channelDescriptionCallback = [];
|
2018-02-27 16:20:49 +00:00
|
|
|
}
|
|
|
|
|
2019-08-21 08:00:01 +00:00
|
|
|
destroy() {
|
|
|
|
this._destroyed = true;
|
2020-04-18 17:37:30 +00:00
|
|
|
|
2020-12-18 16:06:38 +00:00
|
|
|
this.channelDescriptionCallback.forEach(callback => callback(false));
|
|
|
|
this.channelDescriptionCallback = [];
|
|
|
|
|
2020-04-18 17:37:30 +00:00
|
|
|
this.client_list.forEach(e => this.unregisterClient(e, true));
|
|
|
|
this.client_list = [];
|
2019-08-21 08:00:01 +00:00
|
|
|
|
|
|
|
this.channel_previous = undefined;
|
|
|
|
this.parent = undefined;
|
|
|
|
this.channel_next = undefined;
|
|
|
|
this.channelTree = undefined;
|
|
|
|
}
|
|
|
|
|
2018-02-27 16:20:49 +00:00
|
|
|
channelName(){
|
2018-04-16 18:38:35 +00:00
|
|
|
return this.properties.channel_name;
|
2018-02-27 16:20:49 +00:00
|
|
|
}
|
|
|
|
|
2020-06-15 14:56:05 +00:00
|
|
|
channelDepth() {
|
|
|
|
let depth = 0;
|
|
|
|
let parent = this.parent;
|
|
|
|
while(parent) {
|
|
|
|
depth++;
|
|
|
|
parent = parent.parent;
|
|
|
|
}
|
|
|
|
return depth;
|
|
|
|
}
|
|
|
|
|
2019-02-17 15:08:10 +00:00
|
|
|
formattedChannelName() {
|
2020-04-18 17:37:30 +00:00
|
|
|
return this.parsed_channel_name.text;
|
2018-02-27 16:20:49 +00:00
|
|
|
}
|
|
|
|
|
2020-12-18 16:06:38 +00:00
|
|
|
async getChannelDescription() : Promise<string> {
|
|
|
|
if(this.channelDescriptionPromise) {
|
|
|
|
return this.channelDescriptionPromise;
|
|
|
|
}
|
2018-08-12 17:46:40 +00:00
|
|
|
|
2020-12-18 16:06:38 +00:00
|
|
|
const promise = this.doGetChannelDescription();
|
|
|
|
this.channelDescriptionPromise = promise;
|
|
|
|
promise
|
|
|
|
.then(() => this.channelDescriptionPromise = undefined)
|
|
|
|
.catch(() => this.channelDescriptionPromise = undefined);
|
|
|
|
return promise;
|
|
|
|
}
|
2018-08-12 17:46:40 +00:00
|
|
|
|
2020-12-22 12:32:56 +00:00
|
|
|
isDescriptionCached() {
|
|
|
|
return this.channelDescriptionCached;
|
|
|
|
}
|
|
|
|
|
2020-12-18 16:06:38 +00:00
|
|
|
private async doGetChannelDescription() {
|
|
|
|
if(!this.channelDescriptionCached) {
|
|
|
|
await this.channelTree.client.serverConnection.send_command("channelgetdescription", {
|
|
|
|
cid: this.channelId
|
|
|
|
});
|
|
|
|
|
|
|
|
if(!this.channelDescriptionCached) {
|
|
|
|
/* since the channel description is a low command it will not be processed in sync */
|
|
|
|
await new Promise((resolve, reject) => {
|
|
|
|
this.channelDescriptionCallback.push(succeeded => {
|
|
|
|
if(succeeded) {
|
|
|
|
resolve();
|
|
|
|
} else {
|
|
|
|
reject(tr("failed to receive description"));
|
|
|
|
}
|
|
|
|
})
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return this.properties.channel_description;
|
2018-08-12 17:46:40 +00:00
|
|
|
}
|
|
|
|
|
2020-04-18 17:37:30 +00:00
|
|
|
registerClient(client: ClientEntry) {
|
2020-09-26 19:34:46 +00:00
|
|
|
client.events.on("notify_properties_updated", this.clientPropertyChangedListener);
|
2020-04-18 17:37:30 +00:00
|
|
|
this.client_list.push(client);
|
|
|
|
this.reorderClientList(false);
|
|
|
|
}
|
|
|
|
|
2020-09-26 19:34:46 +00:00
|
|
|
unregisterClient(client: ClientEntry, noEvent?: boolean) {
|
|
|
|
client.events.off("notify_properties_updated", this.clientPropertyChangedListener);
|
|
|
|
if(!this.client_list.remove(client)) {
|
2021-01-10 16:36:57 +00:00
|
|
|
logWarn(LogCategory.CHANNEL, tr("Unregistered unknown client from channel %s"), this.channelName());
|
2020-09-26 19:34:46 +00:00
|
|
|
}
|
2020-04-18 17:37:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private reorderClientList(fire_event: boolean) {
|
|
|
|
const original_list = this.client_list.slice(0);
|
|
|
|
|
|
|
|
this.client_list.sort((a, b) => {
|
|
|
|
if(a.properties.client_talk_power < b.properties.client_talk_power)
|
|
|
|
return 1;
|
|
|
|
if(a.properties.client_talk_power > b.properties.client_talk_power)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if(a.properties.client_nickname > b.properties.client_nickname)
|
|
|
|
return 1;
|
|
|
|
if(a.properties.client_nickname < b.properties.client_nickname)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
|
|
|
|
if(fire_event) {
|
|
|
|
/* 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]) {
|
2020-09-26 19:34:46 +00:00
|
|
|
this.channelTree.events.fire("notify_channel_client_order_changed", { channel: this });
|
2020-04-18 17:37:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-17 15:08:10 +00:00
|
|
|
parent_channel() { return this.parent; }
|
2018-02-27 16:20:49 +00:00
|
|
|
hasParent(){ return this.parent != null; }
|
|
|
|
getChannelId(){ return this.channelId; }
|
2018-04-16 18:38:35 +00:00
|
|
|
|
2019-02-17 15:08:10 +00:00
|
|
|
children(deep = false) : ChannelEntry[] {
|
2018-04-16 18:38:35 +00:00
|
|
|
const result: ChannelEntry[] = [];
|
2020-04-18 18:03:04 +00:00
|
|
|
let head = this.child_channel_head;
|
|
|
|
while(head) {
|
|
|
|
result.push(head);
|
|
|
|
head = head.channel_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(deep)
|
|
|
|
return result.map(e => e.children(true)).reduce((prv, now) => { prv.push(...now); return prv; }, []);
|
2018-02-27 16:20:49 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-08-12 11:26:56 +00:00
|
|
|
clients(deep = false) : ClientEntry[] {
|
2020-04-18 17:37:30 +00:00
|
|
|
const result: ClientEntry[] = this.client_list.slice(0);
|
|
|
|
if(!deep) return result;
|
2018-02-27 16:20:49 +00:00
|
|
|
|
2020-04-18 17:37:30 +00:00
|
|
|
return this.children(true).map(e => e.clients(false)).reduce((prev, cur) => {
|
|
|
|
prev.push(...cur);
|
|
|
|
return cur;
|
|
|
|
}, result);
|
2018-02-27 16:20:49 +00:00
|
|
|
}
|
|
|
|
|
2020-09-26 19:34:46 +00:00
|
|
|
channelClientsOrdered() : ClientEntry[] {
|
2020-04-18 17:37:30 +00:00
|
|
|
return this.client_list;
|
2019-02-17 15:08:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
calculate_family_index(enforce_recalculate: boolean = false) : number {
|
|
|
|
if(this._family_index !== undefined && !enforce_recalculate)
|
|
|
|
return this._family_index;
|
|
|
|
|
|
|
|
this._family_index = 0;
|
|
|
|
|
|
|
|
let channel = this.parent_channel();
|
|
|
|
while(channel) {
|
|
|
|
this._family_index++;
|
|
|
|
channel = channel.parent_channel();
|
|
|
|
}
|
|
|
|
|
|
|
|
return this._family_index;
|
|
|
|
}
|
|
|
|
|
2018-02-27 16:20:49 +00:00
|
|
|
showContextMenu(x: number, y: number, on_close: () => void = undefined) {
|
2019-09-18 23:25:57 +00:00
|
|
|
let channelCreate = !![
|
|
|
|
PermissionType.B_CHANNEL_CREATE_TEMPORARY,
|
|
|
|
PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT,
|
|
|
|
PermissionType.B_CHANNEL_CREATE_PERMANENT
|
|
|
|
].find(e => this.channelTree.client.permissions.neededPermission(e).granted(1));
|
|
|
|
|
|
|
|
let channelModify = !![
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_MAKE_DEFAULT,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_MAKE_TEMPORARY,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_NAME,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_TOPIC,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_DESCRIPTION,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_PASSWORD,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_CODEC,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_CODEC_QUALITY,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_CODEC_LATENCY_FACTOR,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_MAXCLIENTS,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_MAXFAMILYCLIENTS,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_SORTORDER,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_NEEDED_TALK_POWER,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED,
|
|
|
|
PermissionType.B_CHANNEL_MODIFY_TEMP_DELETE_DELAY,
|
|
|
|
PermissionType.B_ICON_MANAGE
|
|
|
|
].find(e => this.channelTree.client.permissions.neededPermission(e).granted(1));
|
2018-04-16 18:38:35 +00:00
|
|
|
|
|
|
|
let flagDelete = true;
|
|
|
|
if(this.clients(true).length > 0)
|
|
|
|
flagDelete = this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_DELETE_FLAG_FORCE).granted(1);
|
|
|
|
if(flagDelete) {
|
|
|
|
if (this.properties.channel_flag_permanent)
|
|
|
|
flagDelete = this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_DELETE_PERMANENT).granted(1);
|
|
|
|
else if (this.properties.channel_flag_semi_permanent)
|
|
|
|
flagDelete = this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_DELETE_PERMANENT).granted(1);
|
|
|
|
else
|
|
|
|
flagDelete = this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_DELETE_TEMPORARY).granted(1);
|
|
|
|
}
|
|
|
|
|
2019-03-07 14:30:53 +00:00
|
|
|
let trigger_close = true;
|
2019-06-30 14:03:28 +00:00
|
|
|
|
2020-04-18 19:26:44 +00:00
|
|
|
const collapse_expendable = !!this.child_channel_head || this.client_list.length > 0;
|
2019-06-30 14:03:28 +00:00
|
|
|
const bold = text => contextmenu.get_provider().html_format_enabled() ? "<b>" + text + "</b>" : text;
|
|
|
|
contextmenu.spawn_context_menu(x, y, {
|
|
|
|
type: contextmenu.MenuEntryType.ENTRY,
|
2019-08-21 08:00:01 +00:00
|
|
|
icon_class: "client-channel_switch",
|
|
|
|
name: bold(tr("Switch to channel")),
|
2020-07-21 22:55:28 +00:00
|
|
|
callback: () => this.joinChannel(),
|
|
|
|
visible: this !== this.channelTree.client.getClient()?.currentChannel()
|
2021-02-19 22:05:48 +00:00
|
|
|
}, {
|
2020-06-10 16:13:56 +00:00
|
|
|
type: contextmenu.MenuEntryType.ENTRY,
|
|
|
|
icon_class: "client-filetransfer",
|
|
|
|
name: bold(tr("Open channel file browser")),
|
|
|
|
callback: () => spawnFileTransferModal(this.getChannelId()),
|
2019-08-21 08:00:01 +00:00
|
|
|
}, {
|
|
|
|
type: contextmenu.MenuEntryType.ENTRY,
|
|
|
|
icon_class: "client-channel_switch",
|
|
|
|
name: bold(tr("Join text channel")),
|
2019-03-07 14:30:53 +00:00
|
|
|
callback: () => {
|
2020-12-09 12:36:56 +00:00
|
|
|
const conversation = this.channelTree.client.getChannelConversations().findOrCreateConversation(this.getChannelId());
|
|
|
|
this.channelTree.client.getChannelConversations().setSelectedConversation(conversation);
|
2020-12-18 16:06:38 +00:00
|
|
|
this.channelTree.client.getSideBar().showChannel();
|
2019-03-07 14:30:53 +00:00
|
|
|
},
|
2021-01-10 15:13:15 +00:00
|
|
|
visible: !settings.getValue(Settings.KEY_SWITCH_INSTANT_CHAT)
|
2019-03-07 14:30:53 +00:00
|
|
|
}, {
|
2019-06-30 14:03:28 +00:00
|
|
|
type: contextmenu.MenuEntryType.HR,
|
2019-03-07 14:30:53 +00:00
|
|
|
name: ''
|
|
|
|
}, {
|
2019-06-30 14:03:28 +00:00
|
|
|
type: contextmenu.MenuEntryType.ENTRY,
|
2019-08-21 08:00:01 +00:00
|
|
|
name: tr("Show channel info"),
|
|
|
|
callback: () => {
|
|
|
|
trigger_close = false;
|
2020-03-30 11:44:18 +00:00
|
|
|
openChannelInfo(this);
|
2019-08-21 08:00:01 +00:00
|
|
|
},
|
|
|
|
icon_class: "client-about"
|
2021-02-19 22:05:48 +00:00
|
|
|
}, {
|
|
|
|
type: contextmenu.MenuEntryType.ENTRY,
|
|
|
|
name: tr("Invite People"),
|
|
|
|
callback: () => spawnInviteGenerator(this),
|
|
|
|
icon_class: ClientIcon.InviteBuddy
|
2018-02-27 16:20:49 +00:00
|
|
|
},
|
2019-06-09 08:40:44 +00:00
|
|
|
...(() => {
|
|
|
|
const local_client = this.channelTree.client.getClient();
|
|
|
|
if (!local_client || local_client.currentChannel() !== this)
|
|
|
|
return [
|
2019-06-30 14:03:28 +00:00
|
|
|
contextmenu.Entry.HR(),
|
2019-06-09 08:40:44 +00:00
|
|
|
{
|
2019-06-30 14:03:28 +00:00
|
|
|
type: contextmenu.MenuEntryType.ENTRY,
|
2019-06-09 08:40:44 +00:00
|
|
|
icon: "client-subscribe_to_channel",
|
2019-06-30 14:03:28 +00:00
|
|
|
name: bold(tr("Subscribe to channel")),
|
2019-06-09 08:40:44 +00:00
|
|
|
callback: () => this.subscribe(),
|
2020-12-04 12:36:34 +00:00
|
|
|
visible: !this.isSubscribed()
|
2019-06-09 08:40:44 +00:00
|
|
|
},
|
|
|
|
{
|
2019-06-30 14:03:28 +00:00
|
|
|
type: contextmenu.MenuEntryType.ENTRY,
|
2019-06-09 08:40:44 +00:00
|
|
|
icon: "client-channel_unsubscribed",
|
2019-06-30 14:03:28 +00:00
|
|
|
name: bold(tr("Unsubscribe from channel")),
|
2019-06-09 08:40:44 +00:00
|
|
|
callback: () => this.unsubscribe(),
|
2020-12-04 12:36:34 +00:00
|
|
|
visible: this.isSubscribed()
|
2019-06-09 08:40:44 +00:00
|
|
|
},
|
|
|
|
{
|
2019-06-30 14:03:28 +00:00
|
|
|
type: contextmenu.MenuEntryType.ENTRY,
|
2019-06-09 08:40:44 +00:00
|
|
|
icon: "client-subscribe_mode",
|
2019-06-30 14:03:28 +00:00
|
|
|
name: bold(tr("Use inherited subscribe mode")),
|
2019-06-09 08:40:44 +00:00
|
|
|
callback: () => this.unsubscribe(true),
|
2020-12-04 12:36:34 +00:00
|
|
|
visible: this.subscriptionMode != ChannelSubscribeMode.INHERITED
|
2019-06-09 08:40:44 +00:00
|
|
|
}
|
|
|
|
];
|
|
|
|
return [];
|
|
|
|
})(),
|
2019-06-30 14:03:28 +00:00
|
|
|
contextmenu.Entry.HR(),
|
2018-02-27 16:20:49 +00:00
|
|
|
{
|
2019-06-30 14:03:28 +00:00
|
|
|
type: contextmenu.MenuEntryType.ENTRY,
|
|
|
|
icon_class: "client-channel_edit",
|
2018-12-05 19:46:33 +00:00
|
|
|
name: tr("Edit channel"),
|
2018-04-16 18:38:35 +00:00
|
|
|
invalidPermission: !channelModify,
|
2018-02-27 16:20:49 +00:00
|
|
|
callback: () => {
|
2020-12-22 12:32:56 +00:00
|
|
|
spawnChannelEditNew(this.channelTree.client, this, this.parent, (properties, permissions) => {
|
|
|
|
const changedProperties = Object.keys(properties);
|
|
|
|
if(changedProperties.length > 0) {
|
|
|
|
properties["cid"] = this.channelId;
|
|
|
|
this.channelTree.client.serverConnection.send_command("channeledit", properties).then(() => {
|
|
|
|
this.channelTree.client.sound.play(Sound.CHANNEL_EDITED_SELF);
|
|
|
|
});
|
2021-01-10 16:36:57 +00:00
|
|
|
logInfo(LogCategory.CHANNEL, tr("Changed channel properties of channel %s: %o"), this.channelName(), properties);
|
2018-08-10 19:30:58 +00:00
|
|
|
}
|
|
|
|
|
2020-12-22 12:32:56 +00:00
|
|
|
if(permissions.length > 0) {
|
2018-08-10 19:30:58 +00:00
|
|
|
let perms = [];
|
|
|
|
for(let perm of permissions) {
|
|
|
|
perms.push({
|
|
|
|
permvalue: perm.value,
|
|
|
|
permnegated: false,
|
|
|
|
permskip: false,
|
2020-12-22 12:32:56 +00:00
|
|
|
permsid: perm.permission
|
2018-08-10 19:30:58 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
perms[0]["cid"] = this.channelId;
|
2019-02-23 13:15:22 +00:00
|
|
|
this.channelTree.client.serverConnection.send_command("channeladdperm", perms, {
|
|
|
|
flagset: ["continueonerror"]
|
|
|
|
}).then(() => {
|
2019-04-04 19:47:52 +00:00
|
|
|
this.channelTree.client.sound.play(Sound.CHANNEL_EDITED_SELF);
|
2018-11-03 23:39:29 +00:00
|
|
|
});
|
2018-08-10 19:30:58 +00:00
|
|
|
}
|
2018-04-16 18:38:35 +00:00
|
|
|
});
|
2018-02-27 16:20:49 +00:00
|
|
|
}
|
2018-04-16 18:38:35 +00:00
|
|
|
},
|
|
|
|
{
|
2019-06-30 14:03:28 +00:00
|
|
|
type: contextmenu.MenuEntryType.ENTRY,
|
|
|
|
icon_class: "client-channel_delete",
|
2018-12-05 19:46:33 +00:00
|
|
|
name: tr("Delete channel"),
|
2018-04-16 18:38:35 +00:00
|
|
|
invalidPermission: !flagDelete,
|
2018-11-03 23:39:29 +00:00
|
|
|
callback: () => {
|
2020-12-04 12:36:34 +00:00
|
|
|
this.channelTree.client.serverConnection.send_command("channeldelete", {cid: this.channelId});
|
2018-11-03 23:39:29 +00:00
|
|
|
}
|
2018-04-16 18:38:35 +00:00
|
|
|
},
|
2019-06-30 14:03:28 +00:00
|
|
|
contextmenu.Entry.HR(),
|
2018-11-04 12:54:18 +00:00
|
|
|
{
|
2019-06-30 14:03:28 +00:00
|
|
|
type: contextmenu.MenuEntryType.ENTRY,
|
|
|
|
icon_class: "client-addon-collection",
|
2018-12-05 19:46:33 +00:00
|
|
|
name: tr("Create music bot"),
|
2018-11-04 12:54:18 +00:00
|
|
|
callback: () => {
|
2019-02-23 13:15:22 +00:00
|
|
|
this.channelTree.client.serverConnection.send_command("musicbotcreate", {cid: this.channelId}).then(() => {
|
2019-01-20 17:43:14 +00:00
|
|
|
createInfoModal(tr("Bot successfully created"), tr("Bot has been successfully created.")).open();
|
2018-11-04 12:54:18 +00:00
|
|
|
}).catch(error => {
|
|
|
|
if(error instanceof CommandResult) {
|
|
|
|
error = error.extra_message || error.message;
|
|
|
|
}
|
2018-12-05 19:46:33 +00:00
|
|
|
|
2020-03-30 11:44:18 +00:00
|
|
|
createErrorModal(tr("Failed to create bot"), formatMessage(tr("Failed to create the music bot:<br>{0}"), error)).open();
|
2018-11-04 12:54:18 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
2020-04-18 19:26:44 +00:00
|
|
|
{
|
|
|
|
type: MenuEntryType.HR,
|
|
|
|
name: "",
|
|
|
|
visible: collapse_expendable
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: contextmenu.MenuEntryType.ENTRY,
|
|
|
|
icon_class: "client-channel_collapse_all",
|
|
|
|
name: tr("Collapse sub channels"),
|
|
|
|
visible: collapse_expendable,
|
|
|
|
callback: () => this.channelTree.collapse_channels(this)
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: contextmenu.MenuEntryType.ENTRY,
|
|
|
|
icon_class: "client-channel_expand_all",
|
|
|
|
name: tr("Expend sub channels"),
|
|
|
|
visible: collapse_expendable,
|
|
|
|
callback: () => this.channelTree.expand_channels(this)
|
|
|
|
},
|
2019-06-30 14:03:28 +00:00
|
|
|
contextmenu.Entry.HR(),
|
2018-04-16 18:38:35 +00:00
|
|
|
{
|
2019-06-30 14:03:28 +00:00
|
|
|
type: contextmenu.MenuEntryType.ENTRY,
|
|
|
|
icon_class: "client-channel_create_sub",
|
2018-12-05 19:46:33 +00:00
|
|
|
name: tr("Create sub channel"),
|
2018-04-16 18:38:35 +00:00
|
|
|
invalidPermission: !(channelCreate && this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_CHILD).granted(1)),
|
|
|
|
callback: () => this.channelTree.spawnCreateChannel(this)
|
2018-02-27 16:20:49 +00:00
|
|
|
}, {
|
2019-06-30 14:03:28 +00:00
|
|
|
type: contextmenu.MenuEntryType.ENTRY,
|
|
|
|
icon_class: "client-channel_create",
|
2018-12-05 19:46:33 +00:00
|
|
|
name: tr("Create channel"),
|
2018-04-16 18:38:35 +00:00
|
|
|
invalidPermission: !channelCreate,
|
|
|
|
callback: () => this.channelTree.spawnCreateChannel()
|
2018-02-27 16:20:49 +00:00
|
|
|
},
|
2020-04-21 15:11:06 +00:00
|
|
|
contextmenu.Entry.CLOSE(() => trigger_close && on_close ? on_close() : {})
|
2018-02-27 16:20:49 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-04-16 18:38:35 +00:00
|
|
|
updateVariables(...variables: {key: string, value: string}[]) {
|
2020-04-18 17:37:30 +00:00
|
|
|
/* devel-block(log-channel-property-updates) */
|
2019-02-17 15:08:10 +00:00
|
|
|
let group = log.group(log.LogType.DEBUG, LogCategory.CHANNEL_PROPERTIES, tr("Update properties (%i) of %s (%i)"), variables.length, this.channelName(), this.getChannelId());
|
2018-04-16 18:38:35 +00:00
|
|
|
|
2019-03-25 19:04:04 +00:00
|
|
|
{
|
|
|
|
const entries = [];
|
|
|
|
for(const variable of variables)
|
|
|
|
entries.push({
|
|
|
|
key: variable.key,
|
|
|
|
value: variable.value,
|
|
|
|
type: typeof (this.properties[variable.key])
|
|
|
|
});
|
2019-08-30 21:06:39 +00:00
|
|
|
log.table(LogType.DEBUG, LogCategory.PERMISSIONS, "Clannel update properties", entries);
|
2019-03-25 19:04:04 +00:00
|
|
|
}
|
2020-04-18 17:37:30 +00:00
|
|
|
/* devel-block-end */
|
2019-03-25 19:04:04 +00:00
|
|
|
|
2020-12-09 13:22:22 +00:00
|
|
|
/* TODO: Validate values. Example: channel_conversation_mode */
|
|
|
|
|
2020-12-12 13:07:51 +00:00
|
|
|
for(const variable of variables) {
|
2018-04-16 18:38:35 +00:00
|
|
|
let key = variable.key;
|
|
|
|
let value = variable.value;
|
2020-12-12 13:18:50 +00:00
|
|
|
|
2020-12-18 16:06:38 +00:00
|
|
|
const hasUpdate = JSON.map_field_to(this.properties, value, variable.key);
|
|
|
|
|
|
|
|
if(key == "channel_description") {
|
|
|
|
this.channelDescriptionCached = true;
|
|
|
|
this.channelDescriptionCallback.forEach(callback => callback(true));
|
|
|
|
this.channelDescriptionCallback = [];
|
2020-12-12 13:07:51 +00:00
|
|
|
}
|
2018-04-16 18:38:35 +00:00
|
|
|
|
2020-12-18 16:06:38 +00:00
|
|
|
if(hasUpdate) {
|
|
|
|
if(key == "channel_name") {
|
2021-01-22 16:38:23 +00:00
|
|
|
this.parsed_channel_name = new ChannelNameParser(value, this.hasParent());
|
2020-12-18 16:06:38 +00:00
|
|
|
} else if(key == "channel_order") {
|
|
|
|
let order = this.channelTree.findChannel(this.properties.channel_order);
|
|
|
|
this.channelTree.moveChannel(this, order, this.parent, false);
|
|
|
|
} else if(key === "channel_icon_id") {
|
|
|
|
this.properties.channel_icon_id = variable.value as any >>> 0; /* unsigned 32 bit number! */
|
|
|
|
} else if(key === "channel_flag_conversation_private") {
|
|
|
|
/* "fix" for older TeaSpeak server versions (pre. 1.4.22) */
|
|
|
|
this.properties.channel_conversation_mode = value === "1" ? 0 : 1;
|
|
|
|
variables.push({ key: "channel_conversation_mode", value: this.properties.channel_conversation_mode + "" });
|
|
|
|
}
|
2018-08-12 17:46:40 +00:00
|
|
|
}
|
2018-02-27 16:20:49 +00:00
|
|
|
}
|
2020-04-18 17:37:30 +00:00
|
|
|
/* devel-block(log-channel-property-updates) */
|
2018-04-16 18:38:35 +00:00
|
|
|
group.end();
|
2020-04-18 17:37:30 +00:00
|
|
|
/* devel-block-end */
|
|
|
|
{
|
|
|
|
let properties = {};
|
|
|
|
for(const property of variables)
|
|
|
|
properties[property.key] = this.properties[property.key];
|
|
|
|
this.events.fire("notify_properties_updated", { updated_properties: properties as any, channel_properties: this.properties });
|
|
|
|
}
|
2018-02-27 16:20:49 +00:00
|
|
|
}
|
|
|
|
|
2019-01-20 17:43:14 +00:00
|
|
|
generate_bbcode() {
|
2019-02-17 15:08:10 +00:00
|
|
|
return "[url=channel://" + this.channelId + "/" + encodeURIComponent(this.properties.channel_name) + "]" + this.formattedChannelName() + "[/url]";
|
2019-01-20 17:43:14 +00:00
|
|
|
}
|
2018-04-30 21:57:21 +00:00
|
|
|
|
2019-01-20 17:43:14 +00:00
|
|
|
generate_tag(braces: boolean = false) : JQuery {
|
|
|
|
return $(htmltags.generate_channel({
|
|
|
|
channel_name: this.properties.channel_name,
|
|
|
|
channel_id: this.channelId,
|
|
|
|
add_braces: braces
|
|
|
|
}));
|
2018-02-27 16:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
channelType() : ChannelType {
|
2018-04-16 18:38:35 +00:00
|
|
|
if(this.properties.channel_flag_permanent == true) return ChannelType.PERMANENT;
|
|
|
|
if(this.properties.channel_flag_semi_permanent == true) return ChannelType.SEMI_PERMANENT;
|
2018-02-27 16:20:49 +00:00
|
|
|
return ChannelType.TEMPORARY;
|
|
|
|
}
|
2018-04-16 18:38:35 +00:00
|
|
|
|
2020-06-13 16:47:05 +00:00
|
|
|
joinChannel(ignorePasswordFlag?: boolean) {
|
2020-07-17 21:56:20 +00:00
|
|
|
if(this.channelTree.client.getClient().currentChannel() === this)
|
|
|
|
return;
|
|
|
|
|
2020-06-13 16:47:05 +00:00
|
|
|
if(this.properties.channel_flag_password === true && !this.cachedPasswordHash && !ignorePasswordFlag) {
|
|
|
|
this.requestChannelPassword(PermissionType.B_CHANNEL_JOIN_IGNORE_PASSWORD).then(() => {
|
|
|
|
this.joinChannel(true);
|
2018-04-16 18:38:35 +00:00
|
|
|
});
|
2020-06-10 16:13:56 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-09-07 10:42:00 +00:00
|
|
|
this.channelTree.client.serverConnection.send_command("clientmove", {
|
|
|
|
"clid": this.channelTree.client.getClientId(),
|
|
|
|
"cid": this.getChannelId(),
|
|
|
|
"cpw": this.cachedPasswordHash || ""
|
|
|
|
}).then(() => {
|
2020-06-10 16:13:56 +00:00
|
|
|
this.channelTree.client.sound.play(Sound.CHANNEL_JOINED);
|
|
|
|
}).catch(error => {
|
|
|
|
if(error instanceof CommandResult) {
|
2020-09-07 10:42:00 +00:00
|
|
|
if(error.id == ErrorCode.CHANNEL_INVALID_PASSWORD) { //Invalid password
|
2020-06-10 16:13:56 +00:00
|
|
|
this.invalidateCachedPassword();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async requestChannelPassword(ignorePermission: PermissionType) : Promise<{ hash: string } | undefined> {
|
|
|
|
if(this.cachedPasswordHash)
|
|
|
|
return { hash: this.cachedPasswordHash };
|
|
|
|
|
|
|
|
if(this.channelTree.client.permissions.neededPermission(ignorePermission).granted(1))
|
|
|
|
return { hash: "having ignore permission" };
|
|
|
|
|
|
|
|
const password = await new Promise(resolve => createInputModal(tr("Channel password"), tr("Channel password:"), () => true, resolve).open())
|
|
|
|
if(typeof(password) !== "string" || !password)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const hash = await hashPassword(password);
|
|
|
|
this.cachedPasswordHash = hash;
|
|
|
|
this.events.fire("notify_cached_password_updated", { reason: "password-entered", new_hash: hash });
|
|
|
|
return { hash: this.cachedPasswordHash };
|
|
|
|
}
|
|
|
|
|
|
|
|
invalidateCachedPassword() {
|
|
|
|
this.cachedPasswordHash = undefined;
|
|
|
|
this.events.fire("notify_cached_password_updated", { reason: "password-miss-match" });
|
2018-04-16 18:38:35 +00:00
|
|
|
}
|
2019-03-17 11:15:39 +00:00
|
|
|
|
2020-06-10 16:13:56 +00:00
|
|
|
cached_password() { return this.cachedPasswordHash; }
|
2019-06-19 13:56:51 +00:00
|
|
|
|
2020-12-04 11:49:08 +00:00
|
|
|
async updateSubscribeMode() {
|
|
|
|
let shouldBeSubscribed = false;
|
2020-12-04 12:36:34 +00:00
|
|
|
switch (this.subscriptionMode) {
|
2020-12-04 11:49:08 +00:00
|
|
|
case ChannelSubscribeMode.INHERITED:
|
|
|
|
shouldBeSubscribed = this.channelTree.client.isSubscribeToAllChannels();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ChannelSubscribeMode.SUBSCRIBED:
|
|
|
|
shouldBeSubscribed = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ChannelSubscribeMode.UNSUBSCRIBED:
|
|
|
|
shouldBeSubscribed = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-12-04 12:36:34 +00:00
|
|
|
if(this.subscribed === shouldBeSubscribed) {
|
2020-12-04 11:49:08 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const connection = this.channelTree.client.getServerConnection();
|
|
|
|
if(!connection.connected()) {
|
2020-12-04 12:36:34 +00:00
|
|
|
this.setSubscribed(false);
|
2020-12-04 11:49:08 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(shouldBeSubscribed) {
|
|
|
|
await connection.send_command('channelsubscribe', {
|
|
|
|
'cid': this.getChannelId()
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
await connection.send_command('channelunsubscribe', {
|
|
|
|
'cid': this.getChannelId()
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-17 11:15:39 +00:00
|
|
|
async subscribe() : Promise<void> {
|
2020-12-04 12:36:34 +00:00
|
|
|
this.setSubscriptionMode(ChannelSubscribeMode.SUBSCRIBED);
|
2019-03-17 11:15:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async unsubscribe(inherited_subscription_mode?: boolean) : Promise<void> {
|
2020-12-04 12:36:34 +00:00
|
|
|
this.setSubscriptionMode(inherited_subscription_mode ? ChannelSubscribeMode.INHERITED : ChannelSubscribeMode.UNSUBSCRIBED);
|
2019-03-17 11:15:39 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 12:36:34 +00:00
|
|
|
isCollapsed() : boolean {
|
|
|
|
return this.collapsed;
|
2020-04-18 19:26:44 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 12:36:34 +00:00
|
|
|
setCollapsed(flag: boolean) {
|
|
|
|
if(this.collapsed === flag) {
|
2020-04-18 19:26:44 +00:00
|
|
|
return;
|
2020-09-25 23:22:21 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 12:36:34 +00:00
|
|
|
this.collapsed = flag;
|
2020-04-18 19:26:44 +00:00
|
|
|
this.events.fire("notify_collapsed_state_changed", { collapsed: flag });
|
2021-01-10 15:13:15 +00:00
|
|
|
this.channelTree.client.settings.setValue(Settings.FN_SERVER_CHANNEL_COLLAPSED(this.channelId), flag);
|
2020-04-18 19:26:44 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 12:36:34 +00:00
|
|
|
isSubscribed() : boolean {
|
|
|
|
return this.subscribed;
|
2019-03-17 11:15:39 +00:00
|
|
|
}
|
2019-08-21 08:00:01 +00:00
|
|
|
|
2020-12-04 12:36:34 +00:00
|
|
|
/* Attention: This method is not to subscribe to a channel! It's used to update the current subscription state.*/
|
|
|
|
setSubscribed(flag: boolean) {
|
|
|
|
if(this.subscribed === flag) {
|
2019-03-17 11:15:39 +00:00
|
|
|
return;
|
2020-12-04 12:36:34 +00:00
|
|
|
}
|
2019-03-17 11:15:39 +00:00
|
|
|
|
2020-12-04 12:36:34 +00:00
|
|
|
this.subscribed = flag;
|
2020-04-18 17:37:30 +00:00
|
|
|
this.events.fire("notify_subscribe_state_changed", { channel_subscribed: flag });
|
2019-03-17 11:15:39 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 12:36:34 +00:00
|
|
|
getSubscriptionMode() : ChannelSubscribeMode {
|
|
|
|
return this.subscriptionMode;
|
2019-03-17 11:15:39 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 12:58:05 +00:00
|
|
|
setSubscriptionMode(mode: ChannelSubscribeMode, dontSyncSubscribeMode?: boolean) {
|
2020-12-04 12:36:34 +00:00
|
|
|
if(this.subscriptionMode === mode) {
|
2019-03-17 11:15:39 +00:00
|
|
|
return;
|
2020-12-04 12:36:34 +00:00
|
|
|
}
|
2019-03-17 11:15:39 +00:00
|
|
|
|
2020-12-04 12:36:34 +00:00
|
|
|
this.subscriptionMode = mode;
|
2021-01-10 15:13:15 +00:00
|
|
|
this.channelTree.client.settings.setValue(Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE(this.channelId), mode);
|
2020-12-04 12:58:05 +00:00
|
|
|
if(!dontSyncSubscribeMode) {
|
|
|
|
this.updateSubscribeMode().then(undefined);
|
|
|
|
}
|
2019-03-17 11:15:39 +00:00
|
|
|
}
|
2019-07-09 22:52:08 +00:00
|
|
|
|
2020-07-21 22:55:28 +00:00
|
|
|
log_data() : EventChannelData {
|
2019-07-09 22:52:08 +00:00
|
|
|
return {
|
|
|
|
channel_name: this.channelName(),
|
|
|
|
channel_id: this.channelId
|
|
|
|
}
|
|
|
|
}
|
2020-09-26 19:34:46 +00:00
|
|
|
|
|
|
|
getStatusIcon() : ClientIcon | undefined {
|
|
|
|
if(this.parsed_channel_name.alignment !== "normal") {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
2020-12-04 12:36:34 +00:00
|
|
|
const subscribed = this.isSubscribed();
|
2020-09-26 19:34:46 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2020-12-18 16:06:38 +00:00
|
|
|
|
|
|
|
handleDescriptionChanged() {
|
|
|
|
if(!this.channelDescriptionCached) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.channelDescriptionCached = false;
|
|
|
|
this.properties.channel_description = undefined;
|
|
|
|
this.events.fire("notify_description_changed");
|
|
|
|
}
|
2018-02-27 16:20:49 +00:00
|
|
|
}
|