mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-02 08:13:34 +03:00
495e435daf
no issue Free and Paid are by far the two most common options for email recipients so it makes more sense to have them as very clear options which we felt was not the case with the single token/segment select. - created a new `<GhMembersRecipientSelect>` component that has individual checkboxes for free/paid/segment and when segment is selected an additional token input for specific labels - updated draft and scheduled publish menu components to use the `<GhMembersRecipientSelect>` Co-authored-by: Sanne de Vries <sannedv@protonmail.com>
169 lines
4.7 KiB
JavaScript
169 lines
4.7 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;
|
|
|
|
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);
|
|
}
|
|
|
|
this.specificOptions = options;
|
|
}
|
|
|
|
@task
|
|
*fetchMemberCountsTask() {
|
|
const user = yield this.session.user;
|
|
|
|
if (!user.isOwnerOrAdmin) {
|
|
return;
|
|
}
|
|
|
|
yield Promise.all([
|
|
this.store.query('member', {filter: 'status:free', limit: 1}).then((res) => {
|
|
this.freeMemberCount = res.meta.pagination.total;
|
|
}),
|
|
this.store.query('member', {filter: 'status:-free', limit: 1}).then((res) => {
|
|
this.paidMemberCount = res.meta.pagination.total;
|
|
})
|
|
]);
|
|
}
|
|
}
|