discourse/app/assets/javascripts/discourse/widgets/search-menu.js.es6
2016-11-07 15:21:31 -05:00

197 lines
5.0 KiB
JavaScript

import { searchForTerm, isValidSearchTerm } from 'discourse/lib/search';
import { createWidget } from 'discourse/widgets/widget';
import { h } from 'virtual-dom';
import DiscourseURL from 'discourse/lib/url';
const searchData = {
loading: false,
results: {},
noResults: false,
term: undefined,
typeFilter: null,
invalidTerm: false
};
// Helps with debouncing and cancelling promises
const SearchHelper = {
_activeSearch: null,
_cancelSearch: null,
// for cancelling debounced search
cancel() {
if (this._activeSearch) {
this._activeSearch.abort();
}
this._cancelSearch = true;
Ember.run.later(() => this._cancelSearch = false, 400);
},
perform(widget) {
if (this._cancelSearch){
this._cancelSearch = null;
return;
}
if (this._activeSearch) {
this._activeSearch.abort();
this._activeSearch = null;
}
const { term, typeFilter, contextEnabled } = searchData;
const searchContext = contextEnabled ? widget.searchContext() : null;
const fullSearchUrl = widget.fullSearchUrl();
if (!isValidSearchTerm(term)) {
searchData.noResults = true;
searchData.results = [];
searchData.loading = false;
searchData.invalidTerm = true;
widget.scheduleRerender();
} else {
searchData.invalidTerm = false;
this._activeSearch = searchForTerm(term, { typeFilter, searchContext, fullSearchUrl });
this._activeSearch.then(content => {
searchData.noResults = content.resultTypes.length === 0;
searchData.results = content;
}).finally(() => {
searchData.loading = false;
widget.scheduleRerender();
this._activeSearch = null;
});
}
}
};
export default createWidget('search-menu', {
tagName: 'div.search-menu',
fullSearchUrl(opts) {
const contextEnabled = searchData.contextEnabled;
const ctx = contextEnabled ? this.searchContext() : null;
const type = ctx ? Ember.get(ctx, 'type') : null;
if (contextEnabled && type === 'topic') {
return;
}
let url = '/search';
const params = [];
if (searchData.term) {
let query = '';
query += `q=${encodeURIComponent(searchData.term)}`;
if (contextEnabled) {
if (this.currentUser &&
ctx.id.toString().toLowerCase() === this.currentUser.username_lower &&
type === "private_messages") {
query += ' in:private';
} else {
query += encodeURIComponent(" " + type + ":" + ctx.id);
}
}
if (query) params.push(query);
}
if (opts && opts.expanded) params.push('expanded=true');
if (params.length > 0) {
url = `${url}?${params.join("&")}`;
}
return Discourse.getURL(url);
},
panelContents() {
const contextEnabled = searchData.contextEnabled;
const results = [
this.attach('search-term', { value: searchData.term, contextEnabled }),
this.attach('search-context', {
contextEnabled,
url: this.fullSearchUrl({ expanded: true })
})
];
if (searchData.term) {
if (searchData.loading) {
results.push(h('div.searching', h('div.spinner')));
} else {
results.push(this.attach('search-menu-results', { term: searchData.term,
noResults: searchData.noResults,
results: searchData.results,
invalidTerm: searchData.invalidTerm }));
}
}
return results;
},
searchService() {
if (!this._searchService) {
this._searchService = this.register.lookup('search-service:main');
}
return this._searchService;
},
searchContext() {
if (!this._searchContext) {
this._searchContext = this.searchService().get('searchContext');
}
return this._searchContext;
},
html(attrs) {
searchData.contextEnabled = attrs.contextEnabled;
return this.attach('menu-panel', { maxWidth: 500, contents: () => this.panelContents() });
},
clickOutside() {
this.sendWidgetAction('toggleSearchMenu');
},
triggerSearch() {
searchData.noResults = false;
this.searchService().set('highlightTerm', searchData.term);
searchData.loading = true;
Ember.run.debounce(SearchHelper, SearchHelper.perform, this, 400);
},
moreOfType(type) {
searchData.typeFilter = type;
this.triggerSearch();
},
searchContextChanged(enabled) {
searchData.typeFilter = null;
this.sendWidgetAction('searchMenuContextChanged', enabled);
searchData.contextEnabled = enabled;
this.triggerSearch();
},
searchTermChanged(term) {
searchData.typeFilter = null;
searchData.term = term;
this.triggerSearch();
},
fullSearch() {
if (!isValidSearchTerm(searchData.term)) { return; }
searchData.results = [];
searchData.loading = false;
SearchHelper.cancel();
const url = this.fullSearchUrl();
if (url) {
this.sendWidgetEvent('linkClicked');
DiscourseURL.routeTo(url);
}
}
});