mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-25 19:48:50 +03:00
Memoized member stats with expiration
no issue - added a `member-stats` service to keep member stats state outside of the chart component's lifecycle - returns memoized member stats when fetching if the query hasn't changed and the data is less than a minute old - reduces potentially heavy network requests when quickly navigating between members list and other screens
This commit is contained in:
parent
b7b6c4b1b7
commit
86702ed949
@ -7,7 +7,7 @@ import {task} from 'ember-concurrency';
|
||||
|
||||
export default Component.extend({
|
||||
ajax: service(),
|
||||
ghostPaths: service(),
|
||||
membersStats: service(),
|
||||
|
||||
// public attrs
|
||||
nightShift: false,
|
||||
@ -63,8 +63,7 @@ export default Component.extend({
|
||||
// Tasks -------------------------------------------------------------------
|
||||
|
||||
fetchStatsTask: task(function* () {
|
||||
let statsUrl = this.ghostPaths.url.api('members/stats');
|
||||
let stats = yield this.ajax.request(statsUrl, {data: {days: this.selectedRange.days}});
|
||||
let stats = yield this.membersStats.fetch({days: this.selectedRange.days});
|
||||
|
||||
this.set('stats', stats);
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
import ModalComponent from 'ghost-admin/components/modal-base';
|
||||
import {alias} from '@ember/object/computed';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
export default ModalComponent.extend({
|
||||
membersStats: service(),
|
||||
|
||||
// Allowed actions
|
||||
confirm: () => {},
|
||||
|
||||
@ -17,6 +20,7 @@ export default ModalComponent.extend({
|
||||
deleteMember: task(function* () {
|
||||
try {
|
||||
yield this.confirm();
|
||||
this.membersStats.invalidate();
|
||||
} finally {
|
||||
this.send('closeModal');
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
import ModalComponent from 'ghost-admin/components/modal-base';
|
||||
import ghostPaths from 'ghost-admin/utils/ghost-paths';
|
||||
import {computed} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
|
||||
export default ModalComponent.extend({
|
||||
membersStats: service(),
|
||||
|
||||
labelText: 'Select or drag-and-drop a CSV File',
|
||||
|
||||
response: null,
|
||||
@ -26,6 +29,7 @@ export default ModalComponent.extend({
|
||||
|
||||
uploadSuccess(response) {
|
||||
this.set('response', response.meta.stats);
|
||||
this.membersStats.invalidate();
|
||||
// invoke the passed in confirm action
|
||||
this.confirm();
|
||||
},
|
||||
|
@ -13,6 +13,7 @@ export default class MemberController extends Controller {
|
||||
@controller members;
|
||||
@service session;
|
||||
@service dropdown;
|
||||
@service membersStats;
|
||||
@service notifications;
|
||||
@service router;
|
||||
@service store;
|
||||
@ -66,6 +67,7 @@ export default class MemberController extends Controller {
|
||||
@action
|
||||
deleteMember() {
|
||||
return this.member.destroyRecord().then(() => {
|
||||
this.membersStats.invalidate();
|
||||
return this.transitionToRoute('members');
|
||||
}, (error) => {
|
||||
return this.notifications.showAPIError(error, {key: 'member.delete'});
|
||||
@ -114,6 +116,10 @@ export default class MemberController extends Controller {
|
||||
let scratchProps = scratchMember.getProperties(SCRATCH_PROPS);
|
||||
member.setProperties(scratchProps);
|
||||
|
||||
if (!member.isNew) {
|
||||
this.membersStats.invalidate();
|
||||
}
|
||||
|
||||
try {
|
||||
yield member.save();
|
||||
member.updateLabels();
|
||||
|
33
ghost/admin/app/services/members-stats.js
Normal file
33
ghost/admin/app/services/members-stats.js
Normal file
@ -0,0 +1,33 @@
|
||||
import Service from '@ember/service';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
export default class MembersStatsService extends Service {
|
||||
@service ajax;
|
||||
@service ghostPaths;
|
||||
|
||||
@tracked stats = null;
|
||||
|
||||
fetch({days}) {
|
||||
// return existing stats unless data is > 1 min old
|
||||
let daysChanged = days === this._days;
|
||||
let staleData = this._lastFetched - new Date() > 1 * 60 * 1000;
|
||||
if (!this._forceRefresh && !daysChanged && !staleData) {
|
||||
return Promise.resolve(this.stats);
|
||||
}
|
||||
|
||||
this._days = days;
|
||||
this._lastFetched = new Date();
|
||||
|
||||
let statsUrl = this.ghostPaths.url.api('members/stats');
|
||||
|
||||
return this.ajax.request(statsUrl, {data: {days}}).then((stats) => {
|
||||
this.stats = stats;
|
||||
return stats;
|
||||
});
|
||||
}
|
||||
|
||||
invalidate() {
|
||||
this._forceRefresh = true;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user