Merge pull request #732 from matrix-org/luke/fuse-test
Test to see how fuse feels
This commit is contained in:
commit
ab9aaa9174
3 changed files with 95 additions and 89 deletions
|
@ -26,6 +26,7 @@ import dis from '../../../dispatcher';
|
|||
import Modal from '../../../Modal';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import q from 'q';
|
||||
import Fuse from 'fuse.js';
|
||||
|
||||
const TRUNCATE_QUERY_LIST = 40;
|
||||
|
||||
|
@ -85,6 +86,19 @@ module.exports = React.createClass({
|
|||
// Set the cursor at the end of the text input
|
||||
this.refs.textinput.value = this.props.value;
|
||||
}
|
||||
// Create a Fuse instance for fuzzy searching this._userList
|
||||
this._fuse = new Fuse(
|
||||
// Use an empty list at first that will later be populated
|
||||
// (see this._updateUserList)
|
||||
[],
|
||||
{
|
||||
shouldSort: true,
|
||||
location: 0, // The index of the query in the test string
|
||||
distance: 5, // The distance away from location the query can be
|
||||
// 0.0 = exact match, 1.0 = match anything
|
||||
threshold: 0.3,
|
||||
}
|
||||
);
|
||||
this._updateUserList();
|
||||
},
|
||||
|
||||
|
@ -167,45 +181,60 @@ module.exports = React.createClass({
|
|||
const query = ev.target.value;
|
||||
let queryList = [];
|
||||
|
||||
// Only do search if there is something to search
|
||||
if (query.length > 0 && query != '@') {
|
||||
// filter the known users list
|
||||
queryList = this._userList.filter((user) => {
|
||||
return this._matches(query, user);
|
||||
}).map((user) => {
|
||||
// Return objects, structure of which is defined
|
||||
// by InviteAddressType
|
||||
return {
|
||||
addressType: 'mx',
|
||||
address: user.userId,
|
||||
displayName: user.displayName,
|
||||
avatarMxc: user.avatarUrl,
|
||||
isKnown: true,
|
||||
}
|
||||
});
|
||||
if (query.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the query isn't a user we know about, but is a
|
||||
// valid address, add an entry for that
|
||||
if (queryList.length == 0) {
|
||||
const addrType = getAddressType(query);
|
||||
if (addrType !== null) {
|
||||
queryList[0] = {
|
||||
addressType: addrType,
|
||||
address: query,
|
||||
isKnown: false,
|
||||
};
|
||||
if (this._cancelThreepidLookup) this._cancelThreepidLookup();
|
||||
if (addrType == 'email') {
|
||||
this._lookupThreepid(addrType, query).done();
|
||||
if (this.queryChangedDebouncer) {
|
||||
clearTimeout(this.queryChangedDebouncer);
|
||||
}
|
||||
this.queryChangedDebouncer = setTimeout(() => {
|
||||
// Only do search if there is something to search
|
||||
if (query.length > 0 && query != '@') {
|
||||
// Weighted keys prefer to match userIds when first char is @
|
||||
this._fuse.options.keys = [{
|
||||
name: 'displayName',
|
||||
weight: query[0] === '@' ? 0.1 : 0.9,
|
||||
},{
|
||||
name: 'userId',
|
||||
weight: query[0] === '@' ? 0.9 : 0.1,
|
||||
}];
|
||||
queryList = this._fuse.search(query).map((user) => {
|
||||
// Return objects, structure of which is defined
|
||||
// by InviteAddressType
|
||||
return {
|
||||
addressType: 'mx',
|
||||
address: user.userId,
|
||||
displayName: user.displayName,
|
||||
avatarMxc: user.avatarUrl,
|
||||
isKnown: true,
|
||||
}
|
||||
});
|
||||
|
||||
// If the query isn't a user we know about, but is a
|
||||
// valid address, add an entry for that
|
||||
if (queryList.length == 0) {
|
||||
const addrType = getAddressType(query);
|
||||
if (addrType !== null) {
|
||||
queryList[0] = {
|
||||
addressType: addrType,
|
||||
address: query,
|
||||
isKnown: false,
|
||||
};
|
||||
if (this._cancelThreepidLookup) this._cancelThreepidLookup();
|
||||
if (addrType == 'email') {
|
||||
this._lookupThreepid(addrType, query).done();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
queryList: queryList,
|
||||
error: false,
|
||||
});
|
||||
this.setState({
|
||||
queryList: queryList,
|
||||
error: false,
|
||||
}, () => {
|
||||
this.addressSelector.moveSelectionTop();
|
||||
});
|
||||
}, 200);
|
||||
},
|
||||
|
||||
onDismissed: function(index) {
|
||||
|
@ -331,49 +360,15 @@ module.exports = React.createClass({
|
|||
_updateUserList: new rate_limited_func(function() {
|
||||
// Get all the users
|
||||
this._userList = MatrixClientPeg.get().getUsers();
|
||||
// Remove current user
|
||||
const meIx = this._userList.findIndex((u) => {
|
||||
return u.userId === MatrixClientPeg.get().credentials.userId;
|
||||
});
|
||||
this._userList.splice(meIx, 1);
|
||||
|
||||
this._fuse.set(this._userList);
|
||||
}, 500),
|
||||
|
||||
// This is the search algorithm for matching users
|
||||
_matches: function(query, user) {
|
||||
var name = user.displayName.toLowerCase();
|
||||
var uid = user.userId.toLowerCase();
|
||||
query = query.toLowerCase();
|
||||
|
||||
// don't match any that are already on the invite list
|
||||
if (this._isOnInviteList(uid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ignore current user
|
||||
if (uid === MatrixClientPeg.get().credentials.userId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// direct prefix matches
|
||||
if (name.indexOf(query) === 0 || uid.indexOf(query) === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// strip @ on uid and try matching again
|
||||
if (uid.length > 1 && uid[0] === "@" && uid.substring(1).indexOf(query) === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try to find the query following a "word boundary", except that
|
||||
// this does avoids using \b because it only considers letters from
|
||||
// the roman alphabet to be word characters.
|
||||
// Instead, we look for the query following either:
|
||||
// * The start of the string
|
||||
// * Whitespace, or
|
||||
// * A fixed number of punctuation characters
|
||||
const expr = new RegExp("(?:^|[\\s\\(\)'\",\.-_@\?;:{}\\[\\]\\#~`\\*\\&\\$])" + escapeRegExp(query));
|
||||
if (expr.test(name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_isOnInviteList: function(uid) {
|
||||
for (let i = 0; i < this.state.inviteList.length; i++) {
|
||||
if (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue