mirror of
https://github.com/discourse/discourse.git
synced 2025-02-01 11:03:00 +08:00
FEATURE: on mobile take users to full page search
UX: improve styling on full page search page FEATURE: allow search context in full page search FEATURE: visited color link for full page search FIX: broken search help on fulls page search page FEATURE: allow preload store to return a null FEATURE: "mobileAction" for the header buttons
This commit is contained in:
parent
e37dd5a393
commit
e13ed24122
|
@ -13,6 +13,12 @@ export default Ember.Component.extend({
|
|||
|
||||
actions: {
|
||||
toggle() {
|
||||
|
||||
if (Discourse.Mobile.mobileView && this.get('mobileAction')) {
|
||||
this.sendAction('mobileAction');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.siteSettings.login_required && !this.currentUser) {
|
||||
this.sendAction('loginAction');
|
||||
} else {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import searchForTerm from 'discourse/lib/search-for-term';
|
||||
import {searchForTerm, searchContextDescription} from 'discourse/lib/search';
|
||||
import DiscourseURL from 'discourse/lib/url';
|
||||
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
|
@ -48,18 +48,7 @@ export default Ember.Component.extend({
|
|||
|
||||
@computed('searchService.searchContext')
|
||||
searchContextDescription(ctx) {
|
||||
if (ctx) {
|
||||
switch(Em.get(ctx, 'type')) {
|
||||
case 'topic':
|
||||
return I18n.t('search.context.topic');
|
||||
case 'user':
|
||||
return I18n.t('search.context.user', {username: Em.get(ctx, 'user.username')});
|
||||
case 'category':
|
||||
return I18n.t('search.context.category', {category: Em.get(ctx, 'category.name')});
|
||||
case 'private_messages':
|
||||
return I18n.t('search.context.private_messages');
|
||||
}
|
||||
}
|
||||
return searchContextDescription(Em.get(ctx, 'type'), Em.get(ctx, 'user.username') || Em.get(ctx, 'category.name'));
|
||||
},
|
||||
|
||||
@observes('searchService.searchContextEnabled')
|
||||
|
|
|
@ -1,30 +1,65 @@
|
|||
import { translateResults } from "discourse/lib/search-for-term";
|
||||
import { translateResults, searchContextDescription } from "discourse/lib/search";
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
|
||||
import Category from 'discourse/models/category';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
needs: ["application"],
|
||||
|
||||
loading: Em.computed.not("model"),
|
||||
queryParams: ["q"],
|
||||
queryParams: ["q", "context_id", "context", "skip_context"],
|
||||
q: null,
|
||||
selected: [],
|
||||
context_id: null,
|
||||
context: null,
|
||||
|
||||
modelChanged: function() {
|
||||
@computed('skip_context', 'context')
|
||||
searchContextEnabled: {
|
||||
get(skip,context){
|
||||
return (!skip && context) || skip === "false";
|
||||
},
|
||||
set(val) {
|
||||
this.set('skip_context', val ? "false" : "true" )
|
||||
}
|
||||
},
|
||||
|
||||
@computed('context', 'context_id')
|
||||
searchContextDescription(context, id){
|
||||
var name = id;
|
||||
if (context === 'category') {
|
||||
var category = Category.findById(id);
|
||||
if (!category) {return;}
|
||||
|
||||
name = category.get('name');
|
||||
}
|
||||
return searchContextDescription(context, name);
|
||||
},
|
||||
|
||||
@computed('q')
|
||||
searchActive(q){
|
||||
return q && q.length > 0;
|
||||
},
|
||||
|
||||
@observes('model')
|
||||
modelChanged() {
|
||||
if (this.get("searchTerm") !== this.get("q")) {
|
||||
this.set("searchTerm", this.get("q"));
|
||||
}
|
||||
}.observes("model"),
|
||||
},
|
||||
|
||||
qChanged: function() {
|
||||
@observes('q')
|
||||
qChanged() {
|
||||
const model = this.get("model");
|
||||
if (model && this.get("model.q") !== this.get("q")) {
|
||||
this.set("searchTerm", this.get("q"));
|
||||
this.send("search");
|
||||
}
|
||||
}.observes("q"),
|
||||
},
|
||||
|
||||
_showFooter: function() {
|
||||
@observes('loading')
|
||||
_showFooter() {
|
||||
this.set("controllers.application.showFooter", !this.get("loading"));
|
||||
}.observes("loading"),
|
||||
},
|
||||
|
||||
canBulkSelect: Em.computed.alias('currentUser.staff'),
|
||||
|
||||
|
@ -32,9 +67,19 @@ export default Ember.Controller.extend({
|
|||
this.set("q", this.get("searchTerm"));
|
||||
this.set("model", null);
|
||||
|
||||
Discourse.ajax("/search", { data: { q: this.get("searchTerm") } }).then(results => {
|
||||
var args = { q: this.get("searchTerm") };
|
||||
|
||||
const skip = this.get("skip_context");
|
||||
if ((!skip && this.get('context')) || skip==="false"){
|
||||
args.search_context = {
|
||||
type: this.get('context'),
|
||||
id: this.get('context_id')
|
||||
};
|
||||
}
|
||||
|
||||
Discourse.ajax("/search", { data: args }).then(results => {
|
||||
this.set("model", translateResults(results) || {});
|
||||
this.set("model.q", this.get("q"));
|
||||
// this.set("model.q", this.get("q"));
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -51,6 +96,13 @@ export default Ember.Controller.extend({
|
|||
this.search();
|
||||
},
|
||||
|
||||
showSearchHelp() {
|
||||
// TODO: dupe code should be centralized
|
||||
Discourse.ajax("/static/search_help.html", { dataType: 'html' }).then((model) => {
|
||||
showModal('searchHelp', { model });
|
||||
});
|
||||
},
|
||||
|
||||
search() {
|
||||
this.search();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import DiscourseURL from 'discourse/lib/url';
|
||||
|
||||
const HeaderController = Ember.Controller.extend({
|
||||
topic: null,
|
||||
showExtraInfo: null,
|
||||
|
@ -18,6 +20,17 @@ const HeaderController = Ember.Controller.extend({
|
|||
|
||||
|
||||
actions: {
|
||||
fullPageSearch() {
|
||||
const searchService = this.container.lookup('search-service:main');
|
||||
const context = searchService.get('searchContext');
|
||||
var params = "";
|
||||
|
||||
if (context) {
|
||||
params = `?context=${context.type}&context_id=${context.id}`;
|
||||
}
|
||||
|
||||
DiscourseURL.routeTo('/search' + params);
|
||||
},
|
||||
toggleMenuPanel(visibleProp) {
|
||||
this.toggleProperty(visibleProp);
|
||||
this.appEvents.trigger('dropdowns:closeAll');
|
||||
|
|
|
@ -86,4 +86,19 @@ function searchForTerm(term, opts) {
|
|||
return promise;
|
||||
}
|
||||
|
||||
export default searchForTerm;
|
||||
const searchContextDescription = function(type, name){
|
||||
if (type) {
|
||||
switch(type) {
|
||||
case 'topic':
|
||||
return I18n.t('search.context.topic');
|
||||
case 'user':
|
||||
return I18n.t('search.context.user', {username: name});
|
||||
case 'category':
|
||||
return I18n.t('search.context.category', {category: name});
|
||||
case 'private_messages':
|
||||
return I18n.t('search.context.private_messages');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { searchForTerm, searchContextDescription };
|
|
@ -1,15 +1,24 @@
|
|||
import { translateResults } from "discourse/lib/search-for-term";
|
||||
import { translateResults } from "discourse/lib/search";
|
||||
|
||||
export default Discourse.Route.extend({
|
||||
queryParams: { q: {} },
|
||||
queryParams: { q: {}, "context-id": {}, context: {} },
|
||||
|
||||
model(params) {
|
||||
return PreloadStore.getAndRemove("search", function() {
|
||||
return Discourse.ajax("/search", { data: { q: params.q } });
|
||||
if (params.q && params.q.length > 2) {
|
||||
var args = { q: params.q };
|
||||
if (params.context_id && !args.skip_context) {
|
||||
args.search_context = {
|
||||
type: params.context,
|
||||
id: params.context_id
|
||||
}
|
||||
}
|
||||
return Discourse.ajax("/search", { data: args });
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}).then(results => {
|
||||
const model = translateResults(results) || {};
|
||||
model.q = params.q;
|
||||
return model;
|
||||
return (results && translateResults(results)) || {};
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -9,11 +9,22 @@
|
|||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if context}}
|
||||
<div class='fps-search-context'>
|
||||
<label>
|
||||
{{input type="checkbox" name="searchContext" checked=searchContextEnabled}} {{searchContextDescription}}
|
||||
</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#conditional-loading-spinner condition=loading}}
|
||||
|
||||
{{#unless model.posts}}
|
||||
<h3>
|
||||
{{i18n "search.no_results"}} <a href class="show-help" {{action "showSearchHelp" bubbles=false}}>{{i18n "search.search_help"}}</a>
|
||||
{{#if searchActive}}
|
||||
{{i18n "search.no_results"}}
|
||||
{{/if}}
|
||||
<a href class="show-help" {{action "showSearchHelp" bubbles=false}}>{{i18n "search.search_help"}}</a>
|
||||
</h3>
|
||||
{{/unless}}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
{{#header-dropdown iconId="search-button"
|
||||
icon="search"
|
||||
toggleVisible=searchVisible
|
||||
mobileAction="fullPageSearch"
|
||||
loginAction="showLogin"
|
||||
title="search.title"}}
|
||||
{{/header-dropdown}}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import debounce from 'discourse/lib/debounce';
|
||||
import searchForTerm from 'discourse/lib/search-for-term';
|
||||
import { searchForTerm } from 'discourse/lib/search';
|
||||
|
||||
export default Ember.View.extend({
|
||||
templateName: 'choose_topic',
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
//= require_tree ./discourse/mixins
|
||||
//= require ./discourse/lib/ajax-error
|
||||
//= require ./discourse/lib/markdown
|
||||
//= require ./discourse/lib/search-for-term
|
||||
//= require ./discourse/lib/search
|
||||
//= require ./discourse/lib/user-search
|
||||
//= require ./discourse/lib/export-csv
|
||||
//= require ./discourse/lib/autocomplete
|
||||
|
|
|
@ -42,7 +42,7 @@ window.PreloadStore = {
|
|||
var result = finder();
|
||||
|
||||
// If the finder returns a promise, we support that too
|
||||
if (result.then) {
|
||||
if (result && result.then) {
|
||||
result.then(function(result) {
|
||||
return resolve(result);
|
||||
}, function(result) {
|
||||
|
|
|
@ -18,10 +18,20 @@
|
|||
top: -3px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
a.search-link:visited .topic-title {
|
||||
color: scale-color($tertiary, $lightness: 15%);
|
||||
}
|
||||
.search-link {
|
||||
.topic-statuses, .topic-title {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
.topic-statuses {
|
||||
float: none;
|
||||
display: inline-block;
|
||||
color: $primary;
|
||||
font-size: 1.0em;
|
||||
}
|
||||
}
|
||||
.blurb {
|
||||
font-size: 1.0em;
|
||||
|
@ -50,3 +60,7 @@
|
|||
.search-footer {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.panel-body-contents .search-context label {
|
||||
float: left;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
@import "mobile/history";
|
||||
@import "mobile/directory";
|
||||
@import "mobile/menu-panel";
|
||||
@import "mobile/search";
|
||||
|
||||
/* These files doesn't actually exist, they are injected by DiscourseSassImporter. */
|
||||
|
||||
|
|
16
app/assets/stylesheets/mobile/search.scss
Normal file
16
app/assets/stylesheets/mobile/search.scss
Normal file
|
@ -0,0 +1,16 @@
|
|||
.search button.btn-primary, .search button.btn {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.search.row {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.search.row input.search {
|
||||
height: 25px;
|
||||
width: 69%;
|
||||
}
|
||||
|
||||
.fps-search-context {
|
||||
margin-bottom: 15px;
|
||||
}
|
|
@ -9,7 +9,20 @@ class SearchController < ApplicationController
|
|||
end
|
||||
|
||||
def show
|
||||
search = Search.new(params[:q], type_filter: 'topic', guardian: guardian, include_blurbs: true, blurb_length: 300)
|
||||
search_args = {
|
||||
type_filter: 'topic',
|
||||
guardian: guardian,
|
||||
include_blurbs: true,
|
||||
blurb_length: 300
|
||||
}
|
||||
|
||||
context, type = lookup_search_context
|
||||
if context
|
||||
search_args[:search_context] = context
|
||||
search_args[:type_filter] = type if type
|
||||
end
|
||||
|
||||
search = Search.new(params[:q], search_args)
|
||||
result = search.execute
|
||||
|
||||
serializer = serialize_data(result, GroupedSearchResultSerializer, result: result)
|
||||
|
@ -34,7 +47,29 @@ class SearchController < ApplicationController
|
|||
search_args[:include_blurbs] = params[:include_blurbs] == "true" if params[:include_blurbs].present?
|
||||
search_args[:search_for_id] = true if params[:search_for_id].present?
|
||||
|
||||
context,type = lookup_search_context
|
||||
if context
|
||||
search_args[:search_context] = context
|
||||
search_args[:type_filter] = type if type
|
||||
end
|
||||
|
||||
search = Search.new(params[:term], search_args.symbolize_keys)
|
||||
result = search.execute
|
||||
render_serialized(result, GroupedSearchResultSerializer, result: result)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def lookup_search_context
|
||||
|
||||
return if params[:skip_context] == "true"
|
||||
|
||||
search_context = params[:search_context]
|
||||
unless search_context
|
||||
if (context = params[:context]) && (id = params[:context_id])
|
||||
search_context = {type: context, id: id}
|
||||
end
|
||||
end
|
||||
|
||||
if search_context.present?
|
||||
raise Discourse::InvalidParameters.new(:search_context) unless SearchController.valid_context_types.include?(search_context[:type])
|
||||
|
@ -43,23 +78,21 @@ class SearchController < ApplicationController
|
|||
# A user is found by username
|
||||
context_obj = nil
|
||||
if ['user','private_messages'].include? search_context[:type]
|
||||
context_obj = User.find_by(username_lower: params[:search_context][:id].downcase)
|
||||
context_obj = User.find_by(username_lower: search_context[:id].downcase)
|
||||
else
|
||||
klass = search_context[:type].classify.constantize
|
||||
context_obj = klass.find_by(id: params[:search_context][:id])
|
||||
context_obj = klass.find_by(id: search_context[:id])
|
||||
end
|
||||
|
||||
type_filter = nil
|
||||
if search_context[:type] == 'private_messages'
|
||||
search_args[:type_filter] = 'private_messages'
|
||||
type_filter = 'private_messages'
|
||||
end
|
||||
|
||||
guardian.ensure_can_see!(context_obj)
|
||||
search_args[:search_context] = context_obj
|
||||
end
|
||||
|
||||
search = Search.new(params[:term], search_args.symbolize_keys)
|
||||
result = search.execute
|
||||
render_serialized(result, GroupedSearchResultSerializer, result: result)
|
||||
[context_obj, type_filter]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue
Block a user