Added forum account UI and fixed firefox not working issue

canary
WolverinDEV 2019-01-28 20:36:11 +01:00
parent aa2ab6fbd6
commit 8080d426e7
16 changed files with 780 additions and 94 deletions

View File

@ -3,6 +3,10 @@
- Made sounds configurable
- Added option to mute sounds when output is muted
- Added push to talk delay option
- Have improvements related to identity management
- First load of the app generates identity
- Default VAD is not longer PPT, changed to Voice activity detection via threshold
- Added identity improve gui
* **26.01.19**
- Improved TeaSpeak identities (now generates automatic and are saveable)

View File

@ -243,10 +243,11 @@
}
if(!$_INCLIDE_ONLY) {
$app = getXF();
if(!$app) return;
if (isset($_GET["type"])) {
error_log("Got authX request!");
var_dump($_GET);
var_dump($_POST);
if ($_GET["type"] == "login") {
die(json_encode(checkLogin($_POST["user"], $_POST["pass"])));
} else if ($_GET["type"] == "logout") {
@ -256,8 +257,12 @@
header("Location: login.php");
else
header("Location: https://web.teaspeak.de/");
$session = $app->session();
setcookie($session->getCookieName(), '', time() - 3600, '/');
setcookie("session", '', time() - 3600, '/');
setcookie("user_data", '', time() - 3600, '/');
setcookie("user_sign", '', time() - 3600, '/');
} else die("unknown type!");
} else if(isset($_POST["action"])) {
error_log("Got auth post request!");

View File

@ -1,5 +1,6 @@
.help-tip-container {
/* position: relative; */
display: inline;
.help-tip {
position: absolute;

View File

@ -24,5 +24,7 @@
display: inline-flex;
flex-direction: row;
}
color: red;
}
}

View File

@ -430,6 +430,82 @@
}
}
.modal .settings-general {
padding: 5px;
.not-connected {
/* display: none; */
}
.connected {
display: flex;
flex-direction: column;
justify-content: stretch;
.connected-info {
color: green;
}
.property {
display: flex;
flex-direction: row;
justify-content: stretch;
.key {
flex-grow: 0;
flex-shrink: 0;
width: 140px;
}
&.premium {
.premium {
color: green;
}
.non-premium {
color: red;
}
}
}
.container-info-action {
display: flex;
flex-direction: row;
justify-content: stretch;
.divider {
flex-grow: 0;
flex-shrink: 0;
width: 2px;
background: lightgray;
}
.container-info, .container-actions {
flex-grow: 1;
flex-shrink: 1;
width: 50%;
display: flex;
flex-direction: column;
justify-content: stretch;
}
.container-actions {
display: flex;
flex-direction: column;
justify-content: center;
button {
width: 150px;
align-self: center;
}
}
}
}
}
.modal .settings-profiles {
margin: 5px;
> div:not(:first-of-type) {
@ -565,5 +641,158 @@
}
*/
}
&.identity-settings-teamspeak {
.property {
&:not(:first-of-type) {
margin-top: 5px;
}
display: flex;
flex-direction: row;
justify-content: stretch;
.key {
width: 200px;
}
.value {
flex-grow: 1;
flex-shrink: 1;
input {
width: 100%;
}
}
&.level {
.value {
display: flex;
flex-direction: row;
justify-content: stretch;
button {
margin-left: 5px;
}
}
}
}
.identity-undefined {
text-align: center;
}
.manage {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-top: 5px;
}
}
}
}
.container-teamspeak-import {
.error {
color: red;
/* margin-bottom: 5px; */
}
.success {
color: green;
}
.input-file {
display: none;
}
.load-data {
display: flex;
flex-direction: column;
justify-content: stretch;
.buttons {
flex-grow: 1;
flex-shrink: 1;
display: flex;
flex-direction: row;
justify-content: end;
button:not(:first-of-type) {
margin-left: 20px;
}
}
}
.footer {
margin-top: 5px;
text-align: right;
margin-bottom: 5px;
.button-import {
width: 100px;
}
}
}
.container-teamspeak-improve {
display: flex;
flex-direction: column;
.property {
&:not(:first-of-type) {
margin-top: 5px;
}
display: flex;
flex-direction: row;
justify-content: stretch;
.key {
display: flex;
flex-direction: row;
flex-grow: 0;
flex-shrink: 0;
width: 120px;
.help-tagged {
width: 80px;
}
}
.value {
flex-grow: 1;
flex-shrink: 2;
input {
width: 100%;
}
}
}
.row {
display: flex;
flex-direction: row;
justify-content: space-between;
.property {
margin-top: 0px;
}
}
.buttons {
margin-top: 5px;
margin-bottom: 5px;
display: flex;
flex-direction: row;
justify-content: space-between;
button {
width: 100px;
}
}
.help-tip-container {
margin-left: 5px;
}
}

