Refactor user filter PR to make adding filters easier & cleanup UI

This commit is contained in:
chylex 2018-02-16 16:36:26 +01:00
parent fa52c3aeb0
commit 35183fd270
6 changed files with 133 additions and 97 deletions

View File

@ -31,12 +31,6 @@
<option value="0">All messages&nbsp;</option> <option value="0">All messages&nbsp;</option>
</select> </select>
</div> </div>
<div> <!-- needed to stop the select from messing up -->
<select id="opt-messages-by-user">
<option value="">Filter messages by user&nbsp;</option>
</select>
</div>
<div class="nav"> <div class="nav">
<button id="nav-first" data-nav="first">&laquo;</button> <button id="nav-first" data-nav="first">&laquo;</button>
@ -46,6 +40,21 @@
<button id="nav-last" data-nav="last">&raquo;</button> <button id="nav-last" data-nav="last">&raquo;</button>
</div> </div>
<div class="splitter"></div>
<div> <!-- needed to stop the select from messing up -->
<select id="opt-messages-filter">
<option value="">No filter&nbsp;</option>
<option value="user">Filter messages by user&nbsp;</option>
</select>
</div>
<div id="opt-filter-list">
<select id="opt-filter-user" data-filter-type="user">
<option value="">Select user...</option>
</select>
</div>
<div class="separator"></div> <div class="separator"></div>
<button id="btn-about">About</button> <button id="btn-about">About</button>

View File

@ -26,14 +26,18 @@ document.addEventListener("DOMContentLoaded", () => {
STATE.setMessagesPerPage(GUI.getOptionMessagesPerPage()); STATE.setMessagesPerPage(GUI.getOptionMessagesPerPage());
GUI.onOptionMessagesByUserChanged(() => { GUI.onOptMessageFilterChanged(filter => {
STATE.setMessagesByUser(GUI.getOptionMessagesByUser()); STATE.setActiveFilter(filter);
}); });
GUI.onNavigationButtonClicked(action => { GUI.onNavigationButtonClicked(action => {
STATE.updateCurrentPage(action); STATE.updateCurrentPage(action);
}); });
STATE.onUsersRefreshed(users => {
GUI.updateUserList(users);
});
STATE.onChannelsRefreshed(channels => { STATE.onChannelsRefreshed(channels => {
GUI.updateChannelList(channels, STATE.selectChannel); GUI.updateChannelList(channels, STATE.selectChannel);
}); });
@ -43,8 +47,4 @@ document.addEventListener("DOMContentLoaded", () => {
GUI.updateMessageList(messages); GUI.updateMessageList(messages);
GUI.scrollMessagesToTop(); GUI.scrollMessagesToTop();
}); });
STATE.onUsersRefreshed(users => {
GUI.updateUserFilter(users);
});
}); });

View File

