mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-04-08 02:52:39 -05:00
Added data-cache service for managing limited-lifetime cached data
refs https://github.com/TryGhost/Team/issues/1277 - `data-cache` service has a `.set(key, data, lifetime)` method that will store the data under the key and sets a timeout that will remove the data when the lifetime expires - data can be retrieved with `.get(key)` - allows for components to cache data for use when re-rendering without having to worry about keeping track of their state and it's expiration manually somewhere else - moved caching concern out of the `members-activity` service and into the latest-member-activity dashboard component which is the one that cares about it's data and cache lifetime - frees the `members-activity` service up to be more generic as it's no longer tied to the dashboard component's concerns - component switched to using a task rather than a promise so it is automatically cancelled if it's destroyed before data fetching is complete
This commit is contained in:
parent
3c2a322d79
commit
ec6a5637e8
3 changed files with 61 additions and 26 deletions
|
@ -3,6 +3,8 @@ import {inject as service} from '@ember/service';
|
|||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
export default class DashboardLatestMemberActivityComponent extends Component {
|
||||
@service dataCache;
|
||||
@service feature;
|
||||
@service membersActivity;
|
||||
@service session;
|
||||
@service settings;
|
||||
|
@ -31,10 +33,25 @@ export default class DashboardLatestMemberActivityComponent extends Component {
|
|||
}
|
||||
|
||||
async loadEvents() {
|
||||
const limit = 5;
|
||||
const filter = this.feature.membersActivity ?
|
||||
'type:-[email_delivered_event,email_opened_event,email_failed_event]' :
|
||||
'';
|
||||
|
||||
const dataKey = `dashboard-member-activity::${JSON.stringify({limit, filter})}`;
|
||||
|
||||
if (this.dataCache.get(dataKey)) {
|
||||
this.eventsData = this.dataCache.get(dataKey);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.eventsLoading = true;
|
||||
const {events} = await this.membersActivity.fetchTimeline({limit: 5});
|
||||
const {events} = await this.membersActivity.fetchTask.perform({limit, filter});
|
||||
this.eventsData = events;
|
||||
|
||||
const ONE_MINUTE = 1 * 60 * 1000;
|
||||
this.dataCache.set(dataKey, events, ONE_MINUTE);
|
||||
} catch (error) {
|
||||
this.eventsError = error;
|
||||
} finally {
|
||||
|
|
37
ghost/admin/app/services/data-cache.js
Normal file
37
ghost/admin/app/services/data-cache.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
import Service from '@ember/service';
|
||||
|
||||
const ONE_MINUTE = 1 * 60 * 1000;
|
||||
|
||||
export default class DataCacheService extends Service {
|
||||
cache = {};
|
||||
timeouts = {};
|
||||
|
||||
get(key) {
|
||||
return this.cache[key];
|
||||
}
|
||||
|
||||
set(key, data, lifetime = ONE_MINUTE) {
|
||||
this.cache[key] = data;
|
||||
|
||||
this.timeouts[key] = window.setTimeout(() => {
|
||||
delete this.cache[key];
|
||||
delete this.timeouts[key];
|
||||
}, lifetime);
|
||||
|
||||
return this.cache[key];
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._clearAllTimeouts();
|
||||
this.cache = {};
|
||||
this.timeouts = {};
|
||||
}
|
||||
|
||||
willDestroy() {
|
||||
this._clearAllTimeouts();
|
||||
}
|
||||
|
||||
_clearAllTimeouts() {
|
||||
Object.keys(this.timeouts).forEach(key => window.clearTimeout(this.timeouts[key]));
|
||||
}
|
||||
}
|
|
@ -1,38 +1,19 @@
|
|||
import Service from '@ember/service';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {task} from 'ember-concurrency-decorators';
|
||||
|
||||
const ONE_MINUTE = 1 * 60 * 1000;
|
||||
|
||||
export default class MembersActivityService extends Service {
|
||||
@service ajax;
|
||||
@service ghostPaths;
|
||||
|
||||
_lastFetchedTimeline = null;
|
||||
_lastFetchedTimelineLimit = null;
|
||||
|
||||
async fetchTimeline(options = {}) {
|
||||
let staleData = this._lastFetchedTimeline && (new Date() - this._lastFetchedTimeline) > ONE_MINUTE;
|
||||
let differentLimit = this._lastFetchedTimelineLimit && this._lastFetchedTimelineLimit !== options.limit;
|
||||
|
||||
if (this._fetchTimelineTask.isRunning) {
|
||||
return this._fetchTimelineTask.last;
|
||||
}
|
||||
|
||||
if (this.events && !staleData && !differentLimit) {
|
||||
return this.events;
|
||||
}
|
||||
|
||||
return this._fetchTimelineTask.perform(options.limit);
|
||||
async fetch(options = {}) {
|
||||
return this._fetchTask.perform(options);
|
||||
}
|
||||
|
||||
@task
|
||||
*_fetchTimelineTask(limit) {
|
||||
this._lastFetchedTimeline = new Date();
|
||||
this._lastFetchedTimelineLimit = limit;
|
||||
let eventsUrl = this.ghostPaths.url.api('members/events');
|
||||
let events = yield this.ajax.request(eventsUrl, {data: {limit}});
|
||||
this.events = events;
|
||||
*fetchTask({limit, filter}) {
|
||||
const eventsUrl = this.ghostPaths.url.api('members/events');
|
||||
const events = yield this.ajax.request(eventsUrl, {data: {limit, filter}});
|
||||
|
||||
return events;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue