mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-27 12:53:13 +03:00
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:
parent
76b93c3be7
commit
ff33eb978b
@ -7,17 +7,19 @@ import {formatNumber} from 'ghost-admin/helpers/format-number';
|
|||||||
import {pluralize} from 'ember-inflector';
|
import {pluralize} from 'ember-inflector';
|
||||||
import {inject as service} from '@ember/service';
|
import {inject as service} from '@ember/service';
|
||||||
import {task} from 'ember-concurrency-decorators';
|
import {task} from 'ember-concurrency-decorators';
|
||||||
|
import {timeout} from 'ember-concurrency';
|
||||||
import {tracked} from '@glimmer/tracking';
|
import {tracked} from '@glimmer/tracking';
|
||||||
|
|
||||||
export default class MembersController extends Controller {
|
export default class MembersController extends Controller {
|
||||||
@service feature;
|
@service feature;
|
||||||
@service store;
|
@service store;
|
||||||
|
|
||||||
queryParams = ['label'];
|
queryParams = ['label', {searchParam: 'search'}];
|
||||||
|
|
||||||
@alias('model') members;
|
@alias('model') members;
|
||||||
|
|
||||||
@tracked searchText = '';
|
@tracked searchText = '';
|
||||||
|
@tracked searchParam = '';
|
||||||
@tracked label = null;
|
@tracked label = null;
|
||||||
@tracked modalLabel = null;
|
@tracked modalLabel = null;
|
||||||
@tracked showLabelModal = false;
|
@tracked showLabelModal = false;
|
||||||
@ -56,7 +58,7 @@ export default class MembersController extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get showingAll() {
|
get showingAll() {
|
||||||
return !this.searchText && !this.label;
|
return !this.searchParam && !this.label;
|
||||||
}
|
}
|
||||||
|
|
||||||
get availableLabels() {
|
get availableLabels() {
|
||||||
@ -88,6 +90,11 @@ export default class MembersController extends Controller {
|
|||||||
|
|
||||||
// Actions -----------------------------------------------------------------
|
// Actions -----------------------------------------------------------------
|
||||||
|
|
||||||
|
@action
|
||||||
|
search(e) {
|
||||||
|
this.searchTask.perform(e.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
exportData() {
|
exportData() {
|
||||||
let exportUrl = ghostPaths().url.api('members/csv');
|
let exportUrl = ghostPaths().url.api('members/csv');
|
||||||
@ -141,6 +148,12 @@ export default class MembersController extends Controller {
|
|||||||
|
|
||||||
// Tasks -------------------------------------------------------------------
|
// Tasks -------------------------------------------------------------------
|
||||||
|
|
||||||
|
@task({restartable: true})
|
||||||
|
*searchTask(query) {
|
||||||
|
yield timeout(250); // debounce
|
||||||
|
this.searchParam = query;
|
||||||
|
}
|
||||||
|
|
||||||
@task
|
@task
|
||||||
*fetchLabelsTask() {
|
*fetchLabelsTask() {
|
||||||
if (!this._hasLoadedLabels) {
|
if (!this._hasLoadedLabels) {
|
||||||
@ -149,4 +162,10 @@ export default class MembersController extends Controller {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Internal ----------------------------------------------------------------
|
||||||
|
|
||||||
|
resetSearch() {
|
||||||
|
this.searchText = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,8 @@ export const DEFAULT_QUERY_PARAMS = {
|
|||||||
order: null
|
order: null
|
||||||
},
|
},
|
||||||
'members.index': {
|
'members.index': {
|
||||||
label: null
|
label: null,
|
||||||
|
searchParam: ''
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8,7 +8,8 @@ export default class MembersRoute extends AuthenticatedRoute {
|
|||||||
@service store;
|
@service store;
|
||||||
|
|
||||||
queryParams = {
|
queryParams = {
|
||||||
label: {refreshModel: true}
|
label: {refreshModel: true},
|
||||||
|
searchParam: {refreshModel: true, replace: true}
|
||||||
};
|
};
|
||||||
|
|
||||||
// redirect to posts screen if:
|
// redirect to posts screen if:
|
||||||
@ -24,12 +25,17 @@ export default class MembersRoute extends AuthenticatedRoute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model(params) {
|
model(params) {
|
||||||
|
if (!params.searchParam) {
|
||||||
|
this.controllerFor('members').resetSearch();
|
||||||
|
}
|
||||||
|
|
||||||
// use a fixed created_at date so that subsequent pages have a consistent index
|
// use a fixed created_at date so that subsequent pages have a consistent index
|
||||||
let startDate = new Date();
|
let startDate = new Date();
|
||||||
|
|
||||||
// bypass the stale data shortcut if params change
|
// 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._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
|
// 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
|
// keeps navigation between list->details->list snappy
|
||||||
@ -46,7 +52,8 @@ export default class MembersRoute extends AuthenticatedRoute {
|
|||||||
limit: range.length,
|
limit: range.length,
|
||||||
page: range.start / range.length,
|
page: range.start / range.length,
|
||||||
order: 'created_at desc',
|
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);
|
}, query);
|
||||||
|
|
||||||
return this.store.query('member', query).then((result) => {
|
return this.store.query('member', query).then((result) => {
|
||||||
|
@ -13,9 +13,8 @@
|
|||||||
{{svg-jar "search" class="gh-input-search-icon"}}
|
{{svg-jar "search" class="gh-input-search-icon"}}
|
||||||
<GhTextInput
|
<GhTextInput
|
||||||
placeholder="Search members..."
|
placeholder="Search members..."
|
||||||
@disabled={{true}}
|
|
||||||
@value={{this.searchText}}
|
@value={{this.searchText}}
|
||||||
@input={{action (mut this.searchText) value="target.value"}}
|
@input={{this.search}}
|
||||||
class="gh-members-list-searchfield {{if this.searchText "active"}}" />
|
class="gh-members-list-searchfield {{if this.searchText "active"}}" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user