@ -1,9 +1,22 @@
var GUI = (function(){ var GUI = (function(){
var eventOnFileUploaded; var eventOnFileUploaded;
var eventOnOptMessagesPerPageChanged; var eventOnOptMessagesPerPageChanged;
var eventOnOptMessagesByUserChanged; var eventOnOptMessageFilterChanged;
var eventOnNavButtonClicked; var eventOnNavButtonClicked;
var getActiveFilter = function(){
var active = DOM.fcls("active", DOM.id("opt-filter-list"));
return active && active.value !== "" ? {
"type": active.getAttribute("data-filter-type"),
"value": active.value
} : null;
};
var triggerFilterChanged = function(){
eventOnOptMessageFilterChanged && eventOnOptMessageFilterChanged(getActiveFilter());
};
var showModal = function(width, html){ var showModal = function(width, html){
var dialog = DOM.id("dialog"); var dialog = DOM.id("dialog");
dialog.innerHTML = html; dialog.innerHTML = html;
@ -59,6 +72,8 @@ var GUI = (function(){
*/ */
setup: function(){ setup: function(){
var inputUploadedFile = DOM.id("uploaded-file"); var inputUploadedFile = DOM.id("uploaded-file");
var inputMessageFilter = DOM.id("opt-messages-filter");
var containerFilterList = DOM.id("opt-filter-list");
DOM.id("btn-upload-file").addEventListener("click", () => { DOM.id("btn-upload-file").addEventListener("click", () => {
inputUploadedFile.click(); inputUploadedFile.click();
@ -67,16 +82,31 @@ var GUI = (function(){
inputUploadedFile.addEventListener("change", () => { inputUploadedFile.addEventListener("change", () => {
if (eventOnFileUploaded && eventOnFileUploaded(inputUploadedFile.files)){ if (eventOnFileUploaded && eventOnFileUploaded(inputUploadedFile.files)){
inputUploadedFile.value = null; inputUploadedFile.value = null;
inputMessageFilter.value = "";
inputMessageFilter.dispatchEvent(new Event("change"));
} }
}); });
inputMessageFilter.value = ""; // required to prevent browsers from remembering old value
inputMessageFilter.addEventListener("change", () => {
DOM.cls("active", containerFilterList).forEach(ele => ele.classList.remove("active"));
if (inputMessageFilter.value){
containerFilterList.querySelector("[data-filter-type='"+inputMessageFilter.value+"']").classList.add("active");
}
triggerFilterChanged();
});
Array.prototype.forEach.call(containerFilterList.children, ele => {
ele.addEventListener("change", e => triggerFilterChanged());
});
DOM.id("opt-messages-per-page").addEventListener("change", () => { DOM.id("opt-messages-per-page").addEventListener("change", () => {
eventOnOptMessagesPerPageChanged && eventOnOptMessagesPerPageChanged(); eventOnOptMessagesPerPageChanged && eventOnOptMessagesPerPageChanged();
}); });
DOM.id("opt-messages-by-user").addEventListener("change", () => {
eventOnOptMessagesByUserChanged && eventOnOptMessagesByUserChanged();
});
DOM.tag("button", DOM.fcls("nav")).forEach(button => { DOM.tag("button", DOM.fcls("nav")).forEach(button => {
button.disabled = true; button.disabled = true;
@ -119,10 +149,10 @@ var GUI = (function(){
}, },
/* /*
* Sets a callback for when the user changes the messages by user option. The callback is not passed any arguments. * Sets a callback for when the user changes the active filter. The callback is passed either null or an object such as { type: <filter type>, value: <filter value> }.
*/ */
onOptionMessagesByUserChanged: function(callback){ onOptMessageFilterChanged: function(callback){
eventOnOptMessagesByUserChanged = callback; eventOnOptMessageFilterChanged = callback;
}, },
/* /*
@ -142,13 +172,6 @@ var GUI = (function(){
getOptionMessagesPerPage: function(){ getOptionMessagesPerPage: function(){
return parseInt(DOM.id("opt-messages-per-page").value, 10); return parseInt(DOM.id("opt-messages-per-page").value, 10);
}, },
/*
* Returns the user which to filter messages by.
*/
getOptionMessagesByUser: function(){
return DOM.id("opt-messages-by-user").value;
},
/* /*
* Updates the navigation text and buttons. * Updates the navigation text and buttons.
@ -163,9 +186,9 @@ var GUI = (function(){
DOM.id("nav-last").disabled = currentPage === (totalPages || 1); DOM.id("nav-last").disabled = currentPage === (totalPages || 1);
}, },
// ------------ // --------------
// Channel list // Updating lists
// ------------ // --------------
/* /*
* Updates the channel list and sets up their click events. The callback is triggered whenever a channel is selected, and takes the channel ID as its argument. * Updates the channel list and sets up their click events. The callback is triggered whenever a channel is selected, and takes the channel ID as its argument.
@ -177,7 +200,11 @@ var GUI = (function(){
eleChannels.innerHTML = ""; eleChannels.innerHTML = "";
} }
else{ else{
eleChannels.innerHTML = channels.filter(channel => channel.msgcount > 0).map(channel => DISCORD.getChannelHTML(channel)).join(""); if (getActiveFilter() != null){
channels = channels.filter(channel => channel.msgcount > 0);
}
eleChannels.innerHTML = channels.map(channel => DISCORD.getChannelHTML(channel)).join("");
Array.prototype.forEach.call(eleChannels.children, ele => { Array.prototype.forEach.call(eleChannels.children, ele => {
ele.addEventListener("click", e => { ele.addEventListener("click", e => {
@ -206,31 +233,26 @@ var GUI = (function(){
}, },
/* /*
* Updates the select box with all possibly user names. * Updates the user filter list.
*/ */
updateUserFilter: function(users){ updateUserList: function(users){
var select = DOM.id("opt-messages-by-user"); var eleSelect = DOM.id("opt-filter-user");
// Remove all but first option while(eleSelect.length > 1){
for (var i = select.length; i > 0; i--) { eleSelect.remove(1);
select.remove(i);
} }
var options = []; var options = [];
for (var key in users) {
// Skip loop if the property is from prototype for(var key of Object.keys(users)){
if (!users.hasOwnProperty(key)) continue;
var option = document.createElement("option"); var option = document.createElement("option");
option.value = key; option.value = key;
option.text = users[key].name; option.text = users[key].name;
options.push(option); options.push(option);
} }
options.sort((a, b) => {
return a.text.toLowerCase().localeCompare(b.text.toLowerCase()); options.sort((a, b) => a.text.toLocaleLowerCase().localeCompare(b.text.toLocaleLowerCase()));
}).forEach(option => { options.forEach(option => eleSelect.add(option));
select.add(option);
});
}, },
/* /*

View File

@ -26,6 +26,10 @@ SAVEFILE.prototype.getChannelById = function(channel){
return this.meta.channels[channel] || { "id": channel, "name": channel }; return this.meta.channels[channel] || { "id": channel, "name": channel };
}; };
SAVEFILE.prototype.getUsers = function(){
return this.meta.users;
};
SAVEFILE.prototype.getUser = function(index){ SAVEFILE.prototype.getUser = function(index){
return this.meta.users[this.meta.userindex[index]] || { "name": "&lt;unknown&gt;" }; return this.meta.users[this.meta.userindex[index]] || { "name": "&lt;unknown&gt;" };
}; };
@ -34,20 +38,6 @@ SAVEFILE.prototype.getUserById = function(user){
return this.meta.users[user] || { "name": user }; return this.meta.users[user] || { "name": user };
}; };
SAVEFILE.prototype.getMessageCount = function(channel){
return Object.keys(this.getMessages(channel)).length;
};
SAVEFILE.prototype.getFilteredMessageCount = function(channel, userindex){
if (userindex === -1) return this.getMessageCount(channel);
var count = 0;
UTILS.forEachValue(this.getMessages(channel), messageObject => {
if (messageObject.u === userindex) count++;
});
return count;
};
SAVEFILE.prototype.getAllMessages = function(){ SAVEFILE.prototype.getAllMessages = function(){
var messages = {}; var messages = {};
@ -56,6 +46,8 @@ SAVEFILE.prototype.getAllMessages = function(){
}); });
return messages; return messages;
SAVEFILE.prototype.getUserIndex = function(user){
return this.meta.userindex.indexOf(user);
}; };
SAVEFILE.prototype.getMessages = function(channel){ SAVEFILE.prototype.getMessages = function(channel){

View File

@ -11,7 +11,7 @@ var STATE = (function(){
var selectedChannel; var selectedChannel;
var currentPage; var currentPage;
var messagesPerPage; var messagesPerPage;
var messagesByUser; var filterFunction;
// ---------------------------------- // ----------------------------------
// Channel and message refresh events // Channel and message refresh events
@ -53,10 +53,10 @@ var STATE = (function(){
FILE = file; FILE = file;
MSGS = null; MSGS = null;
currentPage = 1; currentPage = 1;
triggerUsersRefreshed();
triggerChannelsRefreshed(); triggerChannelsRefreshed();
triggerMessagesRefreshed(); triggerMessagesRefreshed();
triggerUsersRefreshed();
}; };
ROOT.getChannelName = function(channel){ ROOT.getChannelName = function(channel){
@ -74,24 +74,34 @@ var STATE = (function(){
// -------------------------- // --------------------------
// Channel list and selection // Channel list and selection
// -------------------------- // --------------------------
var getFilteredMessageKeys = function(channel){
var messages = FILE.getMessages(channel);
var keys = Object.keys(messages);
if (filterFunction){
keys = keys.filter(key => filterFunction(messages[key]));
}
return keys;
};
ROOT.getChannelList = function(){ ROOT.getChannelList = function(){
var channels = FILE.getChannels(); var channels = FILE.getChannels();
var userindex = messagesByUser ? FILE.meta.userindex.indexOf(messagesByUser) : -1;
return Object.keys(channels).map(key => ({ return Object.keys(channels).map(key => ({
"id": key, "id": key,
"name": channels[key].name, "name": channels[key].name,
"server": FILE.getServer(channels[key].server), "server": FILE.getServer(channels[key].server),
"msgcount": FILE.getFilteredMessageCount(key, userindex) "msgcount": getFilteredMessageKeys(key).length
})); }));
}; };
ROOT.selectChannel = function(channel){ ROOT.selectChannel = function(channel){
currentPage = 1; currentPage = 1;
selectedChannel = channel; selectedChannel = channel;
MSGS = Object.keys(FILE.getMessages(channel)).sort(PROCESSOR.SORTER.oldestToNewest);
MSGS = getFilteredMessageKeys(channel).sort(PROCESSOR.SORTER.oldestToNewest);
triggerMessagesRefreshed(); triggerMessagesRefreshed();
}; };
@ -102,19 +112,6 @@ var STATE = (function(){
// ------------ // ------------
// Message list // Message list
// ------------ // ------------
ROOT.getFilteredMessageList = function(){
if (!messagesByUser) {
return MSGS;
}
var pos = FILE.meta.userindex.indexOf(messagesByUser);
var messages = FILE.getMessages(selectedChannel);
return MSGS.filter(key => {
var message = messages[key];
return message.u == pos;
});
};
ROOT.getMessageList = function(){ ROOT.getMessageList = function(){
if (!MSGS){ if (!MSGS){
@ -124,7 +121,7 @@ var STATE = (function(){
var messages = FILE.getMessages(selectedChannel); var messages = FILE.getMessages(selectedChannel);
var startIndex = messagesPerPage*(ROOT.getCurrentPage()-1); var startIndex = messagesPerPage*(ROOT.getCurrentPage()-1);
return ROOT.getFilteredMessageList().slice(startIndex, !messagesPerPage ? undefined : startIndex+messagesPerPage).map(key => { return MSGS.slice(startIndex, !messagesPerPage ? undefined : startIndex+messagesPerPage).map(key => {
var message = messages[key]; var message = messages[key];
return { return {
@ -137,24 +134,32 @@ var STATE = (function(){
}; };
}); });
}; };
ROOT.getUserList = function(){
if (!FILE){
return [];
}
return FILE.meta.users;
};
// ---------- // ----------
// Filtering // Filtering
// ---------- // ----------
ROOT.setActiveFilter = function(filter){
switch(filter ? filter.type : ""){
case "user":
filterFunction = PROCESSOR.FILTER.byUser(FILE.getUserIndex(filter.value));
break;
ROOT.setMessagesByUser = function(key){ default:
currentPage = 1; filterFunction = null;
messagesByUser = key; break;
}
triggerChannelsRefreshed(); triggerChannelsRefreshed();
triggerMessagesRefreshed(); ROOT.selectChannel(selectedChannel); // resets current page and updates messages
};
// -----
// Users
// -----
ROOT.getUserList = function(){
return FILE ? FILE.getUsers() : [];
}; };
// ---------- // ----------
@ -188,7 +193,7 @@ var STATE = (function(){
}; };
ROOT.getPageCount = function(){ ROOT.getPageCount = function(){
return !MSGS ? 0 : (!messagesPerPage ? 1 : Math.ceil(ROOT.getFilteredMessageList().length/messagesPerPage)); return !MSGS ? 0 : (!messagesPerPage ? 1 : Math.ceil(MSGS.length/messagesPerPage));
}; };
// -------- // --------

View File

@ -60,3 +60,11 @@
#menu .nav > button, #menu .nav > p { #menu .nav > button, #menu .nav > p {
margin: 8px 1px; margin: 8px 1px;
} }
#opt-filter-list > select, #opt-filter-list > input {
display: none;
}
#opt-filter-list > .active {
display: block;
}