c646e78fff
no issue Having `session.user` return a promise made dealing with it in components difficult because you always had to remember it returned a promise rather than a model and had to handle the async behaviour. It also meant that you couldn't use any current user properties directly inside getters which made refactors to Glimmer/Octane idioms harder to reason about. `session.user` was a cached computed property so it really made no sense for it to be a promise - it was loaded on first access and then always returned instantly but with a fulfilled promise rather than the underlying model. Refactoring to a synchronous property that is loaded as part of the authentication flows (we load the current user to check that we're logged in - we may as well make use of that!) means one less thing to be aware of/remember and provides a nicer migration process to Glimmer components. As part of the refactor, the auth flows and pre-load of required data across other services was also simplified to make it easier to find and follow. - refactored app setup and `session.user` - added `session.populateUser()` that fetches a user model from the current user endpoint and sets it on `session.user` - removed knowledge of app setup from the `cookie` authenticator and moved it into = `session.postAuthPreparation()`, this means we have the same post-authentication setup no matter which authenticator is used so we have more consistent behaviour in tests which don't use the `cookie` authenticator - switched `session` service to native class syntax to get the expected `super()` behaviour - updated `handleAuthentication()` so it populate's `session.user` and performs post-auth setup before transitioning (handles sign-in after app load) - updated `application` route to remove duplicated knowledge of app preload behaviour that now lives in `session.postAuthPreparation()` (handles already-authed app load) - removed out-of-date attempt at pre-loading data from setup controller as that's now handled automatically via `session.handleAuthentication` - updated app code to not treat `session.user` as a promise - predominant usage was router `beforeModel` hooks that transitioned users without valid permissions, this sets us up for an easier removal of the `current-user-settings` mixin in the future
80 lines
2.4 KiB
JavaScript
80 lines
2.4 KiB
JavaScript
import Service from '@ember/service';
|
|
import moment from 'moment';
|
|
import {ghPluralize} from 'ghost-admin/helpers/gh-pluralize';
|
|
import {inject as service} from '@ember/service';
|
|
import {task} from 'ember-concurrency-decorators';
|
|
|
|
export default class MembersCountCacheService extends Service {
|
|
@service session;
|
|
@service store;
|
|
|
|
cache = {};
|
|
|
|
async count(filter) {
|
|
const cachedValue = this.cache[filter];
|
|
|
|
if (cachedValue && moment().diff(cachedValue.time, 'seconds') > 60) {
|
|
return cachedValue.count;
|
|
}
|
|
|
|
const count = this._countMembersTask.perform(filter);
|
|
|
|
this.cache[filter] = {count, time: moment()};
|
|
|
|
return count;
|
|
}
|
|
|
|
async countString(filter = '', {knownCount} = {}) {
|
|
const user = this.session.user;
|
|
|
|
const basicFilter = filter.replace(/^subscribed:true\+\((.*)\)$/, '$1');
|
|
const filterParts = basicFilter.split(',');
|
|
const isFree = filterParts.length === 1 && filterParts[0] === 'status:free';
|
|
const isPaid = filterParts.length === 1 && filterParts[0] === 'status:-free';
|
|
const isAll = !filter || (filterParts.includes('status:free') && filterParts.includes('status:-free'));
|
|
|
|
// editors don't have permission to browse members so can't retrieve a count
|
|
// TODO: remove when editors have relevant permissions or we have a different way of fetching counts
|
|
if (user.isEditor && knownCount === undefined) {
|
|
if (isFree) {
|
|
return 'all free members';
|
|
}
|
|
if (isPaid) {
|
|
return 'all paid members';
|
|
}
|
|
if (isAll) {
|
|
return 'all members';
|
|
}
|
|
|
|
return 'a custom members segment';
|
|
}
|
|
|
|
const recipientCount = knownCount !== undefined ? knownCount : await this.count(filter);
|
|
|
|
if (isFree) {
|
|
return ghPluralize(recipientCount, 'free member');
|
|
}
|
|
|
|
if (isPaid) {
|
|
return ghPluralize(recipientCount, 'paid member');
|
|
}
|
|
|
|
return ghPluralize(recipientCount, 'member');
|
|
}
|
|
|
|
@task
|
|
*_countMembersTask(filter) {
|
|
if (!filter) {
|
|
return 0;
|
|
}
|
|
|
|
try {
|
|
const result = yield this.store.query('member', {filter, limit: 1, page: 1});
|
|
return result.meta.pagination.total;
|
|
} catch (e) {
|
|
console.error(e); // eslint-disable-line
|
|
return 0;
|
|
}
|
|
}
|
|
}
|