Port part of the settings functionality to vue.

This commit is contained in:
Richard Lewis 2019-03-03 17:19:48 +02:00 committed by Pavel Djundik
parent 08635beb61
commit 6c10a2a6cf
6 changed files with 197 additions and 147 deletions

View File

@ -15,7 +15,6 @@
class="container" class="container"
method="post" method="post"
action="" action=""
:data-event="defaults.uuid ? 'network:edit' : 'network:new'"
@submit.prevent="onSubmit" @submit.prevent="onSubmit"
> >
<div class="row"> <div class="row">

View File

@ -11,13 +11,19 @@
aria-label="Toggle channel list" aria-label="Toggle channel list"
/> />
</div> </div>
<div class="container"> <form
ref="settingsForm"
class="container"
@change="onChange"
@submit.prevent
>
<h1 class="title">Settings</h1> <h1 class="title">Settings</h1>
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<label class="opt"> <label class="opt">
<input <input
v-model="$root.serverConfiguration.advanced"
type="checkbox" type="checkbox"
name="advanced" name="advanced"
> >
@ -28,9 +34,9 @@
<div class="row"> <div class="row">
<div <div
v-if="canRegisterProtocol"
id="native-app" id="native-app"
class="col-sm-12" class="col-sm-12"
hidden
> >
<h2>Native app</h2> <h2>Native app</h2>
<button <button
@ -43,13 +49,13 @@
id="make-default-client" id="make-default-client"
type="button" type="button"
class="btn" class="btn"
@click.prevent="registerProtocol"
>Open irc:// URLs with The Lounge</button> >Open irc:// URLs with The Lounge</button>
</div> </div>
<div <div
v-if="!this.$root.serverConfiguration.public" v-if="!$root.serverConfiguration.public && $root.serverConfiguration.advanced"
class="col-sm-12" class="col-sm-12"
data-advanced
> >
<h2> <h2>
Settings synchronisation Settings synchronisation
@ -65,14 +71,28 @@
</h2> </h2>
<label class="opt"> <label class="opt">
<input <input
v-model="$root.settings.syncSettings"
type="checkbox" type="checkbox"
name="syncSettings" name="syncSettings"
> >
Synchronize settings with other clients. Synchronize settings with other clients.
</label> </label>
<p class="sync-warning-override"><strong>Warning</strong> Checking this box will override the settings of this client with those stored on the server.</p> <p
<p class="sync-warning-base"><strong>Warning</strong> No settings have been synced before. Enabling this will sync all settings of this client as the base for other clients.</p> v-if="!$root.settings.syncSettings"
<div class="opt force-sync-button"> class="sync-warning-override"
>
<strong>Warning</strong> Checking this box will override the settings of this client with those stored on the server.
</p>
<p
v-if="!$root.settings.syncSettings"
class="sync-warning-base"
>
<strong>Warning</strong> No settings have been synced before. Enabling this will sync all settings of this client as the base for other clients.
</p>
<div
v-if="$root.settings.syncSettings"
class="opt force-sync-button"
>
<button <button
type="button" type="button"
class="btn" class="btn"
@ -88,6 +108,7 @@
<div class="col-sm-6"> <div class="col-sm-6">
<label class="opt"> <label class="opt">
<input <input
v-model="$root.settings.motd"
type="checkbox" type="checkbox"
name="motd" name="motd"
> >
@ -97,6 +118,7 @@
<div class="col-sm-6"> <div class="col-sm-6">
<label class="opt"> <label class="opt">
<input <input
v-model="$root.settings.showSeconds"
type="checkbox" type="checkbox"
name="showSeconds" name="showSeconds"
> >
@ -120,6 +142,7 @@
<div class="col-sm-12"> <div class="col-sm-12">
<label class="opt"> <label class="opt">
<input <input
v-model="$root.settings.statusMessages"
type="radio" type="radio"
name="statusMessages" name="statusMessages"
value="shown" value="shown"
@ -128,6 +151,7 @@
</label> </label>
<label class="opt"> <label class="opt">
<input <input
v-model="$root.settings.statusMessages"
type="radio" type="radio"
name="statusMessages" name="statusMessages"
value="condensed" value="condensed"
@ -136,6 +160,7 @@
</label> </label>
<label class="opt"> <label class="opt">
<input <input
v-model="$root.settings.statusMessages"
type="radio" type="radio"
name="statusMessages" name="statusMessages"
value="hidden" value="hidden"
@ -149,6 +174,7 @@
<div class="col-sm-12"> <div class="col-sm-12">
<label class="opt"> <label class="opt">
<input <input
v-model="$root.settings.coloredNicks"
type="checkbox" type="checkbox"
name="coloredNicks" name="coloredNicks"
> >
@ -156,6 +182,7 @@
</label> </label>
<label class="opt"> <label class="opt">
<input <input
v-model="$root.settings.autocomplete"
type="checkbox" type="checkbox"
name="autocomplete" name="autocomplete"
> >
@ -163,8 +190,8 @@
</label> </label>
</div> </div>
<div <div
v-if="$root.serverConfiguration.advanced"
class="col-sm-12" class="col-sm-12"
data-advanced
> >
<label class="opt"> <label class="opt">
<label <label
@ -173,6 +200,7 @@
>Nick autocomplete postfix (e.g. <code>, </code>)</label> >Nick autocomplete postfix (e.g. <code>, </code>)</label>
<input <input
id="nickPostfix" id="nickPostfix"
v-model="$root.settings.nickPostfix"
type="text" type="text"
name="nickPostfix" name="nickPostfix"
class="input" class="input"
@ -191,11 +219,12 @@
>Theme</label> >Theme</label>
<select <select
id="theme-select" id="theme-select"
v-model="$root.settings.theme"
name="theme" name="theme"
class="input" class="input"
> >
<option <option
v-for="theme in this.$root.serverConfiguration.themes" v-for="theme in $root.serverConfiguration.themes"
:key="theme.name" :key="theme.name"
> >
{{ theme.displayName }} {{ theme.displayName }}
@ -203,7 +232,7 @@
</select> </select>
</div> </div>
<template v-if="this.$root.serverConfiguration.prefetch"> <template v-if="$root.serverConfiguration.prefetch">
<div class="col-sm-12"> <div class="col-sm-12">
<h2>Link previews</h2> <h2>Link previews</h2>
</div> </div>
@ -219,6 +248,7 @@
<div class="col-sm-6"> <div class="col-sm-6">
<label class="opt"> <label class="opt">
<input <input
v-model="$root.settings.links"
type="checkbox" type="checkbox"
name="links" name="links"
> >
@ -227,7 +257,7 @@
</div> </div>
</template> </template>
<template v-if="!this.$root.serverConfiguration.public"> <template v-if="!$root.serverConfiguration.public">
<div class="col-sm-12"> <div class="col-sm-12">
<h2>Push Notifications</h2> <h2>Push Notifications</h2>
</div> </div>
@ -240,14 +270,14 @@
data-text-alternate="Unsubscribe from push notifications" data-text-alternate="Unsubscribe from push notifications"
>Subscribe to push notifications</button> >Subscribe to push notifications</button>
<div <div
v-if="this.$root.pushNotificationState === 'nohttps'" v-if="$root.pushNotificationState === 'nohttps'"
class="error" class="error"
> >
<strong>Warning</strong>: <strong>Warning</strong>:
Push notifications are only supported over HTTPS connections. Push notifications are only supported over HTTPS connections.
</div> </div>
<div <div
v-if="this.$root.pushNotificationState === 'unsupported'" v-if="$root.pushNotificationState === 'unsupported'"
class="error" class="error"
> >
<strong>Warning</strong>: <strong>Warning</strong>:
@ -261,6 +291,7 @@
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
<label class="opt"> <label class="opt">
<!-- TODO: handle enabling/disabling notifications -->
<input <input
id="desktopNotifications" id="desktopNotifications"
type="checkbox" type="checkbox"
@ -268,14 +299,14 @@
> >
Enable browser notifications<br> Enable browser notifications<br>
<div <div
v-if="this.$root.desktopNotificationState === 'unsupported'" v-if="$root.desktopNotificationState === 'unsupported'"
class="error" class="error"
> >
<strong>Warning</strong>: <strong>Warning</strong>:
Notifications are not supported by your browser. Notifications are not supported by your browser.
</div> </div>
<div <div
v-if="this.$root.desktopNotificationState === 'blocked'" v-if="$root.desktopNotificationState === 'blocked'"
id="warnBlockedDesktopNotifications" id="warnBlockedDesktopNotifications"
class="error" class="error"
> >
@ -287,6 +318,7 @@
<div class="col-sm-12"> <div class="col-sm-12">
<label class="opt"> <label class="opt">
<input <input
v-model="$root.settings.notification"
type="checkbox" type="checkbox"
name="notification" name="notification"
> >
@ -295,16 +327,20 @@
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
<div class="opt"> <div class="opt">
<button id="play">Play sound</button> <button
id="play"
@click.prevent="playNotification"
>Play sound</button>
</div> </div>
</div> </div>
<div <div
v-if="$root.serverConfiguration.advanced"
class="col-sm-12" class="col-sm-12"
data-advanced
> >
<label class="opt"> <label class="opt">
<input <input
v-model="$root.settings.notifyAllMessages"
type="checkbox" type="checkbox"
name="notifyAllMessages" name="notifyAllMessages"
> >
@ -313,8 +349,8 @@
</div> </div>
<div <div
v-if="$root.serverConfiguration.advanced"
class="col-sm-12" class="col-sm-12"
data-advanced
> >
<label class="opt"> <label class="opt">
<label <label
@ -323,6 +359,7 @@
>Custom highlights (comma-separated keywords)</label> >Custom highlights (comma-separated keywords)</label>
<input <input
id="highlights" id="highlights"
v-model="$root.settings.highlights"
type="text" type="text"
name="highlights" name="highlights"
class="input" class="input"
@ -332,76 +369,87 @@
</div> </div>
<div <div
v-if="!this.$root.serverConfiguration.public && !this.$root.serverConfiguration.ldapEnabled" v-if="!$root.serverConfiguration.public && !$root.serverConfiguration.ldapEnabled"
id="change-password" id="change-password"
>
<form
action=""
method="post"
data-event="change-password"
> >
<div class="col-sm-12"> <div class="col-sm-12">
<h2>Change password</h2> <h2>Change password</h2>
</div> </div>
<div class="col-sm-12 password-container"> <div class="col-sm-12 password-container">
<!-- TODO: use revealPassword -->
<label <label
for="old_password_input" for="old_password_input"
class="sr-only" class="sr-only"
>Enter current password</label> >Enter current password</label>
<RevealPassword v-slot:default="slotProps">
<input <input
id="old_password_input" id="old_password_input"
type="password" :type="slotProps.isVisible ? 'text' : 'password'"
name="old_password" name="old_password"
class="input" class="input"
placeholder="Enter current password" placeholder="Enter current password"
> >
</RevealPassword>
</div> </div>
<div class="col-sm-12 password-container"> <div class="col-sm-12 password-container">
<label <label
for="new_password_input" for="new_password_input"
class="sr-only" class="sr-only"
>Enter desired new password</label> >Enter desired new password</label>
<RevealPassword v-slot:default="slotProps">
<input <input
id="new_password_input" id="new_password_input"
type="password" :type="slotProps.isVisible ? 'text' : 'password'"
name="new_password" name="new_password"
class="input" class="input"
placeholder="Enter desired new password" placeholder="Enter desired new password"
> >
</RevealPassword>
</div> </div>
<div class="col-sm-12 password-container"> <div class="col-sm-12 password-container">
<label <label
for="verify_password_input" for="verify_password_input"
class="sr-only" class="sr-only"
>Repeat new password</label> >Repeat new password</label>
<RevealPassword v-slot:default="slotProps">
<input <input
id="verify_password_input" id="verify_password_input"
type="password" :type="slotProps.isVisible ? 'text' : 'password'"
name="verify_password" name="verify_password"
class="input" class="input"
placeholder="Repeat new password" placeholder="Repeat new password"
> >
</RevealPassword>
</div>
<div
v-if="passwordChangeStatus && passwordChangeStatus.success"
class="col-sm-12 feedback success"
>
Successfully updated your password
</div>
<div
v-else-if="passwordChangeStatus && passwordChangeStatus.error"
class="col-sm-12 feedback error"
>
{{ passwordErrors[passwordChangeStatus.error] }}
</div> </div>
<div class="col-sm-12 feedback" />
<div class="col-sm-12"> <div class="col-sm-12">
<button <button
type="submit" type="submit"
class="btn" class="btn"
@click.prevent="changePassword"
>Change password</button> >Change password</button>
</div> </div>
</form>
</div> </div>
<div <div
v-if="$root.serverConfiguration.advanced"
class="col-sm-12" class="col-sm-12"
data-advanced
> >
<h2>Custom Stylesheet</h2> <h2>Custom Stylesheet</h2>
</div> </div>
<div <div
v-if="$root.serverConfiguration.advanced"
class="col-sm-12" class="col-sm-12"
data-advanced
> >
<label <label
for="user-specified-css-input" for="user-specified-css-input"
@ -418,10 +466,11 @@
</div> </div>
<div <div
v-if="!this.$root.serverConfiguration.public" v-if="!$root.serverConfiguration.public"
class="session-list" class="session-list"
> >
<h2>Sessions</h2> <h2>Sessions</h2>
<!-- TODO: Sessions -->
<h3>Current session</h3> <h3>Current session</h3>
<div id="session-current" /> <div id="session-current" />
@ -429,13 +478,13 @@
<h3>Other sessions</h3> <h3>Other sessions</h3>
<div id="session-list" /> <div id="session-list" />
</div> </div>
</div> </form>
</div> </div>
</template> </template>
<script> <script>
const storage = require("../../js/localStorage"); // const storage = require("../../js/localStorage"); // TODO: use this
import socket from "../../js/socket"; import socket from "../../js/socket";
import RevealPassword from "../RevealPassword.vue"; import RevealPassword from "../RevealPassword.vue";
@ -446,30 +495,95 @@ export default {
}, },
data() { data() {
return { return {
inFlight: false, canRegisterProtocol: false,
errorShown: false, passwordChangeStatus: null,
passwordErrors: {
missing_fields: "Please enter a new password",
password_mismatch: "Both new password fields must match",
password_incorrect: "The current password field does not match your account password",
update_failed: "Failed to update your password",
},
}; };
}, },
mounted() {
// Enable protocol handler registration if supported
if (window.navigator.registerProtocolHandler) {
this.canRegisterProtocol = true;
}
},
methods: { methods: {
onSubmit(event) { onChange(event) {
event.preventDefault(); const ignore = [
"old_password",
"new_password",
"verify_password",
];
this.inFlight = true; const name = event.target.name;
this.errorShown = false;
const values = { if (ignore.includes(name)) {
user: this.$refs.username.value, return;
password: this.$refs.password.value, }
let value;
if (event.target.type === "checkbox") {
value = event.target.checked;
} else {
value = event.target.value;
}
this.storeSetting(name, value);
},
storeSetting(name, value) {
// TODO: port logic from options.js
socket.emit("setting:set", {name, value});
},
changePassword() {
const allFields = new FormData(this.$refs.settingsForm);
const data = {
old_password: allFields.get("old_password"),
new_password: allFields.get("new_password"),
verify_password: allFields.get("verify_password"),
}; };
storage.set("user", values.user); if (!data.old_password || !data.new_password || !data.verify_password) {
this.passwordChangeStatus = {
success: false,
error: "missing_fields",
};
return;
}
socket.emit("auth", values); if (data.new_password !== data.verify_password) {
this.passwordChangeStatus = {
success: false,
error: "password_mismatch",
};
return;
}
socket.once("change-password", (response) => {
this.passwordChangeStatus = response;
});
socket.emit("change-password", data);
}, },
onForceSyncClick() { onForceSyncClick() {
const options = require("../../js/options"); const options = require("../../js/options");
options.syncAllSettings(true); options.syncAllSettings(true);
}, },
registerProtocol() {
const uri = document.location.origin + document.location.pathname + "?uri=%s";
window.navigator.registerProtocolHandler("irc", uri, "The Lounge");
window.navigator.registerProtocolHandler("ircs", uri, "The Lounge");
},
playNotification() {
const pop = new Audio();
pop.src = "audio/pop.wav";
pop.play();
},
}, },
}; };
</script> </script>

