Revert "Replace quadratic emote list impl with Map"

This reverts commit 0f9bc44925.

The original commit was not backwards compatible with use cases that
users were relying on, such as emotes being sorted in insertion order by
default.

I will develop a new patch which fixes the performance issue in a
backwards compatible way.
This commit is contained in:
Calvin Montgomery 2018-02-01 17:39:45 -08:00
parent aeab31825e
commit 966da1ac58
7 changed files with 153 additions and 57 deletions

View file

@ -2,7 +2,7 @@
"author": "Calvin Montgomery",
"name": "CyTube",
"description": "Online media synchronizer and chat",
"version": "3.53.1",
"version": "3.52.4",
"repository": {
"url": "http://github.com/calzoneman/sync"
},

View file

@ -1,42 +1,96 @@
var ChannelModule = require("./module");
var XSS = require("../xss");
class EmoteList {
constructor(list) {
if (!list) {
list = [];
}
this.emotes = new Map(list.map(e => [e.name, e]));
function EmoteList(defaults) {
if (!defaults) {
defaults = [];
}
toJSON() {
let list = [];
for (let [key, value] of this.emotes.entries()) {
list.push(value);
}
return list;
}
hasEmote(name) {
return this.emotes.has(name);
}
setEmote(name, emote) {
this.emotes.set(name, emote);
}
deleteEmote(name) {
return this.emotes.delete(name);
}
size() {
return this.emotes.size;
}
this.emotes = defaults.map(validateEmote).filter(function (f) {
return f !== false;
});
}
EmoteList.prototype = {
pack: function () {
return Array.prototype.slice.call(this.emotes);
},
importList: function (emotes) {
this.emotes = Array.prototype.slice.call(emotes);
},
emoteExists: function (emote){
if(this.emotes.filter((item)=>{ return item.name === emote.name }).length){
return true;
}
return false;
},
renameEmote: function (emote) {
var found = false;
for (var i = 0; i < this.emotes.length; i++) {
if (this.emotes[i].name === emote.old) {
found = true;
this.emotes[i] = emote;
delete this.emotes[i].old;
break;
}
}
if(found){
return true;
}
return false;
},
updateEmote: function (emote) {
var found = false;
for (var i = 0; i < this.emotes.length; i++) {
if (this.emotes[i].name === emote.name) {
found = true;
this.emotes[i] = emote;
break;
}
}
/* If no emote was updated, add a new one */
if (!found) {
this.emotes.push(emote);
}
},
removeEmote: function (emote) {
var found = false;
for (var i = 0; i < this.emotes.length; i++) {
if (this.emotes[i].name === emote.name) {
this.emotes.splice(i, 1);
break;
}
}
},
moveEmote: function (from, to) {
if (from < 0 || to < 0 ||
from >= this.emotes.length || to >= this.emotes.length) {
return false;
}
var f = this.emotes[from];
/* Offset from/to indexes to account for the fact that removing
an element changes the position of one of them.
I could have just done a swap, but it's already implemented this way
and it works. */
to = to > from ? to + 1 : to;
from = to > from ? from : from + 1;
this.emotes.splice(to, 0, f);
this.emotes.splice(from, 1);
return true;
},
};
function validateEmote(f) {
if (typeof f.name !== "string" || typeof f.image !== "string") {
return false;
@ -72,19 +126,21 @@ EmoteModule.prototype = Object.create(ChannelModule.prototype);
EmoteModule.prototype.load = function (data) {
if ("emotes" in data) {
this.emotes = new EmoteList(data.emotes);
for (var i = 0; i < data.emotes.length; i++) {
this.emotes.updateEmote(data.emotes[i]);
}
}
this.dirty = false;
};
EmoteModule.prototype.save = function (data) {
data.emotes = this.emotes.toJSON();
data.emotes = this.emotes.pack();
};
EmoteModule.prototype.packInfo = function (data, isAdmin) {
if (isAdmin) {
data.emoteCount = this.emotes.size();
data.emoteCount = this.emotes.emotes.length;
}
};
@ -92,12 +148,13 @@ EmoteModule.prototype.onUserPostJoin = function (user) {
user.socket.on("renameEmote", this.handleRenameEmote.bind(this, user));
user.socket.on("updateEmote", this.handleUpdateEmote.bind(this, user));
user.socket.on("importEmotes", this.handleImportEmotes.bind(this, user));
user.socket.on("moveEmote", this.handleMoveEmote.bind(this, user));
user.socket.on("removeEmote", this.handleRemoveEmote.bind(this, user));
this.sendEmotes([user]);
};
EmoteModule.prototype.sendEmotes = function (users) {
var f = this.emotes.toJSON();
var f = this.emotes.pack();
var chan = this.channel;
users.forEach(function (u) {
u.socket.emit("emoteList", f);
@ -121,7 +178,7 @@ EmoteModule.prototype.handleRenameEmote = function (user, data) {
return;
}
var e = this.emotes.hasEmote(data.name);
var e = this.emotes.emoteExists(data);
var f = validateEmote(data);
if (!f || e) {
var message = "Unable to rename emote '" + JSON.stringify(data) + "'. " +
@ -140,17 +197,9 @@ EmoteModule.prototype.handleRenameEmote = function (user, data) {
return;
}
var hadOld = this.emotes.deleteEmote(f.old);
if (!hadOld) {
return;
}
this.emotes.setEmote(f.name, {
name: f.name,
source: f.source,
image: f.image
});
// See comment above
var success = this.emotes.renameEmote(Object.assign({}, f));
if(!success){ return; }
this.dirty = true;
@ -183,11 +232,7 @@ EmoteModule.prototype.handleUpdateEmote = function (user, data) {
return;
}
this.emotes.setEmote(f.name, {
name: f.name,
source: f.source,
image: f.image
});
this.emotes.updateEmote(f);
this.dirty = true;
@ -209,7 +254,9 @@ EmoteModule.prototype.handleImportEmotes = function (user, data) {
return;
}
this.emotes = new EmoteList(data.map(validateEmote).filter(f => f));
this.emotes.importList(data.map(validateEmote).filter(function (f) {
return f !== false;
}));
this.dirty = true;
@ -229,7 +276,7 @@ EmoteModule.prototype.handleRemoveEmote = function (user, data) {
return;
}
this.emotes.deleteEmote(data.name);
this.emotes.removeEmote(data);
this.dirty = true;
@ -237,4 +284,22 @@ EmoteModule.prototype.handleRemoveEmote = function (user, data) {
this.channel.broadcastAll("removeEmote", data);
};
EmoteModule.prototype.handleMoveEmote = function (user, data) {
if (typeof data !== "object") {
return;
}
if (!this.channel.modules.permissions.canEditEmotes(user)) {
return;
}
if (typeof data.to !== "number" || typeof data.from !== "number") {
return;
}
this.emotes.moveEmote(data.from, data.to);
this.dirty = true;
};
module.exports = EmoteModule;

View file

@ -189,6 +189,11 @@ html(lang="en")
.modal-body
.pull-left
input.emotelist-search.form-control(type="text", placeholder="Search")
.pull-right
.checkbox
label
input.emotelist-alphabetical(type="checkbox")
| Sort alphabetically
.emotelist-paginator-container
table.emotelist-table
tbody

View file

@ -197,6 +197,11 @@ mixin emotes
form.form-inline
.form-group
input.emotelist-search.form-control(type="text", placeholder="Search")
.form-group
.checkbox
label
input.emotelist-alphabetical(type="checkbox")
| Sort alphabetically
.emotelist-paginator-container
table.emotelist-table.table.table-striped.table-condensed
thead

View file

@ -127,6 +127,7 @@ var USEROPTS = {
default_quality : getOrDefault("default_quality", "auto"),
boop : getOrDefault("boop", "never"),
show_shadowchat : getOrDefault("show_shadowchat", false),
emotelist_sort : getOrDefault("emotelist_sort", true),
no_emotes : getOrDefault("no_emotes", false),
strip_image : getOrDefault("strip_image", false),
chat_tab_method : getOrDefault("chat_tab_method", "Cycle options")

View file

@ -846,6 +846,12 @@ $("#emotelistbtn").click(function () {
EMOTELISTMODAL.modal();
});
EMOTELISTMODAL.find(".emotelist-alphabetical").change(function () {
USEROPTS.emotelist_sort = this.checked;
setOpt("emotelist_sort", USEROPTS.emotelist_sort);
});
EMOTELISTMODAL.find(".emotelist-alphabetical").prop("checked", USEROPTS.emotelist_sort);
$("#fullscreenbtn").click(function () {
var elem = document.querySelector("#videowrap .embed-responsive");
// this shit is why frontend web development sucks

View file

@ -2915,8 +2915,8 @@ function formatScriptAccessPrefs() {
function EmoteList(selector, emoteClickCallback) {
this.elem = $(selector);
this.sortAlphabetical = true;
this.initSearch();
this.initSortOption();
this.table = this.elem.find(".emotelist-table")[0];
this.paginatorContainer = this.elem.find(".emotelist-paginator-container");
this.cols = 5;
@ -2944,6 +2944,18 @@ EmoteList.prototype.initSearch = function () {
});
};
EmoteList.prototype.initSortOption = function () {
this.sortOption = this.elem.find(".emotelist-alphabetical");
this.sortAlphabetical = false;
var self = this;
this.sortOption.change(function () {
self.sortAlphabetical = this.checked;
self.handleChange();
self.loadPage(0);
});
};
EmoteList.prototype.handleChange = function () {
this.emotes = CHANNEL.emotes.slice();
if (this.sortAlphabetical) {
@ -3027,6 +3039,7 @@ function onEmoteClicked(emote) {
}
window.EMOTELIST = new EmoteList("#emotelist", onEmoteClicked);
window.EMOTELIST.sortAlphabetical = USEROPTS.emotelist_sort;
function CSEmoteList(selector) {
EmoteList.call(this, selector);
@ -3179,6 +3192,7 @@ CSEmoteList.prototype.loadPage = function (page) {
};
window.CSEMOTELIST = new CSEmoteList("#cs-emotes");
window.CSEMOTELIST.sortAlphabetical = USEROPTS.emotelist_sort;
function showChannelSettings() {
$("#channeloptions").modal();