Added query account management (Works only with the TeaSpeak server 1.2.31 beta 3)

canary
WolverinDEV 2018-12-18 20:00:29 +01:00
parent 092316f073
commit 09fc3f2ecc
14 changed files with 501 additions and 8 deletions

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -520,6 +520,7 @@
.group-list {
border: lightgray solid 1px;
padding: 3px;
overflow-y: auto;
.group-entry {
display: flex;

View File

@ -50,6 +50,7 @@
<link rel="stylesheet" href="css/ts/icons.css" type="text/css">
<link rel="stylesheet" href="css/general.css" type="text/css">
<link rel="stylesheet" href="css/modals.css" type="text/css">
<link rel="stylesheet" href="css/modal-query.css" type="text/css">
<link rel="stylesheet" href="css/modal-banlist.css" type="text/css">
<link rel="stylesheet" href="css/modal-bancreate.css" type="text/css">
<link rel="stylesheet" href="css/modal-settings.css" type="text/css">

View File

@ -19,7 +19,24 @@
<div class="button btn_disconnect" title="{{tr 'Disconnect from server' /}}" style="display: none">
<div class="icon_x32 client-disconnect"></div>
</div>
<!--<div class="button btn_disconnect"><div class="icon_x32 client-disconnect"></div></div>-->
<!--
<div class="button-dropdown btn_bookmark" title="{{tr 'Bookmarks' /}}">
<div class="buttons">
<div class="button icon_x32 client-bookmark_manager btn_bookmark_list"></div>
<div class="button-dropdown">
<div class="arrow"></div>
</div>
</div>
<div class="dropdown bookmark-dropdown" style="width: 300px">
<div class="btn_bookmark_list"><div class="icon client-bookmark_manager"></div><a>{{tr "Manage bookmarks" /}}</a></div>
<div class="btn_bookmark_add"><div class="icon client-bookmark_add"></div><a>{{tr "Add current server to bookmarks" /}}</a></div>
<div class="btn_bookmark_remove"><div class="icon client-bookmark_remove"></div><a>{{tr "Remove current server to bookmarks" /}}</a></div>
<hr>
</div>
</div>
-->
<div class="divider"></div>
<div class="button-dropdown btn_away" title="{{tr 'Toggle away status' /}}">
@ -62,6 +79,20 @@
<div class="button btn_permissions" title="{{tr 'View/edit permissions' /}}">
<div class="icon_x32 client-permission_overview"></div>
</div>
<!-- the query button -->
<div class="button-dropdown btn_query" title="{{tr 'Show/hide server queries' /}}">
<div class="buttons">
<div class="button icon_x32 client-server_query btn_query_toggle"></div>
<div class="button-dropdown">
<div class="arrow"></div>
</div>
</div>
<div class="dropdown display_left">
<div class="btn_query_toggle"><div class="icon client-away"></div><a>{{tr "Show/hide server queries" /}}</a></div>
<div class="btn_query_create"><div class="icon client-away"></div><a>{{tr "Create server query login" /}}</a></div>
</div>
</div>
<div class="divider"></div>
<div class="button btn_open_settings" title="{{tr 'Edit global client settings' /}}">
<div class="icon_x32 client-settings"></div>
@ -1656,5 +1687,38 @@
<button class="button-close">{{tr "Close" /}}</button>
</div>
</script>
<script class="jsrender-template" id="tmpl_query_create" type="text/html">
<div class="query-create">
<a>{{tr "Set the login name for your Server Query account." /}}</a>
<a>{{tr "You'll receive your password within the next step." /}}</a>
<div class="row-name">
<a>Name:</a>
<input type="text" maxlength="64" minlength="3" class="input-name">
</div>
<div class="buttons">
<button class="button-close">{{tr "Close" /}}</button>
<button class="button-create">{{tr "Create" /}}</button>
</div>
</div>
</script>
<script class="jsrender-template" id="tmpl_query_created" type="text/html">
<div class="query-created">
<a>{{tr "Your server query credentials:" /}}</a>
<div class="property-row">
<a>Name:</a>
<input class="query_name" type="text" maxlength="64" minlength="3" value="{{>username}}">
<div class="btn_copy_name icon client-copy" title="{{tr 'Copy username' /}}"></div>
</div>
<div class="property-row">
<a>Password:</a>
<input class="query_password" type="text" value="{{>password}}">
<div class="btn_copy_password icon client-copy" title="{{tr 'Copy password' /}}"></div>
</div>
<div class="buttons">
<button class="button-close">{{tr "Close" /}}</button>
</div>
</div>
</script>
</body>
</html>

156
shared/js/bookmarks.ts Normal file
View File

@ -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)
}
}

View File

@ -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",

View File

@ -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});

View File

@ -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() {
//<div class="btn_bookmark_connect" target="localhost"><a>Localhost</a></div>
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);
}
}
}

View File

@ -0,0 +1,77 @@
/// <reference path="../../utils/modal.ts" />
/// <reference path="../../proto.ts" />
/// <reference path="../../client.ts" />
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();
}
}

View File

@ -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();
}
}

View File

@ -1 +1,2 @@
nan
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?)

2
vendor/bbcode vendored

@ -1 +1 @@
Subproject commit 7b931ed61cf265937dc742579f9070e7c4e50775
Subproject commit 032446b477a5fec8cccc6c3632a0bcbf521b9c0f