From 7af573fd967affd9bf4a6ed07451f34af67490e7 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Mon, 10 Jul 2017 13:56:37 +0300 Subject: [PATCH 1/4] Handle auto completion order on the server Fixes #289. --- client/js/render.js | 22 +++++----------------- src/models/chan.js | 6 +++++- src/models/user.js | 4 +++- src/plugins/irc-events/message.js | 6 ++++++ 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/client/js/render.js b/client/js/render.js index 721dd603..2cebfdb6 100644 --- a/client/js/render.js +++ b/client/js/render.js @@ -81,7 +81,7 @@ function buildChatMessage(data) { renderPreview(preview, msg); }); - if ((type === "message" || type === "action") && chan.hasClass("channel")) { + if ((type === "message" || type === "action" || type === "notice") && chan.hasClass("channel")) { const nicks = chan.find(".users").data("nicks"); if (nicks) { const find = nicks.indexOf(data.msg.from); @@ -143,22 +143,10 @@ function renderChannelMessages(data) { function renderChannelUsers(data) { const users = chat.find("#chan-" + data.id).find(".users"); - let nicks = users.data("nicks") || []; - const oldSortOrder = {}; - - for (const i in nicks) { - oldSortOrder[nicks[i]] = i; - } - - nicks = []; - - for (const i in data.users) { - nicks.push(data.users[i].nick); - } - - nicks = nicks.sort(function(a, b) { - return (oldSortOrder[a] || Number.MAX_VALUE) - (oldSortOrder[b] || Number.MAX_VALUE); - }); + const nicks = data.users + .concat() + .sort((a, b) => b.lastMessage - a.lastMessage) + .map((a) => a.nick); const search = users .find(".search") diff --git a/src/models/chan.js b/src/models/chan.js index 66418195..6ca5eb54 100644 --- a/src/models/chan.js +++ b/src/models/chan.js @@ -93,8 +93,12 @@ Chan.prototype.sortUsers = function(irc) { }); }; +Chan.prototype.findUser = function(nick) { + return _.find(this.users, {nick: nick}); +}; + Chan.prototype.getMode = function(name) { - var user = _.find(this.users, {nick: name}); + var user = this.findUser(name); if (user) { return user.mode; } diff --git a/src/models/user.js b/src/models/user.js index f1dca1e4..fd6fd25d 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -8,7 +8,8 @@ function User(attr, prefixLookup) { _.defaults(this, attr, { modes: [], mode: "", - nick: "" + nick: "", + lastMessage: 0, }); // irc-framework sets character mode, but lounge works with symbols @@ -23,5 +24,6 @@ User.prototype.toJSON = function() { return { nick: this.nick, mode: this.mode, + lastMessage: this.lastMessage, }; }; diff --git a/src/plugins/irc-events/message.js b/src/plugins/irc-events/message.js index f83f1b4a..acdd6e61 100644 --- a/src/plugins/irc-events/message.js +++ b/src/plugins/irc-events/message.js @@ -71,6 +71,12 @@ module.exports = function(irc, network) { // Query messages (unless self) always highlight if (chan.type === Chan.Type.QUERY) { highlight = !self; + } else if (chan.type === Chan.Type.CHANNEL) { + const user = chan.findUser(data.nick); + + if (user) { + user.lastMessage = data.time || Date.now(); + } } } From d06c279f026a45211670432f50cf29b848e874e6 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Mon, 10 Jul 2017 13:56:58 +0300 Subject: [PATCH 2/4] Lazily load user list --- client/js/render.js | 10 ++++++++-- src/models/chan.js | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/client/js/render.js b/client/js/render.js index 2cebfdb6..bfb653d2 100644 --- a/client/js/render.js +++ b/client/js/render.js @@ -144,7 +144,7 @@ function renderChannelMessages(data) { function renderChannelUsers(data) { const users = chat.find("#chan-" + data.id).find(".users"); const nicks = data.users - .concat() + .concat() // Make a copy of the user list, sort is applied in-place .sort((a, b) => b.lastMessage - a.lastMessage) .map((a) => a.nick); @@ -179,7 +179,13 @@ function renderNetworks(data) { channels: channels }) ); - channels.forEach(renderChannel); + channels.forEach((channel) => { + renderChannel(channel); + + if (channel.type === "channel") { + chat.find("#chan-" + channel.id).data("needsNamesRefresh", true); + } + }); utils.confirmExit(); sorting(); diff --git a/src/models/chan.js b/src/models/chan.js index 6ca5eb54..af4607a8 100644 --- a/src/models/chan.js +++ b/src/models/chan.js @@ -108,6 +108,7 @@ Chan.prototype.getMode = function(name) { Chan.prototype.toJSON = function() { var clone = _.clone(this); + clone.users = []; // Do not send user list, the client will explicitly request it when needed clone.messages = clone.messages.slice(-100); return clone; }; From 48d367e379d91488b91c1722613ef5b98ea8c104 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Tue, 11 Jul 2017 17:30:47 +0300 Subject: [PATCH 3/4] Use findUser --- src/plugins/irc-events/kick.js | 6 ++++-- src/plugins/irc-events/mode.js | 2 +- src/plugins/irc-events/nick.js | 3 +-- src/plugins/irc-events/part.js | 2 +- src/plugins/irc-events/quit.js | 5 ++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/plugins/irc-events/kick.js b/src/plugins/irc-events/kick.js index cbe525a3..6b3174e6 100644 --- a/src/plugins/irc-events/kick.js +++ b/src/plugins/irc-events/kick.js @@ -11,10 +11,12 @@ module.exports = function(irc, network) { return; } + const user = chan.findUser(data.kicked); + if (data.kicked === irc.user.nick) { chan.users = []; } else { - chan.users = _.without(chan.users, _.find(chan.users, {nick: data.kicked})); + chan.users = _.without(chan.users, user); } client.emit("users", { @@ -24,7 +26,7 @@ module.exports = function(irc, network) { var msg = new Msg({ type: Msg.Type.KICK, time: data.time, - mode: chan.getMode(data.nick), + mode: user.mode, from: data.nick, target: data.kicked, text: data.message || "", diff --git a/src/plugins/irc-events/mode.js b/src/plugins/irc-events/mode.js index 28f94052..266515bb 100644 --- a/src/plugins/irc-events/mode.js +++ b/src/plugins/irc-events/mode.js @@ -81,7 +81,7 @@ module.exports = function(irc, network) { return; } - const user = _.find(targetChan.users, {nick: mode.param}); + const user = targetChan.findUser(mode.param); if (!user) { return; } diff --git a/src/plugins/irc-events/nick.js b/src/plugins/irc-events/nick.js index 7c8b61ad..af10f233 100644 --- a/src/plugins/irc-events/nick.js +++ b/src/plugins/irc-events/nick.js @@ -1,6 +1,5 @@ "use strict"; -var _ = require("lodash"); var Msg = require("../../models/msg"); module.exports = function(irc, network) { @@ -25,7 +24,7 @@ module.exports = function(irc, network) { } network.channels.forEach((chan) => { - var user = _.find(chan.users, {nick: data.nick}); + const user = chan.findUser(data.nick); if (typeof user === "undefined") { return; } diff --git a/src/plugins/irc-events/part.js b/src/plugins/irc-events/part.js index 001bbc71..70376cdf 100644 --- a/src/plugins/irc-events/part.js +++ b/src/plugins/irc-events/part.js @@ -18,7 +18,7 @@ module.exports = function(irc, network) { chan: chan.id }); } else { - var user = _.find(chan.users, {nick: from}); + const user = chan.findUser(from); chan.users = _.without(chan.users, user); client.emit("users", { chan: chan.id diff --git a/src/plugins/irc-events/quit.js b/src/plugins/irc-events/quit.js index 3882eadc..fd0c3caa 100644 --- a/src/plugins/irc-events/quit.js +++ b/src/plugins/irc-events/quit.js @@ -7,8 +7,7 @@ module.exports = function(irc, network) { var client = this; irc.on("quit", function(data) { network.channels.forEach((chan) => { - var from = data.nick; - var user = _.find(chan.users, {nick: from}); + const user = chan.findUser(data.nick); if (typeof user === "undefined") { return; } @@ -22,7 +21,7 @@ module.exports = function(irc, network) { mode: user.mode || "", text: data.message || "", hostmask: data.ident + "@" + data.hostname, - from: from + from: data.nick }); chan.pushMessage(client, msg); }); From 7d981d60d855fac737ebe01d8a566c974dea81cf Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Tue, 11 Jul 2017 17:40:43 +0300 Subject: [PATCH 4/4] Recycle existing User objects in names event This is required to keep lastMessage correct. This will also be useful for the away tracking PR. --- src/models/user.js | 14 ++++++++------ src/plugins/irc-events/names.js | 34 ++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/models/user.js b/src/models/user.js index fd6fd25d..2f8340f0 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -12,14 +12,16 @@ function User(attr, prefixLookup) { lastMessage: 0, }); - // irc-framework sets character mode, but lounge works with symbols - this.modes = this.modes.map((mode) => prefixLookup[mode]); - - if (this.modes[0]) { - this.mode = this.modes[0]; - } + this.setModes(this.modes, prefixLookup); } +User.prototype.setModes = function(modes, prefixLookup) { + // irc-framework sets character mode, but lounge works with symbols + this.modes = modes.map((mode) => prefixLookup[mode]); + + this.mode = this.modes[0] || ""; +}; + User.prototype.toJSON = function() { return { nick: this.nick, diff --git a/src/plugins/irc-events/names.js b/src/plugins/irc-events/names.js index 16ade0f3..c75f860f 100644 --- a/src/plugins/irc-events/names.js +++ b/src/plugins/irc-events/names.js @@ -1,19 +1,39 @@ "use strict"; -var User = require("../../models/user"); +const User = require("../../models/user"); module.exports = function(irc, network) { - var client = this; + const client = this; + irc.on("userlist", function(data) { - var chan = network.getChannel(data.channel); + const chan = network.getChannel(data.channel); if (typeof chan === "undefined") { return; } - chan.users = data.users.map((user) => new User({ - nick: user.nick, - modes: user.modes, - }, network.prefixLookup)); + // Create lookup map of current users, + // as we need to keep certain properties + // and we can recycle existing User objects + const oldUsers = new Map(); + + chan.users.forEach((user) => { + oldUsers.set(user.nick, user); + }); + + chan.users = data.users.map((user) => { + const oldUser = oldUsers.get(user.nick); + + // For existing users, we only need to update mode + if (oldUser) { + oldUser.setModes(user.modes, network.prefixLookup); + return oldUser; + } + + return new User({ + nick: user.nick, + modes: user.modes, + }, network.prefixLookup); + }); chan.sortUsers(irc);