mirror of
https://github.com/discourse/discourse.git
synced 2025-06-03 02:48:28 +08:00
Migration logic from SearchView to a controller, where it should be.
This commit is contained in:
@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
Support for searching
|
||||||
|
|
||||||
|
@class SearchController
|
||||||
|
@extends Discourse.Controller
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.SearchController = Em.ArrayController.extend(Discourse.Presence, {
|
||||||
|
|
||||||
|
// If we need to perform another search
|
||||||
|
newSearchNeeded: function() {
|
||||||
|
this.set('noResults', false);
|
||||||
|
var term = this.get('term');
|
||||||
|
if (term && term.length >= Discourse.SiteSettings.min_search_term_length) {
|
||||||
|
this.set('loading', true);
|
||||||
|
this.searchTerm(term, this.get('typeFilter'));
|
||||||
|
} else {
|
||||||
|
this.set('content', Em.A());
|
||||||
|
}
|
||||||
|
this.set('selectedIndex', 0);
|
||||||
|
}.observes('term', 'typeFilter'),
|
||||||
|
|
||||||
|
searchTerm: Discourse.debouncePromise(function(term, typeFilter) {
|
||||||
|
var searchController = this;
|
||||||
|
this.set('count', 0);
|
||||||
|
return Discourse.Search.forTerm(term, typeFilter).then(function(results) {
|
||||||
|
searchController.set('results', results);
|
||||||
|
if (results) {
|
||||||
|
searchController.set('noResults', results.length === 0);
|
||||||
|
|
||||||
|
// Make it easy to find the results by type
|
||||||
|
var results_hashed = {};
|
||||||
|
results.forEach(function(r) { results_hashed[r.type] = r });
|
||||||
|
|
||||||
|
// Default order
|
||||||
|
var order = ['topic', 'category', 'user'];
|
||||||
|
results = order.map(function(o) { return results_hashed[o] }).without(void 0);
|
||||||
|
|
||||||
|
var index = 0;
|
||||||
|
results.forEach(function(r) {
|
||||||
|
r.results.each(function(item) {
|
||||||
|
item.index = index++;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
searchController.set('count', index);
|
||||||
|
searchController.set('content', results);
|
||||||
|
}
|
||||||
|
|
||||||
|
searchController.set('loading', false);
|
||||||
|
});
|
||||||
|
}, 300),
|
||||||
|
|
||||||
|
showCancelFilter: function() {
|
||||||
|
if (this.get('loading')) return false;
|
||||||
|
return this.present('typeFilter');
|
||||||
|
}.property('typeFilter', 'loading'),
|
||||||
|
|
||||||
|
termChanged: function() {
|
||||||
|
this.cancelType();
|
||||||
|
}.observes('term'),
|
||||||
|
|
||||||
|
moreOfType: function(type) {
|
||||||
|
this.set('typeFilter', type);
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelType: function() {
|
||||||
|
this.set('typeFilter', null);
|
||||||
|
},
|
||||||
|
|
||||||
|
moveUp: function() {
|
||||||
|
if (this.get('selectedIndex') === 0) return;
|
||||||
|
this.set('selectedIndex', this.get('selectedIndex') - 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
moveDown: function() {
|
||||||
|
if (this.get('resultCount') === (this.get('selectedIndex') + 1)) return;
|
||||||
|
this.set('selectedIndex', this.get('selectedIndex') + 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
select: function() {
|
||||||
|
if (this.get('loading')) return;
|
||||||
|
var href = $('#search-dropdown li.selected a').prop('href');
|
||||||
|
if (href) {
|
||||||
|
Discourse.URL.routeTo(href);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
@ -46,7 +46,7 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{{view Discourse.SearchView currentUserBinding="view.currentUser"}}
|
{{render search}}
|
||||||
|
|
||||||
<section class='d-dropdown' id='notifications-dropdown'>
|
<section class='d-dropdown' id='notifications-dropdown'>
|
||||||
{{#if view.notifications}}
|
{{#if view.notifications}}
|
||||||
|
@ -1,28 +1,26 @@
|
|||||||
{{textField value=view.term placeholderBinding="view.searchPlaceholder"}}
|
{{textField value=term placeholderKey="search.placeholder"}}
|
||||||
{{#with view}}
|
{{#unless loading}}
|
||||||
{{#unless loading}}
|
{{#unless noResults}}
|
||||||
{{#unless noResults}}
|
{{#each resultType in content}}
|
||||||
{{#each content}}
|
<ul>
|
||||||
<ul>
|
<li class='heading'>
|
||||||
<li class='heading'>
|
{{resultType.name}}
|
||||||
{{name}}
|
{{#if resultType.more}}
|
||||||
{{#if more}}
|
<a href='#' class='filter' {{action moreOfType resultType.type bubbles=false}}>{{i18n show_more}}</a>
|
||||||
<a href='#' class='filter' {{action moreOfType type target="view" bubbles=false}}>{{i18n show_more}}</a>
|
{{else}}
|
||||||
{{else}}
|
{{#if showCancelFilter}}
|
||||||
{{#if view.showCancelFilter}}
|
<a href='#' class='filter' {{action cancelType bubbles=false}}><i class='icon icon-remove-sign'></i></a>
|
||||||
<a href='#' class='filter' {{action cancelType target="view" bubbles=false}}><i class='icon icon-remove-sign'></i></a>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</li>
|
{{/if}}
|
||||||
{{view Discourse.SearchResultsTypeView typeBinding="type" contentBinding="results"}}
|
</li>
|
||||||
</ul>
|
{{view Discourse.SearchResultsTypeView typeBinding="resultType.type" contentBinding="resultType.results"}}
|
||||||
{{/each}}
|
</ul>
|
||||||
{{else}}
|
{{/each}}
|
||||||
<div class='no-results'>
|
|
||||||
{{i18n search.no_results}}
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class='searching'><i class='icon-spinner icon-spin'></i></div>
|
<div class='no-results'>
|
||||||
|
{{i18n search.no_results}}
|
||||||
|
</div>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{/with}}
|
{{else}}
|
||||||
|
<div class='searching'><i class='icon-spinner icon-spin'></i></div>
|
||||||
|
{{/unless}}
|
||||||
|
@ -10,18 +10,17 @@ Discourse.SearchResultsTypeView = Ember.CollectionView.extend({
|
|||||||
tagName: 'ul',
|
tagName: 'ul',
|
||||||
itemViewClass: Ember.View.extend({
|
itemViewClass: Ember.View.extend({
|
||||||
tagName: 'li',
|
tagName: 'li',
|
||||||
classNameBindings: ['selectedClass', 'parentView.type'],
|
classNameBindings: ['selectedClass'],
|
||||||
selectedIndexBinding: 'parentView.parentView.selectedIndex',
|
|
||||||
|
|
||||||
templateName: (function() {
|
templateName: function() {
|
||||||
return "search/" + (this.get('parentView.type')) + "_result";
|
return "search/" + (this.get('parentView.type')) + "_result";
|
||||||
}).property('parentView.type'),
|
}.property('parentView.type'),
|
||||||
|
|
||||||
// Is this row currently selected by the keyboard?
|
// Is this row currently selected by the keyboard?
|
||||||
selectedClass: (function() {
|
selectedClass: function() {
|
||||||
if (this.get('content.index') === this.get('selectedIndex')) return 'selected';
|
if (this.get('content.index') === this.get('controller.selectedIndex')) return 'selected';
|
||||||
return null;
|
return null;
|
||||||
}).property('selectedIndex')
|
}.property('controller.selectedIndex')
|
||||||
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -14,125 +14,21 @@ Discourse.SearchView = Discourse.View.extend({
|
|||||||
|
|
||||||
didInsertElement: function() {
|
didInsertElement: function() {
|
||||||
// Delegate ESC to the composer
|
// Delegate ESC to the composer
|
||||||
var _this = this;
|
var controller = this.get('controller');
|
||||||
return $('body').on('keydown.search', function(e) {
|
return $('body').on('keydown.search', function(e) {
|
||||||
if ($('#search-dropdown').is(':visible')) {
|
if ($('#search-dropdown').is(':visible')) {
|
||||||
switch (e.which) {
|
switch (e.which) {
|
||||||
case 13:
|
case 13:
|
||||||
return _this.select();
|
return controller.select();
|
||||||
case 38:
|
case 38:
|
||||||
return _this.moveUp();
|
return controller.moveUp();
|
||||||
case 40:
|
case 40:
|
||||||
return _this.moveDown();
|
return controller.moveDown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
searchPlaceholder: function() {
|
|
||||||
return Em.String.i18n("search.placeholder");
|
|
||||||
}.property(),
|
|
||||||
|
|
||||||
// If we need to perform another search
|
|
||||||
newSearchNeeded: function() {
|
|
||||||
this.set('noResults', false);
|
|
||||||
var term = this.get('term');
|
|
||||||
if (term && term.length >= Discourse.SiteSettings.min_search_term_length) {
|
|
||||||
this.set('loading', true);
|
|
||||||
this.searchTerm(term, this.get('typeFilter'));
|
|
||||||
} else {
|
|
||||||
this.set('results', null);
|
|
||||||
}
|
|
||||||
return this.set('selectedIndex', 0);
|
|
||||||
}.observes('term', 'typeFilter'),
|
|
||||||
|
|
||||||
searchTerm: Discourse.debouncePromise(function(term, typeFilter) {
|
|
||||||
var searchView = this;
|
|
||||||
return Discourse.Search.forTerm(term, typeFilter).then(function(results) {
|
|
||||||
searchView.set('results', results);
|
|
||||||
});
|
|
||||||
}, 300),
|
|
||||||
|
|
||||||
showCancelFilter: function() {
|
|
||||||
if (this.get('loading')) return false;
|
|
||||||
return this.present('typeFilter');
|
|
||||||
}.property('typeFilter', 'loading'),
|
|
||||||
|
|
||||||
termChanged: function() {
|
|
||||||
return this.cancelType();
|
|
||||||
}.observes('term'),
|
|
||||||
|
|
||||||
// We can re-order them based on the context
|
|
||||||
content: function() {
|
|
||||||
var index, order, path, results, results_hashed;
|
|
||||||
if (results = this.get('results')) {
|
|
||||||
// Make it easy to find the results by type
|
|
||||||
results_hashed = {};
|
|
||||||
results.each(function(r) {
|
|
||||||
results_hashed[r.type] = r;
|
|
||||||
});
|
|
||||||
path = Discourse.get('router.currentState.path');
|
|
||||||
// Default order
|
|
||||||
order = ['topic', 'category', 'user'];
|
|
||||||
results = (order.map(function(o) {
|
|
||||||
return results_hashed[o];
|
|
||||||
})).without(void 0);
|
|
||||||
index = 0;
|
|
||||||
results.each(function(result) {
|
|
||||||
return result.results.each(function(item) {
|
|
||||||
item.index = index++;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}.property('results'),
|
|
||||||
|
|
||||||
updateProgress: function() {
|
|
||||||
var results;
|
|
||||||
if (results = this.get('results')) {
|
|
||||||
this.set('noResults', results.length === 0);
|
|
||||||
}
|
|
||||||
return this.set('loading', false);
|
|
||||||
}.observes('results'),
|
|
||||||
|
|
||||||
resultCount: function() {
|
|
||||||
var count;
|
|
||||||
if (this.blank('content')) return 0;
|
|
||||||
count = 0;
|
|
||||||
this.get('content').each(function(result) {
|
|
||||||
count += result.results.length;
|
|
||||||
});
|
|
||||||
return count;
|
|
||||||
}.property('content'),
|
|
||||||
|
|
||||||
moreOfType: function(type) {
|
|
||||||
this.set('typeFilter', type);
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelType: function() {
|
|
||||||
this.set('typeFilter', null);
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
moveUp: function() {
|
|
||||||
if (this.get('selectedIndex') === 0) return;
|
|
||||||
return this.set('selectedIndex', this.get('selectedIndex') - 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
moveDown: function() {
|
|
||||||
if (this.get('resultCount') === (this.get('selectedIndex') + 1)) return;
|
|
||||||
return this.set('selectedIndex', this.get('selectedIndex') + 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
select: function() {
|
|
||||||
if (this.get('loading')) return;
|
|
||||||
var href = $('#search-dropdown li.selected a').prop('href');
|
|
||||||
if (href) {
|
|
||||||
Discourse.URL.routeTo(href);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@ class Search
|
|||||||
end
|
end
|
||||||
|
|
||||||
def initialize(term, opts=nil)
|
def initialize(term, opts=nil)
|
||||||
|
|
||||||
if term.present?
|
if term.present?
|
||||||
@term = term.to_s
|
@term = term.to_s
|
||||||
@original_term = PG::Connection.escape_string(@term)
|
@original_term = PG::Connection.escape_string(@term)
|
||||||
|
Reference in New Issue
Block a user