Channel list rendering with Vue
Co-Authored-By: Tim Miller-Williams <timmw@users.noreply.github.com>
This commit is contained in:
parent
18bca3bce1
commit
7e332b817d
@ -85,8 +85,15 @@ rules:
|
|||||||
space-in-parens: [error, never]
|
space-in-parens: [error, never]
|
||||||
space-infix-ops: error
|
space-infix-ops: error
|
||||||
spaced-comment: [error, always]
|
spaced-comment: [error, always]
|
||||||
strict: error
|
strict: off
|
||||||
template-curly-spacing: error
|
template-curly-spacing: error
|
||||||
yoda: error
|
yoda: error
|
||||||
|
vue/html-indent: [error, tab]
|
||||||
|
vue/require-default-prop: off
|
||||||
|
|
||||||
extends: eslint:recommended
|
plugins:
|
||||||
|
- vue
|
||||||
|
|
||||||
|
extends:
|
||||||
|
- eslint:recommended
|
||||||
|
- plugin:vue/recommended
|
||||||
|
130
client/components/App.vue
Normal file
130
client/components/App.vue
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
id="viewport"
|
||||||
|
role="tablist">
|
||||||
|
<aside id="sidebar">
|
||||||
|
<div class="scrollable-area">
|
||||||
|
<div class="logo-container">
|
||||||
|
<img
|
||||||
|
:src="`img/logo-${isPublic() ? 'horizontal-' : ''}transparent-bg.svg`"
|
||||||
|
class="logo"
|
||||||
|
alt="The Lounge">
|
||||||
|
<img
|
||||||
|
:src="`img/logo-${isPublic() ? 'horizontal-' : ''}transparent-bg-inverted.svg`"
|
||||||
|
class="logo-inverted"
|
||||||
|
alt="The Lounge">
|
||||||
|
</div>
|
||||||
|
<Network
|
||||||
|
:networks="networks"
|
||||||
|
:active-channel="activeChannel"/>
|
||||||
|
</div>
|
||||||
|
<footer id="footer">
|
||||||
|
<span
|
||||||
|
class="tooltipped tooltipped-n tooltipped-no-touch"
|
||||||
|
aria-label="Sign in"><button
|
||||||
|
class="icon sign-in"
|
||||||
|
data-target="#sign-in"
|
||||||
|
aria-label="Sign in"
|
||||||
|
role="tab"
|
||||||
|
aria-controls="sign-in"
|
||||||
|
aria-selected="false"/></span>
|
||||||
|
<span
|
||||||
|
class="tooltipped tooltipped-n tooltipped-no-touch"
|
||||||
|
aria-label="Connect to network"><button
|
||||||
|
class="icon connect"
|
||||||
|
data-target="#connect"
|
||||||
|
aria-label="Connect to network"
|
||||||
|
role="tab"
|
||||||
|
aria-controls="connect"
|
||||||
|
aria-selected="false"/></span>
|
||||||
|
<span
|
||||||
|
class="tooltipped tooltipped-n tooltipped-no-touch"
|
||||||
|
aria-label="Settings"><button
|
||||||
|
class="icon settings"
|
||||||
|
data-target="#settings"
|
||||||
|
aria-label="Settings"
|
||||||
|
role="tab"
|
||||||
|
aria-controls="settings"
|
||||||
|
aria-selected="false"/></span>
|
||||||
|
<span
|
||||||
|
class="tooltipped tooltipped-n tooltipped-no-touch"
|
||||||
|
aria-label="Help"><button
|
||||||
|
class="icon help"
|
||||||
|
data-target="#help"
|
||||||
|
aria-label="Help"
|
||||||
|
role="tab"
|
||||||
|
aria-controls="help"
|
||||||
|
aria-selected="false"/></span>
|
||||||
|
</footer>
|
||||||
|
</aside>
|
||||||
|
<div id="sidebar-overlay"/>
|
||||||
|
<article id="windows">
|
||||||
|
<div
|
||||||
|
id="chat-container"
|
||||||
|
class="window">
|
||||||
|
<div id="chat"/>
|
||||||
|
<div id="connection-error"/>
|
||||||
|
<form
|
||||||
|
id="form"
|
||||||
|
method="post"
|
||||||
|
action="">
|
||||||
|
<span id="nick"/>
|
||||||
|
<textarea
|
||||||
|
id="input"
|
||||||
|
class="mousetrap"/>
|
||||||
|
<span
|
||||||
|
id="submit-tooltip"
|
||||||
|
class="tooltipped tooltipped-w tooltipped-no-touch"
|
||||||
|
aria-label="Send message">
|
||||||
|
<button
|
||||||
|
id="submit"
|
||||||
|
type="submit"
|
||||||
|
aria-label="Send message"/>
|
||||||
|
</span>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<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"/>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Network from "./Network.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "App",
|
||||||
|
components: {
|
||||||
|
Network,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
activeChannel: Object,
|
||||||
|
networks: Array,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isPublic: () => document.body.classList.contains("public"),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
90
client/components/Channel.vue
Normal file
90
client/components/Channel.vue
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:key="channel.id"
|
||||||
|
:class="[ channel.type, { active: activeChannel && channel.id === activeChannel.channel.id } ]"
|
||||||
|
:aria-label="channel.name"
|
||||||
|
:title="channel.name"
|
||||||
|
:data-id="channel.id"
|
||||||
|
:data-target="'#chan-' + channel.id"
|
||||||
|
:aria-controls="'#chan-' + channel.id"
|
||||||
|
:aria-selected="activeChannel && channel.id === activeChannel.channel.id"
|
||||||
|
class="chan"
|
||||||
|
role="tab"
|
||||||
|
>
|
||||||
|
<template v-if="channel.type === 'lobby'">
|
||||||
|
<button
|
||||||
|
:aria-controls="'network-' + network.uuid"
|
||||||
|
class="collapse-network"
|
||||||
|
aria-label="Collapse"
|
||||||
|
aria-expanded="true">
|
||||||
|
<span class="collapse-network-icon"/>
|
||||||
|
</button>
|
||||||
|
<div class="lobby-wrap">
|
||||||
|
<span
|
||||||
|
:title="channel.name"
|
||||||
|
class="name">{{ channel.name }}</span>
|
||||||
|
<span
|
||||||
|
class="not-secure-tooltip tooltipped tooltipped-w"
|
||||||
|
aria-label="Insecure connection">
|
||||||
|
<span class="not-secure-icon"/>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="not-connected-tooltip tooltipped tooltipped-w"
|
||||||
|
aria-label="Disconnected">
|
||||||
|
<span class="not-connected-icon"/>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="channel.unread"
|
||||||
|
:class="{ highlight: channel.highlight }"
|
||||||
|
class="badge">{{ channel.unread | roundBadgeNumber }}</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="add-channel-tooltip tooltipped tooltipped-w tooltipped-no-touch"
|
||||||
|
aria-label="Join a channel…"
|
||||||
|
data-alt-label="Cancel">
|
||||||
|
<button
|
||||||
|
:aria-controls="'join-channel-' + channel.id"
|
||||||
|
class="add-channel"
|
||||||
|
aria-label="Join a channel…"/>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span
|
||||||
|
:title="channel.name"
|
||||||
|
class="name">{{ channel.name }}</span>
|
||||||
|
<span
|
||||||
|
v-if="channel.unread"
|
||||||
|
:class="{ highlight: channel.highlight }"
|
||||||
|
class="badge">{{ channel.unread | roundBadgeNumber }}</span>
|
||||||
|
<template v-if="channel.type === 'channel'">
|
||||||
|
<span
|
||||||
|
class="close-tooltip tooltipped tooltipped-w"
|
||||||
|
aria-label="Leave">
|
||||||
|
<button
|
||||||
|
class="close"
|
||||||
|
aria-label="Leave"/>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span
|
||||||
|
class="close-tooltip tooltipped tooltipped-w"
|
||||||
|
aria-label="Close">
|
||||||
|
<button
|
||||||
|
class="close"
|
||||||
|
aria-label="Close"/>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Channel",
|
||||||
|
props: {
|
||||||
|
activeChannel: Object,
|
||||||
|
network: Object,
|
||||||
|
channel: Object,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
42
client/components/JoinChannel.vue
Normal file
42
client/components/JoinChannel.vue
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<form
|
||||||
|
:id="'join-channel-' + channel.id"
|
||||||
|
class="join-form"
|
||||||
|
method="post"
|
||||||
|
action=""
|
||||||
|
autocomplete="off"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="input"
|
||||||
|
name="channel"
|
||||||
|
placeholder="Channel"
|
||||||
|
pattern="[^\s]+"
|
||||||
|
maxlength="200"
|
||||||
|
title="The channel name may not contain spaces"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
class="input"
|
||||||
|
name="key"
|
||||||
|
placeholder="Password (optional)"
|
||||||
|
pattern="[^\s]+"
|
||||||
|
maxlength="200"
|
||||||
|
title="The channel password may not contain spaces"
|
||||||
|
autocomplete="new-password"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-small">Join</button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "JoinChannel",
|
||||||
|
props: {
|
||||||
|
channel: Object,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
115
client/components/Network.vue
Normal file
115
client/components/Network.vue
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="networks.length === 0"
|
||||||
|
class="empty">
|
||||||
|
You are not connected to any networks yet.
|
||||||
|
</div>
|
||||||
|
<Draggable
|
||||||
|
v-else
|
||||||
|
:list="networks"
|
||||||
|
:options="{ handle: '.lobby', draggable: '.network', ghostClass: 'network-placeholder' }"
|
||||||
|
class="networks"
|
||||||
|
@change="onNetworkSort"
|
||||||
|
@start="onDragStart"
|
||||||
|
@end="onDragEnd"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="network in networks"
|
||||||
|
:key="network.uuid"
|
||||||
|
:class="{ 'not-connected': !network.status.connected, 'not-secure': !network.status.secure }"
|
||||||
|
:id="'network-' + network.uuid"
|
||||||
|
:data-uuid="network.uuid"
|
||||||
|
:data-nick="network.nick"
|
||||||
|
class="network"
|
||||||
|
role="region"
|
||||||
|
>
|
||||||
|
<Channel
|
||||||
|
:channel="network.channels[0]"
|
||||||
|
:network="network"
|
||||||
|
:active-channel="activeChannel"
|
||||||
|
/>
|
||||||
|
<JoinChannel :channel="network.channels[0]"/>
|
||||||
|
|
||||||
|
<Draggable
|
||||||
|
:options="{ draggable: '.chan', ghostClass: 'chan-placeholder' }"
|
||||||
|
:list="network.channels"
|
||||||
|
class="channels"
|
||||||
|
@change="onChannelSort"
|
||||||
|
@start="onDragStart"
|
||||||
|
@end="onDragEnd"
|
||||||
|
>
|
||||||
|
<Channel
|
||||||
|
v-for="(channel, index) in network.channels"
|
||||||
|
v-if="index > 0"
|
||||||
|
:key="channel.id"
|
||||||
|
:channel="channel"
|
||||||
|
:network="network"
|
||||||
|
:active-channel="activeChannel"
|
||||||
|
/>
|
||||||
|
</Draggable>
|
||||||
|
</div>
|
||||||
|
</Draggable>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Draggable from "vuedraggable";
|
||||||
|
import JoinChannel from "./JoinChannel.vue";
|
||||||
|
import Channel from "./Channel.vue";
|
||||||
|
|
||||||
|
// TODO: ignoreSortSync should be removed
|
||||||
|
import {findChannel} from "../js/vue";
|
||||||
|
import socket from "../js/socket";
|
||||||
|
// import options from "../js/options";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Network",
|
||||||
|
components: {
|
||||||
|
JoinChannel,
|
||||||
|
Channel,
|
||||||
|
Draggable,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
activeChannel: Object,
|
||||||
|
networks: Array,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onDragStart(e) {
|
||||||
|
e.target.classList.add("ui-sortable-helper");
|
||||||
|
},
|
||||||
|
onDragEnd(e) {
|
||||||
|
e.target.classList.remove("ui-sortable-helper");
|
||||||
|
},
|
||||||
|
onNetworkSort(e) {
|
||||||
|
if (!e.moved) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.emit("sort", {
|
||||||
|
type: "networks",
|
||||||
|
order: this.networks.map((n) => n.uuid),
|
||||||
|
});
|
||||||
|
|
||||||
|
// options.settings.ignoreSortSync = true;
|
||||||
|
},
|
||||||
|
onChannelSort(e) {
|
||||||
|
if (!e.moved) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const channel = findChannel(e.moved.element.id);
|
||||||
|
|
||||||
|
if (!channel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.emit("sort", {
|
||||||
|
type: "channels",
|
||||||
|
target: channel.network.uuid,
|
||||||
|
order: channel.network.channels.map((c) => c.id),
|
||||||
|
});
|
||||||
|
|
||||||
|
// options.settings.ignoreSortSync = true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
@ -603,10 +603,6 @@ background on hover (unless active) */
|
|||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar .networks:empty {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sidebar .network,
|
#sidebar .network,
|
||||||
#sidebar .network-placeholder {
|
#sidebar .network-placeholder {
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -631,7 +627,7 @@ background on hover (unless active) */
|
|||||||
#sidebar .chan-placeholder {
|
#sidebar .chan-placeholder {
|
||||||
border: 1px dashed #99a2b4;
|
border: 1px dashed #99a2b4;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
margin: -1px 10px;
|
margin: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar .network-placeholder {
|
#sidebar .network-placeholder {
|
||||||
@ -779,12 +775,6 @@ background on hover (unless active) */
|
|||||||
transform: rotate(45deg) translateZ(0);
|
transform: rotate(45deg) translateZ(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar .network .lobby:nth-last-child(2) .collapse-network {
|
|
||||||
/* Hide collapse button if there are no channels/queries */
|
|
||||||
width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sidebar .network .collapse-network {
|
#sidebar .network .collapse-network {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
@ -896,6 +886,7 @@ background on hover (unless active) */
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#loading,
|
||||||
#windows .window {
|
#windows .window {
|
||||||
background: var(--window-bg-color);
|
background: var(--window-bg-color);
|
||||||
display: none;
|
display: none;
|
||||||
@ -1605,7 +1596,7 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
|
|||||||
content: "Search Results";
|
content: "Search Results";
|
||||||
}
|
}
|
||||||
|
|
||||||
#loading.active {
|
#loading {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -1646,7 +1637,7 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
|
|||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#windows .logo-inverted {
|
#loading .logo-inverted {
|
||||||
display: none; /* In dark themes, inverted logo must be used instead */
|
display: none; /* In dark themes, inverted logo must be used instead */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2410,10 +2401,6 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
|
|||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar .empty::before {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#viewport .lt,
|
#viewport .lt,
|
||||||
#viewport .channel .rt {
|
#viewport .channel .rt {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -104,6 +104,7 @@
|
|||||||
<div id="changelog" class="window" aria-label="Changelog"></div>
|
<div id="changelog" class="window" aria-label="Changelog"></div>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="viewport"></div>
|
||||||
|
|
||||||
<div id="context-menu-container"></div>
|
<div id="context-menu-container"></div>
|
||||||
<div id="image-viewer"></div>
|
<div id="image-viewer"></div>
|
||||||
|
@ -8,7 +8,6 @@ const emojiMap = require("./libs/simplemap.json");
|
|||||||
const options = require("./options");
|
const options = require("./options");
|
||||||
const constants = require("./constants");
|
const constants = require("./constants");
|
||||||
|
|
||||||
const input = $("#input");
|
|
||||||
let textcomplete;
|
let textcomplete;
|
||||||
let enabled = false;
|
let enabled = false;
|
||||||
|
|
||||||
@ -16,6 +15,7 @@ module.exports = {
|
|||||||
enable: enableAutocomplete,
|
enable: enableAutocomplete,
|
||||||
disable() {
|
disable() {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
|
const input = $("#input");
|
||||||
input.off("input.tabcomplete");
|
input.off("input.tabcomplete");
|
||||||
Mousetrap(input.get(0)).unbind("tab", "keydown");
|
Mousetrap(input.get(0)).unbind("tab", "keydown");
|
||||||
textcomplete.destroy();
|
textcomplete.destroy();
|
||||||
@ -74,7 +74,7 @@ const nicksStrategy = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If there is whitespace in the input already, append space to nick
|
// If there is whitespace in the input already, append space to nick
|
||||||
if (position > 0 && /\s/.test(input.val())) {
|
if (position > 0 && /\s/.test($("#input").val())) {
|
||||||
return original + " ";
|
return original + " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,6 +179,7 @@ function enableAutocomplete() {
|
|||||||
let tabCount = 0;
|
let tabCount = 0;
|
||||||
let lastMatch = "";
|
let lastMatch = "";
|
||||||
let currentMatches = [];
|
let currentMatches = [];
|
||||||
|
const input = $("#input");
|
||||||
|
|
||||||
input.on("input.tabcomplete", () => {
|
input.on("input.tabcomplete", () => {
|
||||||
tabCount = 0;
|
tabCount = 0;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// vendor libraries
|
// vendor libraries
|
||||||
require("jquery-ui/ui/widgets/sortable");
|
|
||||||
const $ = require("jquery");
|
const $ = require("jquery");
|
||||||
const moment = require("moment");
|
const moment = require("moment");
|
||||||
|
|
||||||
@ -19,12 +18,12 @@ require("./keybinds");
|
|||||||
require("./clipboard");
|
require("./clipboard");
|
||||||
const contextMenuFactory = require("./contextMenuFactory");
|
const contextMenuFactory = require("./contextMenuFactory");
|
||||||
|
|
||||||
|
const {vueApp, findChannel} = require("./vue");
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
const sidebar = $("#sidebar, #footer");
|
const sidebar = $("#sidebar, #footer");
|
||||||
const chat = $("#chat");
|
const chat = $("#chat");
|
||||||
|
|
||||||
$(document.body).data("app-name", document.title);
|
|
||||||
|
|
||||||
const viewport = $("#viewport");
|
const viewport = $("#viewport");
|
||||||
|
|
||||||
function storeSidebarVisibility(name, state) {
|
function storeSidebarVisibility(name, state) {
|
||||||
@ -176,16 +175,14 @@ $(function() {
|
|||||||
self.data("id")
|
self.data("id")
|
||||||
);
|
);
|
||||||
|
|
||||||
sidebar.find(".active")
|
const channel = findChannel(self.data("id"));
|
||||||
.removeClass("active")
|
|
||||||
.attr("aria-selected", false);
|
|
||||||
|
|
||||||
self.addClass("active")
|
vueApp.activeChannel = channel;
|
||||||
.attr("aria-selected", true)
|
|
||||||
.find(".badge")
|
if (channel) {
|
||||||
.attr("data-highlight", 0)
|
channel.channel.highlight = 0;
|
||||||
.removeClass("highlight")
|
channel.channel.unread = 0;
|
||||||
.empty();
|
}
|
||||||
|
|
||||||
if (sidebar.find(".highlight").length === 0) {
|
if (sidebar.find(".highlight").length === 0) {
|
||||||
utils.toggleNotificationMarkers(false);
|
utils.toggleNotificationMarkers(false);
|
||||||
|
@ -5,7 +5,6 @@ const templates = require("../views");
|
|||||||
const options = require("./options");
|
const options = require("./options");
|
||||||
const renderPreview = require("./renderPreview");
|
const renderPreview = require("./renderPreview");
|
||||||
const utils = require("./utils");
|
const utils = require("./utils");
|
||||||
const sorting = require("./sorting");
|
|
||||||
const constants = require("./constants");
|
const constants = require("./constants");
|
||||||
const condensed = require("./condensed");
|
const condensed = require("./condensed");
|
||||||
const JoinChannel = require("./join-channel");
|
const JoinChannel = require("./join-channel");
|
||||||
@ -215,13 +214,6 @@ function renderChannelUsers(data) {
|
|||||||
function renderNetworks(data, singleNetwork) {
|
function renderNetworks(data, singleNetwork) {
|
||||||
const collapsed = new Set(JSON.parse(storage.get("thelounge.networks.collapsed")));
|
const collapsed = new Set(JSON.parse(storage.get("thelounge.networks.collapsed")));
|
||||||
|
|
||||||
sidebar.find(".empty").hide();
|
|
||||||
sidebar.find(".networks").append(
|
|
||||||
templates.network({
|
|
||||||
networks: data.networks,
|
|
||||||
}).trim()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add keyboard handlers to the "Join a channel…" form inputs/button
|
// Add keyboard handlers to the "Join a channel…" form inputs/button
|
||||||
JoinChannel.handleKeybinds(data.networks);
|
JoinChannel.handleKeybinds(data.networks);
|
||||||
|
|
||||||
@ -287,7 +279,6 @@ function renderNetworks(data, singleNetwork) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
utils.confirmExit();
|
utils.confirmExit();
|
||||||
sorting();
|
|
||||||
|
|
||||||
if (sidebar.find(".highlight").length) {
|
if (sidebar.find(".highlight").length) {
|
||||||
utils.toggleNotificationMarkers(true);
|
utils.toggleNotificationMarkers(true);
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const viewport = document.getElementById("viewport");
|
|
||||||
const menu = document.getElementById("sidebar");
|
|
||||||
const sidebarOverlay = document.getElementById("sidebar-overlay");
|
|
||||||
|
|
||||||
let touchStartPos = null;
|
let touchStartPos = null;
|
||||||
let touchCurPos = null;
|
let touchCurPos = null;
|
||||||
let touchStartTime = 0;
|
let touchStartTime = 0;
|
||||||
@ -14,12 +10,16 @@ let menuIsAbsolute = false;
|
|||||||
|
|
||||||
class SlideoutMenu {
|
class SlideoutMenu {
|
||||||
static enable() {
|
static enable() {
|
||||||
|
this.viewport = document.getElementById("viewport");
|
||||||
|
this.menu = document.getElementById("sidebar");
|
||||||
|
this.sidebarOverlay = document.getElementById("sidebar-overlay");
|
||||||
|
|
||||||
document.body.addEventListener("touchstart", onTouchStart, {passive: true});
|
document.body.addEventListener("touchstart", onTouchStart, {passive: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
static toggle(state) {
|
static toggle(state) {
|
||||||
menuIsOpen = state;
|
menuIsOpen = state;
|
||||||
viewport.classList.toggle("menu-open", state);
|
this.viewport.classList.toggle("menu-open", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static isOpen() {
|
static isOpen() {
|
||||||
@ -35,7 +35,7 @@ function onTouchStart(e) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = window.getComputedStyle(menu);
|
const styles = window.getComputedStyle(this.menu);
|
||||||
|
|
||||||
menuWidth = parseFloat(styles.width);
|
menuWidth = parseFloat(styles.width);
|
||||||
menuIsAbsolute = styles.position === "absolute";
|
menuIsAbsolute = styles.position === "absolute";
|
||||||
@ -65,7 +65,7 @@ function onTouchMove(e) {
|
|||||||
const devicePixelRatio = window.devicePixelRatio || 2;
|
const devicePixelRatio = window.devicePixelRatio || 2;
|
||||||
|
|
||||||
if (Math.abs(distX) > devicePixelRatio) {
|
if (Math.abs(distX) > devicePixelRatio) {
|
||||||
viewport.classList.toggle("menu-dragging", true);
|
this.viewport.classList.toggle("menu-dragging", true);
|
||||||
menuIsMoving = true;
|
menuIsMoving = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,8 +85,8 @@ function onTouchMove(e) {
|
|||||||
distX = 0;
|
distX = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.style.transform = "translate3d(" + distX + "px, 0, 0)";
|
this.menu.style.transform = "translate3d(" + distX + "px, 0, 0)";
|
||||||
sidebarOverlay.style.opacity = distX / menuWidth;
|
this.sidebarOverlay.style.opacity = distX / menuWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTouchEnd() {
|
function onTouchEnd() {
|
||||||
@ -99,9 +99,9 @@ function onTouchEnd() {
|
|||||||
|
|
||||||
document.body.removeEventListener("touchmove", onTouchMove);
|
document.body.removeEventListener("touchmove", onTouchMove);
|
||||||
document.body.removeEventListener("touchend", onTouchEnd);
|
document.body.removeEventListener("touchend", onTouchEnd);
|
||||||
viewport.classList.toggle("menu-dragging", false);
|
this.viewport.classList.toggle("menu-dragging", false);
|
||||||
menu.style.transform = null;
|
this.menu.style.transform = null;
|
||||||
sidebarOverlay.style.opacity = null;
|
this.sidebarOverlay.style.opacity = null;
|
||||||
|
|
||||||
touchStartPos = null;
|
touchStartPos = null;
|
||||||
touchCurPos = null;
|
touchCurPos = null;
|
||||||
|
@ -21,6 +21,7 @@ socket.on("auth", function(data) {
|
|||||||
if (data.serverHash > -1) {
|
if (data.serverHash > -1) {
|
||||||
utils.serverHash = data.serverHash;
|
utils.serverHash = data.serverHash;
|
||||||
|
|
||||||
|
$("#loading").remove();
|
||||||
login.html(templates.windows.sign_in());
|
login.html(templates.windows.sign_in());
|
||||||
|
|
||||||
utils.togglePasswordField("#sign-in .reveal-password");
|
utils.togglePasswordField("#sign-in .reveal-password");
|
||||||
|
@ -9,6 +9,7 @@ const slideoutMenu = require("../slideout");
|
|||||||
const sidebar = $("#sidebar");
|
const sidebar = $("#sidebar");
|
||||||
const storage = require("../localStorage");
|
const storage = require("../localStorage");
|
||||||
const utils = require("../utils");
|
const utils = require("../utils");
|
||||||
|
const {Vue, vueApp} = require("../vue");
|
||||||
|
|
||||||
socket.on("init", function(data) {
|
socket.on("init", function(data) {
|
||||||
$("#loading-page-message, #connection-error").text("Rendering…");
|
$("#loading-page-message, #connection-error").text("Rendering…");
|
||||||
@ -18,13 +19,12 @@ socket.on("init", function(data) {
|
|||||||
|
|
||||||
if (lastMessageId > -1) {
|
if (lastMessageId > -1) {
|
||||||
previousActive = sidebar.find(".active").data("id");
|
previousActive = sidebar.find(".active").data("id");
|
||||||
sidebar.find(".networks").empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.networks.length === 0) {
|
vueApp.networks = data.networks;
|
||||||
sidebar.find(".empty").show();
|
|
||||||
} else {
|
if (data.networks.length > 0) {
|
||||||
render.renderNetworks(data);
|
Vue.nextTick(() => render.renderNetworks(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#connection-error").removeClass("shown");
|
$("#connection-error").removeClass("shown");
|
||||||
@ -66,7 +66,7 @@ socket.on("init", function(data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
openCorrectChannel(previousActive, data.active);
|
Vue.nextTick(() => openCorrectChannel(previousActive, data.active));
|
||||||
});
|
});
|
||||||
|
|
||||||
function openCorrectChannel(clientActive, serverActive) {
|
function openCorrectChannel(clientActive, serverActive) {
|
||||||
|
@ -6,32 +6,31 @@ const render = require("../render");
|
|||||||
const chat = $("#chat");
|
const chat = $("#chat");
|
||||||
const templates = require("../../views");
|
const templates = require("../../views");
|
||||||
const sidebar = $("#sidebar");
|
const sidebar = $("#sidebar");
|
||||||
|
const {Vue, vueApp} = require("../vue");
|
||||||
|
|
||||||
socket.on("join", function(data) {
|
socket.on("join", function(data) {
|
||||||
const id = data.network;
|
vueApp.networks.find((n) => n.uuid === data.network)
|
||||||
const network = sidebar.find(`.network[data-uuid="${id}"]`);
|
.channels.splice(data.index || -1, 0, data.chan);
|
||||||
const channels = network.children();
|
|
||||||
const position = $(channels[data.index || channels.length - 1]); // Put channel in correct position, or the end if we don't have one
|
|
||||||
const sidebarEntry = templates.chan({
|
|
||||||
channels: [data.chan],
|
|
||||||
});
|
|
||||||
$(sidebarEntry).insertAfter(position);
|
|
||||||
chat.append(
|
chat.append(
|
||||||
templates.chat({
|
templates.chat({
|
||||||
channels: [data.chan],
|
channels: [data.chan],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
render.renderChannel(data.chan);
|
|
||||||
|
Vue.nextTick(() => render.renderChannel(data.chan));
|
||||||
|
|
||||||
// Queries do not automatically focus, unless the user did a whois
|
// Queries do not automatically focus, unless the user did a whois
|
||||||
if (data.chan.type === "query" && !data.shouldOpen) {
|
if (data.chan.type === "query" && !data.shouldOpen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vue.nextTick(() => {
|
||||||
sidebar.find(".chan")
|
sidebar.find(".chan")
|
||||||
.sort(function(a, b) {
|
.sort(function(a, b) {
|
||||||
return $(a).data("id") - $(b).data("id");
|
return $(a).data("id") - $(b).data("id");
|
||||||
})
|
})
|
||||||
.last()
|
.last()
|
||||||
.trigger("click");
|
.trigger("click");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -10,6 +10,7 @@ const cleanIrcMessage = require("../libs/handlebars/ircmessageparser/cleanIrcMes
|
|||||||
const webpush = require("../webpush");
|
const webpush = require("../webpush");
|
||||||
const chat = $("#chat");
|
const chat = $("#chat");
|
||||||
const sidebar = $("#sidebar");
|
const sidebar = $("#sidebar");
|
||||||
|
const {vueApp, findChannel} = require("../vue");
|
||||||
|
|
||||||
let pop;
|
let pop;
|
||||||
|
|
||||||
@ -30,26 +31,31 @@ socket.on("msg", function(data) {
|
|||||||
function processReceivedMessage(data) {
|
function processReceivedMessage(data) {
|
||||||
let targetId = data.chan;
|
let targetId = data.chan;
|
||||||
let target = "#chan-" + targetId;
|
let target = "#chan-" + targetId;
|
||||||
let channel = chat.find(target);
|
let channelContainer = chat.find(target);
|
||||||
let sidebarTarget = sidebar.find("[data-target='" + target + "']");
|
let channel = findChannel(data.chan);
|
||||||
|
|
||||||
|
// Clear unread/highlight counter if self-message
|
||||||
|
if (data.msg.self) {
|
||||||
|
channel.channel.highlight = 0;
|
||||||
|
channel.channel.unread = 0;
|
||||||
|
|
||||||
|
utils.updateTitle();
|
||||||
|
}
|
||||||
|
|
||||||
// Display received notices and errors in currently active channel.
|
// Display received notices and errors in currently active channel.
|
||||||
// Reloading the page will put them back into the lobby window.
|
// Reloading the page will put them back into the lobby window.
|
||||||
if (data.msg.showInActive) {
|
|
||||||
const activeOnNetwork = sidebarTarget.parent().find(".active");
|
|
||||||
|
|
||||||
// We only want to put errors/notices in active channel if they arrive on the same network
|
// We only want to put errors/notices in active channel if they arrive on the same network
|
||||||
if (activeOnNetwork.length > 0) {
|
if (data.msg.showInActive && vueApp.activeChannel && vueApp.activeChannel.network === channel.network) {
|
||||||
targetId = data.chan = activeOnNetwork.data("id");
|
channel = vueApp.activeChannel;
|
||||||
|
|
||||||
|
targetId = data.chan = vueApp.activeChannel.channel.id;
|
||||||
|
|
||||||
target = "#chan-" + targetId;
|
target = "#chan-" + targetId;
|
||||||
channel = chat.find(target);
|
channelContainer = chat.find(target);
|
||||||
sidebarTarget = sidebar.find("[data-target='" + target + "']");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const scrollContainer = channel.find(".chat");
|
const scrollContainer = channelContainer.find(".chat");
|
||||||
const container = channel.find(".messages");
|
const container = channelContainer.find(".messages");
|
||||||
const activeChannelId = chat.find(".chan.active").data("id");
|
const activeChannelId = chat.find(".chan.active").data("id");
|
||||||
|
|
||||||
if (data.msg.type === "channel_list" || data.msg.type === "ban_list" || data.msg.type === "ignore_list") {
|
if (data.msg.type === "channel_list" || data.msg.type === "ban_list" || data.msg.type === "ignore_list") {
|
||||||
@ -60,7 +66,7 @@ function processReceivedMessage(data) {
|
|||||||
render.appendMessage(
|
render.appendMessage(
|
||||||
container,
|
container,
|
||||||
targetId,
|
targetId,
|
||||||
channel.data("type"),
|
channelContainer.data("type"),
|
||||||
data.msg
|
data.msg
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -68,7 +74,7 @@ function processReceivedMessage(data) {
|
|||||||
scrollContainer.trigger("keepToBottom");
|
scrollContainer.trigger("keepToBottom");
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyMessage(targetId, channel, data);
|
notifyMessage(targetId, channelContainer, data);
|
||||||
|
|
||||||
let shouldMoveMarker = data.msg.self;
|
let shouldMoveMarker = data.msg.self;
|
||||||
|
|
||||||
@ -95,16 +101,6 @@ function processReceivedMessage(data) {
|
|||||||
.appendTo(container);
|
.appendTo(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear unread/highlight counter if self-message
|
|
||||||
if (data.msg.self) {
|
|
||||||
sidebarTarget.find(".badge")
|
|
||||||
.attr("data-highlight", 0)
|
|
||||||
.removeClass("highlight")
|
|
||||||
.empty();
|
|
||||||
|
|
||||||
utils.updateTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
let messageLimit = 0;
|
let messageLimit = 0;
|
||||||
|
|
||||||
if (activeChannelId !== targetId) {
|
if (activeChannelId !== targetId) {
|
||||||
@ -116,11 +112,11 @@ function processReceivedMessage(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (messageLimit > 0) {
|
if (messageLimit > 0) {
|
||||||
render.trimMessageInChannel(channel, messageLimit);
|
render.trimMessageInChannel(channelContainer, messageLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((data.msg.type === "message" || data.msg.type === "action") && channel.hasClass("channel")) {
|
if ((data.msg.type === "message" || data.msg.type === "action") && channelContainer.hasClass("channel")) {
|
||||||
const nicks = channel.find(".userlist").data("nicks");
|
const nicks = channelContainer.find(".userlist").data("nicks");
|
||||||
|
|
||||||
if (nicks) {
|
if (nicks) {
|
||||||
const find = nicks.indexOf(data.msg.from.nick);
|
const find = nicks.indexOf(data.msg.from.nick);
|
||||||
|
@ -6,13 +6,18 @@ const render = require("../render");
|
|||||||
const templates = require("../../views");
|
const templates = require("../../views");
|
||||||
const sidebar = $("#sidebar");
|
const sidebar = $("#sidebar");
|
||||||
const utils = require("../utils");
|
const utils = require("../utils");
|
||||||
|
const {Vue, vueApp} = require("../vue");
|
||||||
|
|
||||||
socket.on("network", function(data) {
|
socket.on("network", function(data) {
|
||||||
|
vueApp.networks.push(data.networks[0]);
|
||||||
|
|
||||||
|
Vue.nextTick(() => {
|
||||||
render.renderNetworks(data, true);
|
render.renderNetworks(data, true);
|
||||||
|
|
||||||
sidebar.find(".chan")
|
sidebar.find(".chan")
|
||||||
.last()
|
.last()
|
||||||
.trigger("click");
|
.trigger("click");
|
||||||
|
});
|
||||||
|
|
||||||
$("#connect")
|
$("#connect")
|
||||||
.find(".btn")
|
.find(".btn")
|
||||||
@ -20,14 +25,13 @@ socket.on("network", function(data) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on("network_changed", function(data) {
|
socket.on("network_changed", function(data) {
|
||||||
sidebar.find(`.network[data-uuid="${data.network}"]`).data("options", data.serverOptions);
|
vueApp.networks.find((n) => n.uuid === data.network).serverOptions = data.serverOptions;
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("network:status", function(data) {
|
socket.on("network:status", function(data) {
|
||||||
sidebar
|
const network = vueApp.networks.find((n) => n.uuid === data.network);
|
||||||
.find(`.network[data-uuid="${data.network}"]`)
|
network.status.connected = data.connected;
|
||||||
.toggleClass("not-connected", !data.connected)
|
network.status.secure = data.secure;
|
||||||
.toggleClass("not-secure", !data.secure);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("network:info", function(data) {
|
socket.on("network:info", function(data) {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
const $ = require("jquery");
|
const $ = require("jquery");
|
||||||
const socket = require("../socket");
|
const socket = require("../socket");
|
||||||
const utils = require("../utils");
|
const utils = require("../utils");
|
||||||
|
const {vueApp, findChannel} = require("../vue");
|
||||||
|
|
||||||
// Sync unread badge and marker when other clients open a channel
|
// Sync unread badge and marker when other clients open a channel
|
||||||
socket.on("open", function(id) {
|
socket.on("open", function(id) {
|
||||||
@ -10,24 +11,25 @@ socket.on("open", function(id) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const channel = $("#chat #chan-" + id);
|
|
||||||
|
|
||||||
// Don't do anything if the channel is active on this client
|
// Don't do anything if the channel is active on this client
|
||||||
if (channel.length === 0 || channel.hasClass("active")) {
|
if (vueApp.activeChannel && vueApp.activeChannel.channel.id === id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the unread badge
|
// Clear the unread badge
|
||||||
$("#sidebar").find(".chan[data-id='" + id + "'] .badge")
|
const channel = findChannel(id);
|
||||||
.attr("data-highlight", 0)
|
|
||||||
.removeClass("highlight")
|
if (channel) {
|
||||||
.empty();
|
channel.channel.highlight = 0;
|
||||||
|
channel.channel.unread = 0;
|
||||||
|
}
|
||||||
|
|
||||||
utils.updateTitle();
|
utils.updateTitle();
|
||||||
|
|
||||||
// Move unread marker to the bottom
|
// Move unread marker to the bottom
|
||||||
channel
|
const channelContainer = $("#chat #chan-" + id);
|
||||||
|
channelContainer
|
||||||
.find(".unread-marker")
|
.find(".unread-marker")
|
||||||
.data("unread-id", 0)
|
.data("unread-id", 0)
|
||||||
.appendTo(channel.find(".messages"));
|
.appendTo(channelContainer.find(".messages"));
|
||||||
});
|
});
|
||||||
|
@ -2,19 +2,19 @@
|
|||||||
|
|
||||||
const $ = require("jquery");
|
const $ = require("jquery");
|
||||||
const socket = require("../socket");
|
const socket = require("../socket");
|
||||||
const sidebar = $("#sidebar");
|
const {vueApp} = require("../vue");
|
||||||
|
|
||||||
socket.on("part", function(data) {
|
socket.on("part", function(data) {
|
||||||
const chanMenuItem = sidebar.find(".chan[data-id='" + data.chan + "']");
|
|
||||||
|
|
||||||
// When parting from the active channel/query, jump to the network's lobby
|
// When parting from the active channel/query, jump to the network's lobby
|
||||||
if (chanMenuItem.hasClass("active")) {
|
if (vueApp.activeChannel && vueApp.activeChannel.channel.id === data.chan) {
|
||||||
chanMenuItem
|
$("#sidebar .chan[data-id='" + data.chan + "']")
|
||||||
.parent(".network")
|
.closest(".network")
|
||||||
.find(".lobby")
|
.find(".lobby")
|
||||||
.trigger("click");
|
.trigger("click");
|
||||||
}
|
}
|
||||||
|
|
||||||
chanMenuItem.remove();
|
|
||||||
$("#chan-" + data.chan).remove();
|
$("#chan-" + data.chan).remove();
|
||||||
|
|
||||||
|
const network = vueApp.networks.find((n) => n.uuid === data.network);
|
||||||
|
network.channels.splice(network.channels.findIndex((c) => c.id === data.chan), 1);
|
||||||
});
|
});
|
||||||
|
@ -4,8 +4,11 @@ const $ = require("jquery");
|
|||||||
const chat = $("#chat");
|
const chat = $("#chat");
|
||||||
const socket = require("../socket");
|
const socket = require("../socket");
|
||||||
const sidebar = $("#sidebar");
|
const sidebar = $("#sidebar");
|
||||||
|
const {Vue, vueApp} = require("../vue");
|
||||||
|
|
||||||
socket.on("quit", function(data) {
|
socket.on("quit", function(data) {
|
||||||
|
vueApp.networks.splice(vueApp.networks.findIndex((n) => n.uuid === data.network), 1);
|
||||||
|
|
||||||
const id = data.network;
|
const id = data.network;
|
||||||
const network = sidebar.find(`.network[data-uuid="${id}"]`);
|
const network = sidebar.find(`.network[data-uuid="${id}"]`);
|
||||||
|
|
||||||
@ -14,13 +17,10 @@ socket.on("quit", function(data) {
|
|||||||
chat.find($(this).attr("data-target")).remove();
|
chat.find($(this).attr("data-target")).remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
network.remove();
|
Vue.nextTick(() => {
|
||||||
|
|
||||||
const chan = sidebar.find(".chan");
|
const chan = sidebar.find(".chan");
|
||||||
|
|
||||||
if (chan.length === 0) {
|
if (chan.length === 0) {
|
||||||
sidebar.find(".empty").show();
|
|
||||||
|
|
||||||
// Open the connect window
|
// Open the connect window
|
||||||
$("#footer .connect").trigger("click", {
|
$("#footer .connect").trigger("click", {
|
||||||
pushState: false,
|
pushState: false,
|
||||||
@ -28,4 +28,5 @@ socket.on("quit", function(data) {
|
|||||||
} else {
|
} else {
|
||||||
chan.eq(0).trigger("click");
|
chan.eq(0).trigger("click");
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
const $ = require("jquery");
|
|
||||||
const sidebar = $("#sidebar, #footer");
|
|
||||||
const socket = require("./socket");
|
|
||||||
const options = require("./options");
|
|
||||||
|
|
||||||
module.exports = function() {
|
|
||||||
sidebar.find(".networks").sortable({
|
|
||||||
axis: "y",
|
|
||||||
containment: "parent",
|
|
||||||
cursor: "move",
|
|
||||||
distance: 12,
|
|
||||||
items: ".network",
|
|
||||||
handle: ".lobby",
|
|
||||||
placeholder: "network-placeholder",
|
|
||||||
forcePlaceholderSize: true,
|
|
||||||
tolerance: "pointer", // Use the pointer to figure out where the network is in the list
|
|
||||||
|
|
||||||
update() {
|
|
||||||
const order = [];
|
|
||||||
|
|
||||||
sidebar.find(".network").each(function() {
|
|
||||||
const id = $(this).data("uuid");
|
|
||||||
order.push(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.emit("sort", {
|
|
||||||
type: "networks",
|
|
||||||
order: order,
|
|
||||||
});
|
|
||||||
|
|
||||||
options.settings.ignoreSortSync = true;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
sidebar.find(".network").sortable({
|
|
||||||
axis: "y",
|
|
||||||
containment: "parent",
|
|
||||||
cursor: "move",
|
|
||||||
distance: 12,
|
|
||||||
items: ".chan:not(.lobby)",
|
|
||||||
placeholder: "chan-placeholder",
|
|
||||||
forcePlaceholderSize: true,
|
|
||||||
tolerance: "pointer", // Use the pointer to figure out where the channel is in the list
|
|
||||||
|
|
||||||
update(e, ui) {
|
|
||||||
const order = [];
|
|
||||||
const network = ui.item.parent();
|
|
||||||
|
|
||||||
network.find(".chan").each(function() {
|
|
||||||
const id = $(this).data("id");
|
|
||||||
order.push(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.emit("sort", {
|
|
||||||
type: "channels",
|
|
||||||
target: network.data("uuid"),
|
|
||||||
order: order,
|
|
||||||
});
|
|
||||||
|
|
||||||
options.settings.ignoreSortSync = true;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
@ -3,6 +3,7 @@
|
|||||||
const $ = require("jquery");
|
const $ = require("jquery");
|
||||||
const escape = require("css.escape");
|
const escape = require("css.escape");
|
||||||
const viewport = $("#viewport");
|
const viewport = $("#viewport");
|
||||||
|
const {vueApp} = require("./vue");
|
||||||
|
|
||||||
var serverHash = -1; // eslint-disable-line no-var
|
var serverHash = -1; // eslint-disable-line no-var
|
||||||
var lastMessageId = -1; // eslint-disable-line no-var
|
var lastMessageId = -1; // eslint-disable-line no-var
|
||||||
@ -101,18 +102,20 @@ function toggleNotificationMarkers(newState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateTitle() {
|
function updateTitle() {
|
||||||
let title = $(document.body).data("app-name");
|
let title = vueApp.appName;
|
||||||
const chanTitle = $("#sidebar").find(".chan.active").attr("aria-label");
|
|
||||||
|
|
||||||
if (chanTitle && chanTitle.length > 0) {
|
if (vueApp.activeChannel) {
|
||||||
title = `${chanTitle} — ${title}`;
|
title = `${vueApp.activeChannel.channel.name} — ${vueApp.activeChannel.network.name} — ${title}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add highlight count to title
|
// add highlight count to title
|
||||||
let alertEventCount = 0;
|
let alertEventCount = 0;
|
||||||
$(".badge.highlight").each(function() {
|
|
||||||
alertEventCount += parseInt($(this).attr("data-highlight"));
|
for (const network of vueApp.networks) {
|
||||||
});
|
for (const channel of network.channels) {
|
||||||
|
alertEventCount += channel.highlight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (alertEventCount > 0) {
|
if (alertEventCount > 0) {
|
||||||
title = `(${alertEventCount}) ${title}`;
|
title = `(${alertEventCount}) ${title}`;
|
||||||
|
39
client/js/vue.js
Normal file
39
client/js/vue.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const Vue = require("vue").default;
|
||||||
|
const App = require("../components/App.vue").default;
|
||||||
|
const roundBadgeNumber = require("./libs/handlebars/roundBadgeNumber");
|
||||||
|
|
||||||
|
Vue.filter("roundBadgeNumber", roundBadgeNumber);
|
||||||
|
|
||||||
|
const vueApp = new Vue({
|
||||||
|
el: "#viewport",
|
||||||
|
data: {
|
||||||
|
appName: document.title,
|
||||||
|
activeChannel: null,
|
||||||
|
networks: [],
|
||||||
|
},
|
||||||
|
render(createElement) {
|
||||||
|
return createElement(App, {
|
||||||
|
props: this,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function findChannel(id) {
|
||||||
|
for (const network of vueApp.networks) {
|
||||||
|
for (const channel of network.channels) {
|
||||||
|
if (channel.id === id) {
|
||||||
|
return {network, channel};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Vue,
|
||||||
|
vueApp,
|
||||||
|
findChannel,
|
||||||
|
};
|
@ -1,45 +0,0 @@
|
|||||||
{{#each channels}}
|
|
||||||
<div
|
|
||||||
class="chan {{type}} chan-{{slugify name}}"
|
|
||||||
data-id="{{id}}"
|
|
||||||
data-target="#chan-{{id}}"
|
|
||||||
role="tab"
|
|
||||||
aria-label="{{name}}"
|
|
||||||
aria-controls="chan-{{id}}"
|
|
||||||
aria-selected="false"
|
|
||||||
>
|
|
||||||
{{#equal type "lobby"}}
|
|
||||||
<button class="collapse-network" aria-label="Collapse" aria-controls="network-{{../uuid}}" aria-expanded="true">
|
|
||||||
<span class="collapse-network-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div class="lobby-wrap">
|
|
||||||
<span class="name" title="{{name}}">{{name}}</span>
|
|
||||||
<span class="not-secure-tooltip tooltipped tooltipped-w" aria-label="Insecure connection">
|
|
||||||
<span class="not-secure-icon"></span>
|
|
||||||
</span>
|
|
||||||
<span class="not-connected-tooltip tooltipped tooltipped-w" aria-label="Disconnected">
|
|
||||||
<span class="not-connected-icon"></span>
|
|
||||||
</span>
|
|
||||||
<span class="badge{{#if highlight}} highlight{{/if}}" data-highlight="{{highlight}}">{{#if unread}}{{roundBadgeNumber unread}}{{/if}}</span>
|
|
||||||
</div>
|
|
||||||
<span class="add-channel-tooltip tooltipped tooltipped-w tooltipped-no-touch" aria-label="Join a channel…" data-alt-label="Cancel">
|
|
||||||
<button class="add-channel" aria-label="Join a channel…" aria-controls="join-channel-{{id}}"></button>
|
|
||||||
</span>
|
|
||||||
{{else}}
|
|
||||||
<span class="name" title="{{name}}">{{name}}</span>
|
|
||||||
<span class="badge{{#if highlight}} highlight{{/if}}" data-highlight="{{highlight}}">{{#if unread}}{{roundBadgeNumber unread}}{{/if}}</span>
|
|
||||||
{{#equal type "channel"}}
|
|
||||||
<span class="close-tooltip tooltipped tooltipped-w" aria-label="Leave">
|
|
||||||
<button class="close" aria-label="Leave"></button>
|
|
||||||
</span>
|
|
||||||
{{else}}
|
|
||||||
<span class="close-tooltip tooltipped tooltipped-w" aria-label="Close">
|
|
||||||
<button class="close" aria-label="Close"></button>
|
|
||||||
</span>
|
|
||||||
{{/equal}}
|
|
||||||
{{/equal}}
|
|
||||||
</div>
|
|
||||||
{{#equal type "lobby"}}
|
|
||||||
{{> join_channel}}
|
|
||||||
{{/equal}}
|
|
||||||
{{/each}}
|
|
@ -1,5 +0,0 @@
|
|||||||
<form id="join-channel-{{id}}" class="join-form" method="post" action="" autocomplete="off">
|
|
||||||
<input type="text" class="input" name="channel" placeholder="Channel" pattern="[^\s]+" maxlength="200" title="The channel name may not contain spaces" required>
|
|
||||||
<input type="password" class="input" name="key" placeholder="Password (optional)" pattern="[^\s]+" maxlength="200" title="The channel password may not contain spaces" autocomplete="new-password">
|
|
||||||
<button type="submit" class="btn btn-small">Join</button>
|
|
||||||
</form>
|
|
@ -1,12 +0,0 @@
|
|||||||
{{#each networks}}
|
|
||||||
<section
|
|
||||||
class="network name-{{slugify name}} {{#if serverOptions.NETWORK}}network-{{slugify serverOptions.NETWORK}}{{/if}} {{#unless status.connected}}not-connected{{/unless}} {{#unless status.secure}}not-secure{{/unless}}"
|
|
||||||
id="network-{{uuid}}"
|
|
||||||
data-uuid="{{uuid}}"
|
|
||||||
data-nick="{{nick}}"
|
|
||||||
data-options="{{tojson serverOptions}}"
|
|
||||||
role="region"
|
|
||||||
>
|
|
||||||
{{> chan}}
|
|
||||||
</section>
|
|
||||||
{{/each}}
|
|
@ -16,7 +16,7 @@
|
|||||||
"coverage": "run-s test:{client,server} && nyc --nycrc-path=test/.nycrc-report report",
|
"coverage": "run-s test:{client,server} && nyc --nycrc-path=test/.nycrc-report report",
|
||||||
"dev": "run-p watch start",
|
"dev": "run-p watch start",
|
||||||
"lint:css": "stylelint --color \"client/**/*.css\"",
|
"lint:css": "stylelint --color \"client/**/*.css\"",
|
||||||
"lint:js": "eslint . --report-unused-disable-directives --color",
|
"lint:js": "eslint . --ext .js,.vue --report-unused-disable-directives --color",
|
||||||
"start": "node index start",
|
"start": "node index start",
|
||||||
"test": "run-p --aggregate-output --continue-on-error lint:* test:{client,server}",
|
"test": "run-p --aggregate-output --continue-on-error lint:* test:{client,server}",
|
||||||
"test:browser": "webpack-dev-server --config=webpack.config-browser.js",
|
"test:browser": "webpack-dev-server --config=webpack.config-browser.js",
|
||||||
@ -80,6 +80,7 @@
|
|||||||
"css.escape": "1.5.1",
|
"css.escape": "1.5.1",
|
||||||
"emoji-regex": "7.0.3",
|
"emoji-regex": "7.0.3",
|
||||||
"eslint": "5.13.0",
|
"eslint": "5.13.0",
|
||||||
|
"eslint-plugin-vue": "4.5.0",
|
||||||
"fuzzy": "0.1.3",
|
"fuzzy": "0.1.3",
|
||||||
"graphql-request": "1.8.2",
|
"graphql-request": "1.8.2",
|
||||||
"handlebars": "4.1.0",
|
"handlebars": "4.1.0",
|
||||||
@ -105,6 +106,10 @@
|
|||||||
"stylelint-config-standard": "18.2.0",
|
"stylelint-config-standard": "18.2.0",
|
||||||
"textcomplete": "0.17.1",
|
"textcomplete": "0.17.1",
|
||||||
"undate": "0.3.0",
|
"undate": "0.3.0",
|
||||||
|
"vue": "2.5.16",
|
||||||
|
"vue-loader": "15.2.4",
|
||||||
|
"vue-template-compiler": "2.5.16",
|
||||||
|
"vuedraggable": "2.16.0",
|
||||||
"webpack": "4.29.3",
|
"webpack": "4.29.3",
|
||||||
"webpack-cli": "3.2.3",
|
"webpack-cli": "3.2.3",
|
||||||
"webpack-dev-server": "3.1.14"
|
"webpack-dev-server": "3.1.14"
|
||||||
|
@ -35,6 +35,7 @@ exports.input = function(network, chan, cmd, args) {
|
|||||||
network.channels = _.without(network.channels, target);
|
network.channels = _.without(network.channels, target);
|
||||||
target.destroy();
|
target.destroy();
|
||||||
this.emit("part", {
|
this.emit("part", {
|
||||||
|
network: network.uuid,
|
||||||
chan: target.id,
|
chan: target.id,
|
||||||
});
|
});
|
||||||
this.save();
|
this.save();
|
||||||
|
@ -29,6 +29,7 @@ module.exports = function(irc, network) {
|
|||||||
chan.destroy();
|
chan.destroy();
|
||||||
client.save();
|
client.save();
|
||||||
client.emit("part", {
|
client.emit("part", {
|
||||||
|
network: network.uuid,
|
||||||
chan: chan.id,
|
chan: chan.id,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -4,6 +4,7 @@ const webpack = require("webpack");
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const CopyPlugin = require("copy-webpack-plugin");
|
const CopyPlugin = require("copy-webpack-plugin");
|
||||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||||
|
const VueLoaderPlugin = require("vue-loader/lib/plugin");
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
mode: process.env.NODE_ENV === "production" ? "production" : "development",
|
mode: process.env.NODE_ENV === "production" ? "production" : "development",
|
||||||
@ -19,6 +20,12 @@ const config = {
|
|||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.vue$/,
|
||||||
|
use: {
|
||||||
|
loader: "vue-loader",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
test: /\.css$/,
|
test: /\.css$/,
|
||||||
include: [
|
include: [
|
||||||
@ -98,6 +105,7 @@ const config = {
|
|||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new MiniCssExtractPlugin(),
|
new MiniCssExtractPlugin(),
|
||||||
|
new VueLoaderPlugin(),
|
||||||
new CopyPlugin([
|
new CopyPlugin([
|
||||||
{
|
{
|
||||||
from: "./node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff*",
|
from: "./node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff*",
|
||||||
|
147
yarn.lock
147
yarn.lock
@ -594,6 +594,20 @@
|
|||||||
"@types/unist" "*"
|
"@types/unist" "*"
|
||||||
"@types/vfile-message" "*"
|
"@types/vfile-message" "*"
|
||||||
|
|
||||||
|
"@vue/component-compiler-utils@^1.2.1":
|
||||||
|
version "1.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-1.3.1.tgz#686f0b913d59590ae327b2a1cb4b6d9b931bbe0e"
|
||||||
|
dependencies:
|
||||||
|
consolidate "^0.15.1"
|
||||||
|
hash-sum "^1.0.2"
|
||||||
|
lru-cache "^4.1.2"
|
||||||
|
merge-source-map "^1.1.0"
|
||||||
|
postcss "^6.0.20"
|
||||||
|
postcss-selector-parser "^3.1.1"
|
||||||
|
prettier "^1.13.0"
|
||||||
|
source-map "^0.5.6"
|
||||||
|
vue-template-es2015-compiler "^1.6.0"
|
||||||
|
|
||||||
"@webassemblyjs/ast@1.7.11":
|
"@webassemblyjs/ast@1.7.11":
|
||||||
version "1.7.11"
|
version "1.7.11"
|
||||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace"
|
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace"
|
||||||
@ -746,9 +760,23 @@ acorn-dynamic-import@^4.0.0:
|
|||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948"
|
resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948"
|
||||||
|
|
||||||
|
acorn-jsx@^3.0.0:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
|
||||||
|
dependencies:
|
||||||
|
acorn "^3.0.4"
|
||||||
|
|
||||||
acorn-jsx@^5.0.0:
|
acorn-jsx@^5.0.0:
|
||||||
version "5.0.1"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e"
|
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.0.tgz#958584ddb60990c02c97c1bd9d521fce433bb101"
|
||||||
|
|
||||||
|
acorn@^3.0.4:
|
||||||
|
version "3.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
|
||||||
|
|
||||||
|
acorn@^5.5.0:
|
||||||
|
version "5.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8"
|
||||||
|
|
||||||
acorn@^6.0.2, acorn@^6.0.5:
|
acorn@^6.0.2, acorn@^6.0.5:
|
||||||
version "6.0.5"
|
version "6.0.5"
|
||||||
@ -1198,7 +1226,11 @@ blob@0.0.5:
|
|||||||
version "0.0.5"
|
version "0.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683"
|
resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683"
|
||||||
|
|
||||||
bluebird@^3.5.1, bluebird@^3.5.3:
|
bluebird@^3.1.1, bluebird@^3.5.1:
|
||||||
|
version "3.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
|
||||||
|
|
||||||
|
bluebird@^3.5.3:
|
||||||
version "3.5.3"
|
version "3.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7"
|
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7"
|
||||||
|
|
||||||
@ -1845,6 +1877,12 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
|
|||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
||||||
|
|
||||||
|
consolidate@^0.15.1:
|
||||||
|
version "0.15.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7"
|
||||||
|
dependencies:
|
||||||
|
bluebird "^3.1.1"
|
||||||
|
|
||||||
constants-browserify@^1.0.0:
|
constants-browserify@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
|
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
|
||||||
@ -2125,6 +2163,10 @@ date-now@^0.1.4:
|
|||||||
version "0.1.4"
|
version "0.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
|
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
|
||||||
|
|
||||||
|
de-indent@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
||||||
|
|
||||||
debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8:
|
debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8:
|
||||||
version "2.6.9"
|
version "2.6.9"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||||
@ -2544,6 +2586,19 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1
|
|||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||||
|
|
||||||
|
eslint-plugin-vue@4.5.0:
|
||||||
|
version "4.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-4.5.0.tgz#09d6597f4849e31a3846c2c395fccf17685b69c3"
|
||||||
|
dependencies:
|
||||||
|
vue-eslint-parser "^2.0.3"
|
||||||
|
|
||||||
|
eslint-scope@^3.7.1:
|
||||||
|
version "3.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
|
||||||
|
dependencies:
|
||||||
|
esrecurse "^4.1.0"
|
||||||
|
estraverse "^4.1.1"
|
||||||
|
|
||||||
eslint-scope@^4.0.0:
|
eslint-scope@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172"
|
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172"
|
||||||
@ -2600,6 +2655,13 @@ eslint@5.13.0:
|
|||||||
table "^5.0.2"
|
table "^5.0.2"
|
||||||
text-table "^0.2.0"
|
text-table "^0.2.0"
|
||||||
|
|
||||||
|
espree@^3.5.2:
|
||||||
|
version "3.5.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7"
|
||||||
|
dependencies:
|
||||||
|
acorn "^5.5.0"
|
||||||
|
acorn-jsx "^3.0.0"
|
||||||
|
|
||||||
espree@^5.0.0:
|
espree@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.0.tgz#fc7f984b62b36a0f543b13fb9cd7b9f4a7f5b65c"
|
resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.0.tgz#fc7f984b62b36a0f543b13fb9cd7b9f4a7f5b65c"
|
||||||
@ -2616,7 +2678,7 @@ esprima@^4.0.0:
|
|||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
|
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
|
||||||
|
|
||||||
esquery@^1.0.1:
|
esquery@^1.0.0, esquery@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708"
|
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3385,6 +3447,10 @@ hash-base@^3.0.0:
|
|||||||
inherits "^2.0.1"
|
inherits "^2.0.1"
|
||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
|
hash-sum@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04"
|
||||||
|
|
||||||
hash.js@^1.0.0, hash.js@^1.0.3:
|
hash.js@^1.0.0, hash.js@^1.0.3:
|
||||||
version "1.1.7"
|
version "1.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
|
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
|
||||||
@ -3398,7 +3464,7 @@ hasha@^3.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-stream "^1.0.1"
|
is-stream "^1.0.1"
|
||||||
|
|
||||||
he@1.1.1:
|
he@1.1.1, he@^1.1.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
|
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
|
||||||
|
|
||||||
@ -4434,9 +4500,9 @@ lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
|
|||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
|
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
|
||||||
|
|
||||||
lru-cache@^4.0.1, lru-cache@^4.1.1:
|
lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2:
|
||||||
version "4.1.5"
|
version "4.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c"
|
||||||
dependencies:
|
dependencies:
|
||||||
pseudomap "^1.0.2"
|
pseudomap "^1.0.2"
|
||||||
yallist "^2.1.2"
|
yallist "^2.1.2"
|
||||||
@ -5736,7 +5802,7 @@ postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2:
|
|||||||
indexes-of "^1.0.1"
|
indexes-of "^1.0.1"
|
||||||
uniq "^1.0.1"
|
uniq "^1.0.1"
|
||||||
|
|
||||||
postcss-selector-parser@^3.1.0:
|
postcss-selector-parser@^3.1.0, postcss-selector-parser@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865"
|
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -5786,7 +5852,7 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0
|
|||||||
source-map "^0.5.6"
|
source-map "^0.5.6"
|
||||||
supports-color "^3.2.3"
|
supports-color "^3.2.3"
|
||||||
|
|
||||||
postcss@^6.0.1, postcss@^6.0.23:
|
postcss@^6.0.1, postcss@^6.0.20, postcss@^6.0.23:
|
||||||
version "6.0.23"
|
version "6.0.23"
|
||||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -5818,6 +5884,10 @@ prepend-http@^2.0.0:
|
|||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
|
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
|
||||||
|
|
||||||
|
prettier@^1.13.0:
|
||||||
|
version "1.13.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.13.7.tgz#850f3b8af784a49a6ea2d2eaa7ed1428a34b7281"
|
||||||
|
|
||||||
primer-support@5.0.0:
|
primer-support@5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/primer-support/-/primer-support-5.0.0.tgz#d19c7cea59e8783400b9391943c8a2bb2ebddc5e"
|
resolved "https://registry.yarnpkg.com/primer-support/-/primer-support-5.0.0.tgz#d19c7cea59e8783400b9391943c8a2bb2ebddc5e"
|
||||||
@ -6649,6 +6719,10 @@ sort-keys@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-plain-obj "^1.0.0"
|
is-plain-obj "^1.0.0"
|
||||||
|
|
||||||
|
sortablejs@^1.7.0:
|
||||||
|
version "1.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.7.0.tgz#80a2b2370abd568e1cec8c271131ef30a904fa28"
|
||||||
|
|
||||||
source-list-map@^2.0.0:
|
source-list-map@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
||||||
@ -7567,6 +7641,59 @@ vm-browserify@0.0.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
indexof "0.0.1"
|
indexof "0.0.1"
|
||||||
|
|
||||||
|
vue-eslint-parser@^2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz#c268c96c6d94cfe3d938a5f7593959b0ca3360d1"
|
||||||
|
dependencies:
|
||||||
|
debug "^3.1.0"
|
||||||
|
eslint-scope "^3.7.1"
|
||||||
|
eslint-visitor-keys "^1.0.0"
|
||||||
|
espree "^3.5.2"
|
||||||
|
esquery "^1.0.0"
|
||||||
|
lodash "^4.17.4"
|
||||||
|
|
||||||
|
vue-hot-reload-api@^2.3.0:
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.0.tgz#97976142405d13d8efae154749e88c4e358cf926"
|
||||||
|
|
||||||
|
vue-loader@15.2.4:
|
||||||
|
version "15.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.2.4.tgz#a7b923123d3cf87230a8ff54a1c16d31a6c5dbb4"
|
||||||
|
dependencies:
|
||||||
|
"@vue/component-compiler-utils" "^1.2.1"
|
||||||
|
hash-sum "^1.0.2"
|
||||||
|
loader-utils "^1.1.0"
|
||||||
|
vue-hot-reload-api "^2.3.0"
|
||||||
|
vue-style-loader "^4.1.0"
|
||||||
|
|
||||||
|
vue-style-loader@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.0.tgz#7588bd778e2c9f8d87bfc3c5a4a039638da7a863"
|
||||||
|
dependencies:
|
||||||
|
hash-sum "^1.0.2"
|
||||||
|
loader-utils "^1.0.2"
|
||||||
|
|
||||||
|
vue-template-compiler@2.5.16:
|
||||||
|
version "2.5.16"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.16.tgz#93b48570e56c720cdf3f051cc15287c26fbd04cb"
|
||||||
|
dependencies:
|
||||||
|
de-indent "^1.0.2"
|
||||||
|
he "^1.1.0"
|
||||||
|
|
||||||
|
vue-template-es2015-compiler@^1.6.0:
|
||||||
|
version "1.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
|
||||||
|
|
||||||
|
vue@2.5.16:
|
||||||
|
version "2.5.16"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.16.tgz#07edb75e8412aaeed871ebafa99f4672584a0085"
|
||||||
|
|
||||||
|
vuedraggable@2.16.0:
|
||||||
|
version "2.16.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-2.16.0.tgz#52127081a2adb3de5fabd214d404ff3eee63575a"
|
||||||
|
dependencies:
|
||||||
|
sortablejs "^1.7.0"
|
||||||
|
|
||||||
watchpack@^1.5.0:
|
watchpack@^1.5.0:
|
||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"
|
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"
|
||||||
|
Loading…
Reference in New Issue
Block a user