From d70667e3093a7e43968d7514fccc3d10e6dd1011 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sun, 17 Feb 2019 12:17:17 +0100 Subject: [PATCH] Improved and fixed channel tree issues --- shared/css/static/channel-tree.scss | 231 ++++++++++++++++++++++ shared/css/static/general.scss | 2 +- shared/css/static/ts/client.scss | 210 -------------------- shared/html/templates.html | 4 +- shared/js/FileManager.ts | 8 +- shared/js/connection.ts | 7 + shared/js/load.ts | 2 +- shared/js/log.ts | 45 +++-- shared/js/ui/channel.ts | 296 +++++++++++++++++----------- shared/js/ui/client.ts | 96 ++++++--- shared/js/ui/client_move.ts | 2 +- shared/js/ui/server.ts | 24 ++- shared/js/ui/view.ts | 63 +++--- 13 files changed, 567 insertions(+), 423 deletions(-) create mode 100644 shared/css/static/channel-tree.scss delete mode 100644 shared/css/static/ts/client.scss diff --git a/shared/css/static/channel-tree.scss b/shared/css/static/channel-tree.scss new file mode 100644 index 00000000..630f400e --- /dev/null +++ b/shared/css/static/channel-tree.scss @@ -0,0 +1,231 @@ +/* the channel tree */ +.channel-tree { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + width: 100%; + + display: -ms-flex; + display: flex; + + flex-direction: column; + + * { + font-family: sans-serif; + font-size: 12px; + white-space: pre; + line-height: 1; + } + + .tree-entry { + display: flex; + flex-direction: row; + justify-content: stretch; + + margin-left: 16px; + min-height: 16px; + + flex-grow: 0; + flex-shrink: 0; + + &.server { + display: flex; + flex-direction: row; + justify-content: stretch; + + margin-left: 0; + + .server_type { + flex-grow: 0; + flex-shrink: 0; + + margin-right: 2px; + } + + .name { + flex-grow: 1; + flex-shrink: 1; + + align-self: center; + } + + .icon_property { + flex-grow: 0; + flex-shrink: 0; + } + + &.selected { + background-color: blue; + } + } + + &.channel { + display: flex; + flex-direction: column; + + .container-channel { + display: flex; + flex-direction: row; + justify-content: stretch; + + width: 100%; + min-height: 16px; + + align-items: center; + cursor: pointer; + + &.selected { + background-color: blue; + } + + .channel-type { + flex-grow: 0; + flex-shrink: 0; + + margin-right: 2px; + } + + .container-channel-name { + display: flex; + flex-direction: row; + + flex-grow: 1; + flex-shrink: 1; + + justify-content: left; + + &.align-right { + justify-content: right; + } + + &.align-center, &.align-repetitive { + justify-content: center; + } + } + + .icons { + display: flex; + flex-direction: row; + + flex-grow: 0; + flex-shrink: 0; + } + + &.move-selected { + border-bottom: 1px solid black; + } + + .show-channel-normal-only { + display: none; + + &.channel-normal { + display: block; + } + } + } + + .container-clients { + display: flex; + flex-direction: column; + } + } + + &.client { + cursor: pointer; + + position: relative; + + display: flex; + flex-direction: row; + + align-items: center; + + > div { + margin-right: 2px; + } + + .client-name { + &.client-name-own { + font-weight: bold; + } + } + + .container-icons { + margin-right: 0; /* override from previous thing */ + + position: absolute; + right: 0; + + display: flex; + flex-direction: row; + + align-items: center; + + .container-icons-group { + display: flex; + flex-direction: row; + + .container-group-icon { + display: flex; + flex-direction: column; + justify-content: center; + } + } + } + + &.selected { + background-color: blue; + } + } + } +} + +/* all icons related to basic_icons */ +.clicon {width:16px;height:16px;background:url('../../img/ts/basic_icons.png') no-repeat;background-size: 16px 608px;} + +.host {background-position: 0 -448px} + +.server_open {background-position: 0 -352px} +.server_full {background-position: 0 -128px} +.server_pass {background-position: 0 -432px} + +/* Server group icon */ +.group_0 {background-position: 0 -464px} +.group_100 {background-position: 0 -16px} +.group_200 {background-position: 0 -304px} +.group_300 {background-position: 0 -80px} +.group_400 {background-position: 0 -528px} +.group_500 {background-position: 0 -416px} +.group_600 {background-position: 0 -272px} + +.group_server{background-position: 0 -496px} +.group_channel {background-position: 0 -400px} + +/* Channel icons */ +.channel_open {background-position: 0 -64px} +.channel_pass {background-position: 0 -112px} +.channel_full {background-position: 0 -256px} +.channel_flag_music {background-position: 0 -32px} +.channel_flag_default {background-position: 0 -48px} +.channel_flag_moderated {background-position: 0 -192px} +.channel_flag_password {background-position: 0 -480px} + +/* Client icons */ +.client_mic_muted {background-position: 0 -96px} +.client_talker {background-position: 0 -144px} +.client_idle {background-position: 0 -160px} +.client_talk {background-position: 0 -208px} +.client_snd_muted {background-position: 0 -176px} +.client_query {background-position: 0 -224px} +.client_talker_request {background-position: 0 -240px} +.client_snd_disabled {background-position: 0 -320px} +.client_priority {background-position: 0 -336px} +.client_away {background-position: 0 -368px} +.client_cc {background-position: 0 -384px} +.client_cc_talk {background-position: 0 -544px} +.client_cc_idle {background-position: 0 -288px} +.client_mic_disabled {background-position: 0 -512px} \ No newline at end of file diff --git a/shared/css/static/general.scss b/shared/css/static/general.scss index 68d5ca8d..37571ce3 100644 --- a/shared/css/static/general.scss +++ b/shared/css/static/general.scss @@ -262,7 +262,7 @@ $separator_thickness: 4px; flex-direction: column; justify-content: stretch; - .container-channel { + .container-channel-tree { background: white; display: flex; diff --git a/shared/css/static/ts/client.scss b/shared/css/static/ts/client.scss deleted file mode 100644 index eb5305a7..00000000 --- a/shared/css/static/ts/client.scss +++ /dev/null @@ -1,210 +0,0 @@ -.channelTree { - width: 100%; max-width: 100%; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - vertical-align: center; - outline: none; -} -.channelTree * { - font-family: Arial; - font-size: 12px; - white-space: pre; - line-height: 1; -} -.channelTree div { - max-width: 100%; - height: 16px; - position: relative; -} - -.channelTree .channel_type { - margin-right: 4px; -} -.channelTree .icon_client_state { - margin-right: 4px; -} -.channelTree .server_type { - margin-right: 4px; -} - -.channelTree .icons .icon_entry { - margin-left: 2px; -} - -.channelTree img.loading {width: 50px;height: 50px;-webkit-animation:spin 2s linear infinite;-moz-animation:spin 2s linear infinite;animation:spin 2s linear infinite;} -@-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } } -@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } } -@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } } - -.channelTree div.l {justify-content: flex-start; } -.channelTree div.c {justify-content: center;} -.channelTree div.r {justify-content: flex-end;} - -.channelTree div * {vertical-align: middle;display:inline-block;height: 16px;padding: 0px;} -.channelTree div img {border: 0;} -.channelTree div > span {position: absolute; right: 0;} -.channelTree { - .name, .group_prefix, .group_suffix, .away { - vertical-align: middle; - margin-top: 1px; - height: 14px; - display: inline; - } - - .group_prefix { - margin-right: 3px; - } - - .group_suffix { - margin-left: 3px; - } -} -.channelTree .own_name { - font-weight: bold; - display: inline; -} -.channelTree .channel_name { - display: flex; - align-items: center; -} - -.channelTree .channel_name_container { - display: inline-flex; - width: 100%; - overflow-x: hidden; -} - -.channelTree .country {width:16px;height:11px;} - -.channelTree .siblings { - position: absolute; - margin-top: 16px; - margin-bottom: -2px; - display: grid; - width: 100%; - left: 16px; -} - -.channelTree .channel { - cursor: pointer; - margin-top: 1px; -} - -.channelTree .clients { - position: absolute; - margin-top: 16px; - margin-bottom: -2px; - display: grid; - width: 100%; - left: 16px; -} - -.channelTree .client { - cursor: pointer; - margin-top: 1px; - width: calc(100%); -} - -.channelTree .client span > div { - margin-left: 3px; -} - -.channelTree .server { - cursor: pointer; - margin-top: 0px; - width: calc(100%); -} - -.channelTree .channelLine { - position: absolute; - width: calc(100% - 16px); - display: flex; - flex-direction: row; - top: 0px; - left: 16px; - - &.move-selected { - border: 1px black solid; - } -} - - -.channelTree .selected { - background: #005cbf; -} - -.clicon {width:16px;height:16px;background:url('../../../img/ts/basic_icons.png') no-repeat;background-size: 16px 608px;} -.host {background-position: 0 -448px} - -.server_open {background-position: 0 -352px} -.server_full {background-position: 0 -128px} -.server_pass {background-position: 0 -432px} - -/* Server group icon -.group_0 {background-position: 0 -464px} -.group_100 {background-position: 0 -16px} -.group_200 {background-position: 0 -304px} -.group_300 {background-position: 0 -80px} -.group_400 {background-position: 0 -528px} -.group_500 {background-position: 0 -416px} -.group_600 {background-position: 0 -272px} - -.group_server{background-position: 0 -496px} -.group_channel {background-position: 0 -400px} - -/* Channel icons -.channel_open {background-position: 0 -64px} -.channel_pass {background-position: 0 -112px} -.channel_full {background-position: 0 -256px} -.channel_flag_music {background-position: 0 -32px} -.channel_flag_default {background-position: 0 -48px} -.channel_flag_moderated {background-position: 0 -192px} -.channel_flag_password {background-position: 0 -480px} - -/* Client icons */ -.client_mic_muted {background-position: 0 -96px} -.client_talker {background-position: 0 -144px} -.client_idle {background-position: 0 -160px} -.client_talk {background-position: 0 -208px} -.client_snd_muted {background-position: 0 -176px} -.client_query {background-position: 0 -224px} -.client_talker_request {background-position: 0 -240px} -.client_snd_disabled {background-position: 0 -320px} -.client_priority {background-position: 0 -336px} -.client_away {background-position: 0 -368px} -.client_cc {background-position: 0 -384px} -.client_cc_talk {background-position: 0 -544px} -.client_cc_idle {background-position: 0 -288px} -.client_mic_disabled {background-position: 0 -512px} - -/* Channel tree icons -.tree_line {background-position: 0 -560px} -.tree_mid {background-position: 0 -576px} -.tree_end {background-position: 0 -592px} - -.badges {width:16px;height:16px;background-size: 16px !important;} -.badges.overwolf {background:url('../images/viewer/client_overwolf.png') no-repeat;} -*/ - -/* menu header via data attribute */ -.data-title:before { - content: attr(data-menutitle); - display: block; - position: absolute; - top: 0; - right: 0; - left: 0; - background: #DDD; - padding: 2px; - - font-family: Verdana, Arial, Helvetica, sans-serif; - font-size: 11px; - font-weight: bold; -} -.data-title :first-child { - margin-top: 50px; -} \ No newline at end of file diff --git a/shared/html/templates.html b/shared/html/templates.html index 97a0be12..4bb391d9 100644 --- a/shared/html/templates.html +++ b/shared/html/templates.html @@ -106,8 +106,8 @@
-
-
+
+
diff --git a/shared/js/FileManager.ts b/shared/js/FileManager.ts index d1f83878..c4844f63 100644 --- a/shared/js/FileManager.ts +++ b/shared/js/FileManager.ts @@ -368,9 +368,9 @@ class IconManager { //$("\"tick\"") generateTag(id: number) : JQuery { if(id == 0) - return $("
"); + return $.spawn("div").addClass("icon_empty"); else if(id < 1000) - return $("
"); + return $.spawn("div").addClass("icon client-group_" + id); let tag = $.spawn("div"); tag.addClass("icon_empty"); @@ -384,7 +384,7 @@ class IconManager { const media = media_image_type(type); console.debug(tr("Icon has an image type of %o (media: %o)"), type, media); img.attr("src", "data:image/" + media + ";base64," + icon.base64); - tag.append(img); + tag.append(img).removeClass("icon_empty"); } else { img.attr("src", "file://null"); @@ -400,7 +400,7 @@ class IconManager { console.debug(tr("Icon %o loaded :)"), id); img.css("opacity", 0); - tag.append(img); + tag.append(img).removeClass("icon_empty"); loader.animate({opacity: 0}, 50, function () { $(this).detach(); img.animate({opacity: 1}, 150); diff --git a/shared/js/connection.ts b/shared/js/connection.ts index a825d25a..1bee2068 100644 --- a/shared/js/connection.ts +++ b/shared/js/connection.ts @@ -710,6 +710,7 @@ class ConnectionCommandHandler { this.connection = connection; this["error"] = this.handleCommandResult; this["channellist"] = this.handleCommandChannelList; + this["channellistfinished"] = this.handleCommandChannelListFinished; this["notifychannelcreated"] = this.handleCommandChannelCreate; this["notifychanneldeleted"] = this.handleCommandChannelDelete; this["notifychannelhide"] = this.handleCommandChannelHide; @@ -843,11 +844,17 @@ class ConnectionCommandHandler { } handleCommandChannelList(json) { + this.connection._client.channelTree.hide_channel_tree(); /* dont perform channel inserts on the dom to prevent style recalculations */ console.log(tr("Got %d new channels"), json.length); for(let index = 0; index < json.length; index++) this.createChannelFromJson(json[index], true); } + + handleCommandChannelListFinished(json) { + this.connection._client.channelTree.show_channel_tree(); + } + handleCommandChannelCreate(json) { this.createChannelFromJson(json[0]); } diff --git a/shared/js/load.ts b/shared/js/load.ts index cd1f5d23..030e8067 100644 --- a/shared/js/load.ts +++ b/shared/js/load.ts @@ -615,9 +615,9 @@ const loader_style = { "css/static/main.css", "css/static/helptag.css", "css/static/scroll.css", + "css/static/channel-tree.css", "css/static/ts/tab.css", "css/static/ts/chat.css", - "css/static/ts/client.css", "css/static/ts/icons.css", "css/static/general.css", "css/static/modals.css", diff --git a/shared/js/log.ts b/shared/js/log.ts index de491814..49235ce4 100644 --- a/shared/js/log.ts +++ b/shared/js/log.ts @@ -1,5 +1,6 @@ enum LogCategory { CHANNEL, + CHANNEL_PROPERTIES, /* separating channel and channel properties because on channel init logging is a big bottleneck */ CLIENT, SERVER, PERMISSIONS, @@ -19,25 +20,27 @@ namespace log { } let category_mapping = new Map([ - [LogCategory.CHANNEL, "Channel "], - [LogCategory.CLIENT, "Client "], - [LogCategory.SERVER, "Server "], - [LogCategory.PERMISSIONS, "Permission "], - [LogCategory.GENERAL, "General "], - [LogCategory.NETWORKING, "Network "], - [LogCategory.VOICE, "Voice "], - [LogCategory.I18N, "I18N "] + [LogCategory.CHANNEL, "Channel "], + [LogCategory.CLIENT, "Channel "], + [LogCategory.CHANNEL_PROPERTIES, "Client "], + [LogCategory.SERVER, "Server "], + [LogCategory.PERMISSIONS, "Permission "], + [LogCategory.GENERAL, "General "], + [LogCategory.NETWORKING, "Network "], + [LogCategory.VOICE, "Voice "], + [LogCategory.I18N, "I18N "] ]); - let enabled_mapping = new Map([ - [LogCategory.CHANNEL, true], - [LogCategory.CLIENT, true], - [LogCategory.SERVER, true], - [LogCategory.PERMISSIONS, true], - [LogCategory.GENERAL, true], - [LogCategory.NETWORKING, true], - [LogCategory.VOICE, true], - [LogCategory.I18N, true] + export let enabled_mapping = new Map([ + [LogCategory.CHANNEL, true], + [LogCategory.CHANNEL_PROPERTIES, false], + [LogCategory.CLIENT, true], + [LogCategory.SERVER, true], + [LogCategory.PERMISSIONS, true], + [LogCategory.GENERAL, true], + [LogCategory.NETWORKING, true], + [LogCategory.VOICE, true], + [LogCategory.I18N, true] ]); loader.register_task(loader.Stage.LOADED, { @@ -51,7 +54,7 @@ namespace log { for(const category of Object.keys(LogCategory).map(e => parseInt(e))) { if(isNaN(category)) continue; const category_name = LogCategory[category]; - enabled_mapping[category] = settings.static_global("log." + category_name.toLowerCase() + ".enabled", true); + enabled_mapping[category] = settings.static_global("log." + category_name.toLowerCase() + ".enabled", enabled_mapping.get(category)); } } @@ -112,6 +115,8 @@ namespace log { export class Group { readonly level: LogType; readonly category: LogCategory; + readonly enabled: boolean; + owner: Group = undefined; private readonly name: string; @@ -124,6 +129,7 @@ namespace log { this.category = category; this.name = name; this.optionalParams = optionalParams; + this.enabled = enabled_mapping[category]; } group(level: LogType, name: string, ...optionalParams: any[]) : Group { @@ -136,6 +142,9 @@ namespace log { } log(message: string, ...optionalParams: any[]) : this { + if(!this.enabled) + return this; + if(!this.initialized) { if(this._collapsed && console.groupCollapsed) console.groupCollapsed(this.name, ...this.optionalParams); diff --git a/shared/js/ui/channel.ts b/shared/js/ui/channel.ts index 7a23d902..fc50716b 100644 --- a/shared/js/ui/channel.ts +++ b/shared/js/ui/channel.ts @@ -51,20 +51,19 @@ class ChannelEntry { channelId: number; parent?: ChannelEntry; properties: ChannelProperties = new ChannelProperties(); - originalHeight: number; channel_previous?: ChannelEntry; channel_next?: ChannelEntry; - private _channelAlign: string; - private _formatedChannelName: string; + private _channel_name_alignment: string = undefined; + private _channel_name_formatted: string = undefined; private _family_index: number = 0; //HTML DOM elements - private _tag_root: JQuery; - private _tag_siblings: JQuery; - private _tag_clients: JQuery; - private _tag_channel: JQuery; + private _tag_root: JQuery; /* container for the channel, client and children tag */ + private _tag_siblings: JQuery; /* container for all sub channels */ + private _tag_clients: JQuery; /* container for all clients */ + private _tag_channel: JQuery; /* container for the channel info itself */ private _cachedPassword: string; private _cached_channel_description: string = undefined; @@ -75,7 +74,7 @@ class ChannelEntry { constructor(channelId, channelName, parent = null) { this.properties = new ChannelProperties(); this.channelId = channelId; - this._formatedChannelName = channelName; + this.properties.channel_name = channelName; this.parent = parent; this.channelTree = null; @@ -87,8 +86,8 @@ class ChannelEntry { return this.properties.channel_name; } - formatedChannelName() { - return this._formatedChannelName !== undefined ? this._formatedChannelName : this.properties.channel_name; + formattedChannelName() { + return this._channel_name_formatted || this.properties.channel_name; } getChannelDescription() : Promise { @@ -108,7 +107,6 @@ class ChannelEntry { parent_channel() { return this.parent; } hasParent(){ return this.parent != null; } getChannelId(){ return this.channelId; } - channelClass() { return "channel_full"; } children(deep = false) : ChannelEntry[] { const result: ChannelEntry[] = []; @@ -173,54 +171,139 @@ class ChannelEntry { return clients; } + update_family_index() { + const current_index = this._family_index; + const new_index = this.calculate_family_index(true); + if(current_index == new_index) return; + + this._tag_channel.css("z-index", this._family_index); + } + + private 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; + } + private initializeTag() { - let rootTag = $.spawn("div"); + const tag_channel = $.spawn("div").addClass("tree-entry channel"); - rootTag.attr("id", "channel_" + this.getChannelId()); - rootTag.addClass("channel"); - //rootTag.append($.spawn("div").addClass("icon_empty")); + { + const container_entry = $.spawn("div").addClass("container-channel"); - //Tag channel - this._tag_channel = $.spawn("div"); - this._tag_channel.attr('channel-id', this.channelId); - this._tag_channel.addClass("channelLine"); - this._tag_channel.addClass(this._channelAlign); //For left - this._tag_channel.css('z-index', this._family_index); + container_entry.attr("channel-id", this.channelId); + container_entry.addClass(this._channel_name_alignment); + container_entry.css('z-index', this.calculate_family_index()); //TODO Calculate! - let channelType = $.spawn("div"); - channelType.addClass("channel_only_normal channel_type icon client-channel_green_subscribed"); - this._tag_channel.append(channelType); + /* channel icon (type) */ + { + container_entry.append( + $.spawn("div") + .addClass("show-channel-normal-only channel-type icon client-channel_green_subscribed") + ); + } - this._tag_channel.append($.spawn("div").addClass("channel_name_container").append($.spawn("a").addClass("channel_name").text(this.channelName()))); + /* channel name */ + { + container_entry.append( + $.spawn("div") + .addClass("container-channel-name") + .append( + $.spawn("a") + .addClass("channel-name") + .text(this.channelName()) + ) + ) + } - //Icons - let iconTag = $.spawn("span").addClass("icons"); - iconTag.appendTo(this._tag_channel); + /* all icons (last element) */ + { + //Icons + let container_icons = $.spawn("span").addClass("icons"); + + //Default icon (5) + container_icons.append( + $.spawn("div") + .addClass("show-channel-normal-only icon_entry icon_default icon client-channel_default") + .attr("title", tr("Default channel")) + ); + + //Password icon (4) + container_icons.append( + $.spawn("div") + .addClass("show-channel-normal-only icon_entry icon_password icon client-register") + .attr("title", tr("The channel is password protected")) + ); + + //Music icon (3) + container_icons.append( + $.spawn("div") + .addClass("show-channel-normal-only icon_entry icon_music icon client-music") + .attr("title", tr("Music quality")) + ); + + //Channel moderated (2) + container_icons.append( + $.spawn("div") + .addClass("show-channel-normal-only icon_entry icon_moderated icon client-moderated") + .attr("title", tr("Channel is moderated")) + ); + + //Channel Icon (1) + container_icons.append( + $.spawn("div") + .addClass("show-channel-normal-only icon_entry channel_icon") + .attr("title", tr("Channel icon")) + ); + + //Default no sound (0) + let container = $.spawn("div") + .css("position", "relative") + .addClass("icon_no_sound"); + + let noSound = $.spawn("div") + .addClass("icon_entry icon client-conflict-icon") + .attr("title", "You don't support the channel codec"); + + let bg = $.spawn("div") + .width(10) + .height(14) + .css("background", "red") + .css("position", "absolute") + .css("top", "1px") + .css("left", "3px") + .css("z-index", "-1"); + bg.appendTo(container); + noSound.appendTo(container); + container_icons.append(container); + + container_icons.appendTo(container_entry); + } + + tag_channel.append(this._tag_channel = container_entry); + } + { + const container_client = $.spawn("div").addClass("container-clients"); + + + tag_channel.append(this._tag_clients = container_client); + } + { + const container_children = $.spawn("div").addClass("container-children"); + + + tag_channel.append(this._tag_siblings = container_children); + } - //Default icon (5) - iconTag.append($.spawn("div").addClass("channel_only_normal").append($.spawn("div").addClass("icon_entry icon_default icon client-channel_default").attr("title", "Default channel"))); - //Password icon (4) - iconTag.append($.spawn("div").addClass("channel_only_normal").append($.spawn("div").addClass("icon_entry icon_password icon client-register").attr("title", "The channel is password protected"))); - //Music icon (3) - iconTag.append($.spawn("div").addClass("channel_only_normal").append($.spawn("div").addClass("icon_entry icon_music icon client-music").attr("title", "Music quality"))); - //Channel moderated (2) - iconTag.append($.spawn("div").addClass("channel_only_normal").append($.spawn("div").addClass("icon_entry icon_moderated icon client-moderated").attr("title", "Channel is moderated"))); - //Channel Icon (1) - //iconTag.append($.spawn("div").addClass("channel_only_normal").addClass("icon_entry channel_icon").attr("title", "Channel icon")); - iconTag.append($.spawn("div").addClass("channel_only_normal").append($.spawn("div").addClass("icon_entry channel_icon").attr("title", "Channel icon"))); - //Default no sound (0) - let container = $.spawn("div"); - let noSound = $.spawn("div").addClass("icon_entry icon_no_sound icon client-conflict-icon").attr("title", "You don't support the channel codec"); - let bg = $.spawn("div") - .width(10) - .height(14) - .css("background", "red") - .css("position", "absolute") - .css("top", "1px") - .css("left", "3px"); - bg.appendTo(container); - noSound.appendTo(container); - iconTag.append(container); /* setInterval(() => { let color = (Math.random() * 10000000).toString(16).substr(0, 6); @@ -228,20 +311,7 @@ class ChannelEntry { }, 150); */ - //Build siblings - this._tag_siblings = $.spawn("div").addClass("siblings"); - let tag_siblings_box = $.spawn("div").css("position", "absolute").css("width", "calc(100% - 16px)").css("margin", "0px"); - this._tag_siblings.appendTo(tag_siblings_box); - - //Build clients - this._tag_clients = $.spawn("div").addClass("clients"); - let tag_clients_box = $.spawn("div").css("position", "absolute").css("width", "calc(100% - 16px)").css("margin", "0px"); - this._tag_clients.appendTo(tag_clients_box); - - this._tag_root = rootTag; - tag_clients_box.appendTo(this._tag_root); - tag_siblings_box.appendTo(this._tag_root); - this._tag_channel.appendTo(this._tag_root); + this._tag_root = tag_channel; } rootTag() : JQuery { @@ -287,29 +357,6 @@ class ChannelEntry { } } - adjustSize(parent = true) { - const size = this.originalHeight; - let subSize = 0; - let clientSize = 0; - - const sub = this.children(false); - sub.forEach(function (e) { - if(e.rootTag().is(":visible")) - subSize += e.rootTag().outerHeight(true); - }); - - const clients = this.clients(false); - clients.forEach(function (e) { - if(e.tag.is(":visible")) - clientSize += e.tag.outerHeight(true); - }); - - this._tag_root.css({height: size + subSize + clientSize}); - this._tag_siblings.css("margin-top", (clientSize + 16) + "px"); - this._tag_clients.css({height: clientSize}); - if(parent && this.parent_channel()) this.parent_channel().adjustSize(parent); - } - initializeListener() { const _this = this; this.channelTag().click(function () { @@ -465,8 +512,10 @@ class ChannelEntry { this.__updateChannelName(); } + private static NAME_ALIGNMENTS: string[] = ["align-left", "align-center", "align-right", "align-repetitive"]; private __updateChannelName() { - this._formatedChannelName = undefined; + this._channel_name_formatted = undefined; + parseType: if(this.parent_channel() == null && this.properties.channel_name.charAt(0) == '[') { let end = this.properties.channel_name.indexOf(']'); @@ -477,49 +526,64 @@ class ChannelEntry { options = options.substr(0, options.indexOf("spacer")); console.log(tr("Channel options: '%o'"), options); - if(options.length == 0) options = "l"; + if(options.length == 0) options = "align-left"; else if(options.length > 1) options = options[0]; - if(options == "r" || options == "l" || options == "c" || options == "*") - this._channelAlign = options; - else break parseType; + switch (options) { + case "r": + this._channel_name_alignment = "align-right"; + break; + case "l": + this._channel_name_alignment = "align-left"; + break; + case "c": + this._channel_name_alignment = "align-center"; + break; + case "*": + this._channel_name_alignment = "align-repetitive"; + break; + default: + this._channel_name_alignment = undefined; + break parseType; + } - this._formatedChannelName = this.properties.channel_name.substr(end + 1); - console.log(tr("Got channel name: %o"), this._formatedChannelName); + this._channel_name_formatted = this.properties.channel_name.substr(end + 1); + console.log(tr("Got formated channel name: %o"), this._channel_name_formatted); } - let self = this.channelTag(); - let channelName = self.find(".channel_name"); - channelName.text(this.formatedChannelName()); - channelName.parent().removeClass("l r c *"); //Alignments - (this._formatedChannelName !== undefined ? $.fn.hide : $.fn.show).apply(self.find(".channel_only_normal")); + this._tag_channel.find(".show-channel-normal-only").toggleClass("channel-normal", this._channel_name_formatted === undefined); - if(this._formatedChannelName !== undefined) { - channelName.parent().addClass(this._channelAlign); + const tag_container_name = this._tag_channel.find(".container-channel-name"); + tag_container_name.removeClass(ChannelEntry.NAME_ALIGNMENTS.join(" ")); - if(this._channelAlign == "*") { + const tag_name = tag_container_name.find(".channel-name"); + tag_name.text(this._channel_name_formatted || this.properties.channel_name); + + if(this._channel_name_formatted !== undefined) { + tag_container_name.addClass(this._channel_name_alignment); + + if(this._channel_name_alignment == "*") { let lastSuccess = ""; let index = 6; - let name = this.formatedChannelName(); + let name = this.formattedChannelName(); while(index-- > 0) name = name + name; - channelName.text(name); + tag_name.text(name); do { - channelName.text(name = name + name); - } while (channelName.parent().width() >= channelName.width() && ++index < 64); + tag_name.text(name = name + name); + } while (tag_name.parent().width() >= tag_name.width() && ++index < 64); if(index == 64) console.warn(LogCategory.CHANNEL, tr("Repeating spacer took too much repeats!")); if(lastSuccess.length > 0) { - channelName.text(lastSuccess); - self.addClass("c"); + tag_name.text(lastSuccess); } } } - console.log(tr("Align: %s"), this._channelAlign); + console.log(tr("Align: %s"), this._channel_name_alignment); } updateVariables(...variables: {key: string, value: string}[]) { - let group = log.group(log.LogType.DEBUG, LogCategory.CHANNEL, tr("Update properties (%i) of %s (%i)"), variables.length, this.channelName(), this.getChannelId()); + let group = log.group(log.LogType.DEBUG, LogCategory.CHANNEL_PROPERTIES, tr("Update properties (%i) of %s (%i)"), variables.length, this.channelName(), this.getChannelId()); for(let variable of variables) { let key = variable.key; @@ -564,9 +628,11 @@ class ChannelEntry { } updateChannelTypeIcon() { - let tag = this.channelTag().find(".channel_type"); + let tag = this.channelTag().find(".channel-type"); tag.removeAttr('class'); - tag.addClass("channel_only_normal channel_type icon"); + tag.addClass("show-channel-normal-only channel-type icon"); + if(this._channel_name_formatted === undefined) + tag.addClass("channel-normal"); let type; if(this.properties.channel_flag_password == true && !this._cachedPassword) @@ -583,7 +649,7 @@ class ChannelEntry { } generate_bbcode() { - return "[url=channel://" + this.channelId + "/" + encodeURIComponent(this.properties.channel_name) + "]" + this.formatedChannelName() + "[/url]"; + return "[url=channel://" + this.channelId + "/" + encodeURIComponent(this.properties.channel_name) + "]" + this.formattedChannelName() + "[/url]"; } generate_tag(braces: boolean = false) : JQuery { diff --git a/shared/js/ui/client.ts b/shared/js/ui/client.ts index 627bfafe..6a3ebb58 100644 --- a/shared/js/ui/client.ts +++ b/shared/js/ui/client.ts @@ -416,26 +416,58 @@ class ClientEntry { get tag() : JQuery { if(this._tag) return this._tag; - let tag = $.spawn("div"); + let container_client = $.spawn("div") + .addClass("tree-entry client") + .attr("client-id", this.clientId()); - tag.attr("id", "client_" + this.clientId()); - tag.addClass("client"); - tag.append($.spawn("div").addClass("icon_empty")); - tag.append($.spawn("div").addClass("icon_client_state").attr("title", "Client state")); + container_client.append( + $.spawn("div") + .addClass("icon_client_state") + .attr("title", "Client state") + ); - tag.append($.spawn("div").addClass("group_prefix").attr("title", "Server groups prefixes").hide()); - tag.append($.spawn("div").addClass("name").text(this.clientNickName())); - tag.append($.spawn("div").addClass("group_suffix").attr("title", "Server groups suffix").hide()); - tag.append($.spawn("div").addClass("away").text(this.clientNickName())); + container_client.append( + $.spawn("div") + .addClass("group-prefix") + .attr("title", "Server groups prefixes") + .hide() + ); + container_client.append( + $.spawn("div") + .addClass("client-name") + .text(this.clientNickName()) + ); + container_client.append( + $.spawn("div") + .addClass("group-suffix") + .attr("title", "Server groups suffix") + .hide() + ); + container_client.append( + $.spawn("div") + .addClass("client-away-message") + .text(this.clientNickName()) + ); - let clientIcons = $.spawn("span"); - clientIcons.append($.spawn("div").addClass("icon icon_talk_power client-input_muted").hide()); - clientIcons.append($.spawn("span").addClass("group_icons")); - clientIcons.append($.spawn("span").addClass("client_icon")); - tag.append(clientIcons); + let container_icons = $.spawn("div").addClass("container-icons"); - this._tag = tag; + container_icons.append( + $.spawn("div") + .addClass("icon icon_talk_power client-input_muted") + .hide() + ); + container_icons.append( + $.spawn("div") + .addClass("container-icons-group") + ); + container_icons.append( + $.spawn("div") + .addClass("container-icon-client") + ); + container_client.append(container_icons); + + this._tag = container_client; this.initializeListener(); return this._tag; } @@ -470,9 +502,9 @@ class ClientEntry { updateClientStatusIcons() { let talk_power = this.properties.client_talk_power >= this._channel.properties.channel_needed_talk_power; if(talk_power) - this.tag.find("span").find(".icon_talk_power").hide(); + this.tag.find(".icon_talk_power").hide(); else - this.tag.find("span").find(".icon_talk_power").show(); + this.tag.find(".icon_talk_power").show(); } updateClientSpeakIcon() { @@ -519,7 +551,7 @@ class ClientEntry { } updateAwayMessage() { - let tag = this.tag.find(".away"); + let tag = this.tag.find(".client-away-message"); if(this.properties.client_away == true && this.properties.client_away_message){ tag.text("[" + this.properties.client_away_message + "]"); tag.show(); @@ -542,7 +574,7 @@ class ClientEntry { //TODO tr group.log("Updating client " + this.clientId() + ". Key " + variable.key + " Value: '" + variable.value + "' (" + typeof (this.properties[variable.key]) + ")"); if(variable.key == "client_nickname") { - this.tag.find(".name").text(variable.value); + this.tag.find(".client-name").text(variable.value); let chat = this.chat(false); if(chat) chat.name = variable.value; @@ -584,7 +616,7 @@ class ClientEntry { } update_displayed_client_groups() { - this.tag.find("span .group_icons").children().detach(); + this.tag.find(".container-icons-group").children().detach(); for(let id of this.assignedServerGroupIds()) this.updateGroupIcon(this.channelTree.client.groups.serverGroup(id)); @@ -603,8 +635,8 @@ class ClientEntry { suffix_groups.push(group.name); } - const tag_group_prefix = this.tag.find(".group_prefix"); - const tag_group_suffix = this.tag.find(".group_suffix"); + const tag_group_prefix = this.tag.find(".group-prefix"); + const tag_group_suffix = this.tag.find(".group-suffix"); if(prefix_groups.length > 0) { tag_group_prefix.text("[" + prefix_groups.join("][") + "]").show(); } else { @@ -648,21 +680,23 @@ class ClientEntry { } updateClientIcon() { - this.tag.find("span .client_icon").children().detach(); + this.tag.find(".container-icon-client").children().detach(); if(this.properties.client_icon_id > 0) { this.channelTree.client.fileManager.icons.generateTag(this.properties.client_icon_id).attr("title", "Client icon") - .appendTo(this.tag.find("span .client_icon")); + .appendTo(this.tag.find(".container-icon-client")); } } updateGroupIcon(group: Group) { if(!group) return; //TODO group icon order - this.tag.find(".group_icons .icon_group_" + group.id).detach(); + this.tag.find(".container-icons-group .icon_group_" + group.id).detach(); if (group.properties.iconid > 0) { - this.tag.find("span .group_icons").append( - $.spawn("div").addClass("icon_group_" + group.id).append(this.channelTree.client.fileManager.icons.generateTag(group.properties.iconid)).attr("title", group.name) + this.tag.find(".container-icons-group").append( + $.spawn("div") + .addClass("container-group-icon icon_group_" + group.id) + .append(this.channelTree.client.fileManager.icons.generateTag(group.properties.iconid)).attr("title", group.name) ); } } @@ -774,7 +808,7 @@ class LocalClientEntry extends ClientEntry { initializeListener(): void { super.initializeListener(); - this.tag.find(".name").addClass("own_name"); + this.tag.find(".client-name").addClass("client-name-own"); this.tag.dblclick(() => { if($.isArray(this.channelTree.currently_selected)) { //Multiselect @@ -787,9 +821,9 @@ class LocalClientEntry extends ClientEntry { openRename() : void { const _self = this; - const elm = this.tag.find(".name"); + const elm = this.tag.find(".client-name"); elm.attr("contenteditable", "true"); - elm.removeClass("own_name"); + elm.removeClass("client-name-own"); elm.css("background-color", "white"); elm.focus(); _self.renaming = true; @@ -807,7 +841,7 @@ class LocalClientEntry extends ClientEntry { elm.css("background-color", ""); elm.removeAttr("contenteditable"); - elm.addClass("own_name"); + elm.addClass("client-name-own"); let text = elm.text().toString(); if(_self.clientNickName() == text) return; diff --git a/shared/js/ui/client_move.ts b/shared/js/ui/client_move.ts index 816281e8..896f751b 100644 --- a/shared/js/ui/client_move.ts +++ b/shared/js/ui/client_move.ts @@ -88,7 +88,7 @@ class ClientMover { const elements = document.elementsFromPoint(event.pageX, event.pageY); while(elements.length > 0) { - if(elements[0].classList.contains("channelLine")) break; + if(elements[0].classList.contains("container-channel")) break; elements.pop_front(); } diff --git a/shared/js/ui/server.ts b/shared/js/ui/server.ts index 80024b1e..07ddf314 100644 --- a/shared/js/ui/server.ts +++ b/shared/js/ui/server.ts @@ -94,17 +94,23 @@ class ServerEntry { get htmlTag() { if(this._htmlTag) return this._htmlTag; - let tag = $.spawn("div"); + let tag = $.spawn("div").addClass("tree-entry server"); - tag.attr("id", "server"); - tag.addClass("server"); - tag.append($.spawn("div").addClass("server_type icon client-server_green")); - tag.append($.spawn("a").addClass("name").text(this.properties.virtualserver_name)); + tag.append( + $.spawn("div") + .addClass("server_type icon client-server_green") + ); - const serverIcon = $(""); - //we cant spawn an icon on creation :) - serverIcon.append($.spawn("div").addClass("icon_property icon_empty")); - tag.append(serverIcon); + tag.append( + $.spawn("div") + .addClass("name") + .text(this.properties.virtualserver_name) + ); + + tag.append( + $.spawn("div") + .addClass("icon_property icon_empty") + ); return this._htmlTag = tag; } diff --git a/shared/js/ui/view.ts b/shared/js/ui/view.ts index 7a47c3f5..7b118d37 100644 --- a/shared/js/ui/view.ts +++ b/shared/js/ui/view.ts @@ -10,6 +10,7 @@ class ChannelTree { client: TSClient; htmlTree: JQuery; + htmlTree_parent: JQuery; server: ServerEntry; channels: ChannelEntry[]; clients: ClientEntry[]; @@ -29,6 +30,8 @@ class ChannelTree { this.client = client; this.htmlTree = htmlTree; + this.htmlTree_parent = this.htmlTree.parent(); + this.client_mover = new ClientMover(this); this.reset(); @@ -63,6 +66,22 @@ class ChannelTree { }}); } + hide_channel_tree() { + this.htmlTree.detach(); + } + + show_channel_tree() { + this.htmlTree.appendTo(this.htmlTree_parent); + + /* TODO fixup the children (in general the order method) */ + const channel_resize: ChannelEntry[] = []; + const enqueue_resize = (entry: ChannelEntry) => { + channel_resize.push(entry); + entry.children().forEach(e => enqueue_resize(e)); + }; + this.rootChannel().forEach(e => enqueue_resize(e)); + } + showContextMenu(x: number, y: number, on_close: () => void = undefined) { let channelCreate = this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_TEMPORARY).granted(1) || @@ -91,12 +110,6 @@ class ChannelTree { let tag = element instanceof ChannelEntry ? element.rootTag() : element.tag; this.htmlTree.find(tag).fadeOut("slow", () => { tag.remove(); - if(element instanceof ChannelEntry) { - if(element.parent_channel()) - element.parent_channel().adjustSize(true); - } else if(element instanceof ClientEntry) { - element.currentChannel().adjustSize(true); - } }); } @@ -180,7 +193,6 @@ class ChannelTree { let entry = channel.rootTag().css({display: "none"}).fadeIn("slow"); entry.appendTo(tag); - channel.originalHeight = entry.outerHeight(false); if(elm != undefined) elm.after(entry); @@ -189,8 +201,8 @@ class ChannelTree { if(channel.channel_next == channel) /* shall never happen */ channel.channel_next = undefined; - channel.adjustSize(true); channel.initializeListener(); + channel.update_family_index(); } findChannel(channelId: number) : ChannelEntry | undefined { @@ -206,9 +218,9 @@ class ChannelTree { return undefined; } - moveChannel(channel: ChannelEntry, channel_previus: ChannelEntry, parent: ChannelEntry) { - if(channel_previus != null && channel_previus.parent != parent) { - console.error(tr("Invalid channel move (different parents! (%o|%o)"), channel_previus.parent, parent); + 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); return; } @@ -227,16 +239,16 @@ class ChannelTree { let oldParent = channel.parent_channel(); channel.channel_next = undefined; - channel.channel_previous = channel_previus; + channel.channel_previous = channel_previous; channel.parent = parent; - if(channel_previus) { - if(channel_previus == this.channel_last) + if(channel_previous) { + if(channel_previous == this.channel_last) this.channel_last = channel; - channel.channel_next = channel_previus.channel_next; - channel_previus.channel_next = channel; - channel_previus.rootTag().after(channel.rootTag()); + channel.channel_next = channel_previous.channel_next; + channel_previous.channel_next = channel; + channel_previous.rootTag().after(channel.rootTag()); if(channel.channel_next) channel.channel_next.channel_previous = channel; @@ -267,17 +279,11 @@ class ChannelTree { } } + channel.update_family_index(); if(channel.channel_previous == channel) /* shall never happen */ channel.channel_previous = undefined; if(channel.channel_next == channel) /* shall never happen */ channel.channel_next = undefined; - - if(oldParent) { - oldParent.adjustSize(); - } - if(channel) { - channel.adjustSize(); - } } deleteClient(client: ClientEntry) { @@ -300,7 +306,6 @@ class ChannelTree { let tag = client.tag.css({display: "none"}).fadeIn("slow"); tag.appendTo(channel.clientTag()); - channel.adjustSize(true); client.currentChannel().reorderClients(); channel.updateChannelTypeIcon(); @@ -325,11 +330,9 @@ class ChannelTree { tag.detach(); tag.appendTo(client.currentChannel().clientTag()); if(oldChannel) { - oldChannel.adjustSize(); oldChannel.updateChannelTypeIcon(); } if(client.currentChannel()) { - client.currentChannel().adjustSize(); client.currentChannel().reorderClients(); client.currentChannel().updateChannelTypeIcon(); } @@ -397,7 +400,7 @@ class ChannelTree { if(e == entry) { this.currently_selected.remove(e); if(entry instanceof ChannelEntry) - (entry as ChannelEntry).rootTag().find("> .channelLine").removeClass("selected"); + (entry as ChannelEntry).channelTag().removeClass("selected"); else if(entry instanceof ClientEntry) (entry as ClientEntry).tag.removeClass("selected"); else if(entry instanceof ServerEntry) @@ -413,7 +416,7 @@ class ChannelTree { } if(entry instanceof ChannelEntry) - (entry as ChannelEntry).rootTag().find("> .channelLine").addClass("selected"); + (entry as ChannelEntry).channelTag().addClass("selected"); else if(entry instanceof ClientEntry) (entry as ClientEntry).tag.addClass("selected"); else if(entry instanceof ServerEntry) @@ -739,8 +742,6 @@ class ChannelTree { if(channels.indexOf(client.currentChannel()) == -1) channels.push(client.currentChannel()); } - for(const channel of channels) - channel.adjustSize(); } get_first_channel?() : ChannelEntry {