Merge pull request #1712 from thelounge/xpaw/users-map
Convert users list to map
This commit is contained in:
commit
da5a5c7175
@ -483,7 +483,7 @@ Client.prototype.names = function(data) {
|
|||||||
|
|
||||||
client.emit("names", {
|
client.emit("names", {
|
||||||
id: target.chan.id,
|
id: target.chan.id,
|
||||||
users: target.chan.users,
|
users: target.chan.getSortedUsers(target.network.irc),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ function Chan(attr) {
|
|||||||
firstUnread: 0,
|
firstUnread: 0,
|
||||||
unread: 0,
|
unread: 0,
|
||||||
highlight: 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 = {};
|
var userModeSortPriority = {};
|
||||||
irc.network.options.PREFIX.forEach((prefix, index) => {
|
irc.network.options.PREFIX.forEach((prefix, index) => {
|
||||||
userModeSortPriority[prefix.symbol] = index;
|
userModeSortPriority[prefix.symbol] = index;
|
||||||
@ -105,7 +105,9 @@ Chan.prototype.sortUsers = function(irc) {
|
|||||||
|
|
||||||
userModeSortPriority[""] = 99; // No mode is lowest
|
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) {
|
if (a.mode === b.mode) {
|
||||||
return a.nick.toLowerCase() < b.nick.toLowerCase() ? -1 : 1;
|
return a.nick.toLowerCase() < b.nick.toLowerCase() ? -1 : 1;
|
||||||
}
|
}
|
||||||
@ -119,13 +121,21 @@ Chan.prototype.findMessage = function(msgId) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Chan.prototype.findUser = function(nick) {
|
Chan.prototype.findUser = function(nick) {
|
||||||
return _.find(this.users, {nick: nick});
|
return this.users.get(nick.toLowerCase());
|
||||||
};
|
};
|
||||||
|
|
||||||
Chan.prototype.getUser = function(nick) {
|
Chan.prototype.getUser = function(nick) {
|
||||||
return this.findUser(nick) || new User({nick: 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() {
|
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.users = []; // Do not send user list, the client will explicitly request it when needed
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const _ = require("lodash");
|
|
||||||
const Msg = require("../../models/msg");
|
const Msg = require("../../models/msg");
|
||||||
|
|
||||||
module.exports = function(irc, network) {
|
module.exports = function(irc, network) {
|
||||||
@ -9,7 +8,7 @@ module.exports = function(irc, network) {
|
|||||||
const away = data.message;
|
const away = data.message;
|
||||||
|
|
||||||
network.channels.forEach((chan) => {
|
network.channels.forEach((chan) => {
|
||||||
const user = _.find(chan.users, {nick: data.nick});
|
const user = chan.findUser(data.nick);
|
||||||
|
|
||||||
if (!user || user.away === away) {
|
if (!user || user.away === away) {
|
||||||
return;
|
return;
|
||||||
|
@ -35,8 +35,7 @@ module.exports = function(irc, network) {
|
|||||||
});
|
});
|
||||||
chan.pushMessage(client, msg);
|
chan.pushMessage(client, msg);
|
||||||
|
|
||||||
chan.users.push(user);
|
chan.setUser(new User({nick: data.nick}));
|
||||||
chan.sortUsers(irc);
|
|
||||||
client.emit("users", {
|
client.emit("users", {
|
||||||
chan: chan.id,
|
chan: chan.id,
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const _ = require("lodash");
|
|
||||||
const Msg = require("../../models/msg");
|
const Msg = require("../../models/msg");
|
||||||
|
|
||||||
module.exports = function(irc, network) {
|
module.exports = function(irc, network) {
|
||||||
@ -25,9 +24,9 @@ module.exports = function(irc, network) {
|
|||||||
chan.pushMessage(client, msg);
|
chan.pushMessage(client, msg);
|
||||||
|
|
||||||
if (data.kicked === irc.user.nick) {
|
if (data.kicked === irc.user.nick) {
|
||||||
chan.users = [];
|
chan.users = new Map();
|
||||||
} else {
|
} else {
|
||||||
chan.users = _.without(chan.users, msg.target);
|
chan.removeUser(msg.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
client.emit("users", {
|
client.emit("users", {
|
||||||
|
@ -113,8 +113,6 @@ module.exports = function(irc, network) {
|
|||||||
// TODO: This is horrible
|
// TODO: This is horrible
|
||||||
irc.raw("NAMES", data.target);
|
irc.raw("NAMES", data.target);
|
||||||
} else {
|
} else {
|
||||||
targetChan.sortUsers(irc);
|
|
||||||
|
|
||||||
client.emit("users", {
|
client.emit("users", {
|
||||||
chan: targetChan.id,
|
chan: targetChan.id,
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const User = require("../../models/user");
|
|
||||||
|
|
||||||
module.exports = function(irc, network) {
|
module.exports = function(irc, network) {
|
||||||
const client = this;
|
const client = this;
|
||||||
|
|
||||||
@ -11,31 +9,16 @@ module.exports = function(irc, network) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create lookup map of current users,
|
const newUsers = new Map();
|
||||||
// as we need to keep certain properties
|
|
||||||
// and we can recycle existing User objects
|
|
||||||
const oldUsers = new Map();
|
|
||||||
|
|
||||||
chan.users.forEach((user) => {
|
data.users.forEach((user) => {
|
||||||
oldUsers.set(user.nick, 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) => {
|
chan.users = newUsers;
|
||||||
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);
|
|
||||||
|
|
||||||
client.emit("users", {
|
client.emit("users", {
|
||||||
chan: chan.id,
|
chan: chan.id,
|
||||||
|
@ -32,6 +32,10 @@ module.exports = function(irc, network) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chan.removeUser(user);
|
||||||
|
user.nick = data.new_nick;
|
||||||
|
chan.setUser(user);
|
||||||
|
|
||||||
msg = new Msg({
|
msg = new Msg({
|
||||||
time: data.time,
|
time: data.time,
|
||||||
from: user,
|
from: user,
|
||||||
@ -43,7 +47,6 @@ module.exports = function(irc, network) {
|
|||||||
|
|
||||||
user.nick = data.new_nick;
|
user.nick = data.new_nick;
|
||||||
|
|
||||||
chan.sortUsers(irc);
|
|
||||||
client.emit("users", {
|
client.emit("users", {
|
||||||
chan: chan.id,
|
chan: chan.id,
|
||||||
});
|
});
|
||||||
|
@ -32,7 +32,7 @@ module.exports = function(irc, network) {
|
|||||||
});
|
});
|
||||||
chan.pushMessage(client, msg);
|
chan.pushMessage(client, msg);
|
||||||
|
|
||||||
chan.users = _.without(chan.users, user);
|
chan.removeUser(user);
|
||||||
client.emit("users", {
|
client.emit("users", {
|
||||||
chan: chan.id,
|
chan: chan.id,
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const _ = require("lodash");
|
|
||||||
const Msg = require("../../models/msg");
|
const Msg = require("../../models/msg");
|
||||||
|
|
||||||
module.exports = function(irc, network) {
|
module.exports = function(irc, network) {
|
||||||
@ -23,7 +22,7 @@ module.exports = function(irc, network) {
|
|||||||
});
|
});
|
||||||
chan.pushMessage(client, msg);
|
chan.pushMessage(client, msg);
|
||||||
|
|
||||||
chan.users = _.without(chan.users, user);
|
chan.removeUser(user);
|
||||||
client.emit("users", {
|
client.emit("users", {
|
||||||
chan: chan.id,
|
chan: chan.id,
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,31 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var expect = require("chai").expect;
|
const expect = require("chai").expect;
|
||||||
|
const Chan = require("../../src/models/chan");
|
||||||
var Chan = require("../../src/models/chan");
|
const Msg = require("../../src/models/msg");
|
||||||
var Msg = require("../../src/models/msg");
|
const User = require("../../src/models/user");
|
||||||
var User = require("../../src/models/user");
|
|
||||||
|
|
||||||
describe("Chan", function() {
|
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() {
|
describe("#findMessage(id)", function() {
|
||||||
const chan = new Chan({
|
const chan = new Chan({
|
||||||
messages: [
|
messages: [
|
||||||
@ -27,40 +46,51 @@ describe("Chan", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#sortUsers(irc)", function() {
|
describe("#setUser(user)", function() {
|
||||||
var network = {
|
it("should make key lowercase", function() {
|
||||||
network: {
|
const chan = new Chan();
|
||||||
options: {
|
chan.setUser(new User({nick: "TestUser"}));
|
||||||
PREFIX: [
|
|
||||||
{symbol: "~", mode: "q"},
|
|
||||||
{symbol: "&", mode: "a"},
|
|
||||||
{symbol: "@", mode: "o"},
|
|
||||||
{symbol: "%", mode: "h"},
|
|
||||||
{symbol: "+", mode: "v"},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
var prefixLookup = {};
|
expect(chan.users.has("testuser")).to.be.true;
|
||||||
|
|
||||||
network.network.options.PREFIX.forEach((mode) => {
|
|
||||||
prefixLookup[mode.mode] = mode.symbol;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var makeUser = function(nick) {
|
it("should update user object", function() {
|
||||||
return new User({nick: nick}, prefixLookup);
|
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) {
|
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() {
|
it("should sort a simple user list", function() {
|
||||||
var chan = new Chan({users: [
|
const chan = new Chan();
|
||||||
|
[
|
||||||
"JocelynD", "YaManicKill", "astorije", "xPaw", "Max-P",
|
"JocelynD", "YaManicKill", "astorije", "xPaw", "Max-P",
|
||||||
].map(makeUser)});
|
].forEach((nick) => chan.setUser(new User({nick: nick}, prefixLookup)));
|
||||||
chan.sortUsers(network);
|
|
||||||
|
|
||||||
expect(getUserNames(chan)).to.deep.equal([
|
expect(getUserNames(chan)).to.deep.equal([
|
||||||
"astorije", "JocelynD", "Max-P", "xPaw", "YaManicKill",
|
"astorije", "JocelynD", "Max-P", "xPaw", "YaManicKill",
|
||||||
@ -68,14 +98,12 @@ describe("Chan", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should group users by modes", function() {
|
it("should group users by modes", function() {
|
||||||
var chan = new Chan({users: [
|
const chan = new Chan();
|
||||||
new User({nick: "JocelynD", modes: ["a", "o"]}, prefixLookup),
|
chan.setUser(new User({nick: "JocelynD", modes: ["a", "o"]}, prefixLookup));
|
||||||
new User({nick: "YaManicKill", modes: ["v"]}, prefixLookup),
|
chan.setUser(new User({nick: "YaManicKill", modes: ["v"]}, prefixLookup));
|
||||||
new User({nick: "astorije", modes: ["h"]}, prefixLookup),
|
chan.setUser(new User({nick: "astorije", modes: ["h"]}, prefixLookup));
|
||||||
new User({nick: "xPaw", modes: ["q"]}, prefixLookup),
|
chan.setUser(new User({nick: "xPaw", modes: ["q"]}, prefixLookup));
|
||||||
new User({nick: "Max-P", modes: ["o"]}, prefixLookup),
|
chan.setUser(new User({nick: "Max-P", modes: ["o"]}, prefixLookup));
|
||||||
]});
|
|
||||||
chan.sortUsers(network);
|
|
||||||
|
|
||||||
expect(getUserNames(chan)).to.deep.equal([
|
expect(getUserNames(chan)).to.deep.equal([
|
||||||
"xPaw", "JocelynD", "Max-P", "astorije", "YaManicKill",
|
"xPaw", "JocelynD", "Max-P", "astorije", "YaManicKill",
|
||||||
@ -83,14 +111,12 @@ describe("Chan", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should sort a mix of users and modes", function() {
|
it("should sort a mix of users and modes", function() {
|
||||||
var chan = new Chan({users: [
|
const chan = new Chan();
|
||||||
new User({nick: "JocelynD"}, prefixLookup),
|
chan.setUser(new User({nick: "JocelynD"}, prefixLookup));
|
||||||
new User({nick: "YaManicKill", modes: ["o"]}, prefixLookup),
|
chan.setUser(new User({nick: "YaManicKill", modes: ["o"]}, prefixLookup));
|
||||||
new User({nick: "astorije"}, prefixLookup),
|
chan.setUser(new User({nick: "astorije"}, prefixLookup));
|
||||||
new User({nick: "xPaw"}, prefixLookup),
|
chan.setUser(new User({nick: "xPaw"}, prefixLookup));
|
||||||
new User({nick: "Max-P", modes: ["o"]}, prefixLookup),
|
chan.setUser(new User({nick: "Max-P", modes: ["o"]}, prefixLookup));
|
||||||
]});
|
|
||||||
chan.sortUsers(network);
|
|
||||||
|
|
||||||
expect(getUserNames(chan)).to.deep.equal(
|
expect(getUserNames(chan)).to.deep.equal(
|
||||||
["Max-P", "YaManicKill", "astorije", "JocelynD", "xPaw"]
|
["Max-P", "YaManicKill", "astorije", "JocelynD", "xPaw"]
|
||||||
@ -98,18 +124,20 @@ describe("Chan", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should be case-insensitive", function() {
|
it("should be case-insensitive", function() {
|
||||||
var chan = new Chan({users: ["aB", "Ad", "AA", "ac"].map(makeUser)});
|
const chan = new Chan();
|
||||||
chan.sortUsers(network);
|
[
|
||||||
|
"aB", "Ad", "AA", "ac",
|
||||||
|
].forEach((nick) => chan.setUser(new User({nick: nick}, prefixLookup)));
|
||||||
|
|
||||||
expect(getUserNames(chan)).to.deep.equal(["AA", "aB", "ac", "Ad"]);
|
expect(getUserNames(chan)).to.deep.equal(["AA", "aB", "ac", "Ad"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should parse special characters successfully", function() {
|
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}", "<foo>", "_foo", "@foo", "^foo",
|
||||||
"&foo", "!foo", "+foo", "Foo",
|
"&foo", "!foo", "+foo", "Foo",
|
||||||
].map(makeUser)});
|
].forEach((nick) => chan.setUser(new User({nick: nick}, prefixLookup)));
|
||||||
chan.sortUsers(network);
|
|
||||||
|
|
||||||
expect(getUserNames(chan)).to.deep.equal([
|
expect(getUserNames(chan)).to.deep.equal([
|
||||||
"!foo", "&foo", "(foo)", "+foo", "<foo>", "@foo", "[foo", "]foo",
|
"!foo", "&foo", "(foo)", "+foo", "<foo>", "@foo", "[foo", "]foo",
|
||||||
|
Loading…
Reference in New Issue
Block a user