mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-23 22:11:09 +03:00
Added "Opt-in existing subscribers" option to newsletter creation
refs https://github.com/TryGhost/Team/issues/1528 - adds "opt-in existing" toggle to newsletter modal that's only shown when creating a newsletter - defaults to true - updated newsletter save flow to show confirmation before creation - alters message to reflect auto-subscribe selection - count of existing subscribed members is not implemented as it's not yet supported by the API - updated newsletter adapter and save flow to use auto opt-in selection - when option is checked the save URL is changed to `POST /members/?opt_in_existing=true` - modified task button component to ignore a task return value of `canceled` so when it's received the buttons returns to the idle state instead of showing a saved or failed state - used by save routine when the "Back to edit" button is clicked in the create confirmation modal
This commit is contained in:
parent
5437063d4b
commit
502fcaba3c
14
ghost/admin/app/adapters/newsletter.js
Normal file
14
ghost/admin/app/adapters/newsletter.js
Normal file
@ -0,0 +1,14 @@
|
||||
import ApplicationAdapter from 'ghost-admin/adapters/application';
|
||||
|
||||
export default class Newsletter extends ApplicationAdapter {
|
||||
buildIncludeURL(store, modelName, id, snapshot, requestType, query) {
|
||||
const url = this.buildURL(modelName, id, snapshot, requestType, query);
|
||||
const parsedUrl = new URL(url);
|
||||
|
||||
if (snapshot?.adapterOptions?.optInExisting) {
|
||||
parsedUrl.searchParams.append('opt_in_existing', 'true');
|
||||
}
|
||||
|
||||
return parsedUrl.toString();
|
||||
}
|
||||
}
|
@ -84,7 +84,7 @@ const GhTaskButton = Component.extend({
|
||||
}
|
||||
|
||||
let value = this.get('task.last.value');
|
||||
return (taskName === lastTaskName) && !isBlank(value) && value !== false;
|
||||
return (taskName === lastTaskName) && !isBlank(value) && value !== false && value !== 'canceled';
|
||||
}),
|
||||
|
||||
isSuccessClass: computed('isSuccess', function () {
|
||||
@ -99,7 +99,7 @@ const GhTaskButton = Component.extend({
|
||||
return false;
|
||||
}
|
||||
|
||||
return (taskName === lastTaskName) && this.get('task.last.error') !== undefined;
|
||||
return (taskName === lastTaskName) && this.get('task.last.error') !== undefined && this.task.last?.error !== null;
|
||||
}),
|
||||
|
||||
isFailureClass: computed('isFailure', function () {
|
||||
|
@ -13,7 +13,11 @@
|
||||
</div>
|
||||
|
||||
{{#if (eq this.tab "settings")}}
|
||||
<Modals::EditNewsletter::Settings @newsletter={{@data.newsletter}} />
|
||||
<Modals::EditNewsletter::Settings
|
||||
@newsletter={{@data.newsletter}}
|
||||
@optInExisting={{this.optInExisting}}
|
||||
@setOptInExisting={{this.setOptInExisting}}
|
||||
/>
|
||||
{{else}}
|
||||
<Modals::EditNewsletter::Design @newsletter={{@data.newsletter}} />
|
||||
{{/if}}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Component from '@glimmer/component';
|
||||
import ConfirmCreateModal from './edit-newsletter/confirm-create';
|
||||
import ConfirmNewsletterEmailModal from './edit-newsletter/confirm-newsletter-email';
|
||||
import {action} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
@ -13,6 +14,7 @@ export default class EditNewsletterModal extends Component {
|
||||
};
|
||||
|
||||
@tracked tab = 'settings';
|
||||
@tracked optInExisting = this.args.data.newsletter.isNew;
|
||||
|
||||
willDestroy() {
|
||||
super.willDestroy(...arguments);
|
||||
@ -32,12 +34,33 @@ export default class EditNewsletterModal extends Component {
|
||||
this.saveTask.perform();
|
||||
}
|
||||
|
||||
@action
|
||||
setOptInExisting(value) {
|
||||
this.optInExisting = value;
|
||||
}
|
||||
|
||||
@task
|
||||
*saveTask() {
|
||||
try {
|
||||
yield this.args.data.newsletter.validate({});
|
||||
|
||||
const {optInExisting} = this;
|
||||
|
||||
const shouldCreate = yield this.modals.open(ConfirmCreateModal, {
|
||||
optInExisting,
|
||||
newsletter: this.args.data.newsletter
|
||||
});
|
||||
|
||||
if (!shouldCreate) {
|
||||
// ensure task button returns to idle state
|
||||
return 'canceled';
|
||||
}
|
||||
|
||||
const newEmail = this.args.data.newsletter.senderEmail;
|
||||
|
||||
const result = yield this.args.data.newsletter.save();
|
||||
const result = yield this.args.data.newsletter.save({
|
||||
adapterOptions: {optInExisting}
|
||||
});
|
||||
|
||||
if (result._meta?.sent_email_verification) {
|
||||
yield this.modals.open(ConfirmNewsletterEmailModal, {
|
||||
@ -51,8 +74,8 @@ export default class EditNewsletterModal extends Component {
|
||||
return result;
|
||||
} catch (e) {
|
||||
if (e === undefined) {
|
||||
// validation error
|
||||
return false;
|
||||
// ensure task button shows failed state
|
||||
throw new Error('Validation failed');
|
||||
}
|
||||
|
||||
throw e;
|
||||
|
@ -0,0 +1,47 @@
|
||||
<div class="modal-content">
|
||||
<header class="modal-header" data-test-modal="confirm-newsletter-email">
|
||||
<h1>All set? Here's what happens next</h1>
|
||||
</header>
|
||||
<button type="button" class="close" role="button" title="Close" {{on "click" (fn @close false)}}>{{svg-jar "close"}}<span class="hidden">Close</span></button>
|
||||
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
{{#if @data.optInExisting}}
|
||||
{{!-- TODO: add members count once API supports it --}}
|
||||
{{!-- {{#let (members-count-fetcher query=(hash filter="")) as |countFetcher|}}
|
||||
Your newsletter <strong>{{@data.newsletter.name}}</strong> will be
|
||||
immediately made live and your
|
||||
{{#if countFetcher.count}}<strong>{{countFetcher.count}}</strong>{{/if}}
|
||||
existing newsletter subscribers will be automatically opted-in to receive it.
|
||||
{{/let}} --}}
|
||||
|
||||
Your newsletter <strong>{{@data.newsletter.name}}</strong> will be
|
||||
immediately made live and your existing newsletter subscribers
|
||||
will be automatically opted-in to receive it.
|
||||
{{else}}
|
||||
Your newsletter <strong>{{@data.newsletter.name}}</strong> will be
|
||||
immediately made live. Your existing newsletter subscribers will
|
||||
<strong>not</strong> be opted-in to receive it.
|
||||
{{/if}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="gh-btn"
|
||||
{{on "click" (fn @close false)}}
|
||||
>
|
||||
<span>Back to edit</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="gh-btn gh-btn-black"
|
||||
{{on "click" (fn @close true)}}
|
||||
{{on-key "Enter"}}
|
||||
>
|
||||
<span>Create newsletter</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
@ -83,6 +83,23 @@
|
||||
</div>
|
||||
</div>
|
||||
</GhFormGroup>
|
||||
|
||||
{{#if @newsletter.isNew}}
|
||||
<GhFormGroup>
|
||||
<label for="opt-in-existing" class="modal-fullsettings-title">Opt-in existing subscribers</label>
|
||||
<div class="for-switch small">
|
||||
<div class="container">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="opt-in-existing"
|
||||
checked={{@optInExisting}}
|
||||
{{on "check" this.setOptInExisting}}
|
||||
>
|
||||
<button type="button" class="input-toggle-component" {{on "click" this.toggleOptInExisting}}></button>
|
||||
</div>
|
||||
</div>
|
||||
</GhFormGroup>
|
||||
{{/if}}
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
@ -24,4 +24,14 @@ export default class EditNewsletterSettingsForm extends Component {
|
||||
onValueChange(property, value) {
|
||||
this.args.newsletter[property] = value;
|
||||
}
|
||||
|
||||
@action
|
||||
setOptInExisting(event) {
|
||||
this.args.setOptInExisting(event.target.value);
|
||||
}
|
||||
|
||||
@action
|
||||
toggleOptInExisting() {
|
||||
this.args.setOptInExisting(!this.args.optInExisting);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ export default function mockNewsletters(server) {
|
||||
server.get('/newsletters/', paginatedResponse('newsletters'));
|
||||
server.get('/newsletters/:id/');
|
||||
|
||||
server.post('/newsletters/', function ({newsletters}) {
|
||||
server.post('/newsletters/', function ({newsletters}, {queryParams}) {
|
||||
const attrs = this.normalizedRequestAttrs();
|
||||
|
||||
// sender email can't be set without verification
|
||||
@ -23,6 +23,13 @@ export default function mockNewsletters(server) {
|
||||
};
|
||||
}
|
||||
|
||||
if (queryParams.opt_in_existing === 'true') {
|
||||
newsletters.all().models.forEach((n) => {
|
||||
newsletter.members.mergeCollection(n.members);
|
||||
});
|
||||
newsletter.save();
|
||||
}
|
||||
|
||||
return collection;
|
||||
});
|
||||
|
||||
|
5
ghost/admin/mirage/models/newsletter.js
Normal file
5
ghost/admin/mirage/models/newsletter.js
Normal file
@ -0,0 +1,5 @@
|
||||
import {Model, hasMany} from 'miragejs';
|
||||
|
||||
export default Model.extend({
|
||||
members: hasMany()
|
||||
});
|
Loading…
Reference in New Issue
Block a user