Merge pull request #856 from thelounge/astorije/fuzzy-match-user-list

Implement fuzzy-matching for the user list
This commit is contained in:
Pavel Djundik 2017-04-26 12:57:06 +03:00 committed by GitHub
commit 586dde7761
9 changed files with 81 additions and 48 deletions

View File

@ -765,6 +765,9 @@ kbd {
overflow: auto; overflow: auto;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
position: absolute; position: absolute;
}
#chat .channel .chat {
right: 180px; right: 180px;
} }
@ -788,18 +791,6 @@ kbd {
transform: translateZ(0); transform: translateZ(0);
} }
#chat .lobby .chat,
#chat .special .chat,
#chat .query .chat {
right: 0;
}
#chat .lobby .sidebar,
#chat .special .sidebar,
#chat .query .sidebar {
display: none;
}
#chat .show-more { #chat .show-more {
display: none; display: none;
padding: 10px; padding: 10px;
@ -1177,6 +1168,10 @@ kbd {
width: 100%; width: 100%;
} }
#chat .names-filtered {
display: none;
}
#chat .names .user { #chat .names .user {
display: block; display: block;
line-height: 1.6; line-height: 1.6;
@ -1216,6 +1211,10 @@ kbd {
content: "Users"; content: "Users";
} }
#chat .user-mode-search:before {
content: "Search Results";
}
#loading { #loading {
font-size: 14px; font-size: 14px;
z-index: 1; z-index: 1;

View File

@ -1,5 +0,0 @@
"use strict";
module.exports = function(count) {
return count + " " + (count === 1 ? "user" : "users");
};

View File

@ -7,6 +7,7 @@ const $ = require("jquery");
const moment = require("moment"); const moment = require("moment");
const Mousetrap = require("mousetrap"); const Mousetrap = require("mousetrap");
const URI = require("urijs"); const URI = require("urijs");
const fuzzy = require("fuzzy");
// our libraries // our libraries
const emojiMap = require("./libs/simplemap.json"); const emojiMap = require("./libs/simplemap.json");
@ -320,8 +321,11 @@ $(function() {
function renderChannel(data) { function renderChannel(data) {
renderChannelMessages(data); renderChannelMessages(data);
if (data.type === "channel") {
renderChannelUsers(data); renderChannelUsers(data);
} }
}
function renderChannelMessages(data) { function renderChannelMessages(data) {
var documentFragment = buildChannelMessages(data.id, data.messages); var documentFragment = buildChannelMessages(data.id, data.messages);
@ -380,7 +384,19 @@ $(function() {
return (oldSortOrder[a] || Number.MAX_VALUE) - (oldSortOrder[b] || Number.MAX_VALUE); return (oldSortOrder[a] || Number.MAX_VALUE) - (oldSortOrder[b] || Number.MAX_VALUE);
}); });
users.html(templates.user(data)).data("nicks", nicks); const search = users
.find(".search")
.attr("placeholder", nicks.length + " " + (nicks.length === 1 ? "user" : "users"));
users
.find(".names-original")
.html(templates.user(data))
.data("nicks", nicks);
// Refresh user search
if (search.val().length) {
search.trigger("input");
}
} }
function renderNetworks(data) { function renderNetworks(data) {
@ -1022,17 +1038,31 @@ $(function() {
}); });
chat.on("input", ".search", function() { chat.on("input", ".search", function() {
var value = $(this).val().toLowerCase(); const value = $(this).val();
var names = $(this).closest(".users").find(".names"); const parent = $(this).closest(".users");
names.find(".user").each(function() { const names = parent.find(".names-original");
var btn = $(this); const container = parent.find(".names-filtered");
var name = btn.text().toLowerCase().replace(/[+%@~]/, "");
if (name.indexOf(value) > -1) { if (!value.length) {
btn.show(); container.hide();
} else { names.show();
btn.hide(); return;
} }
});
const fuzzyOptions = {
pre: "<b>",
post: "</b>",
extract: el => $(el).text()
};
const result = fuzzy.filter(
value,
names.find(".user").toArray(),
fuzzyOptions
);
names.hide();
container.html(templates.user_filtered({matches: result})).show();
}); });
chat.on("msg", ".messages", function(e, target, msg) { chat.on("msg", ".messages", function(e, target, msg) {

View File

@ -19,8 +19,16 @@
</div> </div>
<div class="messages"></div> <div class="messages"></div>
</div> </div>
{{#equal type "channel"}}
<aside class="sidebar"> <aside class="sidebar">
<div class="users"></div> <div class="users">
<div class="count">
<input type="search" class="search" aria-label="Search among the user list">
</div>
<div class="names names-filtered"></div>
<div class="names names-original"></div>
</div>
</aside> </aside>
{{/equal}}
</div> </div>
{{/each}} {{/each}}

View File

@ -30,4 +30,5 @@ module.exports = {
toggle: require("./toggle.tpl"), toggle: require("./toggle.tpl"),
unread_marker: require("./unread_marker.tpl"), unread_marker: require("./unread_marker.tpl"),
user: require("./user.tpl"), user: require("./user.tpl"),
user_filtered: require("./user_filtered.tpl"),
}; };

View File

@ -1,11 +1,5 @@
{{#if users.length}} {{#diff "reset"}}{{/diff}}
<div class="count"> {{#each users}}
<input class="search" placeholder="{{users users.length}}" aria-label="Search among the user list">
</div>
{{/if}}
<div class="names">
{{#diff "reset"}}{{/diff}}
{{#each users}}
{{#diff mode}} {{#diff mode}}
{{#unless @first}} {{#unless @first}}
</div> </div>
@ -13,6 +7,5 @@
<div class="user-mode {{modes mode}}"> <div class="user-mode {{modes mode}}">
{{/diff}} {{/diff}}
<span role="button" class="user {{colorClass name}}" data-name="{{name}}">{{mode}}{{name}}</span> <span role="button" class="user {{colorClass name}}" data-name="{{name}}">{{mode}}{{name}}</span>
{{/each}} {{/each}}
</div>
</div> </div>

View File

@ -0,0 +1,5 @@
<div class="user-mode user-mode-search">
{{#each matches}}
<span role="button" class="{{original.className}}">{{{string}}}</span>
{{/each}}
</div>

View File

@ -65,6 +65,7 @@
"chai": "3.5.0", "chai": "3.5.0",
"eslint": "3.19.0", "eslint": "3.19.0",
"font-awesome": "4.7.0", "font-awesome": "4.7.0",
"fuzzy": "0.1.3",
"handlebars": "4.0.6", "handlebars": "4.0.6",
"handlebars-loader": "1.5.0", "handlebars-loader": "1.5.0",
"jquery": "3.2.1", "jquery": "3.2.1",

View File

@ -19,6 +19,7 @@ let config = {
"mousetrap", "mousetrap",
"socket.io-client", "socket.io-client",
"urijs", "urijs",
"fuzzy",
], ],
}, },
devtool: "source-map", devtool: "source-map",