mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-27 12:53:13 +03:00
193 lines
5.6 KiB
JavaScript
193 lines
5.6 KiB
JavaScript
import Component from '@glimmer/component';
|
|
import flattenGroupedOptions from 'ghost-admin/utils/flatten-grouped-options';
|
|
import {Promise} from 'rsvp';
|
|
import {TrackedSet} from 'tracked-built-ins';
|
|
import {action} from '@ember/object';
|
|
import {inject as service} from '@ember/service';
|
|
import {task} from 'ember-concurrency-decorators';
|
|
import {tracked} from '@glimmer/tracking';
|
|
|
|
const BASE_FILTERS = ['status:free', 'status:-free'];
|
|
|
|
export default class GhMembersRecipientSelect extends Component {
|
|
@service membersUtils;
|
|
@service session;
|
|
@service store;
|
|
@service feature;
|
|
|
|
baseFilters = new TrackedSet();
|
|
specificFilters = new TrackedSet();
|
|
|
|
@tracked isSpecificChecked = false;
|
|
@tracked specificOptions = [];
|
|
@tracked freeMemberCount;
|
|
@tracked paidMemberCount;
|
|
|
|
constructor() {
|
|
super(...arguments);
|
|
|
|
this.fetchSpecificOptionsTask.perform();
|
|
this.fetchMemberCountsTask.perform();
|
|
|
|
this.baseFilters.clear();
|
|
this.specificFilters.clear();
|
|
|
|
(this.args.filter || '').split(',').forEach((filter) => {
|
|
if (filter?.trim()) {
|
|
if (BASE_FILTERS.includes(filter)) {
|
|
this.baseFilters.add(filter);
|
|
} else {
|
|
this.isSpecificChecked = true;
|
|
this.specificFilters.add(filter);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
get isPaidAvailable() {
|
|
return this.membersUtils.isStripeEnabled;
|
|
}
|
|
|
|
get isFreeChecked() {
|
|
return this.baseFilters.has('status:free');
|
|
}
|
|
|
|
get isPaidChecked() {
|
|
return this.baseFilters.has('status:-free');
|
|
}
|
|
|
|
get selectedSpecificOptions() {
|
|
return flattenGroupedOptions(this.specificOptions)
|
|
.filter(o => this.specificFilters.has(o.segment));
|
|
}
|
|
|
|
get freeMemberCountLabel() {
|
|
if (this.freeMemberCount !== undefined) {
|
|
return `(${this.freeMemberCount})`;
|
|
}
|
|
return '';
|
|
}
|
|
|
|
get paidMemberCountLabel() {
|
|
if (this.paidMemberCount !== undefined) {
|
|
return `(${this.paidMemberCount})`;
|
|
}
|
|
return '';
|
|
}
|
|
|
|
get filterString() {
|
|
const selectedFilters = !this.isSpecificChecked ?
|
|
new Set([...this.baseFilters.values()]) :
|
|
new Set([...this.baseFilters.values(), ...this.specificFilters.values()]);
|
|
|
|
if (!this.isPaidAvailable) {
|
|
selectedFilters.delete('status:-free');
|
|
}
|
|
|
|
return Array.from(selectedFilters).join(',') || null;
|
|
}
|
|
|
|
@action
|
|
toggleFilter(filter, event) {
|
|
event?.preventDefault();
|
|
if (this.args.disabled) {
|
|
return;
|
|
}
|
|
this.baseFilters.has(filter) ? this.baseFilters.delete(filter) : this.baseFilters.add(filter);
|
|
this.args.onChange?.(this.filterString);
|
|
}
|
|
|
|
@action
|
|
toggleSpecificFilter(event) {
|
|
event?.preventDefault();
|
|
if (this.args.disabled) {
|
|
return;
|
|
}
|
|
this.isSpecificChecked = !this.isSpecificChecked;
|
|
this.args.onChange?.(this.filterString);
|
|
}
|
|
|
|
@action
|
|
selectSpecificOptions(selectedOptions) {
|
|
if (this.args.disabled) {
|
|
return;
|
|
}
|
|
this.specificFilters.clear();
|
|
selectedOptions.forEach(o => this.specificFilters.add(o.segment));
|
|
|
|
if (this.isSpecificChecked) {
|
|
this.args.onChange?.(this.filterString);
|
|
}
|
|
}
|
|
|
|
@task
|
|
*fetchSpecificOptionsTask() {
|
|
const options = [];
|
|
|
|
// fetch all labels w̶i̶t̶h̶ c̶o̶u̶n̶t̶s̶
|
|
// TODO: add `include: 'count.members` to query once API is fixed
|
|
const labels = yield this.store.query('label', {limit: 'all'});
|
|
|
|
if (labels.length > 0) {
|
|
const labelsGroup = {
|
|
groupName: 'Labels',
|
|
options: []
|
|
};
|
|
|
|
labels.forEach((label) => {
|
|
labelsGroup.options.push({
|
|
name: label.name,
|
|
segment: `label:${label.slug}`,
|
|
count: label.count?.members,
|
|
class: 'segment-label'
|
|
});
|
|
});
|
|
|
|
options.push(labelsGroup);
|
|
}
|
|
if (this.feature.get('multipleProducts')) {
|
|
// fetch all products w̶i̶t̶h̶ c̶o̶u̶n̶t̶s̶
|
|
// TODO: add `include: 'count.members` to query once API supports
|
|
const products = yield this.store.query('product', {limit: 'all'});
|
|
|
|
if (products.length > 1) {
|
|
const productsGroup = {
|
|
groupName: 'Tiers',
|
|
options: []
|
|
};
|
|
|
|
products.forEach((product) => {
|
|
productsGroup.options.push({
|
|
name: product.name,
|
|
segment: `product:${product.slug}`,
|
|
count: product.count?.members,
|
|
class: 'segment-product'
|
|
});
|
|
});
|
|
|
|
options.push(productsGroup);
|
|
}
|
|
}
|
|
|
|
this.specificOptions = options;
|
|
}
|
|
|
|
@task
|
|
*fetchMemberCountsTask() {
|
|
const user = yield this.session.user;
|
|
|
|
if (!user.isAdmin) {
|
|
return;
|
|
}
|
|
|
|
yield Promise.all([
|
|
this.store.query('member', {filter: 'subscribed:true+status:free', limit: 1}).then((res) => {
|
|
this.freeMemberCount = res.meta.pagination.total;
|
|
}),
|
|
this.store.query('member', {filter: 'subscribed:true+status:-free', limit: 1}).then((res) => {
|
|
this.paidMemberCount = res.meta.pagination.total;
|
|
})
|
|
]);
|
|
}
|
|
}
|