Skip to content

Commit b2c1498

Browse files
Updated gift subscriptions copy across Portal and email (TryGhost#27635)
no issues - **Portal** — gift purchase, share, and redemption pages reworded; account screen for gift members now shows the expiry line above a focused continue-subscription box (matching the existing complimentary-member pattern, which was hidden for gift members) - **Email (purchase confirmation)** — new subject + preheader, reworded copy, removed the prefilled-mailto "Send to recipient" button (read as phishing) and the "Amount paid" row; dropped the now-unused amount/currency plumbing through `gift-service` --------- Co-authored-by: Sag <guptazy@gmail.com>
1 parent 476990d commit b2c1498

78 files changed

Lines changed: 127 additions & 113 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/portal/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tryghost/portal",
3-
"version": "2.68.23",
3+
"version": "2.68.24",
44
"license": "MIT",
55
"repository": "https://github.com/TryGhost/Ghost",
66
"author": "Ghost Foundation",

apps/portal/src/components/pages/AccountHomePage/components/account-welcome.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ const AccountWelcome = () => {
2222
const currentPeriodEnd = subscription?.current_period_end;
2323
const subscriptionExpiry = getSubscriptionExpiry({member});
2424
if (isGiftMember({member})) {
25+
if (subscriptionExpiry) {
26+
return (
27+
<div className='gh-portal-section' style={{marginBottom: 24}}>
28+
<p className='gh-portal-text-center gh-portal-free-ctatext'>{t(`Your gift subscription will expire on {expiryDate}`, {expiryDate: subscriptionExpiry})}</p>
29+
</div>
30+
);
31+
}
2532
return null;
2633
}
2734
if (isComplimentary && subscriptionExpiry) {

apps/portal/src/components/pages/AccountHomePage/components/continue-gift-subscription-banner.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ const ContinueGiftSubscriptionBanner = () => {
2222
return (
2323
<div className='gh-portal-cancelcontinue-container'>
2424
<div className='gh-portal-cancel-banner'>
25-
<p style={{maxWidth: 'none', margin: '0 0 16px', textAlign: 'center'}}>
26-
Your gift subscription ends on <strong>{expiryDate}</strong>. Continue with a paid subscription to keep reading. Any remaining days will be added as free trial time.
25+
<p style={{maxWidth: 'none', margin: '0 0 16px', textAlign: 'center', textWrap: 'pretty'}}>
26+
Continue with a paid subscription anytime. Your remaining gift period will be added as a free trial.
2727
</p>
2828
<ActionButton
2929
onClick={() => doAction('continueGiftSubscription')}

apps/portal/src/components/pages/gift-page.js

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,6 @@ export const GiftPageStyles = `
116116
border-color: transparent;
117117
}
118118
119-
.gh-portal-content.gift .gh-portal-signup-message {
120-
margin: 32px 0 40px;
121-
color: var(--grey5);
122-
text-align: center;
123-
text-wrap: pretty;
124-
}
125-
126119
@media (max-width: 480px) {
127120
.gh-portal-gift-hero {
128121
padding: 40px 24px 32px;
@@ -208,7 +201,7 @@ function GiftProductCard({brandColor, product, selectedInterval, isDisabled, isP
208201
<div className='gh-portal-btn-product'>
209202
<ActionButton
210203
dataTestId='purchase-gift'
211-
label='Gift this'
204+
label='Continue'
212205
onClick={e => onPurchase(e, product)}
213206
disabled={isDisabled}
214207
isRunning={isPurchasing}
@@ -285,8 +278,8 @@ const GiftPage = () => {
285278
<div className='gh-portal-gift-hero-icon'>
286279
<GiftIcon />
287280
</div>
288-
<h1 className='gh-portal-main-title'>Gift someone a membership</h1>
289-
<p className='gh-portal-main-subtitle'>Pick a plan and send it to someone as a gift</p>
281+
<h1 className='gh-portal-main-title'>Gift a membership</h1>
282+
<p className='gh-portal-main-subtitle'>Share a full membership to {siteTitle} with a friend or colleague</p>
290283
</div>
291284
</>
292285
);
@@ -357,10 +350,6 @@ const GiftPage = () => {
357350
))}
358351
</div>
359352
</section>
360-
361-
<div className='gh-portal-signup-message'>
362-
<div>Only redeemable by free or new members.</div>
363-
</div>
364353
</div>
365354
</section>
366355
</div>

apps/portal/src/components/pages/gift-redemption-page.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,9 @@ export const GiftRedemptionStyles = `
9595
margin: 20px 0 0;
9696
font-size: 2.4rem;
9797
font-weight: 700;
98-
line-height: 1.1;
98+
line-height: 1.3;
9999
letter-spacing: -0.015em;
100+
text-wrap: pretty;
100101
color: var(--grey0);
101102
}
102103
@@ -314,7 +315,11 @@ const GiftRedemptionPage = () => {
314315
const isRedeeming = action === 'redeemGift:running';
315316
const buttonLabel = isRedeeming
316317
? 'Redeeming gift...' // TODO: Add translation strings once copy has been finalised
317-
: 'Redeem gift membership'; // TODO: Add translation strings once copy has been finalised
318+
: 'Redeem your membership'; // TODO: Add translation strings once copy has been finalised
319+
const siteTitle = site?.title;
320+
const headerText = siteTitle
321+
? `You've been gifted a membership to ${siteTitle}`
322+
: 'You\'ve been gifted a membership';
318323

319324
return (
320325
<div className='gh-portal-content gh-portal-gift-redemption'>
@@ -324,7 +329,7 @@ const GiftRedemptionPage = () => {
324329
<div className='gh-gift-redemption-panel'>
325330
<div className='gh-gift-redemption-icon'><GiftIcon /></div>
326331
<div className='gh-gift-redemption-kicker'>{'Gift membership'}</div>
327-
<h1 className='gh-gift-redemption-title'>{'You\'ve been gifted a membership'}</h1>
332+
<h1 className='gh-gift-redemption-title'>{headerText}</h1>
328333

329334
<div className='gh-gift-redemption-plan'>
330335
<span className='gh-gift-redemption-tier'>{gift.tier.name}</span>

apps/portal/src/components/pages/gift-success-page.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@ const GiftSuccessPage = () => {
122122

123123
<div className="gh-portal-signup-header">
124124
<div className="gh-gift-success-icon"><GiftIcon /></div>
125-
<h1 className="gh-portal-main-title">Gift ready to share!</h1>
125+
<h1 className="gh-portal-main-title">Your gift is ready!</h1>
126126
<p className="gh-portal-main-subtitle gh-portal-text-center">
127-
Share this link with the recipient to let them redeem their gift membership.
127+
Send the link below to share it with whoever you&apos;d like.
128128
</p>
129129
</div>
130130

@@ -137,8 +137,7 @@ const GiftSuccessPage = () => {
137137
</div>
138138

139139
<p className="gh-gift-footer-text">
140-
This link can be redeemed once and expires in 365 days.<br />
141-
We&apos;ve also sent a confirmation to your email with this link.
140+
Not ready to share? We&apos;ve also emailed a copy to your inbox.
142141
</p>
143142
</div>
144143
);

apps/portal/test/portal-links.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ describe('Portal Data links:', () => {
418418
popupFrame = await utils.findByTitle(/portal-popup/i);
419419
expect(popupFrame).toBeInTheDocument();
420420

421-
const giftSubtitle = within(popupFrame.contentDocument).queryByText(/pick a plan and send it to someone as a gift/i);
421+
const giftSubtitle = within(popupFrame.contentDocument).queryByText(/share a full membership.*with a friend or colleague/i);
422422
expect(giftSubtitle).toBeInTheDocument();
423423
});
424424

@@ -605,7 +605,7 @@ describe('Portal Data links:', () => {
605605
popupFrame = await utils.findByTitle(/portal-popup/i);
606606
expect(popupFrame).toBeInTheDocument();
607607

608-
const giftTitle = within(popupFrame.contentDocument).queryByText(/gift ready to share/i);
608+
const giftTitle = within(popupFrame.contentDocument).queryByText(/your gift is ready/i);
609609
expect(giftTitle).toBeInTheDocument();
610610

611611
const redeemUrl = within(popupFrame.contentDocument).queryByText(/\/gift\/abc123$/);

apps/portal/test/unit/components/pages/AccountHomePage/account-welcome.test.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,31 @@ const setup = (overrides) => {
1414
};
1515

1616
describe('AccountWelcome', () => {
17+
test('shows gift expiry message for gift members', () => {
18+
const products = getProductsData({numOfProducts: 1});
19+
const site = getSiteData({products, portalProducts: products.map(p => p.id)});
20+
21+
const member = getMemberData({
22+
paid: true,
23+
status: 'gift',
24+
subscriptions: [
25+
getSubscriptionData({
26+
status: 'active',
27+
amount: 0,
28+
currency: 'USD',
29+
interval: 'year',
30+
tier: {
31+
expiry_at: new Date('2099-04-12T12:00:00.000Z')
32+
}
33+
})
34+
]
35+
});
36+
37+
const {queryByText} = setup({site, member});
38+
39+
expect(queryByText('Your gift subscription will expire on 12 Apr 2099')).toBeInTheDocument();
40+
});
41+
1742
test('uses current period end for renewal date on free months offers', () => {
1843
const products = getProductsData({numOfProducts: 1});
1944
const site = getSiteData({products, portalProducts: products.map(p => p.id)});

apps/portal/test/unit/components/pages/gift-redemption-page.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ describe('GiftRedemptionPage', () => {
4646
expect(queryByLabelText(/your name/i)).not.toBeInTheDocument();
4747
expect(queryByLabelText(/your email/i)).not.toBeInTheDocument();
4848

49-
fireEvent.click(getByRole('button', {name: 'Redeem gift membership'}));
49+
fireEvent.click(getByRole('button', {name: 'Redeem your membership'}));
5050

5151
expect(mockDoActionFn).toHaveBeenCalledWith('redeemGift', {
5252
giftToken: 'gift-token-123'
@@ -56,7 +56,7 @@ describe('GiftRedemptionPage', () => {
5656
test('shows validation errors for anonymous visitors and only submits once valid', async () => {
5757
const {getByLabelText, getByRole, mockDoActionFn, getByText} = renderGiftRedemptionPage();
5858
const emailInput = getByLabelText(/your email/i);
59-
const submitButton = getByRole('button', {name: 'Redeem gift membership'});
59+
const submitButton = getByRole('button', {name: 'Redeem your membership'});
6060

6161
fireEvent.click(submitButton);
6262
expect(getByText('Enter your email address')).toBeInTheDocument();

ghost/core/core/server/services/gifts/email-templates/gift-purchase-confirmation.hbs

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
</style>
1919
</head>
2020
<body style="background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
21+
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Thank you for supporting {{siteTitle}}.</span>
2122
<table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
2223
<tr>
2324
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">&nbsp;</td>
@@ -40,7 +41,7 @@
4041
<tr>
4142
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
4243
<h1 style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 26px; color: #15212A; font-weight: bold; line-height: 28px; margin: 0; padding-bottom: 12px;">Your gift is ready to&nbsp;share!</h1>
43-
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; margin: 0; padding-bottom: 24px;">Share the link below with the recipient to let them redeem their gift membership.</p>
44+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; margin: 0; padding-bottom: 24px;">Thank you for supporting {{siteTitle}}. Send the link below to share your gift with whoever you'd like.</p>
4445
<table width="100%" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; table-layout: fixed; width: 100%; min-width: 100%; box-sizing: border-box; background: #F4F5F6; border-radius: 8px;">
4546
<tbody>
4647
<tr>
@@ -50,30 +51,11 @@
5051
<td style="padding-right: 8px; background-color: #F4F5F6; text-align: left; vertical-align: middle;" valign="middle">
5152
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 700;">Gift subscription</p>
5253
<p class="large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 400;">{{gift.tierName}} &bull; {{gift.cadenceLabel}}</p>
53-
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 700;">Amount paid</p>
54-
<p class="large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 400;">{{gift.amount}}</p>
55-
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 700;">Redemption link</p>
56-
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 24px; color: #738A94; word-break: break-all;"><a href="{{gift.link}}" style="color: {{accentColor}}; text-decoration: none;">{{gift.link}}</a></p>
54+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 700;">Gift link</p>
55+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 0; color: #738A94; word-break: break-all;"><a href="{{gift.link}}" style="color: {{accentColor}}; text-decoration: none;">{{gift.link}}</a></p>
5756
</td>
5857
</tr>
5958
</table>
60-
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
61-
<tbody>
62-
<tr>
63-
<td align="left" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top;">
64-
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
65-
<tbody>
66-
<tr>
67-
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; vertical-align: top; background-color: {{accentColor}}; border-radius: 8px; text-align: center;">
68-
<a href="{{gift.mailtoUrl}}" target="_blank" style="display: inline-block; color: #ffffff; background-color: {{accentColor}}; border: solid 1px {{accentColor}}; border-radius: 8px; padding: 10px 20px; text-decoration: none;">Send to recipient</a>
69-
</td>
70-
</tr>
71-
</tbody>
72-
</table>
73-
</td>
74-
</tr>
75-
</tbody>
76-
</table>
7759
</td>
7860
</tr>
7961
</tbody>
@@ -83,7 +65,7 @@
8365
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; padding-top: 16px;">
8466
<tr>
8567
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
86-
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 13px; margin: 0; padding-bottom: 0; color: #738A94;">This link can be redeemed once and expires on {{gift.expiresAt}}. It's only available to free or new members.</p>
68+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 13px; margin: 0; padding-bottom: 0; color: #738A94;">This link expires on {{gift.expiresAt}} and can be redeemed once by anyone who isn't already a paid member of {{siteTitle}}.</p>
8769
</td>
8870
</tr>
8971
</table>

0 commit comments

Comments
 (0)