From 5ce67ba09354709babb781fc7c20f37441383b12 Mon Sep 17 00:00:00 2001 From: Al McKinlay Date: Mon, 12 Mar 2018 12:42:59 +0000 Subject: [PATCH] Insert channel/user into channel list at alphabetically sorted point, not just the end Don't sort queries/users after special chans Set all users in tests to be of type query Add test for not inserting infront of lobby Break after finding the index, otherwise it always adds it to the end Add checking for lobby in first test Fix off-by-one error on the frontend Fix utterly idiotic issue adding a duplicate of the channel we are on rather than the new user when we query Check that we always insert before first special chan --- client/js/socket-events/join.js | 11 +- src/models/network.js | 22 ++++ src/plugins/inputs/query.js | 3 +- src/plugins/irc-events/banlist.js | 2 +- src/plugins/irc-events/join.js | 5 +- src/plugins/irc-events/list.js | 3 +- src/plugins/irc-events/message.js | 4 +- src/plugins/irc-events/whois.js | 4 +- test/models/network.js | 190 ++++++++++++++++++++++++++++++ 9 files changed, 232 insertions(+), 12 deletions(-) diff --git a/client/js/socket-events/join.js b/client/js/socket-events/join.js index 1eaab574..31adfb19 100644 --- a/client/js/socket-events/join.js +++ b/client/js/socket-events/join.js @@ -10,11 +10,12 @@ const sidebar = $("#sidebar"); socket.on("join", function(data) { const id = data.network; const network = sidebar.find(`#network-${id}`); - network.append( - templates.chan({ - channels: [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( templates.chat({ channels: [data.chan], diff --git a/src/models/network.js b/src/models/network.js index aba3218b..f3c2a28b 100644 --- a/src/models/network.js +++ b/src/models/network.js @@ -128,6 +128,28 @@ Network.prototype.getNetworkStatus = function() { return status; }; +Network.prototype.addChannel = function(newChan) { + let index = this.channels.length; // Default to putting as the last item in the array + + // Don't sort special channels in amongst channels/users. + if (newChan.type === Chan.Type.CHANNEL || newChan.type === Chan.Type.QUERY) { + // We start at 1 so we don't test against the lobby + for (let i = 1; i < this.channels.length; i++) { + const compareChan = this.channels[i]; + + // Negative if the new chan is alphabetically before the next chan in the list, positive if after + if (newChan.name.localeCompare(compareChan.name, {sensitivity: "base"}) <= 0 + || (compareChan.type !== Chan.Type.CHANNEL && compareChan.type !== Chan.Type.QUERY)) { + index = i; + break; + } + } + } + + this.channels.splice(index, 0, newChan); + return index; +}; + Network.prototype.export = function() { const network = _.pick(this, [ "uuid", diff --git a/src/plugins/inputs/query.js b/src/plugins/inputs/query.js index 7c7875c1..7d8c787a 100644 --- a/src/plugins/inputs/query.js +++ b/src/plugins/inputs/query.js @@ -47,11 +47,12 @@ exports.input = function(network, chan, cmd, args) { type: Chan.Type.QUERY, name: target, }); - network.channels.push(newChan); + this.emit("join", { network: network.id, chan: newChan.getFilteredClone(true), shouldOpen: true, + index: network.addChannel(newChan), }); this.save(); newChan.loadMessages(this, network); diff --git a/src/plugins/irc-events/banlist.js b/src/plugins/irc-events/banlist.js index a0ee92b9..cd61843e 100644 --- a/src/plugins/irc-events/banlist.js +++ b/src/plugins/irc-events/banlist.js @@ -37,10 +37,10 @@ module.exports = function(irc, network) { type: Chan.Type.SPECIAL, name: chanName, }); - network.channels.push(chan); client.emit("join", { network: network.id, chan: chan.getFilteredClone(true), + index: network.addChannel(chan), }); } diff --git a/src/plugins/irc-events/join.js b/src/plugins/irc-events/join.js index 23d1b7e0..cba391a2 100644 --- a/src/plugins/irc-events/join.js +++ b/src/plugins/irc-events/join.js @@ -15,12 +15,13 @@ module.exports = function(irc, network) { name: data.channel, state: Chan.State.JOINED, }); - network.channels.push(chan); - client.save(); + client.emit("join", { network: network.id, chan: chan.getFilteredClone(true), + index: network.addChannel(chan), }); + client.save(); chan.loadMessages(client, network); diff --git a/src/plugins/irc-events/list.js b/src/plugins/irc-events/list.js index 0ddf2059..98cc6a83 100644 --- a/src/plugins/irc-events/list.js +++ b/src/plugins/irc-events/list.js @@ -46,10 +46,11 @@ module.exports = function(irc, network) { type: Chan.Type.SPECIAL, name: "Channel List", }); - network.channels.push(chan); + client.emit("join", { network: network.id, chan: chan.getFilteredClone(true), + index: network.addChannel(chan), }); } diff --git a/src/plugins/irc-events/message.js b/src/plugins/irc-events/message.js index 4187f646..f4fd2221 100644 --- a/src/plugins/irc-events/message.js +++ b/src/plugins/irc-events/message.js @@ -67,11 +67,13 @@ module.exports = function(irc, network) { type: Chan.Type.QUERY, name: target, }); - network.channels.push(chan); + client.emit("join", { network: network.id, chan: chan.getFilteredClone(true), + index: network.addChannel(chan), }); + client.save(); chan.loadMessages(client, network); } } diff --git a/src/plugins/irc-events/whois.js b/src/plugins/irc-events/whois.js index cdc02a7e..3f7e7355 100644 --- a/src/plugins/irc-events/whois.js +++ b/src/plugins/irc-events/whois.js @@ -13,13 +13,15 @@ module.exports = function(irc, network) { type: Chan.Type.QUERY, name: data.nick, }); - network.channels.push(chan); + client.emit("join", { shouldOpen: true, network: network.id, chan: chan.getFilteredClone(true), + index: network.addChannel(chan), }); chan.loadMessages(client, network); + client.save(); } let msg; diff --git a/test/models/network.js b/test/models/network.js index 95987258..900e4f8e 100644 --- a/test/models/network.js +++ b/test/models/network.js @@ -147,4 +147,194 @@ describe("Network", function() { ); }); }); + + describe("#addChannel(newChan)", function() { + it("should add channel", function() { + const chan = new Chan({name: "#thelounge"}); + + const network = new Network({ + channels: [ + chan, + ], + }); + // Lobby and initial channel + expect(network.channels.length).to.equal(2); + + const newChan = new Chan({name: "#freenode"}); + network.addChannel(newChan); + + expect(network.channels.length).to.equal(3); + }); + + it("should add channel alphabetically", function() { + const chan1 = new Chan({name: "#abc"}); + const chan2 = new Chan({name: "#thelounge"}); + const chan3 = new Chan({name: "#zero"}); + + const network = new Network({ + channels: [ + chan1, + chan2, + chan3, + ], + name: "freenode", + }); + + const newChan = new Chan({name: "#freenode"}); + network.addChannel(newChan); + + expect(network.channels[0].name).to.equal("freenode"); + expect(network.channels[1]).to.equal(chan1); + expect(network.channels[2]).to.equal(newChan); + expect(network.channels[3]).to.equal(chan2); + expect(network.channels[4]).to.equal(chan3); + }); + + it("should sort case-insensitively", function() { + const chan1 = new Chan({name: "#abc"}); + const chan2 = new Chan({name: "#THELOUNGE"}); + + const network = new Network({ + channels: [ + chan1, + chan2, + ], + }); + + const newChan = new Chan({name: "#freenode"}); + network.addChannel(newChan); + + expect(network.channels[1]).to.equal(chan1); + expect(network.channels[2]).to.equal(newChan); + expect(network.channels[3]).to.equal(chan2); + }); + + it("should sort users separately from channels", function() { + const chan1 = new Chan({name: "#abc"}); + const chan2 = new Chan({name: "#THELOUNGE"}); + + const network = new Network({ + channels: [ + chan1, + chan2, + ], + }); + + const newUser = new Chan({name: "mcinkay", type: Chan.Type.QUERY}); + network.addChannel(newUser); + + expect(network.channels[1]).to.equal(chan1); + expect(network.channels[2]).to.equal(chan2); + expect(network.channels[3]).to.equal(newUser); + }); + + it("should sort users alphabetically", function() { + const chan1 = new Chan({name: "#abc"}); + const chan2 = new Chan({name: "#THELOUNGE"}); + const user1 = new Chan({name: "astorije", type: Chan.Type.QUERY}); + const user2 = new Chan({name: "xpaw", type: Chan.Type.QUERY}); + + const network = new Network({ + channels: [ + chan1, + chan2, + user1, + user2, + ], + }); + + const newUser = new Chan({name: "mcinkay", type: Chan.Type.QUERY}); + network.addChannel(newUser); + + expect(network.channels[1]).to.equal(chan1); + expect(network.channels[2]).to.equal(chan2); + expect(network.channels[3]).to.equal(user1); + expect(network.channels[4]).to.equal(newUser); + expect(network.channels[5]).to.equal(user2); + }); + + it("should not sort special channels", function() { + const chan1 = new Chan({name: "#abc"}); + const chan2 = new Chan({name: "#THELOUNGE"}); + const user1 = new Chan({name: "astorije", type: Chan.Type.QUERY}); + const user2 = new Chan({name: "xpaw", type: Chan.Type.QUERY}); + + const network = new Network({ + channels: [ + chan1, + chan2, + user1, + user2, + ], + }); + + const newBanlist = new Chan({name: "Banlist for #THELOUNGE", type: Chan.Type.SPECIAL}); + network.addChannel(newBanlist); + + expect(network.channels[1]).to.equal(chan1); + expect(network.channels[2]).to.equal(chan2); + expect(network.channels[3]).to.equal(user1); + expect(network.channels[4]).to.equal(user2); + expect(network.channels[5]).to.equal(newBanlist); + }); + + it("should not compare against special channels", function() { + const chan1 = new Chan({name: "#abc"}); + const chan2 = new Chan({name: "#THELOUNGE"}); + const user1 = new Chan({name: "astorije", type: Chan.Type.QUERY}); + + const network = new Network({ + channels: [ + chan1, + chan2, + user1, + ], + }); + + const newBanlist = new Chan({name: "Banlist for #THELOUNGE", type: Chan.Type.SPECIAL}); + network.addChannel(newBanlist); + const newUser = new Chan({name: "mcinkay", type: Chan.Type.QUERY}); + network.addChannel(newUser); + + expect(network.channels[1]).to.equal(chan1); + expect(network.channels[2]).to.equal(chan2); + expect(network.channels[3]).to.equal(user1); + expect(network.channels[4]).to.equal(newUser); + expect(network.channels[5]).to.equal(newBanlist); + }); + + it("should insert before first special channel", function() { + const banlist = new Chan({name: "Banlist for #THELOUNGE", type: Chan.Type.SPECIAL}); + const chan1 = new Chan({name: "#thelounge"}); + const user1 = new Chan({name: "astorije", type: Chan.Type.QUERY}); + + const network = new Network({ + channels: [ + banlist, + chan1, + user1, + ], + }); + + const newChan = new Chan({name: "#freenode"}); + network.addChannel(newChan); + + expect(network.channels[1]).to.equal(newChan); + expect(network.channels[2]).to.equal(banlist); + expect(network.channels[3]).to.equal(chan1); + expect(network.channels[4]).to.equal(user1); + }); + + it("should never add something in front of the lobby", function() { + const network = new Network({ + name: "freenode", + channels: [], + }); + + const newUser = new Chan({name: "astorije"}); + network.addChannel(newUser); + + expect(network.channels[1]).to.equal(newUser); + }); + }); });