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 - Made sounds configurable
- Added option to mute sounds when output is muted - Added option to mute sounds when output is muted
- Added push to talk delay option - 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** * **26.01.19**
- Improved TeaSpeak identities (now generates automatic and are saveable) - Improved TeaSpeak identities (now generates automatic and are saveable)

View File

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

View File

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

View File

@ -24,5 +24,7 @@
display: inline-flex; display: inline-flex;
flex-direction: row; 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 { .modal .settings-profiles {
margin: 5px; margin: 5px;
> div:not(:first-of-type) { > 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>
<div class="profile-select-container"> <div class="profile-select-container">
<div style="text-align: right;">{{tr "Connection profile:" /}}</div> <div style="text-align: right;">{{tr "Connection profile:" /}}</div>
<select class="profile-select"> </select> <div class="profile-container">
<select class="profile-select"> </select>
<button class="button-manage-profiles">{{tr "Manage profiles" /}}</button>
</div>
</div> </div>
<div class="profile-invalid"> <div class="profile-invalid">
<hr> <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>Click&nbsp;</div><a href="#" class="button-manage-profiles">here</a><div>&nbsp;to manage your connect profiles</div></div>
</div> </div>
</div> </div>
@ -608,7 +611,44 @@
<x-tab> <x-tab>
<x-entry> <x-entry>
<x-tag>{{tr "General"/}}</x-tag> <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-entry> <x-entry>
<x-tag>{{tr "Audio" /}}</x-tag> <x-tag>{{tr "Audio" /}}</x-tag>
@ -845,10 +885,38 @@
</div> </div>
<hr> <hr>
<div class="identity-settings identity-settings-teamspeak"> <div class="identity-settings identity-settings-teamspeak">
{{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"> <div class="import">
<input placeholder="Identity string" style="min-width: 60%; flex-shrink: 1; flex-grow: 2; margin: 5px;" class="identity_string"> {{tr "Please enter your exported TS3 Identity string bellow or select your exported Identity"/}}<br>
<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 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> </div>
<div class="identity-settings identity-settings-teaforo"> <div class="identity-settings identity-settings-teaforo">
@ -857,8 +925,9 @@
</div> </div>
<div class="disconnected"> <div class="disconnected">
<!-- TODO tr --> <!-- TODO tr -->
You cant use your TeaSpeak forum account. You're not connected!<br> <!-- <a href="#" class="native-teaforo-login">here</a> -->
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. 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> </div>
<div class="identity-settings identity-settings-nickname"> <div class="identity-settings identity-settings-nickname">
@ -889,6 +958,80 @@
</div> </div>
</script> </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"> <script class="jsrender-template" id="settings-profile-list-entry" type="text/html">
<div class="entry profile {{if id == 'default'}}default{{/if}}"> <div class="entry profile {{if id == 'default'}}default{{/if}}">
<div class="name">{{>profile_name}}</div> <div class="name">{{>profile_name}}</div>

View File

@ -216,7 +216,7 @@ function base64ArrayBuffer(arrayBuffer) {
return base64 return base64
} }
function Base64EncodeUrl(str) { function Base64EncodeUrl(str){
return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''); return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');
} }

View File

@ -126,11 +126,29 @@ 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 = ""; const profile = create_new_profile("default","default");
profile.default_username = "Another TeaSpeak user"; profile.default_password = "";
profile.profile_name = "Default Profile"; 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(); save();
} }

View File

@ -40,43 +40,67 @@ namespace profiles.identities {
} }
export class TeaForumIdentity implements Identity { export class TeaForumIdentity implements Identity {
private identityData: string; private identity_data: string;
private identityDataJson: string; private identity_data_raw: string;
private identitySign: string; private identity_data_sign: string;
valid() : boolean { 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) { constructor(data: string, sign: string) {
this.identityDataJson = data; this.identity_data_raw = data;
this.identityData = data ? JSON.parse(this.identityDataJson) : undefined; this.identity_data_sign = sign;
this.identitySign = sign; try {
this.identity_data = data ? JSON.parse(this.identity_data_raw) : undefined;
} catch(error) { }
} }
data_json() : string { return this.identityDataJson; } data_json() : string { return this.identity_data_raw; }
data_sign() : string { return this.identitySign; } data_sign() : string { return this.identity_data_sign; }
name() : string { return this.identityData["user_name"]; } name() : string { return this.identity_data["user_name"]; }
uid() : string { return "TeaForo#" + this.identityData["user_id"]; } uid() : string { return "TeaForo#" + this.identity_data["user_id"]; }
type() : IdentitifyType { return IdentitifyType.TEAFORO; } 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> { decode(data) : Promise<void> {
data = JSON.parse(data); data = JSON.parse(data);
if(data.version !== 1) if(data.version !== 1)
throw "invalid version"; throw "invalid version";
this.identityDataJson = data["identity_data"]; this.identity_data_raw = data["identity_data"];
this.identitySign = data["identity_sign"]; this.identity_data_sign = data["identity_sign"];
this.identityData = JSON.parse(this.identityData); this.identity_data = JSON.parse(this.identity_data);
return; return;
} }
encode?() : string { encode?() : string {
return JSON.stringify({ return JSON.stringify({
version: 1, version: 1,
identity_data: this.identityDataJson, identity_data: this.identity_data_raw,
identity_sign: this.identitySign identity_sign: this.identity_data_sign
}); });
} }

View File

@ -580,7 +580,7 @@ namespace profiles.identities {
return await this.improve_level(-1, threads, () => active); 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) if(!this._initialized || !this.public_key)
throw "not initialized"; throw "not initialized";
if(target == -1) /* get the highest level possible */ 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); console.log("Found new best at %s (%d). Old was %d", this.hash_number, worker.current_level(), best_level);
best_level = worker.current_level(); best_level = worker.current_level();
if(callback_level)
callback_level(best_level);
} }
if(active) { if(active) {

View File

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

View File

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

View File

@ -500,6 +500,17 @@ class MusicInfoManager extends ClientInfoManager {
}); });
}); });
_frame.find(".btn-settings").click(() => { _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(); 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 ConnectionProfile = profiles.ConnectionProfile;
import IdentitifyType = profiles.identities.IdentitifyType; import IdentitifyType = profiles.identities.IdentitifyType;
export function spawnSettingsModal() : Modal{ function spawnTeamSpeakIdentityImprove(identity: profiles.identities.TeaSpeakIdentity) : Modal {
let 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: Modal;
modal = createModal({ modal = createModal({
header: tr("Settings"), header: tr("Settings"),
body: () => { body: () => {
@ -21,9 +209,10 @@ namespace Modals {
}); });
template = $.spawn("div").append(template); 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_translations(template.find(".settings-translations"));
initialise_profiles(modal, template.find(".settings-profiles")); initialise_profiles(modal, template.find(".settings-profiles"));
initialise_global(modal, template.find(".settings-general"));
return template; return template;
}, },
@ -47,13 +236,33 @@ namespace Modals {
return modal; return modal;
} }
function initialiseSettingListeners(modal: Modal, tag: JQuery) { function initialise_global(modal: Modal, tag: JQuery) {
//Voice console.log(tag);
initialiseVoiceListeners(modal, tag.find(".settings_audio")); {/* 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) { function initialiseVoiceListeners(modal: Modal, tag: JQuery) {
let currentVAD = settings.global("vad_type", "ppt"); let currentVAD = settings.global("vad_type", "vad");
{ //Initialized voice activation detection { //Initialized voice activation detection
const vad_tag = tag.find(".settings-vad-container"); const vad_tag = tag.find(".settings-vad-container");
@ -658,64 +867,98 @@ namespace Modals {
{ {
{ //TeamSpeak change listener { //TeamSpeak change listener
const teamspeak_tag = settings_tag.find(".identity-settings-teamspeak"); const teamspeak_tag = settings_tag.find(".identity-settings-teamspeak");
teamspeak_tag.find(".identity_file").on('change', event => { const identity_info_tag = teamspeak_tag.find(".identity-info");
if(!selected_profile) return; 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; button_import.on('click', event => {
const file_reader = new FileReader(); const profile = selected_profile.selected_identity(IdentitifyType.TEAMSPEAK) as profiles.identities.TeaSpeakIdentity;
file_reader.onload = function() {
const identity_promise = profiles.identities.TeaSpeakIdentity.import_ts(file_reader.result as string, true); const set_identity = (identity: profiles.identities.TeaSpeakIdentity) => {
identity_promise.then(identity => { selected_profile.set_identity(IdentitifyType.TEAMSPEAK, identity);
(identity as profiles.identities.TeaSpeakIdentity).export_ts().then(e => teamspeak_tag.find(".identity_string").val(e)); teamspeak_tag.trigger('show');
selected_profile.set_identity(IdentitifyType.TEAMSPEAK, identity as any); createInfoModal(tr("Identity imported"), tr("Your identity has been successfully imported!")).open();
profiles.mark_need_save(); };
display_error(undefined);
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;
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 => {
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 => { }).catch(error => {
display_error(tr("Failed to parse identity.<br>Reason: ") + error); createErrorModal(tr("Failed to generate identity"), tr("Failed to generate a new identity.<br>Error:") + error).open();
return;
}); });
}; };
file_reader.onerror = ev => { if(profile && profile.valid()) {
console.error(tr("Failed to read give identity file: %o"), ev); spawnYesNo(tr("Are you sure"), tr("Do you really want to generate a new identity and override the old identity?"), result => {
display_error(tr("Failed to read file!")); if(result)
return; generate_identity();
};
if(element.files && element.files.length > 0)
file_reader.readAsText(element.files[0]);
});
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);
}).catch(error => {
display_error(tr("Failed to parse identity.<br>Reason: ") + error);
return;
}); });
} } 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 => { teamspeak_tag.on('show', event => {
const profile = selected_profile.selected_identity(IdentitifyType.TEAMSPEAK); const profile = selected_profile.selected_identity(IdentitifyType.TEAMSPEAK) as profiles.identities.TeaSpeakIdentity;
if(!profile)
display_error("invalid profile"); if(!profile || !profile.valid()) {
else if(!profile.valid()) identity_info_tag.hide();
display_error("profile isn't valid"); teamspeak_tag.find(".identity-undefined").show();
else button_export.prop("disabled", true);
display_error(); } 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 { 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) { export function on_ready(cb: () => any) {