0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-17 23:44:39 -05:00

Updated members count to use selected newsletter when publishing

refs https://github.com/TryGhost/Team/issues/1576

- Passes the selected newsletter to the recipient selector
- Added a `recipientFilter` getter to the newsletter model to make changes easier in the future
- Updates the `fullRecipientFilter` in the new publishing flow to use the newsletter's recipientFilter
- Already takes future paid only newsletters into account
- The counts in the status message after publishing is not updated yet (requires https://github.com/TryGhost/Team/issues/1569)
- The counts in the scheduled notification is not updated yet (requires https://github.com/TryGhost/Team/issues/1569)
This commit is contained in:
Simon Backx 2022-05-05 14:33:26 +02:00
parent 03ba0da1a7
commit 1f5f9645e6
11 changed files with 44 additions and 24 deletions

View file

@ -162,7 +162,7 @@ export class PublishOptions {
} }
get fullRecipientFilter() { get fullRecipientFilter() {
let filter = `newsletters:${this.newsletter.slug}`; let filter = this.newsletter.recipientFilter;
if (this.recipientFilter) { if (this.recipientFilter) {
filter += `+(${this.recipientFilter})`; filter += `+(${this.recipientFilter})`;

View file

@ -24,9 +24,10 @@
<GhMembersRecipientSelect <GhMembersRecipientSelect
@filter={{@publishOptions.recipientFilter}} @filter={{@publishOptions.recipientFilter}}
@newsletter={{@publishOptions.newsletter}}
@onChange={{@publishOptions.setRecipientFilter}} @onChange={{@publishOptions.setRecipientFilter}}
@renderInPlace={{false}} @renderInPlace={{false}}
@dropdownClass="gh-publishmenu-newsletter-dropdown" @dropdownClass="gh-publishmenu-newsletter-dropdown"
/> />
</div> </div>
{{/if}} {{/if}}

View file

@ -1,4 +1,4 @@
<div class="gh-publishmenu-send-to-option"> <div class="gh-publishmenu-send-to-option" {{did-update this.onUpdateNewsletter @newsletter }}>
<p>Free members <span class="gh-publishmenu-emailcount" data-test-email-count="free-members">{{this.freeMemberCountLabel}}</span></p> <p>Free members <span class="gh-publishmenu-emailcount" data-test-email-count="free-members">{{this.freeMemberCountLabel}}</span></p>
<div class="for-switch x-small {{if @disabled "disabled"}}"> <div class="for-switch x-small {{if @disabled "disabled"}}">
<label class="switch" for="send-email-to-free"> <label class="switch" for="send-email-to-free">

View file

@ -78,6 +78,11 @@ export default class GhMembersRecipientSelect extends Component {
return ''; return '';
} }
@action
onUpdateNewsletter() {
this.fetchMemberCountsTask.perform();
}
@action @action
toggleFilter(filter) { toggleFilter(filter) {
if (this.args.disabled) { if (this.args.disabled) {
@ -202,15 +207,15 @@ export default class GhMembersRecipientSelect extends Component {
*fetchMemberCountsTask() { *fetchMemberCountsTask() {
const user = yield this.session.user; const user = yield this.session.user;
if (!user.isAdmin) { if (!user.isAdmin || !this.args.newsletter) {
return; return;
} }
yield Promise.all([ yield Promise.all([
this.store.query('member', {filter: 'newsletters.status:active+status:free', limit: 1}).then((res) => { this.store.query('member', {filter: this.args.newsletter.recipientFilter + '+status:free', limit: 1}).then((res) => {
this.freeMemberCount = res.meta.pagination.total; this.freeMemberCount = res.meta.pagination.total;
}), }),
this.store.query('member', {filter: 'newsletters.status:active+status:-free', limit: 1}).then((res) => { this.store.query('member', {filter: this.args.newsletter.recipientFilter + '+status:-free', limit: 1}).then((res) => {
this.paidMemberCount = res.meta.pagination.total; this.paidMemberCount = res.meta.pagination.total;
}) })
]); ]);

View file

@ -80,6 +80,7 @@
<GhMembersRecipientSelect <GhMembersRecipientSelect
@filter={{@recipientsFilter}} @filter={{@recipientsFilter}}
@newsletter={{@selectedNewsletter}}
@onChange={{@setSendEmailWhenPublished}} @onChange={{@setSendEmailWhenPublished}}
/> />
</div> </div>
@ -90,4 +91,4 @@
</div> </div>
{{/if}} {{/if}}
</section> </section>
</div> </div>

View file

@ -59,6 +59,7 @@
<div class="form-group"> <div class="form-group">
<GhMembersRecipientSelect <GhMembersRecipientSelect
@filter={{@recipientsFilter}} @filter={{@recipientsFilter}}
@newsletter={{this.selectedNewsletter}}
@disabled={{true}} @disabled={{true}}
/> />
</div> </div>
@ -68,4 +69,4 @@
</section> </section>
{{/if}} {{/if}}
</div> </div>
</div> </div>

View file

@ -401,7 +401,7 @@ export default Component.extend({
post: this.post, post: this.post,
emailOnly: this.emailOnly, emailOnly: this.emailOnly,
sendEmailWhenPublished: this.sendEmailWhenPublished, sendEmailWhenPublished: this.sendEmailWhenPublished,
newsletterId: this.newsletterId, newsletter: this.selectedNewsletter,
isScheduled: saveType === 'schedule', isScheduled: saveType === 'schedule',
confirm: this.saveWithConfirmedPublish.perform, confirm: this.saveWithConfirmedPublish.perform,
retryEmailSend: this.retryEmailSendTask.perform retryEmailSend: this.retryEmailSendTask.perform
@ -449,17 +449,15 @@ export default Component.extend({
}), }),
fetchNewslettersTask: task(function* () { fetchNewslettersTask: task(function* () {
if (this.feature.multipleNewsletters) { const newsletters = yield this.store.query('newsletter', {
const newsletters = yield this.store.query('newsletter', { filter: 'status:active',
filter: 'status:active', order: 'sort_order ASC'
order: 'sort_order ASC' });
});
const defaultNewsletter = newsletters.toArray()[0]; const defaultNewsletter = newsletters.toArray()[0];
this.defaultNewsletter = defaultNewsletter; this.defaultNewsletter = defaultNewsletter;
this.set('selectedNewsletter', defaultNewsletter); this.set('selectedNewsletter', defaultNewsletter);
}
}), }),
_saveTask: task(function* () { _saveTask: task(function* () {

View file

@ -59,8 +59,8 @@ export default class ConfirmPublishModal extends Component {
@task @task
*countRecipientsTask() { *countRecipientsTask() {
const {sendEmailWhenPublished} = this.args.data; const {sendEmailWhenPublished,newsletter} = this.args.data;
const filter = `newsletters.status:active+(${sendEmailWhenPublished})`; const filter = `${newsletter.recipientFilter}+(${sendEmailWhenPublished})`;
this.memberCount = sendEmailWhenPublished ? (yield this.membersCountCache.count(filter)) : 0; this.memberCount = sendEmailWhenPublished ? (yield this.membersCountCache.count(filter)) : 0;
this.memberCountString = sendEmailWhenPublished ? (yield this.membersCountCache.countString(filter)) : '0 members'; this.memberCountString = sendEmailWhenPublished ? (yield this.membersCountCache.countString(filter)) : '0 members';

View file

@ -1079,6 +1079,7 @@ export default class EditorController extends Controller {
let description = emailOnly ? ['Will be sent'] : ['Will be published']; let description = emailOnly ? ['Will be sent'] : ['Will be published'];
if (emailRecipientFilter && emailRecipientFilter !== 'none') { if (emailRecipientFilter && emailRecipientFilter !== 'none') {
// TODO: replace active filter with newsletter.recipientFilter (requires https://github.com/TryGhost/Team/issues/1569)
const recipientCount = await this.membersCountCache.countString(`newsletters.status:active+(${emailRecipientFilter})`); const recipientCount = await this.membersCountCache.countString(`newsletters.status:active+(${emailRecipientFilter})`);
description.push(`${!emailOnly ? 'and delivered ' : ''}to <span><strong>${recipientCount}</strong></span>`); description.push(`${!emailOnly ? 'and delivered ' : ''}to <span><strong>${recipientCount}</strong></span>`);
} }

View file

@ -13,7 +13,6 @@ export default class Newsletter extends Model.extend(ValidationEngine) {
@attr({defaultValue: 'newsletter'}) senderReplyTo; @attr({defaultValue: 'newsletter'}) senderReplyTo;
@attr({defaultValue: 'active'}) status; @attr({defaultValue: 'active'}) status;
@attr({defaultValue: ''}) recipientFilter;
@attr({defaultValue: true}) subscribeOnSignup; @attr({defaultValue: true}) subscribeOnSignup;
@attr({defaultValue: 'members'}) visibility; @attr({defaultValue: 'members'}) visibility;
@attr({defaultValue: 0}) sortOrder; @attr({defaultValue: 0}) sortOrder;
@ -34,4 +33,15 @@ export default class Newsletter extends Model.extend(ValidationEngine) {
// HACK - not a real model attribute but a workaround for Ember Data not // HACK - not a real model attribute but a workaround for Ember Data not
// exposing meta from save responses // exposing meta from save responses
@attr _meta; @attr _meta;
/**
* The filter that we should use to filter out members that are subscribed to this newsletter
*/
get recipientFilter() {
const idFilter = 'newsletters.id:' + this.id;
if (this.visibility === 'paid') {
return idFilter + '+status:-free';
}
return idFilter;
}
} }

View file

@ -6,6 +6,7 @@ import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-sup
import {beforeEach, describe, it} from 'mocha'; import {beforeEach, describe, it} from 'mocha';
import {blur, click, currentRouteName, currentURL, fillIn, find, findAll, triggerEvent} from '@ember/test-helpers'; import {blur, click, currentRouteName, currentURL, fillIn, find, findAll, triggerEvent} from '@ember/test-helpers';
import {datepickerSelect} from 'ember-power-datepicker/test-support'; import {datepickerSelect} from 'ember-power-datepicker/test-support';
import {enableLabsFlag} from '../helpers/labs-flag';
import {enableMailgun} from '../helpers/mailgun'; import {enableMailgun} from '../helpers/mailgun';
import {enableNewsletters} from '../helpers/newsletters'; import {enableNewsletters} from '../helpers/newsletters';
import {enableStripe} from '../helpers/stripe'; import {enableStripe} from '../helpers/stripe';
@ -845,6 +846,7 @@ describe('Acceptance: Editor', function () {
const role = this.server.create('role', {name: 'Administrator'}); const role = this.server.create('role', {name: 'Administrator'});
user = this.server.create('user', {roles: [role]}); user = this.server.create('user', {roles: [role]});
this.server.loadFixtures('settings'); this.server.loadFixtures('settings');
enableLabsFlag(this.server, 'multipleNewsletters');
return await authenticateSession(); return await authenticateSession();
}); });
@ -876,7 +878,8 @@ describe('Acceptance: Editor', function () {
// Enable stripe to also show paid members breakdown // Enable stripe to also show paid members breakdown
enableStripe(this.server); enableStripe(this.server);
const newsletter = this.server.create('newsletter', {status: 'active'}); // Note: we need to set the ID of a newsletter to some string value because of how NQL filters work.
const newsletter = this.server.create('newsletter', {status: 'active', name: 'test newsletter', id: 'test-newsletter'});
this.server.createList('member', 4, {status: 'free', newsletters: [newsletter]}); this.server.createList('member', 4, {status: 'free', newsletters: [newsletter]});
this.server.createList('member', 2, {status: 'paid', newsletters: [newsletter]}); this.server.createList('member', 2, {status: 'paid', newsletters: [newsletter]});
@ -889,8 +892,8 @@ describe('Acceptance: Editor', function () {
await selectChoose('[data-test-distribution-action-select]', 'send'); await selectChoose('[data-test-distribution-action-select]', 'send');
await click('[data-test-publishmenu-scheduled-option]'); await click('[data-test-publishmenu-scheduled-option]');
await datepickerSelect('[data-test-publishmenu-draft] [data-test-date-time-picker-datepicker]', new Date(scheduledTime.format().replace(/\+.*$/, ''))); await datepickerSelect('[data-test-publishmenu-draft] [data-test-date-time-picker-datepicker]', new Date(scheduledTime.format().replace(/\+.*$/, '')));
// Expect 4 free and 2 paid recipients here // Expect 4 free and 2 paid recipients here
expect(find('[data-test-email-count="free-members"]')).to.contain.text('4'); expect(find('[data-test-email-count="free-members"]')).to.contain.text('4');
expect(find('[data-test-email-count="paid-members"]')).to.contain.text('2'); expect(find('[data-test-email-count="paid-members"]')).to.contain.text('2');