View File

@ -159,11 +159,14 @@
</div>
<div class="profile-select-container">
<div style="text-align: right;">{{tr "Connection profile:" /}}</div>
<div class="profile-container">
<select class="profile-select"> </select>
<button class="button-manage-profiles">{{tr "Manage profiles" /}}</button>
</div>
</div>
<div class="profile-invalid">
<hr>
<div>The selected connect profile is invalid</div>
<div>{{tr "The selected connect profile is invalid" /}}</div>
<div><div>Click&nbsp;</div><a href="#" class="button-manage-profiles">here</a><div>&nbsp;to manage your connect profiles</div></div>
</div>
</div>
@ -608,7 +611,44 @@
<x-tab>
<x-entry>
<x-tag>{{tr "General"/}}</x-tag>
<x-content>{{tr "Didnt setuped yet!"/}}</x-content>
<x-content>
<div class="settings-general">
<div class="group_box">
<div class="header">{{tr "TeaSpeak Forum Connection" /}}</div>
<div class="content settings-teaspeak-forum">
<div class="not-connected">
<div>{{tr "You're currently not connected with your TeaSpeak Forum account" /}}</div>
<button class="button-login">{{tr "login" /}}</button>
</div>
<div class="connected">
<div class="connected-info">{{tr "You're connected via TeaSpeak forum" /}}</div>
<div class="container-info-action">
<div class="container-info">
<div class="property username">
<div class="key">{{tr "Username:" /}}</div>
<div class="value">WolverinDEV</div>
</div>
<div class="property premium">
<div class="key">{{tr "Premium:" /}}</div>
<div class="value">YES</div>
</div>
</div>
<div class="divider"></div>
<div class="container-actions">
<button class="button-logout">logout</button>
</div>
</div>
<!--
<div class="property synchronized">
<div class="key">{{tr "Synchronized:" /}}</div>
<div class="value">NO <button class="button-enable-disable-sync">{{tr "enable synchronization" /}}</button></div>
</div>
-->
</div>
</div>
</div>
</div>
</x-content>
</x-entry>
<x-entry>
<x-tag>{{tr "Audio" /}}</x-tag>
@ -845,20 +885,49 @@
</div>
<hr>
<div class="identity-settings identity-settings-teamspeak">
<!--
<div class="import">
{{tr "Please enter your exported TS3 Identity string bellow or select your exported Identity"/}}<br>
<div style="width: 100%; display: flex; justify-content: stretch; flex-direction: row">
<input placeholder="Identity string" style="min-width: 60%; flex-shrink: 1; flex-grow: 2; margin: 5px;" class="identity_string">
<div style="max-width: 200px; flex-grow: 1; flex-shrink: 4; margin: 5px"><input style="display: flex; width: 100%;" class="identity_file" type="file"></div>
</div>
</div>
-->
<div class="identity-info" style="display: none">
<div class="property unique-id">
<div class="key">{{tr "UniqueID:" /}}</div>
<div class="value">
<input type="text" value="xxjnc14LmvTk+Lyrm8OOeo4tOqw=" readonly/>
</div>
</div>
<div class="property level">
<div class="key">{{tr "Level:" /}}</div>
<div class="value">
<input type="text" value="39" readonly/> <button class="button-improve">{{tr "Improve" /}}</button>
</div>
</div>
</div>
<div class="identity-undefined">
<div>{{tr "You have'nt generated/imported an identity.<br>Generate a new one or import one." /}}</div>
</div>
<div class="manage">
<button class="button-generate">{{tr "Generate new" /}}</button>
<div class="export-import">
<button class="button-import">{{tr "Import identity" /}}</button>
<button class="button-export">{{tr "Export identity" /}}</button>
</div>
</div>
</div>
<div class="identity-settings identity-settings-teaforo">
<div class="connected">
{{tr "You're using your forum account as verification"/}}
</div>
<div class="disconnected">
<!-- TODO tr -->
You cant use your TeaSpeak forum account. You're not connected!<br>
Click {{if !client}}<a href="{{:forum_path}}login.php">here</a>{{else}}<a href="#" class="native-teaforo-login">here</a>{{/if}} to login via the TeaSpeak forum.
<!-- <a href="#" class="native-teaforo-login">here</a> -->
You cant use your TeaSpeak forum account. You're not connected with your forum Account!<br>
Setup your connection <a class="forum-setup" href="#">here</a>.
</div>
</div>
<div class="identity-settings identity-settings-nickname">
@ -889,6 +958,80 @@
</div>
</script>
<script class="jsrender-template" id="tmpl_settings-teamspeak_import" type="text/html">
<div class="container-teamspeak-import">
<div class="error">{{tr "Failed to import identity (my little error)" /}}</div>
<div class="load-data">
<div>{{tr "Import an identity from TeamSpeak or an exported identity" /}}</div>
<div class="buttons">
<button class="button-load-text">{{tr "Load from text" /}}</button>
<button class="button-load-file">{{tr "Load from file" /}}</button>
<input class="input-file" type="file">
</div>
</div>
<div class="success">
{{tr "Identity successfully loaded. Ready to import" /}}
</div>
<div class="footer">
<button class="button-import">{{tr "Import" /}}</button>
</div>
</div>
</script>
<script class="jsrender-template" id="tmpl_settings-teamspeak_improve" type="text/html">
<div class="container-teamspeak-improve">
<div class="property identity-unique-id">
<div class="key">{{tr "Identity:" /}}</div>
<div class="value"><input value="" readonly></div>
</div>
<div class="property identity-level">
<div class="key">{{tr "Current level: " /}}</div>
<div class="value"><input value="loading" readonly></div>
</div>
<div class="property identity-target-level">
<div class="key">
<div class="help-tagged">{{tr "Target level: " /}}</div>
<div class="help-tip-container"> <!-- lets be absolute -->
<div class="help-tip tip-right tip-small">
<p>
{{tr "A value of zero means unlimited. Improve until you abort"/}}
</p>
</div>
</div>
</div>
<div class="value"><input value="0" type="number" min="0" max="160"></div>
</div>
<div class="property threads">
<div class="key">
<div class="help-tagged">{{tr "Threads: " /}}</div>
<div class="help-tip-container"> <!-- lets be absolute -->
<div class="help-tip tip-right tip-small">
<p>
{{tr "The number of threads used to improve your identity<br>The optimal value is equal to the amount of kernal threads."/}}
</p>
</div>
</div>
</div>
<div class="value"><input value="2" type="number" min="1" max="32"></div>
</div>
<hr>
<div class="row">
<div class="property hash-rate">
<div class="key">{{tr "Hashes/second:" /}}</div>
<div class="value"><input value="0" readonly></div>
</div>
<div class="property time-elapsed">
<div class="key">{{tr "Elapsed time:" /}}</div>
<div class="value"><input value="00:00" readonly></div>
</div>
</div>
<div class="buttons">
<button class="button-close">{{tr "Close" /}}</button>
<button class="button-start-stop">{{tr "Start" /}}</button>
</div>
</div>
</script>
<script class="jsrender-template" id="settings-profile-list-entry" type="text/html">
<div class="entry profile {{if id == 'default'}}default{{/if}}">
<div class="name">{{>profile_name}}</div>

