Added first pass of recipient options to new publish flow

closes https://github.com/TryGhost/Team/issues/1585

- adds newsletter select, free/paid checkboxes, and specific label select to the email recipients area in publish flow
- updated `PublishOptions`
  - `willEmail` getter now takes into account the recipient filter so when free+paid+specific are all unchecked the flow corresponds to email not being sent
  - save task passes through the real recipient filter
  - added `fullRecipientFilter` for use in count fetchers so the selected newsletter is taken into account whilst we use `recipientFilter` as the main filter value
- fixed issues with dropdowns being cut off
  - `{{liquid-if}}` uses `overflow: hidden` to make it's animation work, this means any popups that are larger than the expanded option size are cut off
  - switched away from rendering the selects inline so they aren't limited by parent container size
  - fixed `z-index` issues to they appear on top of the modal
This commit is contained in:
Kevin Ansfield 2022-05-05 11:18:41 +01:00
parent 7c4674507e
commit 67505f838c
7 changed files with 86 additions and 13 deletions

View File

@ -23,7 +23,7 @@
{{#if @publishOptions.willEmail}}
will be delivered to
{{#let (members-count-fetcher query=(hash filter=@publishOptions.recipientFilter)) as |countFetcher|}}
{{#let (members-count-fetcher query=(hash filter=@publishOptions.fullRecipientFilter)) as |countFetcher|}}
<strong>{{pluralize countFetcher.count "member"}}</strong>
{{/let}}

View File

@ -24,21 +24,36 @@
<div class="gh-publish-setting">
<div class="gh-publish-setting-title {{if (eq @publishOptions.publishType "publish") "disabled"}}">
{{svg-jar "member"}}
<div class="gh-publish-setting-trigger">
{{#if (not-eq @publishOptions.publishType "publish")}}
{{#let (members-count-fetcher query=(hash filter=@publishOptions.recipientFilter)) as |countFetcher|}}
{{#if @publishOptions.willEmail}}
<button type="button" class="gh-publish-setting-trigger" {{on "click" (fn this.toggleSection "emailRecipients")}}>
{{#let (members-count-fetcher query=(hash filter=@publishOptions.fullRecipientFilter)) as |countFetcher|}}
{{countFetcher.count}}
{{/let}}
{{#unless @publishOptions.onlyDefaultNewsletter}}
<span>{{@publishOptions.newsletter.name}}</span>
{{/unless}}
subscribers
</button>
{{else}}
{{#if (eq @publishOptions.publishType "publish")}}
<div class="gh-publish-setting-trigger">
Not sent to any members
{{/if}}
</div>
{{else}}
<button type="button" class="gh-publish-setting-trigger" {{on "click" (fn this.toggleSection "emailRecipients")}}>
Not sent to any members
</button>
{{/if}}
{{/if}}
{{svg-jar "arrow-down" class="icon-expand"}}
</div>
{{#liquid-if (eq this.openSection "emailRecipients")}}
<div class="gh-publish-setting-form">
<EditorLabs::PublishOptions::EmailRecipients
@publishOptions={{@publishOptions}}
/>
</div>
{{/liquid-if}}
</div>
<div class="gh-publish-setting last">

View File

@ -29,7 +29,7 @@ export class PublishOptions {
}
get willEmail() {
return this.publishType !== 'publish';
return this.publishType !== 'publish' && this.recipientFilter;
}
get willPublish() {
@ -139,7 +139,9 @@ export class PublishOptions {
// set in constructor because services are not injected
allNewsletters = [];
@tracked newsletter = null; // set to default in constructor
// both of these are set to site defaults in `setupTask`
@tracked newsletter = null;
@tracked recipientFilter = 'status:free,status:-free';
get newsletters() {
return this.allNewsletters
@ -155,8 +157,24 @@ export class PublishOptions {
return this.newsletters.length === 1;
}
get recipientFilter() {
return `newsletters:${this.newsletter.slug}`;
get fullRecipientFilter() {
let filter = `newsletters:${this.newsletter.slug}`;
if (this.recipientFilter) {
filter += `+(${this.recipientFilter})`;
}
return filter;
}
@action
setNewsletter(newsletter) {
this.newsletter = newsletter;
}
@action
setRecipientFilter(newFilter) {
this.recipientFilter = newFilter;
}
// setup -------------------------------------------------------------------
@ -217,8 +235,7 @@ export class PublishOptions {
if (this.willEmail) {
adapterOptions.newsletterId = this.newsletter.id;
// TODO: replace with real filter
adapterOptions.emailRecipientFilter = 'status:free,status:-free';
adapterOptions.emailRecipientFilter = this.recipientFilter;
}
try {

View File

@ -0,0 +1,32 @@
{{#if (eq @publishOptions.totalMemberCount 0)}}
<p class="gh-box gh-content-box">
<LinkTo @route="members">Add members</LinkTo>
to start sending newsletters!
</p>
{{else}}
<div class="form-group">
{{#if (gt @publishOptions.newsletters.length 1)}}
<div class="mb3">
Newsletter:
<PowerSelect
@selected={{@publishOptions.newsletter}}
@options={{@publishOptions.newsletters}}
@onChange={{@publishOptions.setNewsletter}}
@triggerComponent="gh-power-select/trigger"
@triggerClass="gh-publishmenu-newsletter-trigger"
@dropdownClass="gh-publishmenu-newsletter-dropdown"
as |newsletter|
>
{{newsletter.name}}
</PowerSelect>
</div>
{{/if}}
<GhMembersRecipientSelect
@filter={{@publishOptions.recipientFilter}}
@onChange={{@publishOptions.setRecipientFilter}}
@renderInPlace={{false}}
@dropdownClass="gh-publishmenu-newsletter-dropdown"
/>
</div>
{{/if}}

View File

@ -58,13 +58,14 @@
{{#if this.isSpecificChecked}}
<GhTokenInput
@class="select-members select-members-recipient"
@dropdownClass={{@dropdownClass}}
@options={{this.specificOptions}}
@selected={{this.selectedSpecificOptions}}
@disabled={{@disabled}}
@searchMessage="All labels selected"
@optionsComponent="power-select/options"
@allowCreation={{false}}
@renderInPlace={{true}}
@renderInPlace={{this.renderInPlace}}
@onChange={{this.selectSpecificOptions}}
as |option|
>

View File

@ -27,6 +27,10 @@ export default class GhMembersRecipientSelect extends Component {
this.fetchMemberCountsTask.perform();
}
get renderInPlace() {
return this.args.renderInPlace === undefined ? true : this.args.renderInPlace;
}
get baseFilters() {
const filterItems = (this.args.filter || '').split(',');
const filterItemsArray = filterItems.filter(item => BASE_FILTERS.includes(item?.trim()));

View File

@ -377,6 +377,10 @@
right: 10px;
}
.gh-publishmenu-newsletter-dropdown {
z-index: 99999;
}
.gh-publishmenu-newsletter-dropdown .ember-power-select-option[aria-selected="true"] {
color: var(--black);
font-weight: 600;