mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-27 17:46:05 -05:00
ES6: user-search lib and autocomplete. Cancels many promises rather than
leaving them as pending forever.
This commit is contained in:
parent
d7f28baf77
commit
3b76fd82fd
7 changed files with 119 additions and 126 deletions
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
Default settings for bootbox
|
||||
**/
|
||||
export default {
|
||||
name: "bootbox",
|
||||
initialize: function() {
|
||||
bootbox.animate(false);
|
||||
|
||||
// clicking outside a bootbox modal closes it
|
||||
bootbox.backdrop(true);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
import autocomplete from 'discourse/lib/autocomplete';
|
||||
|
||||
export default {
|
||||
name: "jquery-plugins",
|
||||
initialize: function() {
|
||||
|
||||
// Settings for bootbox
|
||||
bootbox.animate(false);
|
||||
bootbox.backdrop(true);
|
||||
|
||||
// Initialize the autocomplete tool
|
||||
$.fn.autocomplete = autocomplete;
|
||||
}
|
||||
};
|
|
@ -4,6 +4,8 @@
|
|||
@module $.fn.autocomplete
|
||||
**/
|
||||
|
||||
export var CANCELLED_STATUS = "__CANCELLED";
|
||||
|
||||
var shiftMap = [];
|
||||
shiftMap[192] = "~";
|
||||
shiftMap[49] = "!";
|
||||
|
@ -43,7 +45,7 @@ function mapKeyPressToActualCharacter(isShiftKey, characterCode) {
|
|||
return stringValue;
|
||||
}
|
||||
|
||||
$.fn.autocomplete = function(options) {
|
||||
export default function(options) {
|
||||
var autocompletePlugin = this;
|
||||
|
||||
if (this.length === 0) return;
|
||||
|
@ -239,6 +241,12 @@ $.fn.autocomplete = function(options) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Allow an update method to cancel. This allows us to debounce
|
||||
// promises without leaking
|
||||
if (r === CANCELLED_STATUS) {
|
||||
return;
|
||||
}
|
||||
|
||||
autocompleteOptions = r;
|
||||
if (!r || r.length === 0) {
|
||||
closeAutocomplete();
|
||||
|
@ -409,4 +417,4 @@ $.fn.autocomplete = function(options) {
|
|||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
}
|
91
app/assets/javascripts/discourse/lib/user-search.js.es6
Normal file
91
app/assets/javascripts/discourse/lib/user-search.js.es6
Normal file
|
@ -0,0 +1,91 @@
|
|||
import { CANCELLED_STATUS } from 'discourse/lib/autocomplete';
|
||||
|
||||
var cache = {},
|
||||
cacheTopicId,
|
||||
cacheTime,
|
||||
currentTerm;
|
||||
|
||||
function performSearch(term, topicId, includeGroups, resultsFn) {
|
||||
var cached = cache[term];
|
||||
if (cached) {
|
||||
resultsFn(cached);
|
||||
return true;
|
||||
}
|
||||
|
||||
Discourse.ajax('/users/search/users', {
|
||||
data: { term: term,
|
||||
topic_id: topicId,
|
||||
include_groups: includeGroups }
|
||||
}).then(function (r) {
|
||||
cache[term] = r;
|
||||
cacheTime = new Date();
|
||||
|
||||
// If there is a newer search term, return null
|
||||
if (term !== currentTerm) { r = CANCELLED_STATUS; }
|
||||
resultsFn(r);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
var debouncedSearch = _.debounce(performSearch, 300);
|
||||
|
||||
function organizeResults(r, options) {
|
||||
if (r === CANCELLED_STATUS) { return r; }
|
||||
|
||||
var exclude = options.exclude || [],
|
||||
limit = options.limit || 5,
|
||||
users = [],
|
||||
groups = [],
|
||||
results = [];
|
||||
|
||||
r.users.every(function(u) {
|
||||
if (exclude.indexOf(u.username) === -1) {
|
||||
users.push(u);
|
||||
results.push(u);
|
||||
}
|
||||
return results.length <= limit;
|
||||
});
|
||||
|
||||
r.groups.every(function(g) {
|
||||
if (results.length > limit) return false;
|
||||
if (exclude.indexOf(g.name) === -1) {
|
||||
groups.push(g);
|
||||
results.push(g);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
results.users = users;
|
||||
results.groups = groups;
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
export default function userSearch(options) {
|
||||
var term = options.term || "",
|
||||
includeGroups = !!options.include_groups,
|
||||
topicId = options.topicId;
|
||||
|
||||
currentTerm = term;
|
||||
|
||||
return new Ember.RSVP.Promise(function(resolve) {
|
||||
// TODO site setting for allowed regex in username
|
||||
if (term.match(/[^a-zA-Z0-9_\.]/)) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
if (((new Date() - cacheTime) > 30000) || (cacheTopicId !== topicId)) {
|
||||
cache = {};
|
||||
}
|
||||
|
||||
cacheTopicId = topicId;
|
||||
var executed = debouncedSearch(term, topicId, includeGroups, function(r) {
|
||||
resolve(organizeResults(r, options));
|
||||
});
|
||||
|
||||
// TODO: This doesn't cancel all debounced promises, we should figure out
|
||||
// a way to handle that.
|
||||
if (!executed) {
|
||||
resolve(CANCELLED_STATUS);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
/**
|
||||
Helper for searching for Users
|
||||
|
||||
@class UserSearch
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
var cache = {};
|
||||
var cacheTopicId = null;
|
||||
var cacheTime = null;
|
||||
|
||||
var currentTerm;
|
||||
|
||||
var debouncedSearch = _.debounce(function(term, topicId, include_groups, resultsFn) {
|
||||
|
||||
Discourse.ajax('/users/search/users', {
|
||||
data: {
|
||||
term: term,
|
||||
topic_id: topicId,
|
||||
include_groups: include_groups
|
||||
}
|
||||
}).then(function (r) {
|
||||
|
||||
cache[term] = r;
|
||||
cacheTime = new Date();
|
||||
|
||||
if(term === currentTerm){
|
||||
resultsFn(r);
|
||||
}
|
||||
});
|
||||
|
||||
}, 300);
|
||||
|
||||
Discourse.UserSearch = {
|
||||
|
||||
search: function(options) {
|
||||
var term = options.term || "";
|
||||
currentTerm = term;
|
||||
|
||||
var include_groups = options.include_groups || false;
|
||||
var exclude = options.exclude || [];
|
||||
var topicId = options.topicId;
|
||||
var limit = options.limit || 5;
|
||||
|
||||
var promise = Ember.Deferred.create();
|
||||
|
||||
// TODO site setting for allowed regex in username
|
||||
if (term.match(/[^a-zA-Z0-9_\.]/)) {
|
||||
promise.resolve([]);
|
||||
return promise;
|
||||
}
|
||||
if ((new Date() - cacheTime) > 30000) {
|
||||
cache = {};
|
||||
}
|
||||
if (cacheTopicId !== topicId) {
|
||||
cache = {};
|
||||
}
|
||||
|
||||
cacheTopicId = topicId;
|
||||
|
||||
var organizeResults = function(r) {
|
||||
var users = [], groups = [], results = [];
|
||||
_.each(r.users,function(u) {
|
||||
if (exclude.indexOf(u.username) === -1) {
|
||||
users.push(u);
|
||||
results.push(u);
|
||||
}
|
||||
return results.length <= limit;
|
||||
});
|
||||
|
||||
_.each(r.groups,function(g) {
|
||||
if (results.length > limit) return false;
|
||||
if (exclude.indexOf(g.name) === -1) {
|
||||
groups.push(g);
|
||||
results.push(g);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
results.users = users;
|
||||
results.groups = groups;
|
||||
|
||||
promise.resolve(results);
|
||||
};
|
||||
|
||||
if (cache[term]) {
|
||||
// inject a delay to avoid too much repainting
|
||||
setTimeout(function(){
|
||||
if(term !== currentTerm) {
|
||||
return;
|
||||
}
|
||||
organizeResults(cache[term]);
|
||||
}, 300);
|
||||
} else {
|
||||
debouncedSearch(term, topicId, include_groups, organizeResults);
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -1,13 +1,7 @@
|
|||
/*global assetPath:true */
|
||||
|
||||
/**
|
||||
This view handles rendering of the composer
|
||||
import userSearch from 'discourse/lib/user-search';
|
||||
|
||||
@class ComposerView
|
||||
@extends Discourse.View
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
var ComposerView = Discourse.View.extend(Ember.Evented, {
|
||||
templateName: 'composer',
|
||||
elementId: 'reply-control',
|
||||
|
@ -183,7 +177,7 @@ var ComposerView = Discourse.View.extend(Ember.Evented, {
|
|||
$wmdInput.autocomplete({
|
||||
template: template,
|
||||
dataSource: function(term) {
|
||||
return Discourse.UserSearch.search({
|
||||
return userSearch({
|
||||
term: term,
|
||||
topicId: self.get('controller.controllers.topic.model.id'),
|
||||
include_groups: true
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import TextField from 'discourse/components/text-field';
|
||||
import userSearch from 'discourse/lib/user-search';
|
||||
|
||||
var compiled;
|
||||
function templateFunction() {
|
||||
|
@ -61,7 +62,7 @@ var UserSelector = TextField.extend({
|
|||
allowAny: this.get('allowAny'),
|
||||
|
||||
dataSource: function(term) {
|
||||
return Discourse.UserSearch.search({
|
||||
return userSearch({
|
||||
term: term,
|
||||
topicId: userSelectorView.get('topicId'),
|
||||
exclude: excludedUsernames(),
|
||||
|
|
Loading…
Reference in a new issue