View File

@ -116,8 +116,6 @@ function applySetting(name, value) {
if (("Notification" in window) && value && Notification.permission !== "granted") { if (("Notification" in window) && value && Notification.permission !== "granted") {
Notification.requestPermission(updateDesktopNotificationStatus); Notification.requestPermission(updateDesktopNotificationStatus);
} }
} else if (name === "advanced") {
$("#settings [data-advanced]").toggle(settings[name]);
} }
} }
@ -219,6 +217,7 @@ function initialize() {
vueApp.desktopNotificationState = "unsupported"; vueApp.desktopNotificationState = "unsupported";
} }
/*
$settings.on("change", "input, select, textarea", function(e) { $settings.on("change", "input, select, textarea", function(e) {
// We only want to trigger on human triggered changes. // We only want to trigger on human triggered changes.
if (e.originalEvent) { if (e.originalEvent) {
@ -238,27 +237,10 @@ function initialize() {
} }
} }
}); });
*/
// Local init is done, let's sync // Local init is done, let's sync
// We always ask for synced settings even if it is disabled. // We always ask for synced settings even if it is disabled.
// Settings can be mandatory to sync and it is used to determine sync base state. // Settings can be mandatory to sync and it is used to determine sync base state.
socket.emit("setting:get"); socket.emit("setting:get");
// Protocol handler
const defaultClientButton = $("#make-default-client");
if (window.navigator.registerProtocolHandler) {
defaultClientButton.on("click", function() {
const uri = document.location.origin + document.location.pathname + "?uri=%s";
window.navigator.registerProtocolHandler("irc", uri, "The Lounge");
window.navigator.registerProtocolHandler("ircs", uri, "The Lounge");
return false;
});
$("#native-app").prop("hidden", false);
} else {
defaultClientButton.hide();
}
} }

