Change users array to be a Map

This commit is contained in:
Pavel Djundik 2017-11-16 22:32:03 +02:00
parent 56a9ad184d
commit 0f75c1a138
11 changed files with 111 additions and 93 deletions

View file

@ -483,7 +483,7 @@ Client.prototype.names = function(data) {
client.emit("names", {
id: target.chan.id,
users: target.chan.users,
users: target.chan.getSortedUsers(target.network.irc),
});
};

View file

@ -28,7 +28,7 @@ function Chan(attr) {
firstUnread: 0,
unread: 0,
highlight: 0,
users: [],
users: new Map(),
});
}
@ -97,7 +97,7 @@ Chan.prototype.dereferencePreviews = function(messages) {
});
};
Chan.prototype.sortUsers = function(irc) {
Chan.prototype.getSortedUsers = function(irc) {
var userModeSortPriority = {};
irc.network.options.PREFIX.forEach((prefix, index) => {
userModeSortPriority[prefix.symbol] = index;
@ -105,7 +105,9 @@ Chan.prototype.sortUsers = function(irc) {
userModeSortPriority[""] = 99; // No mode is lowest
this.users = this.users.sort(function(a, b) {
const users = Array.from(this.users.values());
return users.sort(function(a, b) {
if (a.mode === b.mode) {
return a.nick.toLowerCase() < b.nick.toLowerCase() ? -1 : 1;
}
@ -119,13 +121,21 @@ Chan.prototype.findMessage = function(msgId) {
};
Chan.prototype.findUser = function(nick) {
return _.find(this.users, {nick: nick});
return this.users.get(nick.toLowerCase());
};
Chan.prototype.getUser = function(nick) {
return this.findUser(nick) || new User({nick: nick});
};
Chan.prototype.setUser = function(user) {
this.users.set(user.nick.toLowerCase(), user);
};
Chan.prototype.removeUser = function(user) {
this.users.delete(user.nick.toLowerCase());
};
Chan.prototype.toJSON = function() {
var clone = _.clone(this);
clone.users = []; // Do not send user list, the client will explicitly request it when needed

View file

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

View file

@ -35,8 +35,7 @@ module.exports = function(irc, network) {
});
chan.pushMessage(client, msg);
chan.users.push(user);
chan.sortUsers(irc);
chan.setUser(new User({nick: data.nick}));
client.emit("users", {
chan: chan.id,
});

View file

@ -1,6 +1,5 @@
"use strict";
const _ = require("lodash");
const Msg = require("../../models/msg");
module.exports = function(irc, network) {
@ -25,9 +24,9 @@ module.exports = function(irc, network) {
chan.pushMessage(client, msg);
if (data.kicked === irc.user.nick) {
chan.users = [];
chan.users = new Map();
} else {
chan.users = _.without(chan.users, msg.target);
chan.removeUser(msg.target);
}
client.emit("users", {

View file

@ -113,8 +113,6 @@ module.exports = function(irc, network) {
// TODO: This is horrible
irc.raw("NAMES", data.target);
} else {
targetChan.sortUsers(irc);
client.emit("users", {
chan: targetChan.id,
});

View file

@ -1,7 +1,5 @@
"use strict";
const User = require("../../models/user");
module.exports = function(irc, network) {
const client = this;
@ -11,31 +9,16 @@ module.exports = function(irc, network) {
return;
}
// Create lookup map of current users,
// as we need to keep certain properties
// and we can recycle existing User objects
const oldUsers = new Map();
const newUsers = new Map();
chan.users.forEach((user) => {
oldUsers.set(user.nick, user);
data.users.forEach((user) => {
const newUser = chan.getUser(user.nick);
newUser.setModes(user.modes, network.prefixLookup);
newUsers.set(user.nick.toLowerCase(), newUser);
});
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.users = newUsers;
client.emit("users", {
chan: chan.id,

View file

@ -32,6 +32,10 @@ module.exports = function(irc, network) {
return;
}
chan.removeUser(user);
user.nick = data.new_nick;
chan.setUser(user);
msg = new Msg({
time: data.time,
from: user,
@ -43,7 +47,6 @@ module.exports = function(irc, network) {
user.nick = data.new_nick;
chan.sortUsers(irc);
client.emit("users", {
chan: chan.id,
});

View file

@ -32,7 +32,7 @@ module.exports = function(irc, network) {
});
chan.pushMessage(client, msg);
chan.users = _.without(chan.users, user);
chan.removeUser(user);
client.emit("users", {
chan: chan.id,
});

View file

@ -1,6 +1,5 @@
"use strict";
const _ = require("lodash");
const Msg = require("../../models/msg");
module.exports = function(irc, network) {
@ -23,7 +22,7 @@ module.exports = function(irc, network) {
});
chan.pushMessage(client, msg);
chan.users = _.without(chan.users, user);
chan.removeUser(user);
client.emit("users", {
chan: chan.id,
});

View file

@ -1,12 +1,31 @@
"use strict";
var expect = require("chai").expect;
var Chan = require("../../src/models/chan");
var Msg = require("../../src/models/msg");
var User = require("../../src/models/user");
const expect = require("chai").expect;
const Chan = require("../../src/models/chan");
const Msg = require("../../src/models/msg");
const User = require("../../src/models/user");
describe("Chan", function() {
const network = {
network: {
options: {
PREFIX: [
{symbol: "~", mode: "q"},
{symbol: "&", mode: "a"},
{symbol: "@", mode: "o"},
{symbol: "%", mode: "h"},
{symbol: "+", mode: "v"},
],
},
},
};
const prefixLookup = {};
network.network.options.PREFIX.forEach((mode) => {
prefixLookup[mode.mode] = mode.symbol;
});
describe("#findMessage(id)", function() {
const chan = new Chan({
messages: [
@ -27,40 +46,51 @@ describe("Chan", function() {
});
});
describe("#sortUsers(irc)", function() {
var network = {
network: {
options: {
PREFIX: [
{symbol: "~", mode: "q"},
{symbol: "&", mode: "a"},
{symbol: "@", mode: "o"},
{symbol: "%", mode: "h"},
{symbol: "+", mode: "v"},
],
},
},
};
describe("#setUser(user)", function() {
it("should make key lowercase", function() {
const chan = new Chan();
chan.setUser(new User({nick: "TestUser"}));
var prefixLookup = {};
network.network.options.PREFIX.forEach((mode) => {
prefixLookup[mode.mode] = mode.symbol;
expect(chan.users.has("testuser")).to.be.true;
});
var makeUser = function(nick) {
return new User({nick: nick}, prefixLookup);
};
it("should update user object", function() {
const chan = new Chan();
chan.setUser(new User({nick: "TestUser"}, prefixLookup));
chan.setUser(new User({nick: "TestUseR", modes: ["o"]}, prefixLookup));
const user = chan.getUser("TestUSER");
expect(user.mode).to.equal("@");
});
});
describe("#getUser(nick)", function() {
it("should returning existing object", function() {
const chan = new Chan();
chan.setUser(new User({nick: "TestUseR", modes: ["o"]}, prefixLookup));
const user = chan.getUser("TestUSER");
expect(user.mode).to.equal("@");
});
it("should make new User object if not found", function() {
const chan = new Chan();
const user = chan.getUser("very-testy-user");
expect(user.nick).to.equal("very-testy-user");
});
});
describe("#getSortedUsers(irc)", function() {
var getUserNames = function(chan) {
return chan.users.map((u) => u.nick);
return chan.getSortedUsers(network).map((u) => u.nick);
};
it("should sort a simple user list", function() {
var chan = new Chan({users: [
const chan = new Chan();
[
"JocelynD", "YaManicKill", "astorije", "xPaw", "Max-P",
].map(makeUser)});
chan.sortUsers(network);
].forEach((nick) => chan.setUser(new User({nick: nick}, prefixLookup)));
expect(getUserNames(chan)).to.deep.equal([
"astorije", "JocelynD", "Max-P", "xPaw", "YaManicKill",
@ -68,14 +98,12 @@ describe("Chan", function() {
});
it("should group users by modes", function() {
var chan = new Chan({users: [
new User({nick: "JocelynD", modes: ["a", "o"]}, prefixLookup),
new User({nick: "YaManicKill", modes: ["v"]}, prefixLookup),
new User({nick: "astorije", modes: ["h"]}, prefixLookup),
new User({nick: "xPaw", modes: ["q"]}, prefixLookup),
new User({nick: "Max-P", modes: ["o"]}, prefixLookup),
]});
chan.sortUsers(network);
const chan = new Chan();
chan.setUser(new User({nick: "JocelynD", modes: ["a", "o"]}, prefixLookup));
chan.setUser(new User({nick: "YaManicKill", modes: ["v"]}, prefixLookup));
chan.setUser(new User({nick: "astorije", modes: ["h"]}, prefixLookup));
chan.setUser(new User({nick: "xPaw", modes: ["q"]}, prefixLookup));
chan.setUser(new User({nick: "Max-P", modes: ["o"]}, prefixLookup));
expect(getUserNames(chan)).to.deep.equal([
"xPaw", "JocelynD", "Max-P", "astorije", "YaManicKill",
@ -83,14 +111,12 @@ describe("Chan", function() {
});
it("should sort a mix of users and modes", function() {
var chan = new Chan({users: [
new User({nick: "JocelynD"}, prefixLookup),
new User({nick: "YaManicKill", modes: ["o"]}, prefixLookup),
new User({nick: "astorije"}, prefixLookup),
new User({nick: "xPaw"}, prefixLookup),
new User({nick: "Max-P", modes: ["o"]}, prefixLookup),
]});
chan.sortUsers(network);
const chan = new Chan();
chan.setUser(new User({nick: "JocelynD"}, prefixLookup));
chan.setUser(new User({nick: "YaManicKill", modes: ["o"]}, prefixLookup));
chan.setUser(new User({nick: "astorije"}, prefixLookup));
chan.setUser(new User({nick: "xPaw"}, prefixLookup));
chan.setUser(new User({nick: "Max-P", modes: ["o"]}, prefixLookup));
expect(getUserNames(chan)).to.deep.equal(
["Max-P", "YaManicKill", "astorije", "JocelynD", "xPaw"]
@ -98,18 +124,20 @@ describe("Chan", function() {
});
it("should be case-insensitive", function() {
var chan = new Chan({users: ["aB", "Ad", "AA", "ac"].map(makeUser)});
chan.sortUsers(network);
const chan = new Chan();
[
"aB", "Ad", "AA", "ac",
].forEach((nick) => chan.setUser(new User({nick: nick}, prefixLookup)));
expect(getUserNames(chan)).to.deep.equal(["AA", "aB", "ac", "Ad"]);
});
it("should parse special characters successfully", function() {
var chan = new Chan({users: [
const chan = new Chan();
[
"[foo", "]foo", "(foo)", "{foo}", "<foo>", "_foo", "@foo", "^foo",
"&foo", "!foo", "+foo", "Foo",
].map(makeUser)});
chan.sortUsers(network);
].forEach((nick) => chan.setUser(new User({nick: nick}, prefixLookup)));
expect(getUserNames(chan)).to.deep.equal([
"!foo", "&foo", "(foo)", "+foo", "<foo>", "@foo", "[foo", "]foo",