Begin moving windows to Vue

This commit is contained in:
Pavel Djundik 2019-02-18 11:18:32 +02:00
parent bdb0a2efca
commit 09e12affe8
21 changed files with 1516 additions and 1081 deletions

View File

@ -64,13 +64,10 @@
<Chat
v-if="activeChannel"
:network="activeChannel.network"
:channel="activeChannel.channel"
/>
<div id="sign-in" class="window" role="tabpanel" aria-label="Sign-in" />
<div id="connect" class="window" role="tabpanel" aria-label="Connect" />
<div id="settings" class="window" role="tabpanel" aria-label="Settings" />
<div id="help" class="window" role="tabpanel" aria-label="Help" />
<div id="changelog" class="window" aria-label="Changelog" />
:channel="activeChannel.channel" />
<component
:is="activeWindow"
ref="window" />
</article>
</div>
</template>
@ -80,14 +77,17 @@ const throttle = require("lodash/throttle");
import NetworkList from "./NetworkList.vue";
import Chat from "./Chat.vue";
import SignIn from "./Windows/SignIn.vue";
export default {
name: "App",
components: {
NetworkList,
Chat,
SignIn,
},
props: {
activeWindow: String,
activeChannel: Object,
networks: Array,
},

View File

@ -0,0 +1,33 @@
<template>
<div>
<slot :isVisible="isVisible" />
<span
ref="revealButton"
type="button"
:class="[
'reveal-password tooltipped tooltipped-n tooltipped-no-delay',
{ 'reveal-password-visible': isVisible },
]"
:aria-label="isVisible ? 'Hide password' : 'Show password'"
@click="onClick">
<span
:aria-label="isVisible ? 'Hide password' : 'Show password'" />
</span>
</div>
</template>
<script>
export default {
name: "RevealPassword",
data() {
return {
isVisible: false,
};
},
methods: {
onClick() {
this.isVisible = !this.isVisible;
},
},
};
</script>

View File

