0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-18 02:21:47 -05:00

🎨 Improved email failure handling and retrying ()

fixes https://github.com/TryGhost/Team/issues/2009

- When an email is sent, but it failed there was no way to retry once you left the retry screen
- There was no indication that the email failed to send in the post list and editor
This commit is contained in:
Simon Backx 2022-10-06 11:12:11 +02:00 committed by GitHub
parent 0bfbee5523
commit aaabf4b103
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 109 additions and 35 deletions

View file

@ -33,6 +33,7 @@
<Editor::Modals::PublishFlow::CompleteWithEmailError
@emailErrorMessage={{this.emailErrorMessage}}
@publishOptions={{@data.publishOptions}}
@setCompleted={{this.setCompleted}}
@close={{@close}}
/>
{{else if this.isConfirming}}
@ -59,4 +60,4 @@
/>
{{/if}}
</div>
</div>
</div>

View file

@ -13,7 +13,7 @@ export default class PublishModalComponent extends Component {
@service store;
@tracked emailErrorMessage;
@tracked emailErrorMessage = this.args.data.publishOptions.post.didEmailFail ? this.args.data.publishOptions.post.email.error : undefined;
@tracked isConfirming = false;
@tracked isComplete = false;
@tracked postCount = null;
@ -49,6 +49,13 @@ export default class PublishModalComponent extends Component {
}
}
@action
setCompleted() {
this.emailErrorMessage = undefined;
this.isConfirming = false;
this.isComplete = true;
}
@task
*saveTask() {
try {

View file

@ -42,6 +42,8 @@ export default class PublishFlowCompleteWithEmailError extends Component {
}
}
this.args.setCompleted();
return email;
} catch (e) {
// update "failed" state if email fails again

View file

@ -107,7 +107,11 @@
<div class="gh-publish-setting-title disabled" data-test-setting-title>
{{svg-jar "member"}}
<div class="gh-publish-setting-trigger">
Already sent to
{{#if (eq @publishOptions.post.email.status "failed") }}
Retry sending to
{{else}}
Already sent to
{{/if}}
{{format-number @publishOptions.post.email.emailCount}}
{{if (not-eq @recipientType "all") @recipientType}} {{!-- free/paid/specific --}}
{{gh-pluralize @publishOptions.post.email.emailCount "subscriber" without-count=true}}
@ -152,4 +156,4 @@
<span>Continue, final review &rarr;</span>
</button>
</div>
{{/unless}}
{{/unless}}

View file

@ -35,8 +35,7 @@
{{if post.isScheduled "will be" "was"}}
{{#if
(or post.isSent
(and post.isPublished post.email)
(or post.hasBeenEmailed
post.willEmail
)
}}

View file

@ -69,4 +69,4 @@
</button>
{{/if}}
{{/if}}
{{/if}}
{{/if}}

View file

@ -3,8 +3,19 @@
{{#if (and this.isSaving @post.isDraft)}}
Saving...
{{else if @post.isSent}}
<button type="button" {{on "click" @openUpdateFlow}} class="gh-editor-post-status-btn">Sent</button>
to {{gh-pluralize @post.email.emailCount "member"}}
{{#if @post.didEmailFail }}
Failed to send. <button
type="button"
class="gh-btn gh-btn-editor midgrey gh-retry-trigger"
{{on "click" @publishManagement.openPublishFlow}}
disabled={{@publishManagement.publishOptions.isLoading}}
>
<span>Retry</span>
</button>
{{else}}
<button type="button" {{on "click" @openUpdateFlow}} class="gh-editor-post-status-btn">Sent</button>
to {{gh-pluralize @post.email.emailCount "member"}}
{{/if}}
{{else if (and @post.emailOnly @post.isScheduled)}}
Scheduled
{{#if this.isHovered}}
@ -19,6 +30,15 @@
and sending to {{gh-pluralize @post.email.emailCount "member"}}
{{else if (eq @post.email.status "submitted")}}
and sent to {{gh-pluralize @post.email.emailCount "member"}}
{{else if (eq @post.email.status "failed")}}
but failed to send. <button
type="button"
class="gh-btn gh-btn-editor midgrey gh-retry-trigger"
{{on "click" @publishManagement.openPublishFlow}}
disabled={{@publishManagement.publishOptions.isLoading}}
>
<span>Retry</span>
</button>
{{/if}}
{{else if @post.isScheduled}}
<time datetime="{{@post.publishedAtUTC}}" class="ml1 green-d1" data-test-schedule-countdown>

View file

@ -91,9 +91,11 @@
{{/if}}
{{#if @post.isPublished}}
<span class="published" {{on "mouseover" (fn (mut this.isStateHovered) true)}} {{on "mouseleave" (fn (mut this.isStateHovered) false)}}>
<span class="published {{this.errorClass}}" {{on "mouseover" (fn (mut this.isStateHovered) true)}} {{on "mouseleave" (fn (mut this.isStateHovered) false)}}>
Published
{{#if @post.hasEmail}}
{{#if @post.didEmailFail}}
but failed to send
{{else if @post.hasBeenEmailed}}
and sent
{{#if this.isHovered}}
<span {{css-transition "anim-fade-in-scale"}}>to {{gh-pluralize @post.email.emailCount "member"}}</span>
@ -103,10 +105,14 @@
{{/if}}
{{#if @post.isSent}}
<span class="sent" {{on "mouseover" (fn (mut this.isStateHovered) true)}} {{on "mouseleave" (fn (mut this.isStateHovered) false)}}>
Sent
{{#if this.isHovered}}
<span {{css-transition "anim-fade-in-scale"}}>to {{gh-pluralize @post.email.emailCount "member"}}</span>
<span class="sent {{this.errorClass}}" {{on "mouseover" (fn (mut this.isStateHovered) true)}} {{on "mouseleave" (fn (mut this.isStateHovered) false)}}>
{{#if @post.didEmailFail}}
Failed to send
{{else}}
Sent
{{#if this.isHovered}}
<span {{css-transition "anim-fade-in-scale"}}>to {{gh-pluralize @post.email.emailCount "member"}}</span>
{{/if}}
{{/if}}
</span>
{{/if}}

View file

@ -15,6 +15,13 @@ export default class PostsListItemClicks extends Component {
return this.args.post;
}
get errorClass() {
if (this.post.didEmailFail) {
return 'error';
}
return '';
}
get scheduledText() {
let text = [];

View file

@ -13,19 +13,19 @@
</h2>
<div class="gh-post-analytics-meta">
<div class="gh-post-analytics-meta-text">
{{#if
(or this.post.isSent
(and this.post.isPublished this.post.email)
this.post.willEmail
)
}}
{{#if this.post.hasBeenEmailed }}
{{#if this.post.emailOnly}}
Sent
{{else}}
Published and sent
{{/if}}
{{else}}
Published on your site
Published
{{#if @post.didEmailFail}}
but failed to send
{{else}}
on your site
{{/if}}
{{/if}}
{{#let (moment-site-tz this.post.publishedAtUTC) as |publishedAt|}}
@ -46,7 +46,7 @@
Engagement
</h4>
<div class="gh-post-analytics-box">
{{#if (or this.post.isSent (and this.post.isPublished this.post.email))}}
{{#if this.post.hasBeenEmailed}}
<div class="gh-post-analytics-item">
<LinkTo @route="members" @query={{hash filterParam=(concat "emails.post_id:[" this.post.id "]") }}>
<h3>{{format-number this.post.email.emailCount}}</h3>

View file

@ -183,24 +183,34 @@ export default Model.extend(Comparable, ValidationEngine, {
return this.isScheduled && !!this.newsletter && !this.email;
}),
showEmailOpenAnalytics: computed('isPost', 'isSent', 'isPublished', 'email', function () {
hasBeenEmailed: computed('isPost', 'isSent', 'isPublished', 'email', function () {
return this.isPost
&& (this.isSent || this.isPublished)
&& this.email && this.email.status !== 'failed';
}),
didEmailFail: computed('isPost', 'isSent', 'isPublished', 'email.status', function () {
return this.isPost
&& (this.isSent || this.isPublished)
&& this.email && this.email.status === 'failed';
}),
showEmailOpenAnalytics: computed('hasBeenEmailed', 'isSent', 'isPublished', function () {
return this.hasBeenEmailed
&& !this.session.user.isContributor
&& this.settings.get('membersSignupAccess') !== 'none'
&& this.settings.get('editorDefaultEmailRecipients') !== 'disabled'
&& (this.isSent || this.isPublished)
&& this.email
&& this.hasBeenEmailed
&& this.email.trackOpens
&& this.settings.get('emailTrackOpens');
}),
showEmailClickAnalytics: computed('isPost', 'isSent', 'isPublished', 'email', function () {
return this.isPost
showEmailClickAnalytics: computed('hasBeenEmailed', 'isSent', 'isPublished', 'email', function () {
return this.hasBeenEmailed
&& !this.session.user.isContributor
&& this.settings.get('membersSignupAccess') !== 'none'
&& this.settings.get('editorDefaultEmailRecipients') !== 'disabled'
&& (this.isSent || this.isPublished)
&& this.email
&& this.email.trackClicks
&& this.settings.get('emailTrackClicks');
}),

View file

@ -1165,6 +1165,11 @@ a.gh-post-list-signups.active:hover > span, a.gh-post-list-conversions.active:ho
background: var(--pink);
}
.gh-posts-list-item-labs .gh-content-entry-status .error {
color: var(--red);
font-weight: 500;
}
.gh-posts-list-item-labs .schedule-details {
margin-left: 3px;
color: var(--midlightgrey-d1);
@ -1340,4 +1345,4 @@ a.gh-post-list-cta.stats.is-hovered:hover > * {
top: -0.3em;
font-size: 1.8rem;
}
}
}

View file

@ -356,6 +356,10 @@
color: var(--green-d1);
}
.gh-btn-editor.midgrey span {
color: var(--midgrey-l2);
}
.gh-editor-wordcount-container {
position: absolute;
right: 30px;

View file

@ -32,11 +32,12 @@
</LinkTo>
{{/if}}
{{/if}}
{{#if (or (not this.ui.isFullScreen) (not this.fromAnalytics)) }}
{{#if (or (not this.ui.isFullScreen) (not this.fromAnalytics) this.post.didEmailFail) }}
<div class="gh-editor-post-status">
<span>
<GhEditorPostStatus
@post={{this.post}}
@publishManagement={{publishManagement}}
@hasDirtyAttributes={{this.hasDirtyAttributes}}
@isSaving={{or this.autosaveTask.isRunning this.saveTasks.isRunning}}
@openUpdateFlow={{publishManagement.openUpdateFlow}}

View file

@ -23,10 +23,14 @@ export default class PublishOptions {
}
get willEmail() {
return this.publishType !== 'publish'
&& this.recipientFilter
&& this.post.isDraft
&& !this.post.email;
return (
(this.publishType !== 'publish'
&& this.recipientFilter
&& this.post.isDraft
&& !this.post.email
)
|| (this.post.isDraft && this.post.email && this.post.email.status === 'failed')
);
}
get willPublish() {
@ -255,6 +259,10 @@ export default class PublishOptions {
) {
this.publishType = 'publish';
}
if (this.post.isSent) {
this.publishType = 'send';
}
}
@task