View File

@ -126,12 +126,30 @@ namespace profiles {
}
}
if(!find_profile("default")) { //Create a default profile
if(!find_profile("default")) { //Create a default profile and teaforo profile
{
const profile = create_new_profile("default","default");
profile.default_password = "";
profile.default_username = "Another TeaSpeak user";
profile.profile_name = "Default Profile";
/* generate default identity */
try {
const identity = await identities.TeaSpeakIdentity.generate_new();
profile.set_identity(identities.IdentitifyType.TEAMSPEAK, identity);
profile.selected_identity_type = identities.IdentitifyType[identities.IdentitifyType.TEAMSPEAK];
} catch(error) {
createErrorModal(tr("Failed to generate default identity"), tr("Failed to generate default identity!<br>Please manually generate the identity within your settings => profiles")).open();
}
}
{ /* 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.profile_name = "TeaSpeak Forum profile";
}
save();
}
}

View File

@ -40,43 +40,67 @@ namespace profiles.identities {
}
export class TeaForumIdentity implements Identity {
private identityData: string;
private identityDataJson: string;
private identitySign: string;
private identity_data: string;
private identity_data_raw: string;
private identity_data_sign: string;
valid() : boolean {
return this.identityDataJson.length > 0 && this.identityDataJson.length > 0 && this.identitySign.length > 0;
return this.identity_data_raw.length > 0 && this.identity_data_raw.length > 0 && this.identity_data_sign.length > 0;
}
constructor(data: string, sign: string) {
this.identityDataJson = data;
this.identityData = data ? JSON.parse(this.identityDataJson) : undefined;
this.identitySign = sign;
this.identity_data_raw = data;
this.identity_data_sign = sign;
try {
this.identity_data = data ? JSON.parse(this.identity_data_raw) : undefined;
} catch(error) { }
}
data_json() : string { return this.identityDataJson; }
data_sign() : string { return this.identitySign; }
data_json() : string { return this.identity_data_raw; }
data_sign() : string { return this.identity_data_sign; }
name() : string { return this.identityData["user_name"]; }
uid() : string { return "TeaForo#" + this.identityData["user_id"]; }
name() : string { return this.identity_data["user_name"]; }
uid() : string { return "TeaForo#" + this.identity_data["user_id"]; }
type() : IdentitifyType { return IdentitifyType.TEAFORO; }
forum_user_id() { return this.identity_data["user_id"]; }
forum_user_group() { return this.identity_data["user_group_id"]; }
is_stuff() : boolean { return this.identity_data["is_staff"]; }
is_premium() : boolean { return (<number[]>this.identity_data["user_groups"]).indexOf(5) != -1; }
data_age() : Date { return new Date(this.identity_data["data_age"]); }
/*
$user_data["user_id"] = $user->user_id;
$user_data["user_name"] = $user->username;
$user_data["user_group"] = $user->user_group_id;
$user_data["user_groups"] = $user->secondary_group_ids;
$user_data["trophy_points"] = $user->trophy_points;
$user_data["register_date"] = $user->register_date;
$user_data["is_staff"] = $user->is_staff;
$user_data["is_admin"] = $user->is_admin;
$user_data["is_super_admin"] = $user->is_super_admin;
$user_data["is_banned"] = $user->is_banned;
$user_data["data_age"] = milliseconds();
*/
decode(data) : Promise<void> {
data = JSON.parse(data);
if(data.version !== 1)
throw "invalid version";
this.identityDataJson = data["identity_data"];
this.identitySign = data["identity_sign"];
this.identityData = JSON.parse(this.identityData);
this.identity_data_raw = data["identity_data"];
this.identity_data_sign = data["identity_sign"];
this.identity_data = JSON.parse(this.identity_data);
return;
}
encode?() : string {
return JSON.stringify({
version: 1,
identity_data: this.identityDataJson,
identity_sign: this.identitySign
identity_data: this.identity_data_raw,
identity_sign: this.identity_data_sign
});
}

View File

@ -580,7 +580,7 @@ namespace profiles.identities {
return await this.improve_level(-1, threads, () => active);
}
async improve_level(target: number, threads: number, active_callback: () => boolean) : Promise<Boolean> {
async improve_level(target: number, threads: number, active_callback: () => boolean, callback_level?: (current: number) => any) : Promise<Boolean> {
if(!this._initialized || !this.public_key)
throw "not initialized";
if(target == -1) /* get the highest level possible */
@ -652,6 +652,8 @@ namespace profiles.identities {
console.log("Found new best at %s (%d). Old was %d", this.hash_number, worker.current_level(), best_level);
best_level = worker.current_level();
if(callback_level)
callback_level(best_level);
}
if(active) {

View File

@ -192,7 +192,7 @@ namespace sound {
register_sound("message.received", "effects/message_received.wav");
register_sound("message.send", "effects/message_send.wav");
reinitialisize_audio();
audio.player.on_ready(reinitialisize_audio);
return new Promise<void>(resolve => {
$.ajax({
url: "audio/speech/mapping.json",

View File

@ -89,6 +89,7 @@ if(!$.fn.dividerfy) {
apply_view(config.property, config.previous, config.next);
}
} catch(e) {
if(!(e instanceof SyntaxError))
console.error(e);
}
}

View File

@ -500,6 +500,17 @@ class MusicInfoManager extends ClientInfoManager {
});
});
_frame.find(".btn-settings").click(() => {
this.handle.handle.serverConnection.helper.request_playlist_list().then(lists => {
for(const entry of lists) {
if(entry.playlist_id == bot.properties.client_playlist_id) {
Modals.spawnPlaylistEdit(bot.channelTree.client, entry);
return;
}
}
createErrorModal(tr("Invalid permissions"), tr("You don't have to see the bots playlist.")).open();
}).catch(error => {
createErrorModal(tr("Failed to query playlist."), tr("Failed to query playlist info.")).open();
});
createErrorModal(tr("Not implemented"), tr("This function is not implemented yet!")).open();
});
}

View File

@ -9,8 +9,196 @@ namespace Modals {
import ConnectionProfile = profiles.ConnectionProfile;
import IdentitifyType = profiles.identities.IdentitifyType;
function spawnTeamSpeakIdentityImprove(identity: profiles.identities.TeaSpeakIdentity) : Modal {
let modal: Modal;
let elapsed_timer: NodeJS.Timer;
modal = createModal({
header: tr("Improve identity"),
body: () => {
let template = $("#tmpl_settings-teamspeak_improve").renderTag();
template = $.spawn("div").append(template);
let active;
const button_start_stop = template.find(".button-start-stop");
const button_close = template.find(".button-close");
const input_current_level = template.find(".property.identity-level input");
const input_target_level = template.find(".property.identity-target-level input");
const input_threads = template.find(".property.threads input");
const input_hash_rate = template.find(".property.hash-rate input");
const input_elapsed = template.find(".property.time-elapsed input");
button_close.on('click', event => {
if(active)
button_start_stop.trigger('click');
if(modal.shown)
modal.close();
});
button_start_stop.on('click', event => {
if(active)
button_start_stop.text(tr("Start"));
else
button_start_stop.text(tr("Stop"));
input_threads.prop("disabled", !active);
input_target_level.prop("disabled", !active);
if(active) {
input_hash_rate.val(0);
clearInterval(elapsed_timer);
active = false;
return;
}
active = true;
input_hash_rate.val("nan");
const threads = parseInt(input_threads.val() as string);
const target_level = parseInt(input_target_level.val() as string);
if(target_level == 0) {
identity.improve_level(-1, threads, () => active, current_level => {
input_current_level.val(current_level);
}).catch(error => {
console.error(error);
createErrorModal(tr("Failed to improve identity"), tr("Failed to improve identity.<br>Error:") + error).open();
if(active)
button_start_stop.trigger('click');
});
} else {
identity.improve_level(target_level, threads, () => active, current_level => {
input_current_level.val(current_level);
}).then(success => {
if(success) {
identity.level().then(level => {
input_current_level.val(level);
createInfoModal(tr("Identity successfully improved"), MessageHelper.formatMessage(tr("Identity successfully improved to level {}"), level)).open();
}).catch(error => {
input_current_level.val("error: " + error);
});
}
if(active)
button_start_stop.trigger('click');
}).catch(error => {
console.error(error);
createErrorModal(tr("Failed to improve identity"), tr("Failed to improve identity.<br>Error:") + error).open();
if(active)
button_start_stop.trigger('click');
});
}
const begin = Date.now();
elapsed_timer = setInterval(() => {
const time = (Date.now() - begin) / 1000;
let seconds = Math.floor(time % 60).toString();
let minutes = Math.floor(time / 60).toString();
if(seconds.length < 2)
seconds = "0" + seconds;
if(minutes.length < 2)
minutes = "0" + minutes;
input_elapsed.val(minutes + ":" + seconds);
}, 1000);
});
template.find(".property.identity-unique-id input").val(identity.uid());
identity.level().then(level => {
input_current_level.val(level);
}).catch(error => {
input_current_level.val("error: " + error);
});
return template;
},
footer: undefined,
width: 750
});
modal.close_listener.push(() => modal.htmlTag.find(".button-close").trigger('click'));
modal.open();
return modal;
}
function spawnTeamSpeakIdentityImport(callback: (identity: profiles.identities.TeaSpeakIdentity) => any) : Modal {
let modal: Modal;
let loaded_identity: profiles.identities.TeaSpeakIdentity;
modal = createModal({
header: tr("Import identity"),
body: () => {
let template = $("#tmpl_settings-teamspeak_import").renderTag();
template = $.spawn("div").append(template);
template.find(".button-load-file").on('click', event => template.find(".input-file").trigger('click'));
const button_import = template.find(".button-import");
const set_error = message => {
template.find(".success").hide();
if(message) {
template.find(".error").text(message).show();
button_import.prop("disabled", true);
} else
template.find(".error").hide();
};
const import_identity = (data: string, ini: boolean) => {
profiles.identities.TeaSpeakIdentity.import_ts(data, ini).then(identity => {
loaded_identity = identity;
set_error("");
button_import.prop("disabled", false);
template.find(".success").show();
}).catch(error => {
set_error("Failed to load identity: " + error);
});
};
{ /* file select button */
template.find(".input-file").on('change', event => {
const element = event.target as HTMLInputElement;
const file_reader = new FileReader();
file_reader.onload = function() {
import_identity(file_reader.result as string, true);
};
file_reader.onerror = ev => {
console.error(tr("Failed to read give identity file: %o"), ev);
set_error(tr("Failed to read file!"));
return;
};
if(element.files && element.files.length > 0)
file_reader.readAsText(element.files[0]);
});
}
{ /* text input */
template.find(".button-load-text").on('click', event => {
createInputModal("Import identity from text", "Please paste your idenity bellow<br>", text => text.length > 0 && text.indexOf('V') != -1, result => {
if(result)
import_identity(result as string, false);
}).open();
});
}
button_import.on('click', event => {
modal.close();
callback(loaded_identity);
});
set_error("");
button_import.prop("disabled", true);
return template;
},
footer: undefined,
width: 750
});
modal.open();
return modal;
}
export function spawnSettingsModal() : Modal {
let modal;
let modal: Modal;
modal = createModal({
header: tr("Settings"),
body: () => {
@ -21,9 +209,10 @@ namespace Modals {
});
template = $.spawn("div").append(template);
initialiseSettingListeners(modal,template = template.tabify());
initialiseVoiceListeners(modal, (template = template.tabify()).find(".settings_audio"));
initialise_translations(template.find(".settings-translations"));
initialise_profiles(modal, template.find(".settings-profiles"));
initialise_global(modal, template.find(".settings-general"));
return template;
},
@ -47,13 +236,33 @@ namespace Modals {
return modal;
}
function initialiseSettingListeners(modal: Modal, tag: JQuery) {
//Voice
initialiseVoiceListeners(modal, tag.find(".settings_audio"));
function initialise_global(modal: Modal, tag: JQuery) {
console.log(tag);
{/* setup the forum */
const identity = profiles.identities.static_forum_identity();
if(identity && identity.valid()) {
tag.find(".not-connected").hide();
tag.find(".property.username .value").text(identity.name());
const premium_tag = tag.find(".property.premium .value").text("");
if(identity.is_stuff() || identity.is_premium())
premium_tag.append($.spawn("div").addClass("premium").text(tr("yes")));
else
premium_tag.append($.spawn("div").addClass("non-premium").text(tr("no")));
} else {
tag.find(".connected").hide();
}
tag.find(".button-logout").on('click', event => {
window.location.href = settings.static("forum_path") + "auth.php?type=logout";
});
tag.find(".button-login").on('click', event => {
window.location.href = settings.static("forum_path") + "login.php";
});
}
}
function initialiseVoiceListeners(modal: Modal, tag: JQuery) {
let currentVAD = settings.global("vad_type", "ppt");
let currentVAD = settings.global("vad_type", "vad");
{ //Initialized voice activation detection
const vad_tag = tag.find(".settings-vad-container");
@ -658,63 +867,97 @@ namespace Modals {
{
{ //TeamSpeak change listener
const teamspeak_tag = settings_tag.find(".identity-settings-teamspeak");
teamspeak_tag.find(".identity_file").on('change', event => {
if(!selected_profile) return;
const identity_info_tag = teamspeak_tag.find(".identity-info");
const button_export = teamspeak_tag.find(".button-export");
const button_import = teamspeak_tag.find(".button-import");
const button_generate = teamspeak_tag.find(".button-generate");
const button_improve = teamspeak_tag.find(".button-improve");
const element = event.target as HTMLInputElement;
const file_reader = new FileReader();
file_reader.onload = function() {
const identity_promise = profiles.identities.TeaSpeakIdentity.import_ts(file_reader.result as string, true);
identity_promise.then(identity => {
(identity as profiles.identities.TeaSpeakIdentity).export_ts().then(e => teamspeak_tag.find(".identity_string").val(e));
selected_profile.set_identity(IdentitifyType.TEAMSPEAK, identity as any);
profiles.mark_need_save();
display_error(undefined);
}).catch(error => {
display_error(tr("Failed to parse identity.<br>Reason: ") + error);
return;
});
button_import.on('click', event => {
const profile = selected_profile.selected_identity(IdentitifyType.TEAMSPEAK) as profiles.identities.TeaSpeakIdentity;
const set_identity = (identity: profiles.identities.TeaSpeakIdentity) => {
selected_profile.set_identity(IdentitifyType.TEAMSPEAK, identity);
teamspeak_tag.trigger('show');
createInfoModal(tr("Identity imported"), tr("Your identity has been successfully imported!")).open();
};
file_reader.onerror = ev => {
console.error(tr("Failed to read give identity file: %o"), ev);
display_error(tr("Failed to read file!"));
return;
};
if(element.files && element.files.length > 0)
file_reader.readAsText(element.files[0]);
if(profile && profile.valid()) {
spawnYesNo(tr("Are you sure"), tr("Do you really want to import a new identity and override the old identity?"), result => {
if(result)
spawnTeamSpeakIdentityImport(set_identity);
});
} else
spawnTeamSpeakIdentityImport(set_identity);
});
button_export.on('click', event => {
const profile = selected_profile.selected_identity(IdentitifyType.TEAMSPEAK) as profiles.identities.TeaSpeakIdentity;
if(!profile) return;
teamspeak_tag.find(".identity_string").on('change', event => {
if(!selected_profile) return;
const element = event.target as HTMLInputElement;
if(element.value.length == 0) {
display_error("Please provide an identity");
selected_profile.set_identity(IdentitifyType.TEAMSPEAK, undefined as any);
profiles.mark_need_save();
} else {
const identity_promise = profiles.identities.TeaSpeakIdentity.import_ts(element.value, false);
identity_promise.then(identity => {
(identity as profiles.identities.TeaSpeakIdentity).export_ts().then(e => teamspeak_tag.find(".identity_string").val(e));
selected_profile.set_identity(IdentitifyType.TEAMSPEAK, identity as any);
profiles.mark_need_save();
display_error(undefined);
createInputModal(tr("File name"), tr("Please enter the file name"), text => !!text, name => {
if(name) {
profile.export_ts(true).then(data => {
const element = $.spawn("a")
.text("donwload")
.attr("href", "data:test/plain;charset=utf-8," + encodeURIComponent(data))
.attr("download", name + ".ini")
.css("display", "none")
.appendTo($("body"));
element[0].click();
element.detach();
}).catch(error => {
display_error(tr("Failed to parse identity.<br>Reason: ") + error);
return;
console.error(error);
createErrorModal(tr("Failed to export identity"), tr("Failed to export and save identity.<br>Error: ") + error).open();
});
}
}).open();
});
button_generate.on('click', event => {
const profile = selected_profile.selected_identity(IdentitifyType.TEAMSPEAK) as profiles.identities.TeaSpeakIdentity;
const generate_identity = () => {
profiles.identities.TeaSpeakIdentity.generate_new().then(identity => {
selected_profile.set_identity(IdentitifyType.TEAMSPEAK, identity);
teamspeak_tag.trigger('show');
createInfoModal(tr("Identity generate"), tr("A new identity had been successfully generated")).open();
}).catch(error => {
createErrorModal(tr("Failed to generate identity"), tr("Failed to generate a new identity.<br>Error:") + error).open();
});
};
if(profile && profile.valid()) {
spawnYesNo(tr("Are you sure"), tr("Do you really want to generate a new identity and override the old identity?"), result => {
if(result)
generate_identity();
});
} else
generate_identity();
});
button_improve.on('click', event => {
const profile = selected_profile.selected_identity(IdentitifyType.TEAMSPEAK) as profiles.identities.TeaSpeakIdentity;
if(!profile) return;
spawnTeamSpeakIdentityImprove(profile).close_listener.push(() => teamspeak_tag.trigger('show'));
});
/* updates the data */
teamspeak_tag.on('show', event => {
const profile = selected_profile.selected_identity(IdentitifyType.TEAMSPEAK);
if(!profile)
display_error("invalid profile");
else if(!profile.valid())
display_error("profile isn't valid");
else
const profile = selected_profile.selected_identity(IdentitifyType.TEAMSPEAK) as profiles.identities.TeaSpeakIdentity;
if(!profile || !profile.valid()) {
identity_info_tag.hide();
teamspeak_tag.find(".identity-undefined").show();
button_export.prop("disabled", true);
} else {
identity_info_tag.show();
teamspeak_tag.find(".identity-undefined").hide();
button_export.prop("disabled", false);
identity_info_tag.find(".property.unique-id input").val(profile.uid());
const input_level = identity_info_tag.find(".property.level input").val("loading...");
profile.level().then(level => input_level.val(level.toString())).catch(error => input_level.val("error: " + error));
}
display_error();
});
}

2
vendor/bbcode vendored

@ -1 +1 @@
Subproject commit 7fe6a479984b77160a9135f9d2182fb79ca56023
Subproject commit 0c9d48b2e1d37ddefa6bafb3e2d0c7d04ad4dac6

View File

@ -54,7 +54,10 @@ namespace audio.player {
}
export function destination() : AudioNode {
return context().destination;
const ctx = context();
if(!ctx) throw tr("Audio player isn't initialized yet!");
return ctx.destination;
}
export function on_ready(cb: () => any) {