Begin moving windows to Vue
This commit is contained in:
parent
bdb0a2efca
commit
09e12affe8
@ -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,
|
||||
},
|
||||
|
33
client/components/RevealPassword.vue
Normal file
33
client/components/RevealPassword.vue
Normal 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>
|
42
client/components/Windows/Changelog.vue
Normal file
42
client/components/Windows/Changelog.vue
Normal 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>
|
655
client/components/Windows/Help.vue
Normal file
655
client/components/Windows/Help.vue
Normal 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>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 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>
|
243
client/components/Windows/NetworkEdit.vue
Normal file
243
client/components/Windows/NetworkEdit.vue
Normal 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>
|
415
client/components/Windows/Settings.vue
Normal file
415
client/components/Windows/Settings.vue
Normal 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>
|
104
client/components/Windows/SignIn.vue
Normal file
104
client/components/Windows/SignIn.vue
Normal 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>
|
@ -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;
|
||||
}
|
||||
|
@ -127,7 +127,8 @@ window.vueMounted = () => {
|
||||
}
|
||||
}
|
||||
|
||||
if (inSidebar) {
|
||||
if (channel) {
|
||||
vueApp.activeWindow = null;
|
||||
vueApp.activeChannel = channel;
|
||||
|
||||
if (channel) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
});
|
||||
|
@ -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() {
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
Loading…
Reference in New Issue
Block a user