diff --git a/ChangeLog.md b/ChangeLog.md index b1839a8b..0b74b8ce 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,4 +1,18 @@ # Changelog: +* **18.12.18** + - Added bookmarks and bookmarks management + - Added query user visibility button and creation (Query management will follow soon) + - Fixed overflow within the group assignment dialog + +* **17.12.18** + - Implemented group prefix and suffix + +* **15.12.18** + - Implemented a translation system with default translated language sets for + - German + - Turkish + - Russian + * **3.12.18** - Fixed url connect parameters diff --git a/shared/css/control_bar.scss b/shared/css/control_bar.scss index bad8261d..5b8188a3 100644 --- a/shared/css/control_bar.scss +++ b/shared/css/control_bar.scss @@ -103,9 +103,7 @@ $background:lightgray; background-color: $background; border-radius: 5px; align-items: center; - border: 2px solid rgba(0, 0, 0, 0); - - border-color: $border_color_activated; + border: 2px solid $border_color_activated; width: 230px; user-select: none; @@ -134,6 +132,10 @@ $background:lightgray; & > div:last-of-type { border-radius: 0 0 2px 2px; } + + &.display_left { + margin-left: -165px; + } } &:hover { @@ -142,4 +144,10 @@ $background:lightgray; } } } + + .bookmark-dropdown { + hr:last-child { + display: none; + } + } } \ No newline at end of file diff --git a/shared/css/modal-query.scss b/shared/css/modal-query.scss new file mode 100644 index 00000000..746b4512 --- /dev/null +++ b/shared/css/modal-query.scss @@ -0,0 +1,59 @@ +.query-create { + display: flex; + flex-direction: column; + + .row-name { + width: 100%; + + display: flex; + flex-direction: row; + justify-content: stretch; + + input { + flex-grow: 1; + flex-shrink: 1; + margin-left: 5px; + } + } + + .buttons { + margin-top: 5px; + text-align: right; + } +} + +.query-created { + display: flex; + flex-direction: column; + + .property-row { + width: 100%; + + display: flex; + flex-direction: row; + justify-content: stretch; + align-items: center; + + margin-top: 2px; + + input { + flex-grow: 1; + flex-shrink: 1; + margin-left: 5px; + } + + a:first-of-type { + width: 150px; + } + + div:last-of-type { + margin-left: 5px; + cursor: pointer; + } + } + + .buttons { + margin-top: 5px; + text-align: right; + } +} \ No newline at end of file diff --git a/shared/css/modals.scss b/shared/css/modals.scss index f69b52b5..4658a269 100644 --- a/shared/css/modals.scss +++ b/shared/css/modals.scss @@ -520,6 +520,7 @@ .group-list { border: lightgray solid 1px; padding: 3px; + overflow-y: auto; .group-entry { display: flex; diff --git a/shared/html/index.php b/shared/html/index.php index f30960aa..4a9ba89d 100644 --- a/shared/html/index.php +++ b/shared/html/index.php @@ -50,6 +50,7 @@ + diff --git a/shared/html/templates.html b/shared/html/templates.html index 15c92438..6e75362c 100644 --- a/shared/html/templates.html +++ b/shared/html/templates.html @@ -19,7 +19,24 @@ - + +
@@ -62,6 +79,20 @@
+ + +
+
+
+
+
+
+
+ +
@@ -1656,5 +1687,38 @@
+ + + \ No newline at end of file diff --git a/shared/js/bookmarks.ts b/shared/js/bookmarks.ts new file mode 100644 index 00000000..64851490 --- /dev/null +++ b/shared/js/bookmarks.ts @@ -0,0 +1,156 @@ +namespace bookmarks { + function guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); + } + + export interface ConnectIdentity { + identity_type: IdentitifyType; + } + + export interface ForumConnectIdentity extends ConnectIdentity { } + export interface NicknameConnectIdentity extends ConnectIdentity { } + export interface TeamSpeakConnectIdentity extends ConnectIdentity { } + + export interface ServerProperties { + server_address: string; + server_port: number; + server_password_hash?: string; + server_password?: string; + } + + export enum BookmarkType { + ENTRY, + DIRECTORY + } + + export interface Bookmark { + type: BookmarkType.ENTRY; + + /* readonly directory: DirectoryBookmark; */ + server_properties: ServerProperties; + display_name: string; + unique_id: string; + + nickname: string; + default_channel?: number | string; + default_channel_password_hash?: string; + default_channel_password?: string; + + } + + export interface DirectoryBookmark { + type: BookmarkType.DIRECTORY; + + readonly content: (Bookmark | DirectoryBookmark)[]; + unique_id: string; + display_name: string; + } + + interface BookmarkConfig { + root_bookmark?: DirectoryBookmark; + default_added?: boolean; + } + + let _bookmark_config: BookmarkConfig; + + function bookmark_config() : BookmarkConfig { + if(_bookmark_config) + return _bookmark_config; + + let bookmark_json = localStorage.getItem("bookmarks"); + let bookmarks = JSON.parse(bookmark_json) || {} as BookmarkConfig; + + _bookmark_config = bookmarks; + _bookmark_config.root_bookmark = _bookmark_config.root_bookmark || { content: [], display_name: "root", type: BookmarkType.DIRECTORY} as DirectoryBookmark; + + if(!_bookmark_config.default_added) { + _bookmark_config.default_added = true; + create_bookmark("TeaSpeak official Test-Server", _bookmark_config.root_bookmark, { + server_address: "ts.teaspeak.de", + server_port: 9987 + }, "Another TeaSpeak user"); + + save_config(); + } + return _bookmark_config; + } + + function save_config() { + localStorage.setItem("bookmarks", JSON.stringify(bookmark_config())); + } + + export function bookmarks() : DirectoryBookmark { + return bookmark_config().root_bookmark; + } + + function find_bookmark_recursive(parent: DirectoryBookmark, uuid: string) : Bookmark | DirectoryBookmark { + for(const entry of parent.content) { + if(entry.unique_id == uuid) + return entry; + if(entry.type == BookmarkType.DIRECTORY) { + const result = find_bookmark_recursive(entry, uuid); + if(result) return result; + } + } + return undefined; + } + + export function find_bookmark(uuid: string) : Bookmark | DirectoryBookmark | undefined { + return find_bookmark_recursive(bookmarks(), uuid); + } + + export function create_bookmark(display_name: string, directory: DirectoryBookmark, server_properties: ServerProperties, nickname: string) : Bookmark { + const bookmark = { + display_name: display_name, + server_properties: server_properties, + nickname: nickname, + type: BookmarkType.ENTRY, + unique_id: guid() + } as Bookmark; + + directory.content.push(bookmark); + return bookmark; + } + + export function create_bookmark_directory(parent: DirectoryBookmark, name: string) : DirectoryBookmark { + const bookmark = { + type: BookmarkType.DIRECTORY, + + display_name: name, + content: [], + unique_id: guid() + } as DirectoryBookmark; + + parent.content.push(bookmark); + return bookmark; + } + + //TODO test if the new parent is within the old bookmark + export function change_directory(parent: DirectoryBookmark, bookmark: Bookmark | DirectoryBookmark) { + delete_bookmark(bookmark) + parent.content.push(bookmark) + } + + export function save_bookmark(bookmark?: Bookmark | DirectoryBookmark) { + save_config(); /* nvm we dont give a fuck... saving everything */ + } + + function delete_bookmark_recursive(parent: DirectoryBookmark, bookmark: Bookmark | DirectoryBookmark) { + const index = parent.content.indexOf(bookmark); + if(index != -1) + parent.content.remove(bookmark); + else + for(const entry of parent.content) + if(entry.type == BookmarkType.DIRECTORY) + delete_bookmark_recursive(entry, bookmark) + } + + export function delete_bookmark(bookmark: Bookmark | DirectoryBookmark) { + delete_bookmark_recursive(bookmarks(), bookmark) + } +} \ No newline at end of file diff --git a/shared/js/load.ts b/shared/js/load.ts index 48fb5fe9..49af45a2 100644 --- a/shared/js/load.ts +++ b/shared/js/load.ts @@ -180,6 +180,7 @@ function loadDebug() { "js/crypto/hex.js", //Load UI + "js/ui/modal/ModalQuery.js", "js/ui/modal/ModalConnect.js", "js/ui/modal/ModalSettings.js", "js/ui/modal/ModalCreateChannel.js", @@ -219,6 +220,7 @@ function loadDebug() { //Load general stuff "js/settings.js", + "js/bookmarks.js", "js/contextMenu.js", "js/connection.js", "js/FileManager.js", diff --git a/shared/js/ui/channel.ts b/shared/js/ui/channel.ts index 6d15f73a..f250cc9d 100644 --- a/shared/js/ui/channel.ts +++ b/shared/js/ui/channel.ts @@ -294,12 +294,14 @@ class ChannelEntry { const sub = this.siblings(false); sub.forEach(function (e) { - subSize += e.rootTag().outerHeight(true); + if(e.rootTag().is(":visible")) + subSize += e.rootTag().outerHeight(true); }); const clients = this.clients(false); clients.forEach(function (e) { - clientSize += e.tag.outerHeight(true); + if(e.tag.is(":visible")) + clientSize += e.tag.outerHeight(true); }); this._tag_root.css({height: size + subSize + clientSize}); diff --git a/shared/js/ui/frames/ControlBar.ts b/shared/js/ui/frames/ControlBar.ts index d0268340..c0a9d9f6 100644 --- a/shared/js/ui/frames/ControlBar.ts +++ b/shared/js/ui/frames/ControlBar.ts @@ -13,11 +13,13 @@ client_away_message Value: '' */ import openBanList = Modals.openBanList; +import spawnConnectModal = Modals.spawnConnectModal; class ControlBar { private _muteInput: boolean; private _muteOutput: boolean; private _away: boolean; + private _query_visible: boolean; private _awayMessage: string; private codec_supported: boolean = false; @@ -64,10 +66,34 @@ class ControlBar { away.find(".btn_away_toggle").on('click', this.on_away_toggle.bind(this)); away.find(".btn_away_message").on('click', this.on_away_set_message.bind(this)); } + { + let bookmark = this.htmlTag.find(".btn_bookmark"); + bookmark.find(".button-dropdown").on('click', () => { + bookmark.find(".dropdown").addClass("displayed"); + }); + bookmark.on('mouseleave', () => { + bookmark.find(".dropdown").removeClass("displayed"); + }); + + this.update_bookmarks() + } + { + let query = this.htmlTag.find(".btn_query"); + query.find(".button-dropdown").on('click', () => { + query.find(".dropdown").addClass("displayed"); + }); + query.on('mouseleave', () => { + query.find(".dropdown").removeClass("displayed"); + }); + + query.find(".btn_query_toggle").on('click', this.on_query_visibility_toggle.bind(this)); + query.find(".btn_query_create").on('click', this.on_query_create.bind(this)) + } //Need an initialise this.muteInput = settings.global("mute_input") == "1"; this.muteOutput = settings.global("mute_output") == "1"; + this.query_visibility = settings.global("show_server_queries") == "1"; } @@ -271,4 +297,62 @@ class ControlBar { openBanList(this.handle); } + + update_bookmarks() { + //
Localhost
+ let tag_bookmark = this.htmlTag.find(".btn_bookmark .dropdown"); + tag_bookmark.find(".bookmark, .bookmark_directory").detach(); + + for(const bookmark of bookmarks.bookmarks().content) { + if(bookmark.type == bookmarks.BookmarkType.ENTRY) { + tag_bookmark.append( + $.spawn("div") + .addClass("bookmark") + /* /.attr("bookmark-uuid", bookmark.unique_id) */ + .text(bookmark.display_name) + .on('click', event => { + spawnConnectModal() + + }) + ) + } + //TODO add bookmark directories here + } + } + + get query_visibility() { + return this._query_visible; + } + + set query_visibility(flag: boolean) { + if(this._query_visible == flag) return; + + this._query_visible = flag; + settings.global("show_server_queries", flag); + this.update_query_visibility_button(); + this.handle.channelTree.toggle_server_queries(flag); + } + + private on_query_visibility_toggle() { + this.query_visibility = !this._query_visible; + this.update_query_visibility_button(); + } + + private update_query_visibility_button() { + let tag = this.htmlTag.find(".btn_query_toggle"); + if(this._query_visible) { + tag.addClass("activated"); + } else { + tag.removeClass("activated"); + } + } + + private on_query_create() { + if(this.handle.permissions.neededPermission(PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN).granted(1)) { + Modals.spawnQueryCreate(); + } else { + createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to create a server query login")).open(); + sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } + } } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalQuery.ts b/shared/js/ui/modal/ModalQuery.ts new file mode 100644 index 00000000..16671c0d --- /dev/null +++ b/shared/js/ui/modal/ModalQuery.ts @@ -0,0 +1,77 @@ +/// +/// +/// + +namespace Modals { + export function spawnQueryCreate() { + let modal; + modal = createModal({ + header: tr("Create a server query login"), + body: () => { + let template = $("#tmpl_query_create").renderTag(); + template = $.spawn("div").append(template); + + template.find(".button-close").on('click', event => modal.close()); + template.find(".button-create").on('click', event => { + const name = template.find(".input-name").val() as string; + if(name.length < 3 || name.length > 64) { + createErrorModal(tr("Invalid username"), tr("Please enter a valid name!")).open(); + return; + } + + //client_login_password + globalClient.serverConnection.commandHandler["notifyclientserverqueryloginpassword"] = json => { + json = json[0]; + + spawnQueryCreated({ + username: name, + password: json.client_login_password + }); + }; + + globalClient.serverConnection.sendCommand("clientsetserverquerylogin", { + client_login_name: name + }); + + modal.close(); + //TODO create account + }); + return template; + }, + footer: undefined, + width: 750 + }); + modal.open(); + } + + export function spawnQueryCreated(credentials: { + username: string, + password: string + }) { + let modal; + modal = createModal({ + header: tr("Server query credentials"), + body: () => { + let template = $("#tmpl_query_created").renderTag(credentials); + template = $.spawn("div").append(template); + + template.find(".button-close").on('click', event => modal.close()); + template.find(".query_name").text(credentials.username); + template.find(".query_password").text(credentials.password); + + template.find(".btn_copy_name").on('click', () => { + template.find(".query_name").select(); + document.execCommand("copy"); + }); + template.find(".btn_copy_password").on('click', () => { + template.find(".query_password").select(); + document.execCommand("copy"); + }); + return template; + }, + footer: undefined, + width: 750 + }); + modal.open(); + } +} \ No newline at end of file diff --git a/shared/js/ui/view.ts b/shared/js/ui/view.ts index 403373c9..e828b918 100644 --- a/shared/js/ui/view.ts +++ b/shared/js/ui/view.ts @@ -18,6 +18,7 @@ class ChannelTree { currently_selected_context_callback: (event) => any = undefined; readonly client_mover: ClientMover; + private _show_queries: boolean; private channel_last?: ChannelEntry; private channel_first?: ChannelEntry; @@ -290,6 +291,10 @@ class ChannelTree { if(newClient) client = newClient; //Got new client :) else this.clients.push(client); + + if(!this._show_queries && client.properties.client_type == ClientType.CLIENT_QUERY) + client.tag.hide(); + client.channelTree = this; client["_channel"] = channel; @@ -709,4 +714,23 @@ class ChannelTree { } } } + + toggle_server_queries(flag: boolean) { + if(this._show_queries == flag) return; + this._show_queries = flag; + + //FIXME resize channels + const channels: ChannelEntry[] = [] + for(const client of this.clients) + if(client.properties.client_type == ClientType.CLIENT_QUERY) { + if(this._show_queries) + client.tag.show(); + else + client.tag.hide(); + if(channels.indexOf(client.currentChannel()) == -1) + channels.push(client.currentChannel()); + } + for(const channel of channels) + channel.adjustSize(); + } } \ No newline at end of file diff --git a/todo.md b/todo.md index 0982929c..eb905562 100644 --- a/todo.md +++ b/todo.md @@ -1 +1,2 @@ -nan \ No newline at end of file +Add connect profiles for bookmarks, like TeaSpeak-Forum or multiple TeamSpeak Identities +Fix server group management dialog for a lots of groups (May even add a search function?) \ No newline at end of file diff --git a/vendor/bbcode b/vendor/bbcode index 7b931ed6..032446b4 160000 --- a/vendor/bbcode +++ b/vendor/bbcode @@ -1 +1 @@ -Subproject commit 7b931ed61cf265937dc742579f9070e7c4e50775 +Subproject commit 032446b477a5fec8cccc6c3632a0bcbf521b9c0f