Merge branch 'master' into feature/socks-support

This commit is contained in:
Max Leiter 2021-06-29 22:48:00 -07:00 committed by GitHub
commit d106889127
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1538 additions and 1629 deletions

View File

@ -2,7 +2,7 @@
root: true
parserOptions:
ecmaVersion: 2018
ecmaVersion: 2020
env:
es6: true

View File

@ -4,7 +4,7 @@ about: Create a bug report
labels: "Type: Bug"
---
<!-- Have a question? Join #thelounge on freenode -->
<!-- Have a question? Join #thelounge on Libera.Chat -->
- _Node version:_
- _Browser version:_

View File

@ -4,7 +4,7 @@ about: Request a new feature
labels: "Type: Feature"
---
<!-- Have a question? Join #thelounge on freenode. -->
<!-- Have a question? Join #thelounge on Libera.Chat. -->
<!-- Make sure to check the existing issues prior to submitting your suggestion. -->
### Feature Description

View File

@ -13,4 +13,4 @@ contact_links:
- name: General support
url: https://demo.thelounge.chat/?join=%23thelounge
about: "Join #thelounge on Freenode to ask a question before creating an issue"
about: "Join #thelounge on Libera.Chat to ask a question before creating an issue"

2
.github/SUPPORT.md vendored
View File

