Added new support and reply-to address for members

no issue

- Adds 2 new email address fields for members in email settings section - support address and reply-to address
- Support address - Is used for member auth emails as well as in themes and Portal for allowing members to contact support.
- Reply-to address - Is used to set `reply-to` address for newsletter emails that allows configuring where member's reply to emails will go
- Disabled from address update for empty value
- Updated success toast message and copy for from/support address update
- Changed section title + description for email addresses
- Added "public" to support email description
This commit is contained in:
Rish 2020-08-25 17:35:45 +05:30 committed by Rishabh Garg
parent 4955b0d3f5
commit 301fdde0f7
7 changed files with 181 additions and 36 deletions

View File

@ -140,6 +140,20 @@
{{/if}}
</section>
{{#if this.config.enableDeveloperExperiments}}
<section class="bb b--whitegrey pa5">
<div class="flex justify-between">
<div>
<h4 class="gh-setting-title">Portal settings</h4>
<p class="gh-setting-desc pa0 ma0">Customize members modal signup flow</p>
</div>
<div>
<button type="button" class="gh-btn" {{action (toggle "showMembersModalSettings" this)}} data-test-toggle-membersFrom><span> Customize </span></button>
</div>
</div>
</section>
{{/if}}
<section class="bb b--whitegrey pa5">
<div class="flex justify-between">
<div>
@ -270,8 +284,8 @@
<section class="bb b--whitegrey pa5">
<div class="flex justify-between">
<div>
<h4 class="gh-setting-title">From address</h4>
<p class="gh-setting-desc pa0 ma0">The email address your members receive newsletters from</p>
<h4 class="gh-setting-title">Email addresses</h4>
<p class="gh-setting-desc pa0 ma0">Contact information used for newsletters and member login emails</p>
</div>
<div>
<button type="button" class="gh-btn" {{action (toggle "membersFromOpen" this)}} data-test-toggle-membersFrom><span>{{if this.membersFromOpen "Close" "Expand"}}</span></button>
@ -279,46 +293,91 @@
</div>
{{#liquid-if this.membersFromOpen}}
<div class="flex flex-column w-100 w-50-l flex mt8">
<div class="mt8">
<GhFormGroup>
<label class="fw6 f8">Support email address</label>
<div class="flex items-center justify-center mt1">
<GhTextInput
@value={{readonly this.supportAddress}}
@input={{action "setSupportAddress" value="target.value"}}
@class="gh-labs-members-emailinput"
/>
<GhTaskButton
@buttonText="Update support address"
@runningText="Sending..."
@successText="Confirmation Email Sent"
@disabled={{this.disableUpdateSupportAddressButton}}
@task={{this.updateSupportAddress}}
@class="gh-btn gh-btn-icon gh-btn-textfield-group gh-labs-members-emaildropdown"
data-test-button="update-support-address"
/>
</div>
</GhFormGroup>
<div class="nt5 mb5">
<span class="mt1 fw4 f8 midgrey">
How members can reach you for help with their account (public)
</span>
</div>
{{#if this.showSupportAddressConfirmation}}
<div class="flex items-center green-d1 nt3 lh-1">
{{svg-jar "check-circle" class="w4 h4 mr1 stroke-green-d1"}} <span class="nudge-left--2">Check your inbox and click the link to confirm</span>
</div>
{{/if}}
</div>
<div class="mt8">
<GhFormGroup>
<label class="fw6 f8">Newsletter email address</label>
<div class="flex items-center justify-center mt1">
<GhTextInput
@value={{readonly this.fromAddress}}
@input={{action "setFromAddress" value="target.value"}}
@class="w20"
@class="gh-labs-members-emailinput"
/>
<GhTaskButton
@buttonText="Update from address"
@buttonText="Update newsletter address"
@runningText="Sending..."
@successText="Confirmation Email Sent"
@disabled={{this.disableUpdateFromAddressButton}}
@task={{this.updateFromAddress}}
@class="gh-btn gh-btn-icon gh-btn-textfield-group ml2"
@class="gh-btn gh-btn-icon gh-btn-textfield-group gh-labs-members-emaildropdown"
data-test-button="update-from-address"
/>
</div>
</GhFormGroup>
<div class="nt5 mb5">
<span class="mt1 fw4 f8 midgrey">
The address your newsletter posts are sent from
</span>
</div>
{{#if this.showFromAddressConfirmation}}
<div class="flex items-center green-d1 nt3 lh-1">
{{svg-jar "check-circle" class="w4 h4 mr1 stroke-green-d1"}} <span class="nudge-left--2">Check your inbox and click the link to confirm</span>
</div>
{{/if}}
</div>
{{/liquid-if}}
</section>
{{#if this.config.enableDeveloperExperiments}}
<section class="bb b--whitegrey pa5">
<div class="flex justify-between">
<div>
<h4 class="gh-setting-title">Portal settings</h4>
<p class="gh-setting-desc pa0 ma0">Customize members modal signup flow</p>
</div>
<div>
<button type="button" class="gh-btn" {{action (toggle "showMembersModalSettings" this)}} data-test-toggle-membersFrom><span> Customize </span></button>
<div class="mt8">
<GhFormGroup @class="for-select gh-labs-members-defaultemaildd">
<label class="fw6 f8" for="reply-address">Newsletter replies go to</label>
<span class="gh-select mt1">
{{one-way-select this.selectedReplyAddress
id="reply-address"
name="reply-address"
options=(readonly this.replyAddresses)
optionValuePath="value"
optionLabelPath="label"
update=(action "setReplyAddress")
}}
{{svg-jar "arrow-down-small"}}
</span>
</GhFormGroup>
<div class="nt5 mb5">
<span class="mt1 fw4 f8 midgrey">
Where you receive responses to newsletters
</span>
</div>
</div>
</section>
{{/if}}
{{/liquid-if}}
</section>
{{#unless this.mailgunIsConfigured}}
<section class="bb b--whitegrey pa5">
<div class="flex justify-between">

View File

@ -28,6 +28,17 @@ const CURRENCIES = [
}
];
const REPLY_ADDRESSES = [
{
label: 'Newsletter email address',
value: 'newsletter'
},
{
label: 'Support email address',
value: 'support'
}
];
export default Component.extend({
feature: service(),
config: service(),
@ -37,7 +48,9 @@ export default Component.extend({
settings: service(),
currencies: null,
replyAddresses: null,
showFromAddressConfirmation: false,
showSupportAddressConfirmation: false,
showMembersModalSettings: false,
stripePlanInvalidAmount: false,
_scratchStripeYearlyAmount: null,
@ -63,6 +76,10 @@ export default Component.extend({
stripeConnectAccountName: reads('settings.stripeConnectDisplayName'),
stripeConnectLivemode: reads('settings.stripeConnectLivemode'),
selectedReplyAddress: computed('settings.membersReplyAddress', function () {
return REPLY_ADDRESSES.findBy('value', this.get('settings.membersReplyAddress'));
}),
selectedCurrency: computed('stripePlans.monthly.currency', function () {
return CURRENCIES.findBy('value', this.get('stripePlans.monthly.currency'));
}),
@ -70,9 +87,17 @@ export default Component.extend({
disableUpdateFromAddressButton: computed('fromAddress', function () {
const savedFromAddress = this.get('settings.membersFromAddress') || '';
if (!savedFromAddress.includes('@') && this.blogDomain) {
return (this.fromAddress === `${savedFromAddress}@${this.blogDomain}`);
return !this.fromAddress || (this.fromAddress === `${savedFromAddress}@${this.blogDomain}`);
}
return (this.fromAddress === savedFromAddress);
return !this.fromAddress || (this.fromAddress === savedFromAddress);
}),
disableUpdateSupportAddressButton: computed('supportAddress', function () {
const savedSupportAddress = this.get('settings.membersSupportAddress') || '';
if (!savedSupportAddress.includes('@') && this.blogDomain) {
return !this.supportAddress || (this.supportAddress === `${savedSupportAddress}@${this.blogDomain}`);
}
return !this.supportAddress || (this.supportAddress === savedSupportAddress);
}),
blogDomain: computed('config.blogDomain', function () {
@ -120,6 +145,7 @@ export default Component.extend({
this._super(...arguments);
this.set('mailgunRegions', [US, EU]);
this.set('currencies', CURRENCIES);
this.set('replyAddresses', REPLY_ADDRESSES);
},
actions: {
@ -159,7 +185,11 @@ export default Component.extend({
},
setFromAddress(fromAddress) {
this.setFromAddress(fromAddress);
this.setEmailAddress('fromAddress', fromAddress);
},
setSupportAddress(supportAddress) {
this.setEmailAddress('supportAddress', supportAddress);
},
toggleSelfSignup() {
@ -245,6 +275,12 @@ export default Component.extend({
this.set('settings.stripePlans', updatedPlans);
},
setReplyAddress(event) {
const newReplyAddress = event.value;
this.set('settings.membersReplyAddress', newReplyAddress);
},
setStripeConnectIntegrationToken(event) {
this.set('settings.stripeProductName', this.get('settings.title'));
this.setStripeConnectIntegrationTokenSetting(event.target.value);
@ -322,7 +358,8 @@ export default Component.extend({
try {
const response = yield this.ajax.post(url, {
data: {
from_address: this.fromAddress
email: this.fromAddress,
type: 'fromAddressUpdate'
}
});
this.toggleProperty('showFromAddressConfirmation');
@ -333,6 +370,23 @@ export default Component.extend({
}
}).drop(),
updateSupportAddress: task(function* () {
let url = this.get('ghostPaths.url').api('/settings/members/email');
try {
const response = yield this.ajax.post(url, {
data: {
email: this.supportAddress,
type: 'supportAddressUpdate'
}
});
this.toggleProperty('showSupportAddressConfirmation');
return response;
} catch (e) {
// Failed to send email, retry
return false;
}
}).drop(),
get liveStripeConnectAuthUrl() {
return this.ghostPaths.url.api('members/stripe_connect') + '?mode=live';
},

View File

@ -43,8 +43,9 @@ export default Controller.extend({
session: service(),
settings: service(),
queryParams: ['fromAddressUpdate'],
queryParams: ['fromAddressUpdate', 'supportAddressUpdate'],
fromAddressUpdate: null,
supportAddressUpdate: null,
importErrors: null,
importSuccessful: false,
showDeleteAllModal: false,
@ -72,7 +73,11 @@ export default Controller.extend({
},
fromAddress: computed(function () {
return this.parseFromAddress();
return this.parseEmailAddress(this.settings.get('membersFromAddress'));
}),
supportAddress: computed(function () {
return this.parseEmailAddress(this.settings.get('membersSupportAddress'));
}),
blogDomain: computed('config.blogDomain', function () {
@ -184,8 +189,8 @@ export default Controller.extend({
this.set('settings.stripeConnectIntegrationToken', stripeConnectIntegrationToken);
},
setFromAddress(fromAddress) {
this.set('fromAddress', fromAddress);
setEmailAddress(type, emailAddress) {
this.set(type, emailAddress);
}
},
@ -230,19 +235,20 @@ export default Controller.extend({
return RSVP.resolve();
},
parseFromAddress() {
const fromAddress = this.settings.get('membersFromAddress') || 'noreply';
parseEmailAddress(address) {
const emailAddress = address || 'noreply';
// Adds default domain as site domain
if (fromAddress.indexOf('@') < 0 && this.blogDomain) {
return `${fromAddress}@${this.blogDomain}`;
if (emailAddress.indexOf('@') < 0 && this.blogDomain) {
return `${emailAddress}@${this.blogDomain}`;
}
return fromAddress;
return emailAddress;
},
saveSettings: task(function* () {
const response = yield this.settings.save();
// Reset from address value on save
this.set('fromAddress', this.parseFromAddress());
this.set('fromAddress', this.parseEmailAddress(this.settings.get('membersFromAddress')));
this.set('supportAddress', this.parseEmailAddress(this.settings.get('membersSupportAddress')));
return response;
}).drop(),
@ -272,6 +278,7 @@ export default Controller.extend({
this.set('importErrors', null);
this.set('importSuccessful', false);
this.set('fromAddressUpdate', null);
this.set('supportAddressUpdate', null);
// stripeConnectIntegrationToken is not a persisted value so we don't want
// to keep it around across transitions
this.settings.set('stripeConnectIntegrationToken', undefined);

View File

@ -55,6 +55,8 @@ export default Model.extend(ValidationEngine, {
defaultContentVisibility: attr('string'),
membersAllowFreeSignup: attr('boolean'),
membersFromAddress: attr('string'),
membersSupportAddress: attr('string'),
membersReplyAddress: attr('string'),
stripeProductName: attr('string'),
stripeSecretKey: attr('string'),
stripePublishableKey: attr('string'),

View File

@ -8,6 +8,9 @@ export default AuthenticatedRoute.extend(CurrentUserSettings, {
queryParams: {
fromAddressUpdate: {
replace: true
},
supportAddressUpdate: {
replace: true
}
},
@ -25,9 +28,14 @@ export default AuthenticatedRoute.extend(CurrentUserSettings, {
setupController(controller) {
if (controller.fromAddressUpdate === 'success') {
this.notifications.showAlert(
`Done! Newsletter “From address” has been updated`.htmlSafe(),
`Newsletter email address has been updated`.htmlSafe(),
{type: 'success', key: 'members.settings.from-address.updated'}
);
} else if (controller.supportAddressUpdate === 'success') {
this.notifications.showAlert(
`Support email address has been updated`.htmlSafe(),
{type: 'success', key: 'members.settings.support-address.updated'}
);
}
},

View File

@ -47,7 +47,8 @@
cursor: default;
}
/* Members */
/* Members settings */
/* ------------------------------------------------ */
.gh-labs-members-radio {
cursor: pointer;
@ -171,6 +172,19 @@
}
}
.gh-labs-members-emailinput {
}
.gh-labs-members-emaildropdown {
min-width: 192px;
margin-left: 8px;
}
.gh-labs-members-defaultemaildd {
padding-right: 200px;
}
.gh-labs-mailgun-region {
width: 140px !important;
margin-right: 12px;

View File

@ -28,9 +28,10 @@
<GhMembersLabSetting
@settings={{this.settings}}
@fromAddress={{this.fromAddress}}
@supportAddress={{this.supportAddress}}
@setDefaultContentVisibility={{action "setDefaultContentVisibility"}}
@setStripeConnectIntegrationTokenSetting={{action "setStripeConnectIntegrationTokenSetting"}}
@setFromAddress={{action "setFromAddress"}}
@setEmailAddress={{action "setEmailAddress"}}
/>
<div class="mt5 pl5 pr5 pb5">
<GhTaskButton @buttonText="Save members settings"