Merge pull request #1194 from thelounge/xpaw/lazy-user-list

Lazily load user list in channels on init, keep autocompletion sort on server
This commit is contained in:
Pavel Djundik 2017-07-21 17:25:23 +03:00 committed by GitHub
commit ed9bfcf2fa
10 changed files with 71 additions and 42 deletions

View File

@ -81,7 +81,7 @@ function buildChatMessage(data) {
renderPreview(preview, msg); 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"); const nicks = chan.find(".users").data("nicks");
if (nicks) { if (nicks) {
const find = nicks.indexOf(data.msg.from); const find = nicks.indexOf(data.msg.from);
@ -143,22 +143,10 @@ function renderChannelMessages(data) {
function renderChannelUsers(data) { function renderChannelUsers(data) {
const users = chat.find("#chan-" + data.id).find(".users"); const users = chat.find("#chan-" + data.id).find(".users");
let nicks = users.data("nicks") || []; const nicks = data.users
const oldSortOrder = {}; .concat() // Make a copy of the user list, sort is applied in-place
.sort((a, b) => b.lastMessage - a.lastMessage)
for (const i in nicks) { .map((a) => a.nick);
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 search = users const search = users
.find(".search") .find(".search")
@ -191,7 +179,13 @@ function renderNetworks(data) {
channels: channels channels: channels
}) })
); );
channels.forEach(renderChannel); channels.forEach((channel) => {
renderChannel(channel);
if (channel.type === "channel") {
chat.find("#chan-" + channel.id).data("needsNamesRefresh", true);
}
});
utils.confirmExit(); utils.confirmExit();
sorting(); sorting();

View File

@ -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) { Chan.prototype.getMode = function(name) {
var user = _.find(this.users, {nick: name}); var user = this.findUser(name);
if (user) { if (user) {
return user.mode; return user.mode;
} }
@ -104,6 +108,7 @@ Chan.prototype.getMode = function(name) {
Chan.prototype.toJSON = function() { Chan.prototype.toJSON = function() {
var clone = _.clone(this); 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); clone.messages = clone.messages.slice(-100);
return clone; return clone;
}; };

View File

@ -8,20 +8,24 @@ function User(attr, prefixLookup) {
_.defaults(this, attr, { _.defaults(this, attr, {
modes: [], modes: [],
mode: "", mode: "",
nick: "" nick: "",
lastMessage: 0,
}); });
// irc-framework sets character mode, but lounge works with symbols this.setModes(this.modes, prefixLookup);
this.modes = this.modes.map((mode) => prefixLookup[mode]);
if (this.modes[0]) {
this.mode = this.modes[0];
}
} }
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() { User.prototype.toJSON = function() {
return { return {
nick: this.nick, nick: this.nick,
mode: this.mode, mode: this.mode,
lastMessage: this.lastMessage,
}; };
}; };

View File

@ -11,10 +11,12 @@ module.exports = function(irc, network) {
return; return;
} }
const user = chan.findUser(data.kicked);
if (data.kicked === irc.user.nick) { if (data.kicked === irc.user.nick) {
chan.users = []; chan.users = [];
} else { } else {
chan.users = _.without(chan.users, _.find(chan.users, {nick: data.kicked})); chan.users = _.without(chan.users, user);
} }
client.emit("users", { client.emit("users", {
@ -24,7 +26,7 @@ module.exports = function(irc, network) {
var msg = new Msg({ var msg = new Msg({
type: Msg.Type.KICK, type: Msg.Type.KICK,
time: data.time, time: data.time,
mode: chan.getMode(data.nick), mode: user.mode,
from: data.nick, from: data.nick,
target: data.kicked, target: data.kicked,
text: data.message || "", text: data.message || "",

View File

@ -71,6 +71,12 @@ module.exports = function(irc, network) {
// Query messages (unless self) always highlight // Query messages (unless self) always highlight
if (chan.type === Chan.Type.QUERY) { if (chan.type === Chan.Type.QUERY) {
highlight = !self; highlight = !self;
} else if (chan.type === Chan.Type.CHANNEL) {
const user = chan.findUser(data.nick);
if (user) {
user.lastMessage = data.time || Date.now();
}
} }
} }

View File

@ -81,7 +81,7 @@ module.exports = function(irc, network) {
return; return;
} }
const user = _.find(targetChan.users, {nick: mode.param}); const user = targetChan.findUser(mode.param);
if (!user) { if (!user) {
return; return;
} }

View File

@ -1,19 +1,39 @@
"use strict"; "use strict";
var User = require("../../models/user"); const User = require("../../models/user");
module.exports = function(irc, network) { module.exports = function(irc, network) {
var client = this; const client = this;
irc.on("userlist", function(data) { irc.on("userlist", function(data) {
var chan = network.getChannel(data.channel); const chan = network.getChannel(data.channel);
if (typeof chan === "undefined") { if (typeof chan === "undefined") {
return; return;
} }
chan.users = data.users.map((user) => new User({ // Create lookup map of current users,
nick: user.nick, // as we need to keep certain properties
modes: user.modes, // and we can recycle existing User objects
}, network.prefixLookup)); 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); chan.sortUsers(irc);

View File

@ -1,6 +1,5 @@
"use strict"; "use strict";
var _ = require("lodash");
var Msg = require("../../models/msg"); var Msg = require("../../models/msg");
module.exports = function(irc, network) { module.exports = function(irc, network) {
@ -25,7 +24,7 @@ module.exports = function(irc, network) {
} }
network.channels.forEach((chan) => { network.channels.forEach((chan) => {
var user = _.find(chan.users, {nick: data.nick}); const user = chan.findUser(data.nick);
if (typeof user === "undefined") { if (typeof user === "undefined") {
return; return;
} }

View File

@ -18,7 +18,7 @@ module.exports = function(irc, network) {
chan: chan.id chan: chan.id
}); });
} else { } else {
var user = _.find(chan.users, {nick: from}); const user = chan.findUser(from);
chan.users = _.without(chan.users, user); chan.users = _.without(chan.users, user);
client.emit("users", { client.emit("users", {
chan: chan.id chan: chan.id

View File

@ -7,8 +7,7 @@ module.exports = function(irc, network) {
var client = this; var client = this;
irc.on("quit", function(data) { irc.on("quit", function(data) {
network.channels.forEach((chan) => { network.channels.forEach((chan) => {
var from = data.nick; const user = chan.findUser(data.nick);
var user = _.find(chan.users, {nick: from});
if (typeof user === "undefined") { if (typeof user === "undefined") {
return; return;
} }
@ -22,7 +21,7 @@ module.exports = function(irc, network) {
mode: user.mode || "", mode: user.mode || "",
text: data.message || "", text: data.message || "",
hostmask: data.ident + "@" + data.hostname, hostmask: data.ident + "@" + data.hostname,
from: from from: data.nick
}); });
chan.pushMessage(client, msg); chan.pushMessage(client, msg);
}); });