Added server-side search to new members screen (#1582)

requires https://github.com/TryGhost/Ghost/pull/11854

- ties the search input on the members screen to a `?search` query param, debounced at 250ms to avoid unnecessary API requests and UI churn
- updated the members route's `model` hook to pass through the search param in the API request query parameters
This commit is contained in:
Kevin Ansfield 2020-05-28 10:15:17 +01:00 committed by GitHub
parent cbc085fb14
commit 64d7639b57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 34 additions and 8 deletions

View File

@ -7,17 +7,19 @@ import {formatNumber} from 'ghost-admin/helpers/format-number';
import {pluralize} from 'ember-inflector';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency-decorators';
import {timeout} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';
export default class MembersController extends Controller {
@service feature;
@service store;
queryParams = ['label'];
queryParams = ['label', {searchParam: 'search'}];
@alias('model') members;
@tracked searchText = '';
@tracked searchParam = '';
@tracked label = null;
@tracked modalLabel = null;
@tracked showLabelModal = false;
@ -56,7 +58,7 @@ export default class MembersController extends Controller {
}
get showingAll() {
return !this.searchText && !this.label;
return !this.searchParam && !this.label;
}
get availableLabels() {
@ -88,6 +90,11 @@ export default class MembersController extends Controller {
// Actions -----------------------------------------------------------------
@action
search(e) {
this.searchTask.perform(e.target.value);
}
@action
exportData() {
let exportUrl = ghostPaths().url.api('members/csv');
@ -141,6 +148,12 @@ export default class MembersController extends Controller {
// Tasks -------------------------------------------------------------------
@task({restartable: true})
*searchTask(query) {
yield timeout(250); // debounce
this.searchParam = query;
}
@task
*fetchLabelsTask() {
if (!this._hasLoadedLabels) {
@ -149,4 +162,10 @@ export default class MembersController extends Controller {
});
}
}
// Internal ----------------------------------------------------------------
resetSearch() {
this.searchText = '';
}
}

View File

@ -14,7 +14,8 @@ export const DEFAULT_QUERY_PARAMS = {
order: null
},
'members.index': {
label: null
label: null,
searchParam: ''
}
};

View File

@ -8,7 +8,8 @@ export default class MembersRoute extends AuthenticatedRoute {
@service store;
queryParams = {
label: {refreshModel: true}
label: {refreshModel: true},
searchParam: {refreshModel: true, replace: true}
};
// redirect to posts screen if:
@ -24,12 +25,17 @@ export default class MembersRoute extends AuthenticatedRoute {
}
model(params) {
if (!params.searchParam) {
this.controllerFor('members').resetSearch();
}
// use a fixed created_at date so that subsequent pages have a consistent index
let startDate = new Date();
// bypass the stale data shortcut if params change
let forceReload = params.label !== this._lastLabel;
let forceReload = params.label !== this._lastLabel || params.searchParam !== this._lastSearchParam;
this._lastLabel = params.label;
this._lastSearchParam = params.searchParam;
// unless we have a forced reload, do not re-fetch the members list unless it's more than a minute old
// keeps navigation between list->details->list snappy
@ -46,7 +52,8 @@ export default class MembersRoute extends AuthenticatedRoute {
limit: range.length,
page: range.start / range.length,
order: 'created_at desc',
filter: `${labelFilter}created_at:<='${moment.utc(this._startDate).format('YYYY-MM-DD HH:mm:ss')}'`
filter: `${labelFilter}created_at:<='${moment.utc(this._startDate).format('YYYY-MM-DD HH:mm:ss')}'`,
search: params.searchParam
}, query);
return this.store.query('member', query).then((result) => {

View File

@ -13,9 +13,8 @@
{{svg-jar "search" class="gh-input-search-icon"}}
<GhTextInput
placeholder="Search members..."
@disabled={{true}}
@value={{this.searchText}}
@input={{action (mut this.searchText) value="target.value"}}
@input={{this.search}}
class="gh-members-list-searchfield {{if this.searchText "active"}}" />
</div>