diff --git a/shared/js/ConnectionHandler.ts b/shared/js/ConnectionHandler.ts
index ed3b8300..883056d2 100644
--- a/shared/js/ConnectionHandler.ts
+++ b/shared/js/ConnectionHandler.ts
@@ -76,6 +76,7 @@ interface ConnectParameters {
};
token?: string;
password?: {password: string, hashed: boolean};
+ auto_reconnect_attempt?: boolean;
}
class ConnectionHandler {
@@ -99,8 +100,10 @@ class ConnectionHandler {
private _clientId: number = 0;
private _local_client: LocalClientEntry;
+
private _reconnect_timer: NodeJS.Timer;
private _reconnect_attempt: boolean = false;
+
private _connect_initialize_id: number = 1;
client_status: VoiceStatus = {
@@ -169,7 +172,7 @@ class ConnectionHandler {
async startConnection(addr: string, profile: profiles.ConnectionProfile, user_action: boolean, parameters: ConnectParameters) {
this.tab_set_name(tr("Connecting"));
this.cancel_reconnect(false);
- this._reconnect_attempt = false;
+ this._reconnect_attempt = parameters.auto_reconnect_attempt || false;
if(this.serverConnection)
this.handleDisconnect(DisconnectReason.REQUESTED);
@@ -475,10 +478,12 @@ class ConnectionHandler {
break;
case DisconnectReason.CONNECTION_CLOSED:
log.error(LogCategory.CLIENT, tr("Lost connection to remote server!"));
- createErrorModal(
- tr("Connection closed"),
- tr("The connection was closed by remote host")
- ).open();
+ if(!this._reconnect_attempt) {
+ createErrorModal(
+ tr("Connection closed"),
+ tr("The connection was closed by remote host")
+ ).open();
+ }
this.sound.play(Sound.CONNECTION_DISCONNECTED);
auto_reconnect = true;
@@ -494,7 +499,7 @@ class ConnectionHandler {
break;
case DisconnectReason.SERVER_CLOSED:
this.log.log(log.server.Type.SERVER_CLOSED, {message: data.reasonmsg});
- //this.chat.serverChat().appendError(tr("Server closed ({0})"), data.reasonmsg);
+
createErrorModal(
tr("Server closed"),
"The server is closed.
" + //TODO tr
@@ -506,7 +511,6 @@ class ConnectionHandler {
break;
case DisconnectReason.SERVER_REQUIRES_PASSWORD:
this.log.log(log.server.Type.SERVER_REQUIRES_PASSWORD, {});
- //this.chat.serverChat().appendError(tr("Server requires password"));
createInputModal(tr("Server password"), tr("Enter server password:"), password => password.length != 0, password => {
if(!(typeof password === "string")) return;
@@ -526,8 +530,8 @@ class ConnectionHandler {
break;
case DisconnectReason.CLIENT_KICKED:
createErrorModal(
- tr("You've been banned"),
- MessageHelper.formatMessage(tr("You've been banned from this server.{:br:}{0}"), data["extra_message"])
+ tr("You've been kicked"),
+ MessageHelper.formatMessage(tr("You've been kicked from this server.{:br:}{0}"), data["extra_message"])
).open();
this.sound.play(Sound.SERVER_KICKED);
auto_reconnect = false;
@@ -590,8 +594,7 @@ class ConnectionHandler {
this.log.log(log.server.Type.RECONNECT_CANCELED, {});
log.info(LogCategory.NETWORKING, tr("Reconnecting..."));
- this.startConnection(server_address.host + ":" + server_address.port, profile, false, this.reconnect_properties(profile));
- this._reconnect_attempt = true;
+ this.startConnection(server_address.host + ":" + server_address.port, profile, false, Object.assign(this.reconnect_properties(profile), {auto_reconnect_attempt: true}));
}, 5000);
}
}
diff --git a/shared/js/FileManager.ts b/shared/js/FileManager.ts
index 55bdc364..e20cc336 100644
--- a/shared/js/FileManager.ts
+++ b/shared/js/FileManager.ts
@@ -729,7 +729,7 @@ class IconManager {
throw "icon not found";
}
- static generate_tag(icon: Promise | Icon, options?: {
+ static generate_tag(icon: Promise | Icon | undefined, options?: {
animate?: boolean
}) : JQuery {
options = options || {};
diff --git a/shared/js/connection/CommandHandler.ts b/shared/js/connection/CommandHandler.ts
index d7efbf09..cdb28d13 100644
--- a/shared/js/connection/CommandHandler.ts
+++ b/shared/js/connection/CommandHandler.ts
@@ -557,8 +557,8 @@ namespace connection {
let client = tree.findClient(json["clid"]);
let self = client instanceof LocalClientEntry;
- let channel_to = tree.findChannel(json["ctid"]);
- let channel_from = tree.findChannel(json["cfid"]);
+ let channel_to = tree.findChannel(parseInt(json["ctid"]));
+ let channel_from = tree.findChannel(parseInt(json["cfid"]));
if(!client) {
log.error(LogCategory.NETWORKING, tr("Unknown client move (Client)!"));
@@ -573,6 +573,7 @@ namespace connection {
if(!self) {
if(!channel_from) {
log.error(LogCategory.NETWORKING, tr("Unknown client move (Channel from)!"));
+ channel_from = client.currentChannel();
} else if(channel_to !== client.currentChannel()) {
log.error(LogCategory.NETWORKING,
tr("Client move from invalid source channel! Local client registered in channel %d but server send %d."),
diff --git a/shared/js/connection/ServerConnectionDeclaration.ts b/shared/js/connection/ServerConnectionDeclaration.ts
index c027e480..6f2b0a56 100644
--- a/shared/js/connection/ServerConnectionDeclaration.ts
+++ b/shared/js/connection/ServerConnectionDeclaration.ts
@@ -1,4 +1,7 @@
enum ErrorID {
+ NOT_IMPLEMENTED = 0x2,
+ COMMAND_NOT_FOUND = 0x100,
+
PERMISSION_ERROR = 2568,
EMPTY_RESULT = 0x0501,
PLAYLIST_IS_IN_USE = 0x2103,
diff --git a/shared/js/permission/PermissionManager.ts b/shared/js/permission/PermissionManager.ts
index beebc7bd..7166d352 100644
--- a/shared/js/permission/PermissionManager.ts
+++ b/shared/js/permission/PermissionManager.ts
@@ -383,7 +383,14 @@ class PermissionValue {
granted(requiredValue: number, required: boolean = true) : boolean {
let result;
result = this.value == -1 || this.value >= requiredValue || (this.value == -2 && requiredValue == -2 && !required);
- log.trace(LogCategory.PERMISSIONS, tr("Test needed required: %o | %i | %o => %o"), this, requiredValue, required, result);
+
+ log.trace(LogCategory.PERMISSIONS,
+ tr("Required permission test resulted for permission %s: %s. Required value: %s, Granted value: %s"),
+ this.type.name,
+ result ? tr("granted") : tr("denied"),
+ requiredValue + (required ? " (" + tr("required") + ")" : ""),
+ this.hasValue() ? this.value : tr("none")
+ );
return result;
}
diff --git a/shared/js/profiles/ConnectionProfile.ts b/shared/js/profiles/ConnectionProfile.ts
index ddd707cd..5fe130a2 100644
--- a/shared/js/profiles/ConnectionProfile.ts
+++ b/shared/js/profiles/ConnectionProfile.ts
@@ -14,6 +14,15 @@ namespace profiles {
this.id = id;
}
+ connect_username() : string {
+ if(this.default_username && this.default_username !== "Another TeaSpeak user")
+ return this.default_username;
+
+ let selected = this.selected_identity();
+ let name = selected ? selected.fallback_name() : undefined;
+ return name || "Another TeaSpeak user";
+ }
+
selected_identity(current_type?: identities.IdentitifyType) : identities.Identity {
if(!current_type)
current_type = this.selected_type();
@@ -139,7 +148,7 @@ namespace profiles {
{
const profile = create_new_profile("default","default");
profile.default_password = "";
- profile.default_username = "Another TeaSpeak user";
+ profile.default_username = "";
profile.profile_name = "Default Profile";
/* generate default identity */
@@ -160,7 +169,7 @@ namespace profiles {
{ /* forum identity (works only when connected to the forum) */
const profile = create_new_profile("TeaSpeak Forum","teaforo");
profile.default_password = "";
- profile.default_username = "Another TeaSpeak user";
+ profile.default_username = "";
profile.profile_name = "TeaSpeak Forum profile";
profile.set_identity(identities.IdentitifyType.TEAFORO, identities.static_forum_identity());
@@ -174,7 +183,7 @@ namespace profiles {
export function create_new_profile(name: string, id?: string) : ConnectionProfile {
const profile = new ConnectionProfile(id || guid());
profile.profile_name = name;
- profile.default_username = "Another TeaSpeak user";
+ profile.default_username = "";
available_profiles.push(profile);
return profile;
}
diff --git a/shared/js/profiles/Identity.ts b/shared/js/profiles/Identity.ts
index e7b0bea7..c658f46d 100644
--- a/shared/js/profiles/Identity.ts
+++ b/shared/js/profiles/Identity.ts
@@ -6,7 +6,7 @@ namespace profiles.identities {
}
export interface Identity {
- name() : string;
+ fallback_name(): string | undefined ;
uid() : string;
type() : IdentitifyType;
diff --git a/shared/js/profiles/identities/NameIdentity.ts b/shared/js/profiles/identities/NameIdentity.ts
index 02af0b00..51cbefc7 100644
--- a/shared/js/profiles/identities/NameIdentity.ts
+++ b/shared/js/profiles/identities/NameIdentity.ts
@@ -47,7 +47,9 @@ namespace profiles.identities {
set_name(name: string) { this._name = name; }
- name(): string {
+ name() : string { return this._name; }
+
+ fallback_name(): string | undefined {
return this._name;
}
diff --git a/shared/js/profiles/identities/TeaForumIdentity.ts b/shared/js/profiles/identities/TeaForumIdentity.ts
index 95fce4e9..7fdcb4e1 100644
--- a/shared/js/profiles/identities/TeaForumIdentity.ts
+++ b/shared/js/profiles/identities/TeaForumIdentity.ts
@@ -84,8 +84,8 @@ namespace profiles.identities {
return new TeaForumHandshakeHandler(connection, this);
}
- name(): string {
- return (this.identity_data ? this.identity_data.name() : "Another TeaSpeak user");
+ fallback_name(): string | undefined {
+ return this.identity_data ? this.identity_data.name() : undefined;
}
type(): profiles.identities.IdentitifyType {
diff --git a/shared/js/profiles/identities/TeamSpeakIdentity.ts b/shared/js/profiles/identities/TeamSpeakIdentity.ts
index bf5d620c..751ae4d6 100644
--- a/shared/js/profiles/identities/TeamSpeakIdentity.ts
+++ b/shared/js/profiles/identities/TeamSpeakIdentity.ts
@@ -503,7 +503,7 @@ namespace profiles.identities {
}
}
- name(): string {
+ fallback_name(): string | undefined {
return this._name;
}
@@ -808,7 +808,7 @@ namespace profiles.identities {
return "[Identity]\n" +
"id=TeaWeb-Exported\n" +
"identity=\"" + identity + "\"\n" +
- "nickname=\"" + this.name() + "\"\n" +
+ "nickname=\"" + this.fallback_name() + "\"\n" +
"phonetic_nickname=";
}
diff --git a/shared/js/settings.ts b/shared/js/settings.ts
index d9e5109e..e476fc42 100644
--- a/shared/js/settings.ts
+++ b/shared/js/settings.ts
@@ -295,6 +295,11 @@ class Settings extends StaticSettings {
key: "font_size"
};
+ static readonly KEY_ICON_SIZE: SettingsKey = {
+ key: "icon_size",
+ default_value: 100
+ };
+
static readonly KEY_LAST_INVITE_LINK_TYPE: SettingsKey = {
key: "last_invite_link_type",
default_value: "tea-web"
diff --git a/shared/js/ui/channel.ts b/shared/js/ui/channel.ts
index 019975eb..1a510f05 100644
--- a/shared/js/ui/channel.ts
+++ b/shared/js/ui/channel.ts
@@ -694,7 +694,7 @@ class ChannelEntry {
if(this._channel_name_formatted !== undefined) {
tag_container_name.addClass(this._channel_name_alignment);
- if(this._channel_name_alignment == "align-repetitive") {
+ if(this._channel_name_alignment == "align-repetitive" && text.length > 0) {
while(text.length < 1024 * 8)
text += text;
}
diff --git a/shared/js/ui/client.ts b/shared/js/ui/client.ts
index 5c72e1f7..b3957a6e 100644
--- a/shared/js/ui/client.ts
+++ b/shared/js/ui/client.ts
@@ -147,7 +147,11 @@ class ClientEntry {
}
if(this._audio_handle) {
log.warn(LogCategory.AUDIO, tr("Destroying client with an active audio handle. This could cause memory leaks!"));
- this._audio_handle.abort_replay();
+ try {
+ this._audio_handle.abort_replay();
+ } catch(error) {
+ log.warn(LogCategory.AUDIO, tr("Failed to abort replay: %o"), error);
+ }
this._audio_handle.callback_playback = undefined;
this._audio_handle.callback_stopped = undefined;
this._audio_handle = undefined;
@@ -159,7 +163,11 @@ class ClientEntry {
tree_unregistered() {
this.channelTree = undefined;
if(this._audio_handle) {
- this._audio_handle.abort_replay();
+ try {
+ this._audio_handle.abort_replay();
+ } catch(error) {
+ log.warn(LogCategory.AUDIO, tr("Failed to abort replay: %o"), error);
+ }
this._audio_handle.callback_playback = undefined;
this._audio_handle.callback_stopped = undefined;
this._audio_handle = undefined;
diff --git a/shared/js/ui/frames/chat.ts b/shared/js/ui/frames/chat.ts
index 7d783023..492524fb 100644
--- a/shared/js/ui/frames/chat.ts
+++ b/shared/js/ui/frames/chat.ts
@@ -128,7 +128,35 @@ namespace MessageHelper {
]
});
- container.find("a").attr('target', "_blank");
+ container.find("a")
+ .attr('target', "_blank")
+ .on('contextmenu', event => {
+ if(event.isDefaultPrevented()) return;
+ event.preventDefault();
+
+ const url = $(event.target).attr("href");
+ contextmenu.spawn_context_menu(event.pageX, event.pageY, {
+ callback: () => {
+ const win = window.open(url, '_blank');
+ win.focus();
+ },
+ name: tr("Open URL"),
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-browse-addon-online"
+ }, {
+ callback: () => {
+ //TODO
+ },
+ name: tr("Open URL in Browser"),
+ type: contextmenu.MenuEntryType.ENTRY,
+ visible: !app.is_web() && false // Currently not possible
+ }, contextmenu.Entry.HR(), {
+ callback: () => copy_to_clipboard(url),
+ name: tr("Copy URL to clipboard"),
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-copy"
+ });
+ });
return [container.contents() as JQuery];
//return result.root_tag.content.map(e => e.build_html()).map((entry, idx, array) => $.spawn("a").css("display", (idx == 0 ? "inline" : "") + "block").html(entry == "" && idx != 0 ? " " : entry));
@@ -264,11 +292,24 @@ namespace MessageHelper {
return result.length > 0 ? result.substring(1) : default_value;
}
+ let _icon_size_style: JQuery;
+ export function set_icon_size(size: string) {
+ if(!_icon_size_style)
+ _icon_size_style = $.spawn("style").appendTo($("#style"));
+
+ _icon_size_style.text("\n" +
+ ".message > .emoji {\n" +
+ " height: " + size + "!important;\n" +
+ " width: " + size + "!important;\n" +
+ "}\n"
+ );
+ }
+
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
name: "XBBCode code tag init",
function: async () => {
/* override default parser */
- xbbcode.register.register_parser( {
+ xbbcode.register.register_parser({
tag: ["code", "icode", "i-code"],
content_tags_whitelist: [],
@@ -297,5 +338,13 @@ namespace MessageHelper {
});
},
priority: 10
- })
+ });
+
+ loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
+ name: "icon size init",
+ function: async () => {
+ MessageHelper.set_icon_size((settings.static_global(Settings.KEY_ICON_SIZE) / 100).toFixed(2) + "em");
+ },
+ priority: 10
+ });
}
\ No newline at end of file
diff --git a/shared/js/ui/frames/chat_frame.ts b/shared/js/ui/frames/chat_frame.ts
index 50bbe55b..744a93c6 100644
--- a/shared/js/ui/frames/chat_frame.ts
+++ b/shared/js/ui/frames/chat_frame.ts
@@ -1791,11 +1791,14 @@ test
private _container_new_message: JQuery;
private _container_no_permissions: JQuery;
- private _container_no_permissions_shown: boolean = false
+ private _container_no_permissions_shown: boolean = false;
private _container_is_private: JQuery;
private _container_is_private_shown: boolean = false;
+ private _container_no_support: JQuery;
+ private _container_no_support_shown: boolean = false;
+
private _view_max_messages = 40; /* reset to 40 again as soon we tab out :) */
private _view_older_messages: ViewEntry;
private _has_older_messages: boolean; /* undefined := not known | else flag */
@@ -1837,6 +1840,7 @@ test
this._container_new_message = this._html_tag.find(".new-message");
this._container_no_permissions = this._html_tag.find(".no-permissions").hide();
this._container_is_private = this._html_tag.find(".private-conversation").hide();
+ this._container_no_support = this._html_tag.find(".not-supported").hide();
this._container_messages = this._html_tag.find(".container-messages");
this._container_messages.on('scroll', event => {
@@ -1969,6 +1973,12 @@ test
} else if(error.id == ErrorID.CONVERSATION_IS_PRIVATE) {
this.set_flag_private(true);
}
+ /*
+ else if(error.id == ErrorID.NOT_IMPLEMENTED || error.id == ErrorID.COMMAND_NOT_FOUND) {
+ this._container_no_support.show();
+ this._container_no_support_shown = true;
+ }
+ */
}
//TODO log and handle!
log.error(LogCategory.CHAT, tr("Failed to fetch conversation history. %o"), error);
@@ -2124,7 +2134,7 @@ test
}
chat_available() : boolean {
- return !this._container_no_permissions_shown && !this._container_is_private_shown;
+ return !this._container_no_permissions_shown && !this._container_is_private_shown && !this._container_no_support_shown;
}
text_send_failed(error: CommandResult | any) {
diff --git a/shared/js/ui/modal/ModalConnect.ts b/shared/js/ui/modal/ModalConnect.ts
index 6ca9ac0f..46382f90 100644
--- a/shared/js/ui/modal/ModalConnect.ts
+++ b/shared/js/ui/modal/ModalConnect.ts
@@ -154,18 +154,16 @@ namespace Modals {
}
console.log("Updating");
- if(selected_profile)
- input_nickname.attr("placeholder", selected_profile.default_username);
- else
- input_nickname.attr("placeholder", "");
-
let address = input_address.val().toString();
settings.changeGlobal(Settings.KEY_CONNECT_ADDRESS, address);
let flag_address = !!address.match(Regex.IP_V4) || !!address.match(Regex.IP_V6) || !!address.match(Regex.DOMAIN);
let nickname = input_nickname.val().toString();
- settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, nickname);
- let flag_nickname = (nickname.length == 0 && selected_profile && selected_profile.default_username.length > 0) || nickname.length >= 3 && nickname.length <= 32;
+ if(nickname)
+ settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, nickname);
+ else
+ nickname = input_nickname.attr("placeholder") || "";
+ let flag_nickname = nickname.length >= 3 && nickname.length <= 32;
input_address.attr('pattern', flag_address ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_address);
input_nickname.attr('pattern', flag_nickname ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_nickname);
@@ -202,8 +200,10 @@ namespace Modals {
input_profile.on('change', event => {
selected_profile = profiles.find_profile(input_profile.val() as string);
{
- settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, selected_profile.default_username);
- input_nickname.val(selected_profile.default_username);
+ settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, undefined);
+ input_nickname
+ .attr('placeholder', selected_profile.connect_username() || "Another TeaSpeak user")
+ .val("");
}
input_profile.toggleClass("is-invalid", !selected_profile || !selected_profile.valid());
updateFields(true);
@@ -233,7 +233,7 @@ namespace Modals {
selected_profile,
true,
{
- nickname: input_nickname.val().toString() || selected_profile.default_username,
+ nickname: input_nickname.val().toString() || input_nickname.attr("placeholder"),
password: (current_connect_data && current_connect_data.password_hash) ? {password: current_connect_data.password_hash, hashed: true} : {password: input_password.val().toString(), hashed: false}
}
);
@@ -251,7 +251,7 @@ namespace Modals {
selected_profile,
true,
{
- nickname: input_nickname.val().toString() || selected_profile.default_username,
+ nickname: input_nickname.val().toString() || input_nickname.attr("placeholder"),
password: (current_connect_data && current_connect_data.password_hash) ? {password: current_connect_data.password_hash, hashed: true} : {password: input_password.val().toString(), hashed: false}
}
);
diff --git a/shared/js/ui/modal/ModalCreateChannel.ts b/shared/js/ui/modal/ModalCreateChannel.ts
index 51a6e721..5f7be3bc 100644
--- a/shared/js/ui/modal/ModalCreateChannel.ts
+++ b/shared/js/ui/modal/ModalCreateChannel.ts
@@ -227,13 +227,12 @@ namespace Modals {
else
type = "temp";
- console.log(type);
- console.log(Object.assign({}, properties));
simple.find("option[name='channel-type'][value='" + type + "']").prop("selected", true);
};
input_advanced_type.on('change', event => {
- switch(input_advanced_type.val()) {
+ const value = [...input_advanced_type as JQuery].find(e => e.checked).value;
+ switch(value) {
case "semi":
properties.channel_flag_permanent = false;
properties.channel_flag_semi_permanent = true;
@@ -264,20 +263,8 @@ namespace Modals {
const select_default = tag.find(".input-flag-default");
{
-
- if(!channel) {
- if(permission_perm)
- tag_type_perm.find("input").trigger('click');
- else if(permission_semi)
- tag_type_semi.find("input").trigger('click');
- else
- tag_type_temp.find("input").trigger('click');
- }
-
select_default.on('change', event => {
const node = select_default[0] as HTMLInputElement;
- console.log(node.checked);
-
properties.channel_flag_default = node.checked;
if(node.checked)
@@ -345,6 +332,25 @@ namespace Modals {
}
});
}
+
+ /* init */
+ setTimeout(() => {
+ if(!channel) {
+ if(permission_perm)
+ tag_type_perm.find("input").trigger('click');
+ else if(permission_semi)
+ tag_type_semi.find("input").trigger('click');
+ else
+ tag_type_temp.find("input").trigger('click');
+ } else {
+ if(channel.properties.channel_flag_permanent)
+ tag_type_perm.find("input").trigger('click');
+ else if(channel.properties.channel_flag_semi_permanent)
+ tag_type_semi.find("input").trigger('click');
+ else
+ tag_type_temp.find("input").trigger('click');
+ }
+ }, 0);
}
/* Talk power */
@@ -481,7 +487,8 @@ namespace Modals {
function applyPermissionListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, button: JQuery, permissions: PermissionManager, channel?: ChannelEntry) {
let apply_permissions = (channel_permissions: PermissionValue[]) => {
- console.log(tr("Got permissions: %o"), channel_permissions);
+ log.trace(LogCategory.CHANNEL, tr("Received channel permissions: %o"), channel_permissions);
+
let required_power = -2;
for(let cperm of channel_permissions)
if(cperm.type.name == PermissionType.I_CHANNEL_NEEDED_MODIFY_POWER) {
@@ -509,7 +516,7 @@ namespace Modals {
}
});
- const permission = permissions.neededPermission(PermissionType.I_CHANNEL_MODIFY_POWER).granted(required_power, false);
+ const permission = permissions.neededPermission(PermissionType.I_CHANNEL_PERMISSION_MODIFY_POWER).granted(required_power, false);
tag.find("input[permission]").prop("disabled", !permission).parent(".input-boxed").toggleClass("disabled", !permission); //No permissions
};
diff --git a/shared/js/ui/modal/ModalIconSelect.ts b/shared/js/ui/modal/ModalIconSelect.ts
index ed5e3bb5..ec6f9292 100644
--- a/shared/js/ui/modal/ModalIconSelect.ts
+++ b/shared/js/ui/modal/ModalIconSelect.ts
@@ -380,9 +380,6 @@ namespace Modals {
let upload_key: transfer.UploadKey;
try {
- await new Promise(resolve => setTimeout(resolve, 1000));
- throw "test error";;
-
upload_key = await client.fileManager.upload_file({
channel: undefined,
channel_password: undefined,
diff --git a/shared/js/ui/modal/ModalSettings.ts b/shared/js/ui/modal/ModalSettings.ts
index 218f0c14..7a130096 100644
--- a/shared/js/ui/modal/ModalSettings.ts
+++ b/shared/js/ui/modal/ModalSettings.ts
@@ -16,7 +16,6 @@ namespace Modals {
left.find(".selected").removeClass("selected");
const target = entry.attr("container");
- console.log(target);
if(!target) return;
right.find("> .container." + target).removeClass("hidden");
@@ -74,7 +73,7 @@ namespace Modals {
select.on('change', event => {
const value = parseInt(select.val() as string);
settings.changeGlobal(Settings.KEY_FONT_SIZE, value);
- console.log("Changed font size of %dpx", value);
+ console.log("Changed font size to %dpx", value);
$(document.body).css("font-size", value + "px");
});
@@ -349,6 +348,28 @@ namespace Modals {
settings.changeGlobal(Settings.KEY_CHAT_TAG_URLS, option[0].checked);
}).prop("checked", settings.static_global(Settings.KEY_CHAT_TAG_URLS));
}
+ /* Icon size */
+ {
+ const container_slider = container.find(".container-icon-size .container-slider");
+ const container_value = container.find(".container-icon-size .value");
+
+ sliderfy(container_slider, {
+ unit: '%',
+ min_value: 25,
+ max_value: 300,
+ step: 5,
+ initial_value: settings.static_global(Settings.KEY_ICON_SIZE),
+ value_field: container_value
+ });
+
+ container_slider.on('change', event => {
+ const value = parseInt(container_slider.attr("value") as string);
+ settings.changeGlobal(Settings.KEY_ICON_SIZE, value);
+ console.log("Changed icon size to %sem", (value / 100).toFixed(2));
+
+ MessageHelper.set_icon_size((value / 100).toFixed(2) + "em");
+ });
+ }
}
function settings_audio_microphone(container: JQuery, modal: Modal) {
@@ -942,7 +963,10 @@ namespace Modals {
select_type.parent().toggleClass("is-invalid", true);
} else {
input_name.val(selected_profile.identity.profile_name).prop("disabled", false);
- input_default_name.val(selected_profile.identity.default_username).prop("disabled", false);
+ input_default_name
+ .val(selected_profile.identity.default_username)
+ .attr("placeholder", selected_profile.identity.connect_username() || "Another TeaSpeak user")
+ .prop("disabled", false);
select_type.val(selected_profile.identity.selected_identity_type || "unset").prop("disabled", false);
}
diff --git a/shared/js/ui/modal/permission/HTMLPermissionEditor.ts b/shared/js/ui/modal/permission/HTMLPermissionEditor.ts
index 172ea243..52509f9a 100644
--- a/shared/js/ui/modal/permission/HTMLPermissionEditor.ts
+++ b/shared/js/ui/modal/permission/HTMLPermissionEditor.ts
@@ -205,7 +205,7 @@ namespace pe {
this._tag_granted_input.on('change', event => {
const str_value = this._tag_granted_input.val() as string;
const value = parseInt(str_value);
- if(!HTMLPermission.number_filter_re.test(str_value) || value == NaN) {
+ if(!HTMLPermission.number_filter_re.test(str_value) || Number.isNaN(value)) {
console.warn(tr("Failed to parse given permission granted value string: %s"), this._tag_granted_input.val());
this._reset_value();
return;
@@ -369,6 +369,8 @@ namespace pe {
return (this._mask & 0x03) > 0;
}
+ get_value() { return this._value; }
+
value(value: number | undefined, skip?: boolean, negate?: boolean) {
if(typeof value === "undefined") {
this._tag_value.detach();
@@ -672,6 +674,25 @@ namespace pe {
}
}
+ private update_icon() {
+ const permission = this.permission_map.find(e => e && e.permission.name === "i_icon_id");
+ const icon_id = permission ? permission.get_value() : 0;
+
+ const icon_node = this.container.find(".container-icon-select .icon-preview");
+ icon_node.children().remove();
+
+ let resolve: Promise>;
+ if(icon_id >= 0 && icon_id <= 1000)
+ resolve = Promise.resolve(IconManager.generate_tag({id: icon_id, url: ""}));
+ else
+ resolve = this.icon_resolver(permission ? permission.get_value() : 0).then(e => $(e));
+
+ resolve.then(tag => tag.appendTo(icon_node))
+ .catch(error => {
+ log.error(LogCategory.PERMISSIONS, tr("Failed to generate empty icon preview: %o"), error);
+ });
+ }
+
private build_tag() {
this.container = $("#tmpl_permission_editor_html").renderTag();
this.container.find("input").on('change', event => {
@@ -765,6 +786,51 @@ namespace pe {
build_group(undefined, group, 0);
}
+ {
+ const container = this.container.find(".container-icon-select");
+ container.find(".button-select-icon").on('click', event => {
+ const permission = this.permission_map.find(e => e && e.permission.name === "i_icon_id");
+ this.icon_selector(permission ? permission.get_value() : 0).then(id => {
+ const permission = this.permission_map.find(e => e && e.permission.name === "i_icon_id");
+ if(permission) {
+ this.trigger_change(permission.permission, {
+ remove: false,
+ value: id,
+ flag_skip: false,
+ flag_negate: false
+ }, false).then(() => {
+ log.debug(LogCategory.PERMISSIONS, tr("Selected new icon %s"), id);
+
+ permission.value(id, false, false);
+ this.update_icon();
+ }).catch(error => {
+ log.warn(LogCategory.PERMISSIONS, tr("Failed to set icon permission within permission editor: %o"), error);
+ });
+ } else {
+ log.warn(LogCategory.PERMISSIONS, tr("Failed to find icon permissions within permission editor"));
+ }
+ }).catch(error => {
+ log.error(LogCategory.PERMISSIONS, tr("Failed to select an icon for the icon permission: %o"), error);
+ });
+ });
+
+ container.find(".button-icon-remove").on('click', event => {
+ const permission = this.permission_map.find(e => e && e.permission.name === "i_icon_id");
+ if(permission) {
+ this.trigger_change(permission.permission, {
+ remove: true,
+ }, false).then(() => {
+ permission.value(undefined);
+ this.update_icon();
+ }).catch(error => {
+ log.warn(LogCategory.PERMISSIONS, tr("Failed to remove icon permission within permission editor: %o"), error);
+ });
+ } else {
+ log.warn(LogCategory.PERMISSIONS, tr("Failed to find icon permission within permission editor"));
+ }
+ });
+ }
+
this.mode_container_permissions.on('contextmenu', event => {
if(event.isDefaultPrevented())
return;
@@ -812,6 +878,7 @@ namespace pe {
permission_handle.granted(new_permission.granted_value);
}
+ this.update_icon();
this.update_filter();
}
@@ -821,9 +888,17 @@ namespace pe {
this.mode_container_unset.css('display', mode == Modals.PermissionEditorMode.UNSET ? 'block' : 'none');
}
- trigger_change(permission: PermissionInfo, value?: Modals.PermissionEditor.PermissionValue) : Promise {
- if(this._listener_change)
- return this._listener_change(permission, value);
+ trigger_change(permission: PermissionInfo, value?: Modals.PermissionEditor.PermissionValue, update_icon?: boolean) : Promise {
+ if(this._listener_change) {
+ if((typeof(update_icon) !== "boolean" || update_icon) && permission && permission.name === "i_icon_id")
+ return this._listener_change(permission, value).then(e => {
+ setTimeout(() => this.update_icon(), 0); /* we need to fully handle the response and then only we're able to update the icon */
+ return e;
+ });
+ else
+ return this._listener_change(permission, value);
+ }
+
return Promise.reject();
}
diff --git a/todo.txt b/todo.txt
index 3f40b978..f95ec28a 100644
--- a/todo.txt
+++ b/todo.txt
@@ -118,4 +118,6 @@ Fix these icons: https://img.did.science/Screenshot_20-11-06.png
- Crash
- Focus crash window on crash
- Add a notification (Like the browser notifications)
-Connection state sometimes does not update
\ No newline at end of file
+Connection state sometimes does not update
+The teaforum account does not show the premium status
+Allow channel chatting in the current channel
\ No newline at end of file