View File

@ -1,31 +0,0 @@
"use strict";
const $ = require("jquery");
const socket = require("../socket");
socket.on("change-password", function(data) {
const passwordForm = $("#change-password");
if (data.error || data.success) {
const message = data.success ? data.success : data.error;
const feedback = passwordForm.find(".feedback");
if (data.success) {
feedback.addClass("success").removeClass("error");
} else {
feedback.addClass("error").removeClass("success");
}
feedback.text(message).show();
feedback.closest("form").one("submit", function() {
feedback.hide();
});
}
passwordForm
.find("input")
.val("")
.end()
.find(".btn")
.prop("disabled", false);
});

View File

@ -39,12 +39,6 @@ socket.on("configuration", function(data) {
socket.emit("sessions:get"); socket.emit("sessions:get");
}); });
$("#play").on("click", () => {
const pop = new Audio();
pop.src = "audio/pop.wav";
pop.play();
});
if (data.fileUpload) { if (data.fileUpload) {
upload.initialize(data.fileUploadMaxFileSize); upload.initialize(data.fileUploadMaxFileSize);
} }
@ -62,6 +56,7 @@ socket.on("configuration", function(data) {
document.querySelector('meta[name="theme-color"]').content = currentTheme.themeColor; document.querySelector('meta[name="theme-color"]').content = currentTheme.themeColor;
} }
/*
function handleFormSubmit() { function handleFormSubmit() {
const form = $(this); const form = $(this);
const event = form.data("event"); const event = form.data("event");
@ -79,10 +74,9 @@ socket.on("configuration", function(data) {
return false; return false;
} }
*/
$("#change-password form").on("submit", handleFormSubmit); // TODO: move to component (this mirrors the nick to the username field if the username is empty)
connect.on("submit", "form", handleFormSubmit);
connect.on("show", function() { connect.on("show", function() {
connect connect
.html(templates.windows.connect(data)) .html(templates.windows.connect(data))

View File

@ -423,16 +423,9 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
const p1 = data.new_password; const p1 = data.new_password;
const p2 = data.verify_password; const p2 = data.verify_password;
if (typeof p1 === "undefined" || p1 === "") { if (typeof p1 === "undefined" || p1 === "" || p1 !== p2) {
socket.emit("change-password", { socket.emit("change-password", {
error: "Please enter a new password", error: "",
});
return;
}
if (p1 !== p2) {
socket.emit("change-password", {
error: "Both new password fields must match",
}); });
return; return;
} }
@ -442,8 +435,7 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
.then((matching) => { .then((matching) => {
if (!matching) { if (!matching) {
socket.emit("change-password", { socket.emit("change-password", {
error: error: "password_incorrect",
"The current password field does not match your account password",
}); });
return; return;
} }
@ -454,9 +446,9 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
const obj = {}; const obj = {};
if (success) { if (success) {
obj.success = "Successfully updated your password"; obj.success = true;
} else { } else {
obj.error = "Failed to update your password"; obj.error = "update_failed";
} }
socket.emit("change-password", obj); socket.emit("change-password", obj);