thelounge/client/components/Windows/Settings.vue

591 lines
16 KiB
Vue
Raw Normal View History

2019-02-18 10:18:32 +01:00
<template>
2019-08-03 21:03:45 +02:00
<div id="settings" class="window" role="tabpanel" aria-label="Settings">
2019-02-18 10:18:32 +01:00
<div class="header">
<SidebarToggle />
2019-02-18 10:18:32 +01:00
</div>
2019-08-03 21:03:45 +02:00
<form ref="settingsForm" class="container" @change="onChange" @submit.prevent>
2019-02-18 10:18:32 +01:00
<h1 class="title">Settings</h1>
<div class="row">
<div class="col-sm-6">
<label class="opt">
<input
:checked="$store.state.settings.advanced"
2019-02-18 10:18:32 +01:00
type="checkbox"
2019-03-01 15:18:16 +01:00
name="advanced"
2019-08-03 21:03:45 +02:00
/>
2019-02-18 10:18:32 +01:00
Advanced settings
</label>
</div>
</div>
<div class="row">
2019-11-15 20:13:26 +01:00
<div v-if="canRegisterProtocol || hasInstallPromptEvent" class="col-sm-12">
2019-02-18 10:18:32 +01:00
<h2>Native app</h2>
2019-11-15 20:13:26 +01:00
<button
v-if="hasInstallPromptEvent"
type="button"
class="btn"
@click.prevent="nativeInstallPrompt"
>
2019-08-03 21:03:45 +02:00
Add The Lounge to Home screen
</button>
2019-02-18 10:18:32 +01:00
<button
2019-11-15 20:13:26 +01:00
v-if="canRegisterProtocol"
2019-02-18 10:18:32 +01:00
type="button"
2019-03-01 15:18:16 +01:00
class="btn"
@click.prevent="registerProtocol"
2019-08-03 21:03:45 +02:00
>
Open irc:// URLs with The Lounge
</button>
2019-02-18 10:18:32 +01:00
</div>
<div
v-if="
!$store.state.serverConfiguration.public && $store.state.settings.advanced
"
2019-02-18 10:18:32 +01:00
class="col-sm-12"
2019-03-01 15:18:16 +01:00
>
2019-02-18 10:18:32 +01:00
<h2>
Settings synchronisation
<span
class="tooltipped tooltipped-n tooltipped-no-delay"
2019-03-01 15:18:16 +01:00
aria-label="Note: This is an experimental feature and may change in future releases."
>
2019-02-18 10:18:32 +01:00
<button
class="extra-experimental"
2019-03-01 15:18:16 +01:00
aria-label="Note: This is an experimental feature and may change in future releases."
/>
2019-02-18 10:18:32 +01:00
</span>
</h2>
<label class="opt">
<input
:checked="$store.state.settings.syncSettings"
2019-02-18 10:18:32 +01:00
type="checkbox"
2019-03-01 15:18:16 +01:00
name="syncSettings"
2019-08-03 21:03:45 +02:00
/>
Synchronize settings with other clients
2019-02-18 10:18:32 +01:00
</label>
<template v-if="!$store.state.settings.syncSettings">
<div v-if="$store.state.serverHasSettings" class="settings-sync-panel">
<p>
<strong>Warning:</strong> Checking this box will override the
settings of this client with those stored on the server.
</p>
<p>
Use the button below to enable synchronization, and override any
settings already synced to the server.
</p>
<button type="button" class="btn btn-small" @click="onForceSyncClick">
Sync settings and enable
</button>
</div>
<div v-else class="settings-sync-panel">
<p>
<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>
</template>
2019-02-18 10:18:32 +01:00
</div>
2019-02-20 10:10:18 +01:00
2019-02-18 10:18:32 +01:00
<div class="col-sm-12">
<h2>Messages</h2>
</div>
<div class="col-sm-6">
<label class="opt">
<input :checked="$store.state.settings.motd" type="checkbox" name="motd" />
2019-02-18 10:18:32 +01:00
Show <abbr title="Message Of The Day">MOTD</abbr>
</label>
</div>
<div class="col-sm-6">
<label class="opt">
<input
:checked="$store.state.settings.showSeconds"
2019-02-18 10:18:32 +01:00
type="checkbox"
2019-03-01 15:18:16 +01:00
name="showSeconds"
2019-08-03 21:03:45 +02:00
/>
2019-02-18 10:18:32 +01:00
Show seconds in timestamp
</label>
</div>
<div class="col-sm-12">
<h2>
Status messages
<span
class="tooltipped tooltipped-n tooltipped-no-delay"
2019-03-01 15:18:16 +01:00
aria-label="Joins, parts, kicks, nick changes, away changes, and mode changes"
>
2019-02-18 10:18:32 +01:00
<button
class="extra-help"
2019-03-01 15:18:16 +01:00
aria-label="Joins, parts, kicks, nick changes, away changes, and mode changes"
/>
2019-02-18 10:18:32 +01:00
</span>
</h2>
</div>
<div class="col-sm-12">
<label class="opt">
<input
:checked="$store.state.settings.statusMessages === 'shown'"
2019-02-18 10:18:32 +01:00
type="radio"
name="statusMessages"
2019-03-01 15:18:16 +01:00
value="shown"
2019-08-03 21:03:45 +02:00
/>
2019-02-18 10:18:32 +01:00
Show all status messages individually
</label>
<label class="opt">
<input
:checked="$store.state.settings.statusMessages === 'condensed'"
2019-02-18 10:18:32 +01:00
type="radio"
name="statusMessages"
2019-03-01 15:18:16 +01:00
value="condensed"
2019-08-03 21:03:45 +02:00
/>
2019-02-18 10:18:32 +01:00
Condense status messages together
</label>
<label class="opt">
<input
:checked="$store.state.settings.statusMessages === 'hidden'"
2019-02-18 10:18:32 +01:00
type="radio"
name="statusMessages"
2019-03-01 15:18:16 +01:00
value="hidden"
2019-08-03 21:03:45 +02:00
/>
2019-02-18 10:18:32 +01:00
Hide all status messages
</label>
</div>
<div class="col-sm-12">
<h2>Visual Aids</h2>
</div>
<div class="col-sm-12">
<label class="opt">
<input
:checked="$store.state.settings.coloredNicks"
2019-02-18 10:18:32 +01:00
type="checkbox"
2019-03-01 15:18:16 +01:00
name="coloredNicks"
2019-08-03 21:03:45 +02:00
/>
2019-02-18 10:18:32 +01:00
Enable colored nicknames
</label>
<label class="opt">
<input
:checked="$store.state.settings.autocomplete"
2019-02-18 10:18:32 +01:00
type="checkbox"
2019-03-01 15:18:16 +01:00
name="autocomplete"
2019-08-03 21:03:45 +02:00
/>
2019-02-18 10:18:32 +01:00
Enable autocomplete
</label>
</div>
<div v-if="$store.state.settings.advanced" class="col-sm-12">
2019-02-18 10:18:32 +01:00
<label class="opt">
<label for="nickPostfix" class="sr-only">
Nick autocomplete postfix (e.g. <code>, </code>)
</label>
2019-02-18 10:18:32 +01:00
<input
id="nickPostfix"
:value="$store.state.settings.nickPostfix"
2019-02-18 10:18:32 +01:00
type="text"
name="nickPostfix"
class="input"
2019-03-01 15:18:16 +01:00
placeholder="Nick autocomplete postfix (e.g. ', ')"
2019-08-03 21:03:45 +02:00
/>
2019-02-18 10:18:32 +01:00
</label>
</div>
<div class="col-sm-12">
<h2>Theme</h2>
</div>
<div class="col-sm-12">
2019-08-03 21:03:45 +02:00
<label for="theme-select" class="sr-only">Theme</label>
2019-02-18 10:18:32 +01:00
<select
id="theme-select"
:value="$store.state.settings.theme"
2019-02-18 10:18:32 +01:00
name="theme"
2019-03-01 15:18:16 +01:00
class="input"
>
2019-02-20 10:10:18 +01:00
<option
v-for="theme in $store.state.serverConfiguration.themes"
2019-03-01 15:18:16 +01:00
:key="theme.name"
:value="theme.name"
2019-03-01 15:18:16 +01:00
>
2019-02-20 10:10:18 +01:00
{{ theme.displayName }}
2019-02-18 10:18:32 +01:00
</option>
</select>
</div>
2019-02-20 10:10:18 +01:00
<template v-if="$store.state.settings.prefetch">
2019-02-20 10:10:18 +01:00
<div class="col-sm-12">
<h2>Link previews</h2>
2019-02-18 10:18:32 +01:00
</div>
2019-02-20 10:10:18 +01:00
<div class="col-sm-6">
<label class="opt">
<input
:checked="$store.state.settings.media"
type="checkbox"
name="media"
/>
2019-02-20 10:10:18 +01:00
Auto-expand media
</label>
2019-02-18 10:18:32 +01:00
</div>
2019-02-20 10:10:18 +01:00
<div class="col-sm-6">
<label class="opt">
<input
:checked="$store.state.settings.links"
type="checkbox"
name="links"
/>
2019-02-20 10:10:18 +01:00
Auto-expand websites
</label>
</div>
</template>
<template v-if="!$store.state.serverConfiguration.public">
2019-02-20 10:10:18 +01:00
<div class="col-sm-12">
<h2>Push Notifications</h2>
</div>
<div class="col-sm-12">
<button
id="pushNotifications"
type="button"
class="btn"
2019-11-04 15:11:17 +01:00
:disabled="
$store.state.pushNotificationState !== 'supported' &&
$store.state.pushNotificationState !== 'subscribed'
"
@click="onPushButtonClick"
2019-03-01 15:18:16 +01:00
>
2019-11-04 15:11:17 +01:00
<template v-if="$store.state.pushNotificationState === 'subscribed'">
Unsubscribe from push notifications
</template>
<template v-else-if="$store.state.pushNotificationState === 'loading'">
Loading
</template>
<template v-else>
Subscribe to push notifications
</template>
2019-08-03 21:03:45 +02:00
</button>
<div v-if="$store.state.pushNotificationState === 'nohttps'" class="error">
2019-08-03 21:03:45 +02:00
<strong>Warning</strong>: Push notifications are only supported over
HTTPS connections.
2019-02-20 10:10:18 +01:00
</div>
<div
v-if="$store.state.pushNotificationState === 'unsupported'"
class="error"
>
2019-02-20 10:10:18 +01:00
<strong>Warning</strong>:
<span>Push notifications are not supported by your browser.</span>
</div>
</div>
</template>
2019-02-18 10:18:32 +01:00
<div class="col-sm-12">
<h2>Browser Notifications</h2>
</div>
<div class="col-sm-12">
<label class="opt">
<input
id="desktopNotifications"
2019-11-04 15:11:17 +01:00
:checked="$store.state.settings.desktopNotifications"
2019-02-18 10:18:32 +01:00
type="checkbox"
2019-03-01 15:18:16 +01:00
name="desktopNotifications"
2019-08-03 21:03:45 +02:00
/>
Enable browser notifications<br />
<div
v-if="$store.state.desktopNotificationState === 'unsupported'"
class="error"
>
2019-08-03 21:03:45 +02:00
<strong>Warning</strong>: Notifications are not supported by your
browser.
2019-02-18 10:18:32 +01:00
</div>
<div
v-if="$store.state.desktopNotificationState === 'blocked'"
2019-02-18 10:18:32 +01:00
id="warnBlockedDesktopNotifications"
2019-03-01 15:18:16 +01:00
class="error"
>
2019-08-03 21:03:45 +02:00
<strong>Warning</strong>: Notifications are blocked by your browser.
2019-02-18 10:18:32 +01:00
</div>
</label>
</div>
<div class="col-sm-12">
<label class="opt">
<input
:checked="$store.state.settings.notification"
2019-02-18 10:18:32 +01:00
type="checkbox"
2019-03-01 15:18:16 +01:00
name="notification"
2019-08-03 21:03:45 +02:00
/>
2019-02-18 10:18:32 +01:00
Enable notification sound
</label>
</div>
<div class="col-sm-12">
<div class="opt">
2019-08-03 21:03:45 +02:00
<button id="play" @click.prevent="playNotification">Play sound</button>
2019-02-18 10:18:32 +01:00
</div>
</div>
<div v-if="$store.state.settings.advanced" class="col-sm-12">
2019-02-18 10:18:32 +01:00
<label class="opt">
<input
:checked="$store.state.settings.notifyAllMessages"
2019-02-18 10:18:32 +01:00
type="checkbox"
2019-03-01 15:18:16 +01:00
name="notifyAllMessages"
2019-08-03 21:03:45 +02:00
/>
2019-02-18 10:18:32 +01:00
Enable notification for all messages
</label>
</div>
<div v-if="$store.state.settings.advanced" class="col-sm-12">
2019-02-18 10:18:32 +01:00
<label class="opt">
<label for="highlights" class="sr-only">
Custom highlights (comma-separated keywords)
</label>
2019-02-18 10:18:32 +01:00
<input
id="highlights"
:value="$store.state.settings.highlights"
2019-02-18 10:18:32 +01:00
type="text"
name="highlights"
class="input"
2019-03-01 15:18:16 +01:00
placeholder="Custom highlights (comma-separated keywords)"
2019-08-03 21:03:45 +02:00
/>
2019-02-18 10:18:32 +01:00
</label>
</div>
2019-02-20 10:10:18 +01:00
<div
2019-08-03 21:03:45 +02:00
v-if="
!$store.state.serverConfiguration.public &&
!$store.state.serverConfiguration.ldapEnabled
2019-08-03 21:03:45 +02:00
"
2019-03-01 15:18:16 +01:00
id="change-password"
>
<div class="col-sm-12">
<h2>Change password</h2>
</div>
<div class="col-sm-12 password-container">
<label for="old_password_input" class="sr-only">
Enter current password
</label>
<RevealPassword v-slot:default="slotProps">
2019-02-18 10:18:32 +01:00
<input
id="old_password_input"
:type="slotProps.isVisible ? 'text' : 'password'"
2019-02-18 10:18:32 +01:00
name="old_password"
class="input"
2019-03-01 15:18:16 +01:00
placeholder="Enter current password"
2019-08-03 21:03:45 +02:00
/>
</RevealPassword>
</div>
<div class="col-sm-12 password-container">
<label for="new_password_input" class="sr-only">
Enter desired new password
</label>
<RevealPassword v-slot:default="slotProps">
2019-02-18 10:18:32 +01:00
<input
id="new_password_input"
:type="slotProps.isVisible ? 'text' : 'password'"
2019-02-18 10:18:32 +01:00
name="new_password"
class="input"
2019-03-01 15:18:16 +01:00
placeholder="Enter desired new password"
2019-08-03 21:03:45 +02:00
/>
</RevealPassword>
</div>
<div class="col-sm-12 password-container">
<label for="verify_password_input" class="sr-only">
Repeat new password
</label>
<RevealPassword v-slot:default="slotProps">
2019-02-18 10:18:32 +01:00
<input
id="verify_password_input"
:type="slotProps.isVisible ? 'text' : 'password'"
2019-02-18 10:18:32 +01:00
name="verify_password"
class="input"
2019-03-01 15:18:16 +01:00
placeholder="Repeat new password"
2019-08-03 21:03:45 +02:00
/>
</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 class="col-sm-12">
2019-08-03 21:03:45 +02:00
<button type="submit" class="btn" @click.prevent="changePassword">
Change password
</button>
</div>
2019-02-18 10:18:32 +01:00
</div>
2019-02-20 10:10:18 +01:00
<div v-if="$store.state.settings.advanced" class="col-sm-12">
2019-02-18 10:18:32 +01:00
<h2>Custom Stylesheet</h2>
</div>
<div v-if="$store.state.settings.advanced" class="col-sm-12">
<label for="user-specified-css-input" class="sr-only">
Custom stylesheet. You can override any style with CSS here.
</label>
2019-02-18 10:18:32 +01:00
<textarea
id="user-specified-css-input"
:value="$store.state.settings.userStyles"
2019-02-18 10:18:32 +01:00
class="input"
name="userStyles"
2019-03-01 15:18:16 +01:00
placeholder="/* You can override any style with CSS here */"
/>
2019-02-18 10:18:32 +01:00
</div>
</div>
<div v-if="!$store.state.serverConfiguration.public" class="session-list">
2019-02-18 10:18:32 +01:00
<h2>Sessions</h2>
<h3>Current session</h3>
2019-08-03 21:03:45 +02:00
<div v-if="$store.getters.currentSession" id="session-current">
2019-03-03 18:47:49 +01:00
<Session :session="$store.getters.currentSession" />
</div>
2019-02-18 10:18:32 +01:00
<h3>Other sessions</h3>
2019-03-03 18:47:49 +01:00
<div id="session-list">
<p v-if="$store.state.sessions.length == 0">Loading</p>
<p v-else-if="$store.getters.otherSessions.length == 0">
<em>You are not currently logged in to any other device.</em>
2019-03-03 18:47:49 +01:00
</p>
<template v-else>
<Session
v-for="session in $store.getters.otherSessions"
:key="session.token"
:session="session"
/>
</template>
</div>
2019-02-18 10:18:32 +01:00
</div>
</form>
2019-02-18 10:18:32 +01:00
</div>
</template>
<script>
import socket from "../../js/socket";
2019-11-12 21:03:59 +01:00
import webpush from "../../js/webpush";
2019-02-18 10:18:32 +01:00
import RevealPassword from "../RevealPassword.vue";
2019-03-03 18:47:49 +01:00
import Session from "../Session.vue";
import SidebarToggle from "../SidebarToggle.vue";
2019-02-18 10:18:32 +01:00
2019-11-15 20:13:26 +01:00
let installPromptEvent = null;
window.addEventListener("beforeinstallprompt", (e) => {
e.preventDefault();
installPromptEvent = e;
});
2019-02-18 10:18:32 +01:00
export default {
name: "Settings",
components: {
RevealPassword,
2019-03-03 18:47:49 +01:00
Session,
SidebarToggle,
2019-02-18 10:18:32 +01:00
},
data() {
return {
canRegisterProtocol: false,
passwordChangeStatus: null,
passwordErrors: {
missing_fields: "Please enter a new password",
password_mismatch: "Both new password fields must match",
2019-08-03 21:03:45 +02:00
password_incorrect:
"The current password field does not match your account password",
update_failed: "Failed to update your password",
},
2019-02-18 10:18:32 +01:00
};
},
2019-11-15 20:13:26 +01:00
computed: {
hasInstallPromptEvent() {
// TODO: This doesn't hide the button after clicking
return installPromptEvent !== null;
},
},
mounted() {
2019-03-03 18:47:49 +01:00
socket.emit("sessions:get");
// Enable protocol handler registration if supported
if (window.navigator.registerProtocolHandler) {
this.canRegisterProtocol = true;
}
},
2019-02-18 10:18:32 +01:00
methods: {
onChange(event) {
2019-08-03 21:03:45 +02:00
const ignore = ["old_password", "new_password", "verify_password"];
2019-02-18 10:18:32 +01:00
const name = event.target.name;
2019-02-18 10:18:32 +01:00
if (ignore.includes(name)) {
return;
}
let value;
if (event.target.type === "checkbox") {
value = event.target.checked;
} else {
value = event.target.value;
}
this.$store.dispatch("settings/update", {name, value, sync: true});
},
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"),
2019-02-18 10:18:32 +01:00
};
if (!data.old_password || !data.new_password || !data.verify_password) {
this.passwordChangeStatus = {
success: false,
error: "missing_fields",
};
return;
}
2019-02-18 10:18:32 +01:00
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);
2019-02-18 10:18:32 +01:00
},
2019-02-20 10:10:18 +01:00
onForceSyncClick() {
this.$store.dispatch("settings/syncAll", true);
this.$store.dispatch("settings/update", {
name: "syncSettings",
value: true,
sync: true,
});
2019-02-20 10:10:18 +01:00
},
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");
},
2019-11-15 20:13:26 +01:00
nativeInstallPrompt() {
installPromptEvent.prompt();
installPromptEvent = null;
},
playNotification() {
const pop = new Audio();
pop.src = "audio/pop.wav";
pop.play();
},
onPushButtonClick() {
2019-11-12 21:03:59 +01:00
webpush.togglePushSubscription();
},
2019-02-18 10:18:32 +01:00
},
};
</script>