@ -0,0 +1,42 @@
<template>
<div
id="changelog"
class="window"
aria-label="Changelog">
<div class="header">
<button
class="lt"
aria-label="Toggle channel list" />
</div>
<div class="container">
<a
id="back-to-help"
href="#"
data-target="#help">« Help</a>
{{#if version}}
<h1 class="title">Release notes for {{ version }}</h1>
{{#if changelog}}
<h3>Introduction</h3>
<div class="changelog-text">{{{changelog}}}</div>
{{else}}
<p>Unable to retrieve releases from GitHub.</p>
<p><a
href="https://github.com/thelounge/thelounge/releases/tag/v{{version}}"
target="_blank"
rel="noopener">View release notes for this version on GitHub</a></p>
{{/if}}
{{else}}
<p>Loading changelog</p>
{{/if}}
</div>
</div>
</template>
<script>
export default {
name: "Changelog",
};
</script>

View File

@ -0,0 +1,655 @@
<template>
<div
id="help"
class="window"
role="tabpanel"
aria-label="Help">
<div class="header">
<button
class="lt"
aria-label="Toggle channel list" />
</div>
<div class="container">
<h1 class="title">Help</h1>
<h2>
<small class="pull-right">
v{{ version }}
(<a
id="view-changelog"
href="#"
data-target="#changelog">release notes</a>)
</small>
About The Lounge
</h2>
<div class="about">
<div id="version-checker" />
{{#if gitCommit}}
<p>
The Lounge is running from source
(<a
href="https://github.com/thelounge/thelounge/tree/{{gitCommit}}"
target="_blank"
rel="noopener">commit <code>{{ gitCommit }}</code></a>).
</p>
<ul>
<li>
Compare
<a
href="https://github.com/thelounge/thelounge/compare/{{gitCommit}}...master"
target="_blank"
rel="noopener">between <code>{{ gitCommit }}</code> and <code>master</code></a>
to see what you are missing
</li>
<li>
Compare
<a
href="https://github.com/thelounge/thelounge/compare/{{version}}...{{gitCommit}}"
target="_blank"
rel="noopener">between <code>{{ version }}</code> and <code>{{ gitCommit }}</code></a>
to see your local changes</li>
</ul>
{{/if}}
<p>
<a
href="https://thelounge.chat/"
target="_blank"
rel="noopener"
class="website-link">Website</a>
</p>
<p>
<a
href="https://thelounge.chat/docs/"
target="_blank"
rel="noopener"
class="documentation-link">Documentation</a>
</p>
<p>
<a
href="https://github.com/thelounge/thelounge/issues/new"
target="_blank"
rel="noopener"
class="report-issue-link">Report an issue</a>
</p>
</div>
<h2>Keyboard Shortcuts</h2>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd></kbd></span>
<span class="key-apple"><kbd></kbd> <kbd></kbd> <kbd></kbd></span>
</div>
<div class="description">
<p>Switch to the next lobby in the channel list.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd></kbd></span>
<span class="key-apple"><kbd></kbd> <kbd></kbd> <kbd></kbd></span>
</div>
<div class="description">
<p>Switch to the previous lobby in the channel list.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Alt</kbd> <kbd></kbd></span>
<span class="key-apple"><kbd></kbd> <kbd></kbd></span>
</div>
<div class="description">
<p>Switch to the next window in the channel list.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Alt</kbd> <kbd></kbd></span>
<span class="key-apple"><kbd></kbd> <kbd></kbd></span>
</div>
<div class="description">
<p>Switch to the previous window in the channel list.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Alt</kbd> <kbd>A</kbd></span>
<span class="key-apple"><kbd></kbd> <kbd>A</kbd></span>
</div>
<div class="description">
<p>Switch to the first window with unread messages.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Ctrl</kbd> <kbd>K</kbd></span>
<span class="key-apple"><kbd></kbd> <kbd>K</kbd></span>
</div>
<div class="description">
<p>
Mark any text typed after this shortcut to be colored. After
hitting this shortcut, enter an integer in the range
<code>015</code> to select the desired color, or use the
autocompletion menu to choose a color name (see below).
</p>
<p>
Background color can be specified by putting a comma and
another integer in the range <code>015</code> after the
foreground color number (autocompletion works too).
</p>
<p>
A color reference can be found
<a
href="https://modern.ircdocs.horse/formatting.html#colors"
target="_blank"
rel="noopener">here</a>.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Ctrl</kbd> <kbd>B</kbd></span>
<span class="key-apple"><kbd></kbd> <kbd>B</kbd></span>
</div>
<div class="description">
<p>Mark all text typed after this shortcut as <span class="irc-bold">bold</span>.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Ctrl</kbd> <kbd>U</kbd></span>
<span class="key-apple"><kbd></kbd> <kbd>U</kbd></span>
</div>
<div class="description">
<p>Mark all text typed after this shortcut as <span class="irc-underline">underlined</span>.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Ctrl</kbd> <kbd>I</kbd></span>
<span class="key-apple"><kbd></kbd> <kbd>I</kbd></span>
</div>
<div class="description">
<p>Mark all text typed after this shortcut as <span class="irc-italic">italics</span>.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Ctrl</kbd> <kbd>S</kbd></span>
<span class="key-apple"><kbd></kbd> <kbd>S</kbd></span>
</div>
<div class="description">
<p>Mark all text typed after this shortcut as <span class="irc-strikethrough">struck through</span>.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Ctrl</kbd> <kbd>M</kbd></span>
<span class="key-apple"><kbd></kbd> <kbd>M</kbd></span>
</div>
<div class="description">
<p>Mark all text typed after this shortcut as <span class="irc-monospace">monospaced</span>.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Ctrl</kbd> <kbd>O</kbd></span>
<span class="key-apple"><kbd></kbd> <kbd>O</kbd></span>
</div>
<div class="description">
<p>
Mark all text typed after this shortcut to be reset to its
original formatting.
</p>
</div>
</div>
<h2>Autocompletion</h2>
<p>
To auto-complete nicknames, channels, commands, and emoji, type one of the characters below to open
a suggestion list. Use the <kbd></kbd> and <kbd></kbd> keys to highlight an item, and insert it by
pressing <kbd>Tab</kbd> or <kbd>Enter</kbd> (or by clicking the desired item).
</p>
<p>
Autocompletion can be disabled in settings.
</p>
<div class="help-item">
<div class="subject">
<code>@</code>
</div>
<div class="description">
<p>Nickname</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>#</code>
</div>
<div class="description">
<p>Channel</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/</code>
</div>
<div class="description">
<p>Commands (see list of commands below)</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>:</code>
</div>
<div class="description">
<p>Emoji (note: requires two search characters, to avoid conflicting with common emoticons like <code>:)</code>)</p>
</div>
</div>
<h2>Commands</h2>
<div class="help-item">
<div class="subject">
<code>/away [message]</code>
</div>
<div class="description">
<p>Mark yourself as away with an optional message.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/back</code>
</div>
<div class="description">
<p>Remove your away status (set with <code>/away</code>).</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/ban nick</code>
</div>
<div class="description">
<p>Ban (<code>+b</code>) a user from the current channel.
This can be a nickname or a hostmask.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/banlist</code>
</div>
<div class="description">
<p>Load the banlist for the current channel.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/collapse</code>
</div>
<div class="description">
<p>
Collapse all previews in the current channel (opposite of
<code>/expand</code>)
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/connect host [port]</code>
</div>
<div class="description">
<p>
Connect to a new IRC network. If <code>port</code> starts with
a <code>+</code> sign, the connection will be made secure
using TLS.
</p>
<p>Alias: <code>/server</code></p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/ctcp target cmd [args]</code>
</div>
<div class="description">
<p>
Send a <abbr title="Client-to-client protocol">CTCP</abbr>
request. Read more about this on
<a
href="https://en.wikipedia.org/wiki/Client-to-client_protocol"
target="_blank"
rel="noopener">the dedicated Wikipedia article</a>.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/deop nick [...nick]</code>
</div>
<div class="description">
<p>
Remove op (<code>-o</code>) from one or several users in the
current channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/devoice nick [...nick]</code>
</div>
<div class="description">
<p>
Remove voice (<code>-v</code>) from one or several users in
the current channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/disconnect [message]</code>
</div>
<div class="description">
<p>
Disconnect from the current network with an
optionally-provided message.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/expand</code>
</div>
<div class="description">
<p>
Expand all previews in the current channel (opposite of
<code>/collapse</code>)
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/invite nick [channel]</code>
</div>
<div class="description">
<p>
Invite a user to the specified channel. If
<code>channel</code> is ommitted, user will be invited to the
current channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/ignore nick</code>
</div>
<div class="description">
<p>
Block any messages from the specified user on the current network.
This can be a nickname or a hostmask.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/ignorelist</code>
</div>
<div class="description">
<p>Load the list of ignored users for the current network.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/join channel</code>
</div>
<div class="description">
<p>Join a channel.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/kick nick</code>
</div>
<div class="description">
<p>Kick a user from the current channel.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/list</code>
</div>
<div class="description">
<p>Retrieve a list of available channels on this network.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/me message</code>
</div>
<div class="description">
<p>
Send an action message to the current channel. The Lounge will
display it inline, as if the message was posted in the third
person.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/mode flags [args]</code>
</div>
<div class="description">
<p>
Set the given flags to the current channel if the active
window is a channel, another user if the active window is a
private message window, or yourself if the current window is a
server window.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/msg channel message</code>
</div>
<div class="description">
<p>Send a message to the specified channel.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/nick newnick</code>
</div>
<div class="description">
<p>Change your nickname on the current network.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/notice channel message</code>
</div>
<div class="description">
<p>Sends a notice message to the specified channel.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/op nick [...nick]</code>
</div>
<div class="description">
<p>
Give op (<code>+o</code>) to one or several users in the
current channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/part [channel]</code>
</div>
<div class="description">
<p>
Close the specified channel or private message window, or the
current channel if <code>channel</code> is ommitted.
</p>
<p>Aliases: <code>/close</code>, <code>/leave</code></p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/rejoin</code>
</div>
<div class="description">
<p>
Leave and immediately rejoin the current channel. Useful to
quickly get op from ChanServ in an empty channel, for example.
</p>
<p>Alias: <code>/cycle</code></p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/query nick</code>
</div>
<div class="description">
<p>Send a private message to the specified user.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/quit [message]</code>
</div>
<div class="description">
<p>
Disconnect from the current network with an optional message.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/raw message</code>
</div>
<div class="description">
<p>Send a raw message to the current IRC network.</p>
<p>Aliases: <code>/quote</code>, <code>/send</code></p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/slap nick</code>
</div>
<div class="description">
<p>Slap someone in the current channel with a trout!</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/topic [newtopic]</code>
</div>
<div class="description">
<p>
Get the topic in the current channel.
If <code>newtopic</code> is specified, sets the
topic in the current channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/unban nick</code>
</div>
<div class="description">
<p>Unban (<code>-b</code>) a user from the current channel.
This can be a nickname or a hostmask.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/unignore nick</code>
</div>
<div class="description">
<p>
Unblock messages from the specified user on the current network.
This can be a nickname or a hostmask.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/voice nick [...nick]</code>
</div>
<div class="description">
<p>
Give voice (<code>+v</code>) to one or several users in the
current channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/whois nick</code>
</div>
<div class="description">
<p>
Retrieve information about the given user on the current
network.
</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Help",
};
</script>

View File

@ -0,0 +1,243 @@
<template>
<div
id="connect"
class="window"
role="tabpanel"
aria-label="Connect">
<div class="header">
<button
class="lt"
aria-label="Toggle channel list" />
</div>
<form
class="container"
method="post"
action=""
data-event="{{#if defaults.uuid}}network:edit{{else}}network:new{{/if}}">
<div class="row">
<div class="col-sm-12">
<h1 class="title">
{{#if defaults.uuid}}
<input
type="hidden"
name="uuid"
value="{{defaults.uuid}}">
Edit {{ defaults.name }}
{{else}}
{{#if public}}The Lounge - {{/if}}
Connect
{{#unless displayNetwork}}
{{#if lockNetwork}}
to {{ defaults.name }}
{{/if}}
{{/unless}}
{{/if}}
</h1>
</div>
{{#if displayNetwork}}
<div>
<div class="col-sm-12">
<h2>Network settings</h2>
</div>
<div class="col-sm-3">
<label for="connect:name">Name</label>
</div>
<div class="col-sm-9">
<input
id="connect:name"
class="input"
name="name"
value="{{defaults.name}}"
maxlength="100">
</div>
<div class="col-sm-3">
<label for="connect:host">Server</label>
</div>
<div class="col-sm-6 col-xs-8">
<input
id="connect:host"
class="input"
name="host"
value="{{defaults.host}}"
aria-label="Server address"
maxlength="255"
required
{{#if
lockNetwork}}disabled{{
if}}>
</div>
<div class="col-sm-3 col-xs-4">
<div class="port">
<input
class="input"
type="number"
min="1"
max="65535"
name="port"
value="{{defaults.port}}"
aria-label="Server port"
{{#if
lockNetwork}}disabled{{
if}}>
</div>
</div>
<div class="clearfix" />
<div class="col-sm-9 col-sm-offset-3">
<label class="tls">
<input
type="checkbox"
name="tls"
{{#if
defaults.tls}}checked{{
if}}
{{#if
lockNetwork}}disabled{{
if}}>
Use secure connection (TLS)
</label>
</div>
<div class="col-sm-9 col-sm-offset-3">
<label class="tls">
<input
type="checkbox"
name="rejectUnauthorized"
{{#if
defaults.rejectUnauthorized}}checked{{
if}}
{{#if
lockNetwork}}disabled{{
if}}>
Only allow trusted certificates
</label>
</div>
<div class="clearfix" />
</div>
{{/if}}
<div class="col-sm-12">
<h2>User preferences</h2>
</div>
<div class="col-sm-3">
<label for="connect:nick">Nick</label>
</div>
<div class="col-sm-9">
<input
id="connect:nick"
class="input nick"
name="nick"
value="{{defaults.nick}}"
maxlength="100"
required>
</div>
{{#unless useHexIp}}
<div class="col-sm-3">
<label for="connect:username">Username</label>
</div>
<div class="col-sm-9">
<input
id="connect:username"
class="input username"
name="username"
value="{{defaults.username}}"
maxlength="512">
</div>
{{/unless}}
<div class="col-sm-3">
<label for="connect:password">Password</label>
</div>
<div class="col-sm-9 password-container">
<input
id="connect:password"
class="input"
type="password"
name="password"
value="{{defaults.password}}"
maxlength="512">
{{> ../reveal-password}}
</div>
<div class="col-sm-3">
<label for="connect:realname">Real name</label>
</div>
<div class="col-sm-9">
<input
id="connect:realname"
class="input"
name="realname"
value="{{defaults.realname}}"
maxlength="512">
</div>
{{#if defaults.uuid}}
<div class="col-sm-3">
<label for="connect:commands">Commands</label>
</div>
<div class="col-sm-9">
<textarea
id="connect:commands"
class="input"
name="commands"
placeholder="One raw command per line, each command will be executed on new connection">{{~#each defaults.commands~}}{{ ~this }}
{{/each~}}</textarea>
</div>
<div class="col-sm-9 col-sm-offset-3">
<button
type="submit"
class="btn">Save</button>
</div>
{{else}}
<div class="col-sm-3">
<label for="connect:channels">Channels</label>
</div>
<div class="col-sm-9">
<input
id="connect:channels"
class="input"
name="join"
value="{{defaults.join}}">
</div>
<div class="col-sm-9 col-sm-offset-3">
<button
type="submit"
class="btn">Connect</button>
</div>
{{/if}}
</div>
</form>
</div>
</template>
<script>
const storage = require("../../js/localStorage");
import socket from "../../js/socket";
import RevealPassword from "../RevealPassword.vue";
export default {
name: "NetworkEdit",
components: {
RevealPassword,
},
data() {
return {
inFlight: false,
errorShown: false,
};
},
methods: {
onSubmit(event) {
event.preventDefault();
this.inFlight = true;
this.errorShown = false;
const values = {
user: this.$refs.username.value,
password: this.$refs.password.value,
};
storage.set("user", values.user);
socket.emit("auth", values);
},
},
};
</script>

View File

@ -0,0 +1,415 @@
<template>
<div
id="settings"
class="window"
role="tabpanel"
aria-label="Settings">
<div class="header">
<button
class="lt"
aria-label="Toggle channel list" />
</div>
<div class="container">
<h1 class="title">Settings</h1>
<div class="row">
<div class="col-sm-6">
<label class="opt">
<input
type="checkbox"
name="advanced">
Advanced settings
</label>
</div>
</div>
<div class="row">
<div
id="native-app"
class="col-sm-12"
hidden>
<h2>Native app</h2>
<button
id="webapp-install-button"
type="button"
class="btn"
hidden>Add The Lounge to Home screen</button>
<button
id="make-default-client"
type="button"
class="btn">Open irc:// URLs with The Lounge</button>
</div>
{{#unless public}}
<div
class="col-sm-12"
data-advanced>
<h2>
Settings synchronisation
<span
class="tooltipped tooltipped-n tooltipped-no-delay"
aria-label="Note: This is an experimental feature and may change in future releases.">
<button
class="extra-experimental"
aria-label="Note: This is an experimental feature and may change in future releases." />
</span>
</h2>
<label class="opt">
<input
type="checkbox"
name="syncSettings">
Synchronize settings with other clients.
</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 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 class="opt force-sync-button">
<button
id="forceSync"
type="button"
class="btn">Force sync settings</button>
<p>This will override any settings already synced to the server.</p>
</div>
</div>
{{/unless}}
<div class="col-sm-12">
<h2>Messages</h2>
</div>
<div class="col-sm-6">
<label class="opt">
<input
type="checkbox"
name="motd">
Show <abbr title="Message Of The Day">MOTD</abbr>
</label>
</div>
<div class="col-sm-6">
<label class="opt">
<input
type="checkbox"
name="showSeconds">
Show seconds in timestamp
</label>
</div>
<div class="col-sm-12">
<h2>
Status messages
<span
class="tooltipped tooltipped-n tooltipped-no-delay"
aria-label="Joins, parts, kicks, nick changes, away changes, and mode changes">
<button
class="extra-help"
aria-label="Joins, parts, kicks, nick changes, away changes, and mode changes" />
</span>
</h2>
</div>
<div class="col-sm-12">
<label class="opt">
<input
type="radio"
name="statusMessages"
value="shown">
Show all status messages individually
</label>
<label class="opt">
<input
type="radio"
name="statusMessages"
value="condensed">
Condense status messages together
</label>
<label class="opt">
<input
type="radio"
name="statusMessages"
value="hidden">
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
type="checkbox"
name="coloredNicks">
Enable colored nicknames
</label>
<label class="opt">
<input
type="checkbox"
name="autocomplete">
Enable autocomplete
</label>
</div>
<div
class="col-sm-12"
data-advanced>
<label class="opt">
<label
for="nickPostfix"
class="sr-only">Nick autocomplete postfix (e.g. <code>, </code>)</label>
<input
id="nickPostfix"
type="text"
name="nickPostfix"
class="input"
placeholder="Nick autocomplete postfix (e.g. ', ')">
</label>
</div>
<div class="col-sm-12">
<h2>Theme</h2>
</div>
<div class="col-sm-12">
<label
for="theme-select"
class="sr-only">Theme</label>
<select
id="theme-select"
name="theme"
class="input">
{{#each themes}}
<option value="{{name}}">
{{ displayName }}
</option>
{{/each}}
</select>
</div>
{{#if prefetch}}
<div class="col-sm-12">
<h2>Link previews</h2>
</div>
<div class="col-sm-6">
<label class="opt">
<input
type="checkbox"
name="media">
Auto-expand media
</label>
</div>
<div class="col-sm-6">
<label class="opt">
<input
type="checkbox"
name="links">
Auto-expand websites
</label>
</div>
{{/if}}
{{#unless public}}
<div class="col-sm-12">
<h2>Push Notifications</h2>
</div>
<div class="col-sm-12">
<button
id="pushNotifications"
type="button"
class="btn"
disabled
data-text-alternate="Unsubscribe from push notifications">Subscribe to push notifications</button>
<div
id="pushNotificationsHttps"
class="error">
<strong>Warning</strong>:
Push notifications are only supported over HTTPS connections.
</div>
<div
id="pushNotificationsUnsupported"
class="error">
<strong>Warning</strong>:
<span>Push notifications are not supported by your browser.</span>
</div>
</div>
{{/unless}}
<div class="col-sm-12">
<h2>Browser Notifications</h2>
</div>
<div class="col-sm-12">
<label class="opt">
<input
id="desktopNotifications"
type="checkbox"
name="desktopNotifications">
Enable browser notifications<br>
<div
id="warnUnsupportedDesktopNotifications"
class="error">
<strong>Warning</strong>:
Notifications are not supported by your browser.
</div>
<div
id="warnBlockedDesktopNotifications"
class="error">
<strong>Warning</strong>:
Notifications are blocked by your browser.
</div>
</label>
</div>
<div class="col-sm-12">
<label class="opt">
<input
type="checkbox"
name="notification">
Enable notification sound
</label>
</div>
<div class="col-sm-12">
<div class="opt">
<button id="play">Play sound</button>
</div>
</div>
<div
class="col-sm-12"
data-advanced>
<label class="opt">
<input
type="checkbox"
name="notifyAllMessages">
Enable notification for all messages
</label>
</div>
<div
class="col-sm-12"
data-advanced>
<label class="opt">
<label
for="highlights"
class="sr-only">Custom highlights (comma-separated keywords)</label>
<input
id="highlights"
type="text"
name="highlights"
class="input"
placeholder="Custom highlights (comma-separated keywords)">
</label>
</div>
{{#unless public}}
{{#unless ldapEnabled}}
<div id="change-password">
<form
action=""
method="post"
data-event="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>
<input
id="old_password_input"
type="password"
name="old_password"
class="input"
placeholder="Enter current password">
{{> ../reveal-password}}
</div>
<div class="col-sm-12 password-container">
<label
for="new_password_input"
class="sr-only">Enter desired new password</label>
<input
id="new_password_input"
type="password"
name="new_password"
class="input"
placeholder="Enter desired new password">
{{> ../reveal-password}}
</div>
<div class="col-sm-12 password-container">
<label
for="verify_password_input"
class="sr-only">Repeat new password</label>
<input
id="verify_password_input"
type="password"
name="verify_password"
class="input"
placeholder="Repeat new password">
{{> ../reveal-password}}
</div>
<div class="col-sm-12 feedback" />
<div class="col-sm-12">
<button
type="submit"
class="btn">Change password</button>
</div>
</form>
</div>
{{/unless}}
{{/unless}}
<div
class="col-sm-12"
data-advanced>
<h2>Custom Stylesheet</h2>
</div>
<div
class="col-sm-12"
data-advanced>
<label
for="user-specified-css-input"
class="sr-only">Custom stylesheet. You can override any style with CSS here.</label>
<textarea
id="user-specified-css-input"
class="input"
name="userStyles"
placeholder="/* You can override any style with CSS here */" />
</div>
</div>
{{#unless public}}
<div class="session-list">
<h2>Sessions</h2>
<h3>Current session</h3>
<div id="session-current" />
<h3>Other sessions</h3>
<div id="session-list" />
</div>
{{/unless}}
</div>
</div>
</template>
<script>
const storage = require("../../js/localStorage");
import socket from "../../js/socket";
import RevealPassword from "../RevealPassword.vue";
export default {
name: "Settings",
components: {
RevealPassword,
},
data() {
return {
inFlight: false,
errorShown: false,
};
},
methods: {
onSubmit(event) {
event.preventDefault();
this.inFlight = true;
this.errorShown = false;
const values = {
user: this.$refs.username.value,
password: this.$refs.password.value,
};
storage.set("user", values.user);
socket.emit("auth", values);
},
},
};
</script>

View File

@ -0,0 +1,104 @@
<template>
<div
id="sign-in"
class="window"
role="tabpanel"
aria-label="Sign-in">
<form
class="container"
method="post"
action=""
@submit="onSubmit">
<img
src="img/logo-vertical-transparent-bg.svg"
class="logo"
alt="The Lounge"
width="256"
height="170">
<img
src="img/logo-vertical-transparent-bg-inverted.svg"
class="logo-inverted"
alt="The Lounge"
width="256"
height="170">
<label for="signin-username">Username</label>
<input
id="signin-username"
ref="username"
class="input"
type="text"
name="username"
autocapitalize="none"
autocorrect="off"
autocomplete="username"
:value="getStoredUser()"
required
autofocus>
<div class="password-container">
<label for="signin-password">Password</label>
<RevealPassword v-slot:default="slotProps">
<input
id="signin-password"
ref="password"
:type="slotProps.isVisible ? 'text' : 'password'"
name="password"
class="input"
autocapitalize="none"
autocorrect="off"
autocomplete="current-password"
required>
</RevealPassword>
</div>
<div
v-if="errorShown"
class="error">Authentication failed.</div>
<button
:disabled="inFlight"
type="submit"
class="btn">Sign in</button>
</form>
</div>
</template>
<script>
const storage = require("../../js/localStorage");
import socket from "../../js/socket";
import RevealPassword from "../RevealPassword.vue";
export default {
name: "SignIn",
components: {
RevealPassword,
},
data() {
return {
inFlight: false,
errorShown: false,
};
},
methods: {
onSubmit(event) {
event.preventDefault();
this.inFlight = true;
this.errorShown = false;
const values = {
user: this.$refs.username.value,
password: this.$refs.password.value,
};
storage.set("user", values.user);
socket.emit("auth", values);
},
getStoredUser() {
return storage.get("user");
},
},
};
</script>

View File

@ -908,7 +908,8 @@ background on hover (unless active) */
.window {
background: var(--window-bg-color);
display: none;
display: flex;
flex-direction: column;
overflow-y: auto;
height: 100%;
scrollbar-width: thin;
@ -949,11 +950,6 @@ background on hover (unless active) */
margin: 20px 0 10px;
}
#windows .window.active {
display: flex;
flex-direction: column;
}
#windows .header {
line-height: 45px;
height: 45px;
@ -1749,7 +1745,6 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
#sign-in .error {
color: #e74c3c;
display: none; /* This message gets displayed on error only */
margin-top: 1em;
width: 100%;
}
@ -1878,7 +1873,7 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
content: "\f00c"; /* https://fontawesome.com/icons/check?style=solid */
}
.password-container .reveal-password.visible span::before {
.password-container .reveal-password-visible span::before {
content: "\f070"; /* https://fontawesome.com/icons/eye-slash?style=solid */
color: #ff4136;
}

View File

@ -127,7 +127,8 @@ window.vueMounted = () => {
}
}
if (inSidebar) {
if (channel) {
vueApp.activeWindow = null;
vueApp.activeChannel = channel;
if (channel) {

View File

@ -4,8 +4,7 @@ const $ = require("jquery");
const socket = require("../socket");
const storage = require("../localStorage");
const utils = require("../utils");
const templates = require("../../views");
const {vueApp} = require("../vue");
const {vueApp, getActiveWindowComponent} = require("../vue");
socket.on("auth", function(data) {
// If we reconnected and serverHash differs, that means the server restarted
@ -18,40 +17,19 @@ socket.on("auth", function(data) {
return;
}
const login = $("#sign-in");
if (data.serverHash > -1) {
utils.serverHash = data.serverHash;
login.html(templates.windows.sign_in());
utils.togglePasswordField("#sign-in .reveal-password");
login.find("form").on("submit", function() {
const form = $(this);
form.find(".btn").prop("disabled", true);
const values = {};
$.each(form.serializeArray(), function(i, obj) {
values[obj.name] = obj.value;
});
storage.set("user", values.user);
socket.emit("auth", values);
return false;
});
vueApp.activeWindow = "SignIn";
} else {
login.find(".btn").prop("disabled", false);
getActiveWindowComponent().inFlight = false;
}
let token;
const user = storage.get("user");
if (!data.success) {
if (login.length === 0) {
if (vueApp.activeWindow !== "SignIn") {
socket.disconnect();
vueApp.isConnected = false;
vueApp.currentUserVisibleError = "Authentication failed, reloading…";
@ -61,13 +39,7 @@ socket.on("auth", function(data) {
storage.remove("token");
const error = login.find(".error");
error
.show()
.closest("form")
.one("submit", function() {
error.hide();
});
getActiveWindowComponent().errorShown = true;
} else if (user) {
token = storage.get("token");
@ -95,10 +67,6 @@ socket.on("auth", function(data) {
}
}
if (user) {
login.find("input[name='user']").val(user);
}
if (token) {
return;
}

View File

@ -52,8 +52,6 @@ socket.on("configuration", function(data) {
upload.initialize(data.fileUploadMaxFileSize);
}
utils.togglePasswordField("#change-password .reveal-password");
options.initialize();
webpush.initialize();
@ -112,8 +110,6 @@ socket.on("configuration", function(data) {
// Store the "previous" value, for next time
$(this).data("lastvalue", nick);
});
utils.togglePasswordField("#connect .reveal-password");
});
if ("URLSearchParams" in window) {

View File

@ -49,7 +49,6 @@ socket.on("init", function(data) {
$(document.body).removeClass("signed-out");
$("#loading").remove();
$("#sign-in").remove();
if (window.g_LoungeErrorHandler) {
window.removeEventListener("error", window.g_LoungeErrorHandler);

View File

@ -75,6 +75,4 @@ socket.on("network:info", function(data) {
sidebar.find(`.network[data-uuid="${uuid}"] .chan.lobby .name`).click();
});
utils.togglePasswordField("#connect .reveal-password");
});

View File

@ -18,7 +18,6 @@ module.exports = {
move,
closeChan,
synchronizeNotifiedState,
togglePasswordField,
requestIdleCallback,
};
@ -104,25 +103,6 @@ function updateTitle() {
document.title = title;
}
function togglePasswordField(elem) {
$(elem).on("click", function() {
const $this = $(this);
const input = $this.closest("div").find("input");
input.attr("type", input.attr("type") === "password" ? "text" : "password");
swapLabel($this);
swapLabel($this.find("span"));
$this.toggleClass("visible");
});
}
// Given a element, swap its aria-label with the content of `data-alt-label`
function swapLabel(element) {
const altText = element.data("alt-label");
element.data("alt-label", element.attr("aria-label")).attr("aria-label", altText);
}
function confirmExit() {
if ($(document.body).hasClass("public")) {
window.onbeforeunload = function() {

View File

@ -15,6 +15,7 @@ Vue.filter("roundBadgeNumber", roundBadgeNumber);
const vueApp = new Vue({
el: "#viewport",
data: {
activeWindow: null,
activeChannel: null,
appName: document.title,
currentUserVisibleError: null,
@ -48,6 +49,7 @@ const vueApp = new Vue({
},
render(createElement) {
return createElement(App, {
ref: "app",
props: this,
});
},
@ -86,8 +88,13 @@ function initChannel(channel) {
}
}
function getActiveWindowComponent() {
return vueApp.$refs.app.$refs.window;
}
module.exports = {
vueApp,
findChannel,
initChannel,
getActiveWindowComponent,
};

View File

@ -1,3 +0,0 @@
<span class="reveal-password tooltipped tooltipped-n tooltipped-no-delay" aria-label="Show password" data-alt-label="Hide password">
<span type="button" aria-label="Show password" data-alt-label="Hide password"></span>
</span>

View File

@ -1,20 +0,0 @@
<div class="header">
<button class="lt" aria-label="Toggle channel list"></button>
</div>
<div class="container">
<a href="#" id="back-to-help" data-target="#help">« Help</a>
{{#if version}}
<h1 class="title">Release notes for {{version}}</h1>
{{#if changelog}}
<h3>Introduction</h3>
<div class="changelog-text">{{{changelog}}}</div>
{{else}}
<p>Unable to retrieve releases from GitHub.</p>
<p><a href="https://github.com/thelounge/thelounge/releases/tag/v{{version}}" target="_blank" rel="noopener">View release notes for this version on GitHub</a></p>
{{/if}}
{{else}}
<p>Loading changelog…</p>
{{/if}}
</div>

View File

@ -1,114 +0,0 @@
<div class="header">
<button class="lt" aria-label="Toggle channel list"></button>
</div>
<form class="container" method="post" action="" data-event="{{#if defaults.uuid}}network:edit{{else}}network:new{{/if}}">
<div class="row">
<div class="col-sm-12">
<h1 class="title">
{{#if defaults.uuid}}
<input type="hidden" name="uuid" value="{{defaults.uuid}}">
Edit {{defaults.name}}
{{else}}
{{#if public}}The Lounge - {{/if}}
Connect
{{#unless displayNetwork}}
{{#if lockNetwork}}
to {{defaults.name}}
{{/if}}
{{/unless}}
{{/if}}
</h1>
</div>
{{#if displayNetwork}}
<div>
<div class="col-sm-12">
<h2>Network settings</h2>
</div>
<div class="col-sm-3">
<label for="connect:name">Name</label>
</div>
<div class="col-sm-9">
<input class="input" id="connect:name" name="name" value="{{defaults.name}}" maxlength="100">
</div>
<div class="col-sm-3">
<label for="connect:host">Server</label>
</div>
<div class="col-sm-6 col-xs-8">
<input class="input" id="connect:host" name="host" value="{{defaults.host}}" aria-label="Server address" maxlength="255" required {{#if lockNetwork}}disabled{{/if}}>
</div>
<div class="col-sm-3 col-xs-4">
<div class="port">
<input class="input" type="number" min="1" max="65535" name="port" value="{{defaults.port}}" aria-label="Server port" {{#if lockNetwork}}disabled{{/if}}>
</div>
</div>
<div class="clearfix"></div>
<div class="col-sm-9 col-sm-offset-3">
<label class="tls">
<input type="checkbox" name="tls" {{#if defaults.tls}}checked{{/if}} {{#if lockNetwork}}disabled{{/if}}>
Use secure connection (TLS)
</label>
</div>
<div class="col-sm-9 col-sm-offset-3">
<label class="tls">
<input type="checkbox" name="rejectUnauthorized" {{#if defaults.rejectUnauthorized}}checked{{/if}} {{#if lockNetwork}}disabled{{/if}}>
Only allow trusted certificates
</label>
</div>
<div class="clearfix"></div>
</div>
{{/if}}
<div class="col-sm-12">
<h2>User preferences</h2>
</div>
<div class="col-sm-3">
<label for="connect:nick">Nick</label>
</div>
<div class="col-sm-9">
<input class="input nick" id="connect:nick" name="nick" value="{{defaults.nick}}" maxlength="100" required>
</div>
{{#unless useHexIp}}
<div class="col-sm-3">
<label for="connect:username">Username</label>
</div>
<div class="col-sm-9">
<input class="input username" id="connect:username" name="username" value="{{defaults.username}}" maxlength="512">
</div>
{{/unless}}
<div class="col-sm-3">
<label for="connect:password">Password</label>
</div>
<div class="col-sm-9 password-container">
<input class="input" id="connect:password" type="password" name="password" value="{{defaults.password}}" maxlength="512">
{{> ../reveal-password}}
</div>
<div class="col-sm-3">
<label for="connect:realname">Real name</label>
</div>
<div class="col-sm-9">
<input class="input" id="connect:realname" name="realname" value="{{defaults.realname}}" maxlength="512">
</div>
{{#if defaults.uuid}}
<div class="col-sm-3">
<label for="connect:commands">Commands</label>
</div>
<div class="col-sm-9">
<textarea class="input" id="connect:commands" name="commands" placeholder="One raw command per line, each command will be executed on new connection">{{~#each defaults.commands~}}{{~this}}
{{/each~}}</textarea>
</div>
<div class="col-sm-9 col-sm-offset-3">
<button type="submit" class="btn">Save</button>
</div>
{{else}}
<div class="col-sm-3">
<label for="connect:channels">Channels</label>
</div>
<div class="col-sm-9">
<input class="input" id="connect:channels" name="join" value="{{defaults.join}}">
</div>
<div class="col-sm-9 col-sm-offset-3">
<button type="submit" class="btn">Connect</button>
</div>
{{/if}}
</div>
</form>

View File

@ -1,608 +0,0 @@
<div class="header">
<button class="lt" aria-label="Toggle channel list"></button>
</div>
<div class="container">
<h1 class="title">Help</h1>
<h2>
<small class="pull-right">
v{{version}}
(<a href="#" id="view-changelog" data-target="#changelog">release notes</a>)
</small>
About The Lounge
</h2>
<div class="about">
<div id="version-checker"></div>
{{#if gitCommit}}
<p>
The Lounge is running from source
(<a href="https://github.com/thelounge/thelounge/tree/{{gitCommit}}" target="_blank" rel="noopener">commit <code>{{gitCommit}}</code></a>).
</p>
<ul>
<li>
Compare
<a href="https://github.com/thelounge/thelounge/compare/{{gitCommit}}...master" target="_blank" rel="noopener">between <code>{{gitCommit}}</code> and <code>master</code></a>
to see what you are missing
</li>
<li>
Compare
<a href="https://github.com/thelounge/thelounge/compare/{{version}}...{{gitCommit}}" target="_blank" rel="noopener">between <code>{{version}}</code> and <code>{{gitCommit}}</code></a>
to see your local changes</li>
</ul>
{{/if}}
<p>
<a href="https://thelounge.chat/" target="_blank" rel="noopener" class="website-link">Website</a>
</p>
<p>
<a href="https://thelounge.chat/docs/" target="_blank" rel="noopener" class="documentation-link">Documentation</a>
</p>
<p>
<a href="https://github.com/thelounge/thelounge/issues/new" target="_blank" rel="noopener" class="report-issue-link">Report an issue…</a>
</p>
</div>
<h2>Keyboard Shortcuts</h2>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd>↓</kbd></span>
<span class="key-apple"><kbd>⌥</kbd> <kbd>⇧</kbd> <kbd>↓</kbd></span>
</div>
<div class="description">
<p>Switch to the next lobby in the channel list.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd>↑</kbd></span>
<span class="key-apple"><kbd>⌥</kbd> <kbd>⇧</kbd> <kbd>↑</kbd></span>
</div>
<div class="description">
<p>Switch to the previous lobby in the channel list.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Alt</kbd> <kbd>↓</kbd></span>
<span class="key-apple"><kbd>⌥</kbd> <kbd>↓</kbd></span>
</div>
<div class="description">
<p>Switch to the next window in the channel list.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Alt</kbd> <kbd>↑</kbd></span>
<span class="key-apple"><kbd>⌥</kbd> <kbd>↑</kbd></span>
</div>
<div class="description">
<p>Switch to the previous window in the channel list.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Alt</kbd> <kbd>A</kbd></span>
<span class="key-apple"><kbd>⌥</kbd> <kbd>A</kbd></span>
</div>
<div class="description">
<p>Switch to the first window with unread messages.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Ctrl</kbd> <kbd>K</kbd></span>
<span class="key-apple"><kbd>⌘</kbd> <kbd>K</kbd></span>
</div>
<div class="description">
<p>
Mark any text typed after this shortcut to be colored. After
hitting this shortcut, enter an integer in the range
<code>0—15</code> to select the desired color, or use the
autocompletion menu to choose a color name (see below).
</p>
<p>
Background color can be specified by putting a comma and
another integer in the range <code>0—15</code> after the
foreground color number (autocompletion works too).
</p>
<p>
A color reference can be found
<a href="https://modern.ircdocs.horse/formatting.html#colors" target="_blank" rel="noopener">here</a>.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Ctrl</kbd> <kbd>B</kbd></span>
<span class="key-apple"><kbd>⌘</kbd> <kbd>B</kbd></span>
</div>
<div class="description">
<p>Mark all text typed after this shortcut as <span class="irc-bold">bold</span>.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Ctrl</kbd> <kbd>U</kbd></span>
<span class="key-apple"><kbd>⌘</kbd> <kbd>U</kbd></span>
</div>
<div class="description">
<p>Mark all text typed after this shortcut as <span class="irc-underline">underlined</span>.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Ctrl</kbd> <kbd>I</kbd></span>
<span class="key-apple"><kbd>⌘</kbd> <kbd>I</kbd></span>
</div>
<div class="description">
<p>Mark all text typed after this shortcut as <span class="irc-italic">italics</span>.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Ctrl</kbd> <kbd>S</kbd></span>
<span class="key-apple"><kbd>⌘</kbd> <kbd>S</kbd></span>
</div>
<div class="description">
<p>Mark all text typed after this shortcut as <span class="irc-strikethrough">struck through</span>.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Ctrl</kbd> <kbd>M</kbd></span>
<span class="key-apple"><kbd>⌘</kbd> <kbd>M</kbd></span>
</div>
<div class="description">
<p>Mark all text typed after this shortcut as <span class="irc-monospace">monospaced</span>.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span class="key-all"><kbd>Ctrl</kbd> <kbd>O</kbd></span>
<span class="key-apple"><kbd>⌘</kbd> <kbd>O</kbd></span>
</div>
<div class="description">
<p>
Mark all text typed after this shortcut to be reset to its
original formatting.
</p>
</div>
</div>
<h2>Autocompletion</h2>
<p>
To auto-complete nicknames, channels, commands, and emoji, type one of the characters below to open
a suggestion list. Use the <kbd>↑</kbd> and <kbd>↓</kbd> keys to highlight an item, and insert it by
pressing <kbd>Tab</kbd> or <kbd>Enter</kbd> (or by clicking the desired item).
</p>
<p>
Autocompletion can be disabled in settings.
</p>
<div class="help-item">
<div class="subject">
<code>@</code>
</div>
<div class="description">
<p>Nickname</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>#</code>
</div>
<div class="description">
<p>Channel</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/</code>
</div>
<div class="description">
<p>Commands (see list of commands below)</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>:</code>
</div>
<div class="description">
<p>Emoji (note: requires two search characters, to avoid conflicting with common emoticons like <code>:)</code>)</p>
</div>
</div>
<h2>Commands</h2>
<div class="help-item">
<div class="subject">
<code>/away [message]</code>
</div>
<div class="description">
<p>Mark yourself as away with an optional message.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/back</code>
</div>
<div class="description">
<p>Remove your away status (set with <code>/away</code>).</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/ban nick</code>
</div>
<div class="description">
<p>Ban (<code>+b</code>) a user from the current channel.
This can be a nickname or a hostmask.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/banlist</code>
</div>
<div class="description">
<p>Load the banlist for the current channel.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/collapse</code>
</div>
<div class="description">
<p>
Collapse all previews in the current channel (opposite of
<code>/expand</code>)
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/connect host [port]</code>
</div>
<div class="description">
<p>
Connect to a new IRC network. If <code>port</code> starts with
a <code>+</code> sign, the connection will be made secure
using TLS.
</p>
<p>Alias: <code>/server</code></p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/ctcp target cmd [args]</code>
</div>
<div class="description">
<p>
Send a <abbr title="Client-to-client protocol">CTCP</abbr>
request. Read more about this on
<a href="https://en.wikipedia.org/wiki/Client-to-client_protocol" target="_blank" rel="noopener">the dedicated Wikipedia article</a>.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/deop nick [...nick]</code>
</div>
<div class="description">
<p>
Remove op (<code>-o</code>) from one or several users in the
current channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/devoice nick [...nick]</code>
</div>
<div class="description">
<p>
Remove voice (<code>-v</code>) from one or several users in
the current channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/disconnect [message]</code>
</div>
<div class="description">
<p>
Disconnect from the current network with an
optionally-provided message.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/expand</code>
</div>
<div class="description">
<p>
Expand all previews in the current channel (opposite of
<code>/collapse</code>)
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/invite nick [channel]</code>
</div>
<div class="description">
<p>
Invite a user to the specified channel. If
<code>channel</code> is omitted, user will be invited to the
current channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/ignore nick</code>
</div>
<div class="description">
<p>
Block any messages from the specified user on the current network.
This can be a nickname or a hostmask.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/ignorelist</code>
</div>
<div class="description">
<p>Load the list of ignored users for the current network.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/join channel</code>
</div>
<div class="description">
<p>Join a channel.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/kick nick</code>
</div>
<div class="description">
<p>Kick a user from the current channel.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/list</code>
</div>
<div class="description">
<p>Retrieve a list of available channels on this network.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/me message</code>
</div>
<div class="description">
<p>
Send an action message to the current channel. The Lounge will
display it inline, as if the message was posted in the third
person.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/mode flags [args]</code>
</div>
<div class="description">
<p>
Set the given flags to the current channel if the active
window is a channel, another user if the active window is a
private message window, or yourself if the current window is a
server window.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/msg channel message</code>
</div>
<div class="description">
<p>Send a message to the specified channel.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/nick newnick</code>
</div>
<div class="description">
<p>Change your nickname on the current network.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/notice channel message</code>
</div>
<div class="description">
<p>Sends a notice message to the specified channel.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/op nick [...nick]</code>
</div>
<div class="description">
<p>
Give op (<code>+o</code>) to one or several users in the
current channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/part [channel]</code>
</div>
<div class="description">
<p>
Close the specified channel or private message window, or the
current channel if <code>channel</code> is omitted.
</p>
<p>Aliases: <code>/close</code>, <code>/leave</code></p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/rejoin</code>
</div>
<div class="description">
<p>
Leave and immediately rejoin the current channel. Useful to
quickly get op from ChanServ in an empty channel, for example.
</p>
<p>Alias: <code>/cycle</code></p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/query nick</code>
</div>
<div class="description">
<p>Send a private message to the specified user.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/quit [message]</code>
</div>
<div class="description">
<p>
Disconnect from the current network with an optional message.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/raw message</code>
</div>
<div class="description">
<p>Send a raw message to the current IRC network.</p>
<p>Aliases: <code>/quote</code>, <code>/send</code></p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/slap nick</code>
</div>
<div class="description">
<p>Slap someone in the current channel with a trout!</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/topic [newtopic]</code>
</div>
<div class="description">
<p>
Get the topic in the current channel.
If <code>newtopic</code> is specified, sets the
topic in the current channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/unban nick</code>
</div>
<div class="description">
<p>Unban (<code>-b</code>) a user from the current channel.
This can be a nickname or a hostmask.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/unignore nick</code>
</div>
<div class="description">
<p>
Unblock messages from the specified user on the current network.
This can be a nickname or a hostmask.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/voice nick [...nick]</code>
</div>
<div class="description">
<p>
Give voice (<code>+v</code>) to one or several users in the
current channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/whois nick</code>
</div>
<div class="description">
<p>
Retrieve information about the given user on the current
network.
</p>
</div>
</div>
</div>

View File

@ -1,239 +0,0 @@
<div class="header">
<button class="lt" aria-label="Toggle channel list"></button>
</div>
<div class="container">
<h1 class="title">Settings</h1>
<div class="row">
<div class="col-sm-6">
<label class="opt">
<input type="checkbox" name="advanced">
Advanced settings
</label>
</div>
</div>
<div class="row">
<div class="col-sm-12" id="native-app" hidden>
<h2>Native app</h2>
<button type="button" class="btn" id="webapp-install-button" hidden>Add The Lounge to Home screen</button>
<button type="button" class="btn" id="make-default-client">Open irc:// URLs with The Lounge</button>
</div>
{{#unless public}}
<div class="col-sm-12" data-advanced>
<h2>
Settings synchronisation
<span class="tooltipped tooltipped-n tooltipped-no-delay" aria-label="Note: This is an experimental feature and may change in future releases.">
<button class="extra-experimental" aria-label="Note: This is an experimental feature and may change in future releases."></button>
</span>
</h2>
<label class="opt">
<input type="checkbox" name="syncSettings">
Synchronize settings with other clients.
</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 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 class="opt force-sync-button">
<button type="button" class="btn" id="forceSync">Force sync settings</button>
<p>This will override any settings already synced to the server.</p>
</div>
</div>
{{/unless}}
<div class="col-sm-12">
<h2>Messages</h2>
</div>
<div class="col-sm-6">
<label class="opt">
<input type="checkbox" name="motd">
Show <abbr title="Message Of The Day">MOTD</abbr>
</label>
</div>
<div class="col-sm-6">
<label class="opt">
<input type="checkbox" name="showSeconds">
Show seconds in timestamp
</label>
</div>
<div class="col-sm-12">
<h2>
Status messages
<span class="tooltipped tooltipped-n tooltipped-no-delay" aria-label="Joins, parts, kicks, nick changes, away changes, and mode changes">
<button class="extra-help" aria-label="Joins, parts, kicks, nick changes, away changes, and mode changes"></button>
</span>
</h2>
</div>
<div class="col-sm-12">
<label class="opt">
<input type="radio" name="statusMessages" value="shown">
Show all status messages individually
</label>
<label class="opt">
<input type="radio" name="statusMessages" value="condensed">
Condense status messages together
</label>
<label class="opt">
<input type="radio" name="statusMessages" value="hidden">
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 type="checkbox" name="coloredNicks">
Enable colored nicknames
</label>
<label class="opt">
<input type="checkbox" name="autocomplete">
Enable autocomplete
</label>
</div>
<div class="col-sm-12" data-advanced>
<label class="opt">
<label for="nickPostfix" class="sr-only">Nick autocomplete postfix (e.g. <code>, </code>)</label>
<input type="text" id="nickPostfix" name="nickPostfix" class="input" placeholder="Nick autocomplete postfix (e.g. ', ')">
</label>
</div>
<div class="col-sm-12">
<h2>Theme</h2>
</div>
<div class="col-sm-12">
<label for="theme-select" class="sr-only">Theme</label>
<select id="theme-select" name="theme" class="input">
{{#each themes}}
<option value="{{name}}" data-theme-color="{{themeColor}}">
{{displayName}}
</option>
{{/each}}
</select>
</div>
{{#if prefetch}}
<div class="col-sm-12">
<h2>Link previews</h2>
</div>
<div class="col-sm-6">
<label class="opt">
<input type="checkbox" name="media">
Auto-expand media
</label>
</div>
<div class="col-sm-6">
<label class="opt">
<input type="checkbox" name="links">
Auto-expand websites
</label>
</div>
{{/if}}
{{#unless public}}
<div class="col-sm-12">
<h2>Push Notifications</h2>
</div>
<div class="col-sm-12">
<button type="button" class="btn" id="pushNotifications" disabled data-text-alternate="Unsubscribe from push notifications">Subscribe to push notifications</button>
<div class="error" id="pushNotificationsHttps">
<strong>Warning</strong>:
Push notifications are only supported over HTTPS connections.
</div>
<div class="error" id="pushNotificationsUnsupported">
<strong>Warning</strong>:
<span>Push notifications are not supported by your browser.</span>
</div>
</div>
{{/unless}}
<div class="col-sm-12">
<h2>Browser Notifications</h2>
</div>
<div class="col-sm-12">
<label class="opt">
<input id="desktopNotifications" type="checkbox" name="desktopNotifications">
Enable browser notifications<br>
<div class="error" id="warnUnsupportedDesktopNotifications">
<strong>Warning</strong>:
Notifications are not supported by your browser.
</div>
<div class="error" id="warnBlockedDesktopNotifications">
<strong>Warning</strong>:
Notifications are blocked by your browser.
</div>
</label>
</div>
<div class="col-sm-12">
<label class="opt">
<input type="checkbox" name="notification">
Enable notification sound
</label>
</div>
<div class="col-sm-12">
<div class="opt">
<button id="play">Play sound</button>
</div>
</div>
<div class="col-sm-12" data-advanced>
<label class="opt">
<input type="checkbox" name="notifyAllMessages">
Enable notification for all messages
</label>
</div>
<div class="col-sm-12" data-advanced>
<label class="opt">
<label for="highlights" class="sr-only">Custom highlights (comma-separated keywords)</label>
<input type="text" id="highlights" name="highlights" class="input" placeholder="Custom highlights (comma-separated keywords)">
</label>
</div>
{{#unless public}}
{{#unless ldapEnabled}}
<div id="change-password">
<form action="" method="post" data-event="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>
<input type="password" id="old_password_input" name="old_password" class="input" placeholder="Enter current password">
{{> ../reveal-password}}
</div>
<div class="col-sm-12 password-container">
<label for="new_password_input" class="sr-only">Enter desired new password</label>
<input type="password" id="new_password_input" name="new_password" class="input" placeholder="Enter desired new password">
{{> ../reveal-password}}
</div>
<div class="col-sm-12 password-container">
<label for="verify_password_input" class="sr-only">Repeat new password</label>
<input type="password" id="verify_password_input" name="verify_password" class="input" placeholder="Repeat new password">
{{> ../reveal-password}}
</div>
<div class="col-sm-12 feedback"></div>
<div class="col-sm-12">
<button type="submit" class="btn">Change password</button>
</div>
</form>
</div>
{{/unless}}
{{/unless}}
<div class="col-sm-12" data-advanced>
<h2>Custom Stylesheet</h2>
</div>
<div class="col-sm-12" data-advanced>
<label for="user-specified-css-input" class="sr-only">Custom stylesheet. You can override any style with CSS here.</label>
<textarea class="input" name="userStyles" id="user-specified-css-input" placeholder="/* You can override any style with CSS here */"></textarea>
</div>
</div>
{{#unless public}}
<div class="session-list">
<h2>Sessions</h2>
<h3>Current session</h3>
<div id="session-current"></div>
<h3>Other sessions</h3>
<div id="session-list"></div>
</div>
{{/unless}}
</div>

View File

@ -1,17 +0,0 @@
<form class="container" method="post" action="">
<img src="img/logo-vertical-transparent-bg.svg" class="logo" alt="The Lounge" width="256" height="170">
<img src="img/logo-vertical-transparent-bg-inverted.svg" class="logo-inverted" alt="The Lounge" width="256" height="170">
<label>Username</label>
<input class="input" type="text" name="user" autocapitalize="none" autocorrect="off" autocomplete="username" required autofocus>
<div class="password-container">
<label>Password</label>
<input class="input" type="password" name="password" autocapitalize="none" autocorrect="off" autocomplete="current-password" required>
{{> ../reveal-password}}
</div>
<div class="error">Authentication failed.</div>
<button type="submit" class="btn">Sign in</button>
</form>