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;
-webkit-overflow-scrolling: touch;
position: absolute;
}
#chat .channel .chat {
right: 180px;
}
@ -788,18 +791,6 @@ kbd {
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 {
display: none;
padding: 10px;
@ -1177,6 +1168,10 @@ kbd {
width: 100%;
}
#chat .names-filtered {
display: none;
}
#chat .names .user {
display: block;
line-height: 1.6;
@ -1216,6 +1211,10 @@ kbd {
content: "Users";
}
#chat .user-mode-search:before {
content: "Search Results";
}
#loading {
font-size: 14px;
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 Mousetrap = require("mousetrap");
const URI = require("urijs");
const fuzzy = require("fuzzy");
// our libraries
const emojiMap = require("./libs/simplemap.json");
@ -320,7 +321,10 @@ $(function() {
function renderChannel(data) {
renderChannelMessages(data);
renderChannelUsers(data);
if (data.type === "channel") {
renderChannelUsers(data);
}
}
function renderChannelMessages(data) {
@ -380,7 +384,19 @@ $(function() {
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) {
@ -1022,17 +1038,31 @@ $(function() {
});
chat.on("input", ".search", function() {
var value = $(this).val().toLowerCase();
var names = $(this).closest(".users").find(".names");
names.find(".user").each(function() {
var btn = $(this);
var name = btn.text().toLowerCase().replace(/[+%@~]/, "");
if (name.indexOf(value) > -1) {
btn.show();
} else {
btn.hide();
}
});
const value = $(this).val();
const parent = $(this).closest(".users");
const names = parent.find(".names-original");
const container = parent.find(".names-filtered");
if (!value.length) {
container.hide();
names.show();
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) {

View file

@ -19,8 +19,16 @@
</div>
<div class="messages"></div>
</div>
{{#equal type "channel"}}
<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>
{{/equal}}
</div>
{{/each}}

View file

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

View file

@ -1,18 +1,11 @@
{{#if users.length}}
<div class="count">
<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}}
{{#unless @first}}
</div>
{{/unless}}
<div class="user-mode {{modes mode}}">
{{/diff}}
<span role="button" class="user {{colorClass name}}" data-name="{{name}}">{{mode}}{{name}}</span>
{{/each}}
</div>
{{#diff "reset"}}{{/diff}}
{{#each users}}
{{#diff mode}}
{{#unless @first}}
</div>
{{/unless}}
<div class="user-mode {{modes mode}}">
{{/diff}}
<span role="button" class="user {{colorClass name}}" data-name="{{name}}">{{mode}}{{name}}</span>
{{/each}}
</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",
"eslint": "3.19.0",
"font-awesome": "4.7.0",
"fuzzy": "0.1.3",
"handlebars": "4.0.6",
"handlebars-loader": "1.5.0",
"jquery": "3.2.1",

View file

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