@ -6,6 +6,6 @@ need help, you have a few options:
- Check out [existing questions on Stack Overflow](https://stackoverflow.com/questions/tagged/thelounge)
to see if yours has been answered before. If not, feel free to [ask for a new question](https://stackoverflow.com/questions/ask?tags=thelounge)
(using `thelounge` tag so that other people can easily find it).
- Find us on the Freenode channel `#thelounge`. You might not get an answer
- Find us on the Libera.Chat channel `#thelounge`. You might not get an answer
right away, but this channel is full of nice people who will be happy to
help you.

View File

@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
<!-- New entries go after this line -->
## v4.3.0-pre.2 - 2021-06-07 [Pre-release]
[See the full changelog](https://github.com/thelounge/thelounge/compare/v4.3.0-pre.1...v4.3.0-pre.2)
This is a pre-release for v4.3.0 to offer latest changes without having to wait for a stable release.
At this stage, features may still be added or modified until the first release candidate for this version gets released.
Please refer to the commit list given above for a complete list of changes, or wait for the stable release to get a thoroughly prepared change log entry.
As with all pre-releases, this version requires explicit use of the `next` tag to be installed:
```sh
yarn global add thelounge@next
```
## v4.3.0-pre.1 - 2021-03-02 [Pre-release]
[See the full changelog](https://github.com/thelounge/thelounge/compare/v4.2.0...v4.3.0-pre.1)

View File

@ -20,8 +20,8 @@
</p>
<p align="center">
<a href="https://demo.thelounge.chat/"><img
alt="#thelounge IRC channel on freenode"
src="https://img.shields.io/badge/freenode-%23thelounge-415364.svg?colorA=ff9e18"></a>
alt="#thelounge IRC channel on Libera.Chat"
src="https://img.shields.io/badge/Libera.Chat-%23thelounge-415364.svg?colorA=ff9e18"></a>
<a href="https://yarn.pm/thelounge"><img
alt="npm version"
src="https://img.shields.io/npm/v/thelounge.svg?colorA=333a41&maxAge=3600"></a>

View File

@ -4,6 +4,6 @@
- Contact us privately first, in a
[responsible disclosure](https://en.wikipedia.org/wiki/Responsible_disclosure)
manner.
- On IRC, send a private message to any voiced user on our Freenode channel,
- On IRC, send a private message to any voiced user on our Libera.Chat channel,
`#thelounge`.
- By email, send us your report at <security@thelounge.chat>.

View File

@ -51,6 +51,7 @@ export default {
Mousetrap.bind("esc", this.escapeKey);
Mousetrap.bind("alt+u", this.toggleUserList);
Mousetrap.bind("alt+s", this.toggleSidebar);
Mousetrap.bind("alt+m", this.toggleMentions);
// Make a single throttled resize listener available to all components
this.debouncedResize = throttle(() => {
@ -72,6 +73,7 @@ export default {
Mousetrap.unbind("esc", this.escapeKey);
Mousetrap.unbind("alt+u", this.toggleUserList);
Mousetrap.unbind("alt+s", this.toggleSidebar);
Mousetrap.unbind("alt+m", this.toggleMentions);
window.removeEventListener("resize", this.debouncedResize);
clearTimeout(this.dayChangeTimeout);
@ -98,6 +100,11 @@ export default {
return false;
},
toggleMentions() {
if (this.$store.state.networks.length !== 0) {
eventbus.emit("mentions:toggle");
}
},
msUntilNextDay() {
// Compute how many milliseconds are remaining until the next day starts
const today = new Date();

View File

@ -183,6 +183,10 @@ export default {
},
setInputSize() {
this.$nextTick(() => {
if (!this.$refs.input) {
return;
}
const style = window.getComputedStyle(this.$refs.input);
const lineHeight = parseFloat(style.lineHeight, 10) || 1;

View File

@ -4,7 +4,7 @@
<input
ref="searchInputField"
v-model="searchInput"
type="text"
type="search"
name="search"
class="input"
placeholder="Search messages…"
@ -104,6 +104,10 @@ export default {
mounted() {
this.searchInput = this.$route.query.q;
this.searchOpened = this.onSearchPage;
if (!this.searchInput && this.searchOpened) {
this.$refs.searchInputField.focus();
}
},
methods: {
closeSearch() {

View File

@ -6,17 +6,20 @@
>username to <b>{{ message.new_ident }}</b></span
>
<span v-if="message.new_host"
>hostname to <i class="hostmask">{{ message.new_host }}</i></span
>
>hostname to
<i class="hostmask"><ParsedMessage :network="network" :text="message.new_host" /></i
></span>
</span>
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
name: "MessageTypeChangeHost",
components: {
ParsedMessage,
Username,
},
props: {

View File

@ -1,7 +1,7 @@
<template>
<span class="content">
<Username :user="message.from" />
<i class="hostmask"> ({{ message.hostmask }})</i>
<i class="hostmask"> (<ParsedMessage :network="network" :text="message.hostmask" />)</i>
<template v-if="message.account">
<i class="account"> [{{ message.account }}]</i>
</template>
@ -13,11 +13,13 @@
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
name: "MessageTypeJoin",
components: {
ParsedMessage,
Username,
},
props: {

View File

@ -1,7 +1,8 @@
<template>
<span class="content">
<Username :user="message.from" />
<i class="hostmask"> ({{ message.hostmask }})</i> has left the channel
<i class="hostmask"> (<ParsedMessage :network="network" :text="message.hostmask" />)</i> has
left the channel
<i v-if="message.text" class="part-reason"
>(<ParsedMessage :network="network" :message="message" />)</i
>

View File

@ -1,7 +1,8 @@
<template>
<span class="content">
<Username :user="message.from" />
<i class="hostmask"> ({{ message.hostmask }})</i> has quit
<i class="hostmask"> (<ParsedMessage :network="network" :text="message.hostmask" />)</i> has
quit
<i v-if="message.text" class="quit-reason"
>(<ParsedMessage :network="network" :message="message" />)</i
>

View File

@ -12,7 +12,12 @@
</template>
<dt>Host mask:</dt>
<dd class="hostmask">{{ message.whois.ident }}@{{ message.whois.hostname }}</dd>
<dd class="hostmask">
<ParsedMessage
:network="network"
:text="message.whois.ident + '@' + message.whois.hostname"
/>
</dd>
<template v-if="message.whois.actual_hostname">
<dt>Actual host:</dt>

View File

@ -9,7 +9,7 @@
</thead>
<tbody>
<tr v-for="ban in channel.data" :key="ban.hostmask">
<td class="hostmask">{{ ban.hostmask }}</td>
<td class="hostmask"><ParsedMessage :network="network" :text="ban.hostmask" /></td>
<td class="banned_by">{{ ban.banned_by }}</td>
<td class="banned_at">{{ localetime(ban.banned_at) }}</td>
</tr>
@ -18,10 +18,14 @@
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
import localetime from "../../js/helpers/localetime";
export default {
name: "ListBans",
components: {
ParsedMessage,
},
props: {
network: Object,
channel: Object,

View File

@ -8,7 +8,7 @@
</thead>
<tbody>
<tr v-for="user in channel.data" :key="user.hostmask">
<td class="hostmask">{{ user.hostmask }}</td>
<td class="hostmask"><ParsedMessage :network="network" :text="user.hostmask" /></td>
<td class="when">{{ localetime(user.when) }}</td>
</tr>
</tbody>
@ -16,10 +16,14 @@
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
import localetime from "../../js/helpers/localetime";
export default {
name: "ListIgnored",
components: {
ParsedMessage,
},
props: {
network: Object,
channel: Object,

View File

@ -9,7 +9,9 @@
</thead>
<tbody>
<tr v-for="invite in channel.data" :key="invite.hostmask">
<td class="hostmask">{{ invite.hostmask }}</td>
<td class="hostmask">
<ParsedMessage :network="network" :text="invite.hostmask" />
</td>
<td class="invitened_by">{{ invite.invited_by }}</td>
<td class="invitened_at">{{ localetime(invite.invited_at) }}</td>
</tr>
@ -18,10 +20,14 @@
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
import localetime from "../../js/helpers/localetime";
export default {
name: "ListInvites",
components: {
ParsedMessage,
},
props: {
network: Object,
channel: Object,

View File

@ -189,6 +189,16 @@
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Alt</kbd> <kbd>M</kbd></span>
<span v-else><kbd></kbd> <kbd>M</kbd></span>
</div>
<div class="description">
<p>Toggle recent mentions popup.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span><kbd>Esc</kbd></span>
@ -673,6 +683,15 @@
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/search query</code>
</div>
<div class="description">
<p>Search for messages in the current channel / user</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/topic [newtopic]</code>

View File

@ -1195,7 +1195,6 @@ textarea.input {
}
#chat .show-more {
margin-top: 50px;
padding: 10px;
padding-top: 15px;
padding-bottom: 0;
@ -1508,8 +1507,11 @@ textarea.input {
}
#chat .msg[data-type="notice"] .time,
#chat .msg[data-type="wallops"] .time,
#chat .msg[data-type="notice"] .content,
#chat .msg[data-type="notice"] .user {
#chat .msg[data-type="wallops"] .content,
#chat .msg[data-type="notice"] .user,
#chat .msg[data-type="wallops"] .user {
color: #0074d9;
}
@ -1517,6 +1519,10 @@ textarea.input {
content: "Notice: ";
}
#chat .msg[data-type="wallops"] .from .user::before {
content: "Wallops: ";
}
#chat .msg[data-type="error"],
#chat .msg[data-type="error"] .from {
color: #e74c3c;

View File

@ -0,0 +1,20 @@
"use strict";
import store from "../store";
import {router} from "../router";
function input(args) {
router.push({
name: "SearchResults",
params: {
id: store.state.activeChannel.channel.id,
},
query: {
q: args.join(" "),
},
});
return true;
}
export default {input};

View File

@ -6,17 +6,24 @@ import socket from "../socket";
import store from "../store";
socket.on("more", function (data) {
const channel = store.getters.findChannel(data.chan);
const channel = store.getters.findChannel(data.chan)?.channel;
if (!channel) {
return;
}
channel.channel.moreHistoryAvailable =
data.totalMessages > channel.channel.messages.length + data.messages.length;
channel.channel.messages.unshift(...data.messages);
channel.inputHistory = channel.inputHistory.concat(
data.messages
.filter((m) => m.self && m.text && m.type === "message")
.map((m) => m.text)
.reverse()
.slice(null, 100 - channel.inputHistory.length)
);
channel.moreHistoryAvailable =
data.totalMessages > channel.messages.length + data.messages.length;
channel.messages.unshift(...data.messages);
Vue.nextTick(() => {
channel.channel.historyLoading = false;
channel.historyLoading = false;
});
});

View File

@ -190,7 +190,14 @@ const store = new Vuex.Store({
// TODO: This should be a mutation
channel.pendingMessage = "";
channel.inputHistoryPosition = 0;
channel.inputHistory = [""];
channel.inputHistory = [""].concat(
channel.messages
.filter((m) => m.self && m.text && m.type === "message")
.map((m) => m.text)
.reverse()
.slice(null, 99)
);
channel.historyLoading = false;
channel.scrolledToBottom = true;
channel.editTopic = false;

View File

@ -231,12 +231,12 @@ module.exports = {
// - `join`: Comma-separated list of channels to auto-join once connected.
//
// This value is set to connect to the official channel of The Lounge on
// Freenode by default:
// Libera.Chat by default:
//
// ```js
// defaults: {
// name: "Freenode",
// host: "chat.freenode.net",
// name: "Libera.Chat",
// host: "irc.libera.chat",
// port: 6697,
// password: "",
// tls: true,
@ -248,8 +248,8 @@ module.exports = {
// }
// ```
defaults: {
name: "Freenode",
host: "chat.freenode.net",
name: "Libera.Chat",
host: "irc.libera.chat",
port: 6697,
password: "",
tls: true,

View File

@ -1,7 +1,7 @@
{
"name": "thelounge",
"description": "The self-hosted Web IRC client",
"version": "4.3.0-pre.1",
"version": "4.3.0-pre.2",
"preferGlobal": true,
"bin": {
"thelounge": "index.js"
@ -43,18 +43,18 @@
"bcryptjs": "2.4.3",
"busboy": "0.3.1",
"chalk": "4.1.1",
"cheerio": "1.0.0-rc.5",
"cheerio": "1.0.0-rc.10",
"commander": "7.2.0",
"content-disposition": "0.5.3",
"express": "4.17.1",
"file-type": "16.2.0",
"filenamify": "4.2.0",
"got": "11.8.1",
"irc-framework": "github:mstrodl/irc-framework#feature/fix-socks",
"irc-framework": "4.11.0",
"is-utf8": "0.2.1",
"ldapjs": "2.2.3",
"linkify-it": "3.0.2",
"lodash": "4.17.20",
"lodash": "4.17.21",
"mime-types": "2.1.28",
"node-forge": "0.10.0",
"package-json": "6.5.0",
@ -73,8 +73,8 @@
"sqlite3": "5.0.2"
},
"devDependencies": {
"@babel/core": "7.14.0",
"@babel/preset-env": "7.14.0",
"@babel/core": "7.14.6",
"@babel/preset-env": "7.14.7",
"@fortawesome/fontawesome-free": "5.15.3",
"@vue/server-test-utils": "1.1.3",
"@vue/test-utils": "1.1.3",
@ -83,8 +83,8 @@
"chai": "4.3.4",
"copy-webpack-plugin": "7.0.0",
"css-loader": "5.1.1",
"cssnano": "4.1.10",
"dayjs": "1.10.4",
"cssnano": "4.1.11",
"dayjs": "1.10.5",
"emoji-regex": "9.2.1",
"eslint": "7.23.0",
"eslint-config-prettier": "6.15.0",

View File

@ -34,7 +34,7 @@ try {
createPackagesFolder();
// Merge config key-values passed as CLI options into the main config
Helper.mergeConfig(Helper.config, program.config);
Helper.mergeConfig(Helper.config, program.opts().config);
require("./start");

View File

@ -13,6 +13,8 @@ program
.on("--help", Utils.extraHelp)
.action(function (packageName) {
const fs = require("fs");
const fspromises = fs.promises;
const path = require("path");
const packageJson = require("package-json");
if (!fs.existsSync(Helper.getConfigPath())) {
@ -21,22 +23,31 @@ program
}
log.info("Retrieving information about the package...");
let readFile = null;
let isLocalFile = false;
if (packageName.startsWith("file:")) {
isLocalFile = true;
readFile = fspromises
.readFile(path.join(packageName.substr("file:".length), "package.json"), "utf-8")
.then((data) => JSON.parse(data));
} else {
const split = packageName.split("@");
packageName = split[0];
const packageVersion = split[1] || "latest";
packageJson(packageName, {
readFile = packageJson(packageName, {
fullMetadata: true,
version: packageVersion,
})
});
}
readFile
.then((json) => {
const humanVersion = isLocalFile ? packageName : `${json.name} v${json.version}`;
if (!("thelounge" in json)) {
log.error(
`${colors.red(
json.name + " v" + json.version
)} does not have The Lounge metadata.`
);
log.error(`${colors.red(humanVersion)} does not have The Lounge metadata.`);
process.exit(1);
}
@ -47,7 +58,7 @@ program
) {
log.error(
`${colors.red(
json.name + " v" + json.version
humanVersion
)} does not support The Lounge v${Helper.getVersionNumber()}. Supported version(s): ${
json.thelounge.supports
}`
@ -56,20 +67,23 @@ program
process.exit(2);
}
log.info(`Installing ${colors.green(json.name + " v" + json.version)}...`);
return Utils.executeYarnCommand("add", "--exact", `${json.name}@${json.version}`)
log.info(`Installing ${colors.green(humanVersion)}...`);
const yarnVersion = isLocalFile ? packageName : `${json.name}@${json.version}`;
return Utils.executeYarnCommand("add", "--exact", yarnVersion)
.then(() => {
log.info(
`${colors.green(
json.name + " v" + json.version
)} has been successfully installed.`
);
log.info(`${colors.green(humanVersion)} has been successfully installed.`);
if (isLocalFile) {
// yarn v1 is buggy if a local filepath is used and doesn't update
// the lockfile properly. We need to run an install in that case
// even though that's supposed to be done by the add subcommand
return Utils.executeYarnCommand("install").catch((err) => {
throw `Failed to update lockfile after package install ${err}`;
});
}
})
.catch((code) => {
throw `Failed to install ${colors.green(
json.name + " v" + json.version
)}. Exit code: ${code}`;
throw `Failed to install ${colors.red(humanVersion)}. Exit code: ${code}`;
});
})
.catch((e) => {

View File

@ -79,6 +79,7 @@ Msg.Type = {
WHOIS: "whois",
RAW: "raw",
PLUGIN: "plugin",
WALLOPS: "wallops",
};
module.exports = Msg;

View File

@ -1,4 +1,4 @@
const clientSideCommands = ["/collapse", "/expand"];
const clientSideCommands = ["/collapse", "/expand", "/search"];
const passThroughCommands = [
"/as",

View File

@ -222,6 +222,7 @@ function parse(msg, chan, preview, res, client) {
case "image/gif":
case "image/jpg":
case "image/jpeg":
case "image/jxl":
case "image/webp":
case "image/avif":
if (!Helper.config.prefetchStorage && Helper.config.disableMediaPreview) {

View File

@ -33,7 +33,7 @@ module.exports = function (irc, network) {
irc.on("wallops", function (data) {
data.from_server = true;
data.type = Msg.Type.NOTICE;
data.type = Msg.Type.WALLOPS;
handleMessage(data);
});
@ -51,8 +51,13 @@ module.exports = function (irc, network) {
return Helper.compareHostmask(entry, data);
});
// Server messages go to server window, no questions asked
if (data.from_server) {
// Server messages that aren't targeted at a channel go to the server window
if (
data.from_server &&
(!data.target ||
!network.getChannel(data.target) ||
network.getChannel(data.target).type !== Chan.Type.CHANNEL)
) {
chan = network.channels[0];
from = chan.getUser(data.nick);
} else {

View File

@ -29,6 +29,7 @@ const inlineContentDispositionTypes = {
"image/png": "image.png",
"image/webp": "image.webp",
"image/avif": "image.avif",
"image/jxl": "image.jxl",
"text/plain": "text.txt",
"video/mp4": "video.mp4",
"video/ogg": "video.ogv",

View File

@ -55,8 +55,8 @@ describe("cleanIrcMessage", function () {
expected: "bold bold",
},
{
input: "\x02irc\x0f://\x1dfreenode.net\x0f/\x034,8thelounge",
expected: "irc://freenode.net/thelounge",
input: "\x02irc\x0f://\x1dirc.example.com\x0f/\x034,8thelounge",
expected: "irc://irc.example.com/thelounge",
},
{
input: "\x02#\x038,9thelounge",

View File

@ -8,12 +8,12 @@ const {
describe("findLinks", () => {
it("should find url", () => {
const input = "irc://freenode.net/thelounge";
const input = "irc://irc.example.com/thelounge";
const expected = [
{
start: 0,
end: 28,
link: "irc://freenode.net/thelounge",
end: 31,
link: "irc://irc.example.com/thelounge",
},
];

View File

@ -63,10 +63,10 @@ describe("IRC formatted message parser", () => {
it("should find urls", async () => {
const testCases = [
{
input: "irc://freenode.net/thelounge",
input: "irc://irc.example.com/thelounge",
expected:
'<a href="irc://freenode.net/thelounge" dir="auto" target="_blank" rel="noopener">' +
"irc://freenode.net/thelounge" +
'<a href="irc://irc.example.com/thelounge" dir="auto" target="_blank" rel="noopener">' +
"irc://irc.example.com/thelounge" +
"</a>",
},
{
@ -416,12 +416,12 @@ describe("IRC formatted message parser", () => {
it("should go bonkers like mirc", async () => {
const testCases = [
{
input: "\x02irc\x0f://\x1dfreenode.net\x0f/\x034,8thelounge",
input: "\x02irc\x0f://\x1dirc.example.com\x0f/\x034,8thelounge",
expected:
'<a href="irc://freenode.net/thelounge" dir="auto" target="_blank" rel="noopener">' +
'<a href="irc://irc.example.com/thelounge" dir="auto" target="_blank" rel="noopener">' +
'<span class="irc-bold">irc</span>' +
"://" +
'<span class="irc-italic">freenode.net</span>' +
'<span class="irc-italic">irc.example.com</span>' +
"/" +
'<span class="irc-fg4 irc-bg8">thelounge</span>' +
"</a>",

View File

@ -2,6 +2,8 @@
var config = require("../../../defaults/config.js");
config.defaults.name = "Example IRC Server";
config.defaults.host = "irc.example.com";
config.public = true;
config.prefetch = true;
config.host = config.bind = "127.0.0.1";

View File

@ -95,7 +95,7 @@ describe("Network", function () {
rejectUnauthorized: false,
});
expect(network.validate()).to.be.true;
expect(network.host).to.equal("chat.freenode.net");
expect(network.host).to.equal("irc.example.com");
expect(network.port).to.equal(6697);
expect(network.tls).to.be.true;
expect(network.rejectUnauthorized).to.be.true;
@ -107,7 +107,7 @@ describe("Network", function () {
host: "some.fake.tld",
});
expect(network2.validate()).to.be.true;
expect(network2.host).to.equal("chat.freenode.net");
expect(network2.host).to.equal("irc.example.com");
Helper.config.lockNetwork = false;
});
@ -269,7 +269,7 @@ describe("Network", function () {
// Lobby and initial channel
expect(network.channels.length).to.equal(2);
const newChan = new Chan({name: "#freenode"});
const newChan = new Chan({name: "#foo"});
network.addChannel(newChan);
expect(network.channels.length).to.equal(3);
@ -282,13 +282,13 @@ describe("Network", function () {
const network = new Network({
channels: [chan1, chan2, chan3],
name: "freenode",
name: "foo",
});
const newChan = new Chan({name: "#freenode"});
const newChan = new Chan({name: "#foo"});
network.addChannel(newChan);
expect(network.channels[0].name).to.equal("freenode");
expect(network.channels[0].name).to.equal("foo");
expect(network.channels[1]).to.equal(chan1);
expect(network.channels[2]).to.equal(newChan);
expect(network.channels[3]).to.equal(chan2);
@ -303,7 +303,7 @@ describe("Network", function () {
channels: [chan1, chan2],
});
const newChan = new Chan({name: "#freenode"});
const newChan = new Chan({name: "#foo"});
network.addChannel(newChan);
expect(network.channels[1]).to.equal(chan1);
@ -397,7 +397,7 @@ describe("Network", function () {
channels: [banlist, chan1, user1],
});
const newChan = new Chan({name: "#freenode"});
const newChan = new Chan({name: "#foo"});
network.addChannel(newChan);
expect(network.channels[1]).to.equal(newChan);
@ -408,7 +408,7 @@ describe("Network", function () {
it("should never add something in front of the lobby", function () {
const network = new Network({
name: "freenode",
name: "foo",
channels: [],
});

2852
yarn.lock

File diff suppressed because it is too large Load Diff