mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-25 02:31:59 -05:00
Added unsubscribe page for email unsubscribe links
refs https://github.com/TryGhost/Team/issues/1495 With the addition of multiple newsletters, members can now view and mange their newsletter preferences via Portal when on the site. This change allows members to manage their newsletter subscription via unsubscribe link on emails. - adds new Unsubscribe page in portal to manage member's unsubscribe link handling - adds 2 new endpoints to fetch/update member's newsletters via UUID
This commit is contained in:
parent
926df30ccd
commit
adcf65a821
4 changed files with 135 additions and 1 deletions
|
@ -19,7 +19,7 @@ const DEV_MODE_DATA = {
|
|||
showPopup: true,
|
||||
site: Fixtures.site,
|
||||
member: Fixtures.member.free,
|
||||
page: 'signup',
|
||||
page: 'accountEmail',
|
||||
...Fixtures.paidMemberOnTier(),
|
||||
pageData: Fixtures.offer
|
||||
};
|
||||
|
@ -393,6 +393,16 @@ export default class App extends React.Component {
|
|||
|
||||
/** Fetch state from Portal Links */
|
||||
fetchLinkData() {
|
||||
const qParams = new URLSearchParams(window.location.search);
|
||||
if (qParams.get('uuid') && qParams.get('action') === 'unsubscribe') {
|
||||
return {
|
||||
showPopup: true,
|
||||
page: 'unsubscribe',
|
||||
pageData: {
|
||||
uuid: qParams.get('uuid')
|
||||
}
|
||||
};
|
||||
}
|
||||
const productMonthlyPriceQueryRegex = /^(?:(\w+?))?\/monthly$/;
|
||||
const productYearlyPriceQueryRegex = /^(?:(\w+?))?\/yearly$/;
|
||||
const offersRegex = /^offers\/(\w+?)\/?$/;
|
||||
|
|
85
ghost/portal/src/components/pages/UnsubscribePage.js
Normal file
85
ghost/portal/src/components/pages/UnsubscribePage.js
Normal file
|
@ -0,0 +1,85 @@
|
|||
import AppContext from '../../AppContext';
|
||||
|
||||
import {useContext, useEffect, useState} from 'react';
|
||||
import {getSiteNewsletters} from '../../utils/helpers';
|
||||
import setupGhostApi from '../../utils/api';
|
||||
import NewsletterManagement from '../common/NewsletterManagement';
|
||||
|
||||
const React = require('react');
|
||||
|
||||
function AccountHeader() {
|
||||
return (
|
||||
<header className='gh-portal-detail-header'>
|
||||
<h3 className='gh-portal-main-title'>Email preferences</h3>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export default function UnsubscribePage() {
|
||||
const {site, pageData, onAction} = useContext(AppContext);
|
||||
const api = setupGhostApi({siteUrl: site.url});
|
||||
const [member, setMember] = useState();
|
||||
const siteNewsletters = getSiteNewsletters({site});
|
||||
const defaultNewsletters = siteNewsletters.filter((d) => {
|
||||
return d.subscribe_on_signup;
|
||||
});
|
||||
const [subscribedNewsletters, setSubscribedNewsletters] = useState(defaultNewsletters);
|
||||
|
||||
useEffect(() => {
|
||||
const ghostApi = setupGhostApi({siteUrl: site.url});
|
||||
(async () => {
|
||||
const memberData = await ghostApi.member.newsletters({uuid: pageData.uuid});
|
||||
|
||||
setMember(memberData);
|
||||
setSubscribedNewsletters(memberData?.newsletters || []);
|
||||
})();
|
||||
}, [pageData.uuid, site.url]);
|
||||
|
||||
// Case: Email not found
|
||||
if (member === null) {
|
||||
return (
|
||||
<div className='gh-portal-content with-footer'>
|
||||
<AccountHeader />
|
||||
<p className="gh-portal-text-center">
|
||||
<h4>Unsubscribe Failed</h4>
|
||||
</p>
|
||||
<div className='gh-portal-section'>
|
||||
<p>Email address not found.</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Case: Single active newsletter
|
||||
if (siteNewsletters?.length === 1) {
|
||||
return (
|
||||
<div className='gh-portal-content with-footer'>
|
||||
<AccountHeader />
|
||||
<p className="gh-portal-text-center">
|
||||
<h4>Successfully unsubscribed</h4>
|
||||
</p>
|
||||
<div className='gh-portal-section'>
|
||||
<p><strong>{member?.email}</strong> will no longer receive this newsletter.</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<NewsletterManagement
|
||||
subscribedNewsletters={subscribedNewsletters}
|
||||
updateSubscribedNewsletters={async (newsletters) => {
|
||||
setSubscribedNewsletters(newsletters);
|
||||
await api.member.updateNewsletters({uuid: pageData.uuid, newsletters});
|
||||
}}
|
||||
unsubscribeAll={async () => {
|
||||
setSubscribedNewsletters([]);
|
||||
onAction('showPopupNotification', {
|
||||
action: 'updated:success',
|
||||
message: `Newsletter preference updated.`
|
||||
});
|
||||
await api.member.updateNewsletters({uuid: pageData.uuid, newsletters: []});
|
||||
}}
|
||||
isPaidMember={member?.status !== 'free'}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -8,6 +8,7 @@ import AccountProfilePage from './components/pages/AccountProfilePage';
|
|||
import AccountEmailPage from './components/pages/AccountEmailPage';
|
||||
import OfferPage from './components/pages/OfferPage';
|
||||
import NewsletterSelectionPage from './components/pages/NewsletterSelectionPage';
|
||||
import UnsubscribePage from './components/pages/UnsubscribePage';
|
||||
|
||||
/** List of all available pages in Portal, mapped to their UI component
|
||||
* Any new page added to portal needs to be mapped here
|
||||
|
@ -20,6 +21,7 @@ const Pages = {
|
|||
accountProfile: AccountProfilePage,
|
||||
accountEmail: AccountEmailPage,
|
||||
signupNewsletter: NewsletterSelectionPage,
|
||||
unsubscribe: UnsubscribePage,
|
||||
magiclink: MagicLinkPage,
|
||||
loading: LoadingPage,
|
||||
offer: OfferPage
|
||||
|
|
|
@ -192,6 +192,43 @@ function setupGhostApi({siteUrl = window.location.origin}) {
|
|||
});
|
||||
},
|
||||
|
||||
async newsletters({uuid}) {
|
||||
let url = endpointFor({type: 'members', resource: `member/newsletters`});
|
||||
url = url + `?uuid=${uuid}`;
|
||||
return makeRequest({
|
||||
url,
|
||||
credentials: 'same-origin'
|
||||
}).then(function (res) {
|
||||
if (!res.ok || res.status === 204) {
|
||||
return null;
|
||||
}
|
||||
return res.json();
|
||||
});
|
||||
},
|
||||
|
||||
async updateNewsletters({uuid, newsletters}) {
|
||||
let url = endpointFor({type: 'members', resource: `member/newsletters`});
|
||||
url = url + `?uuid=${uuid}`;
|
||||
const body = {
|
||||
newsletters
|
||||
};
|
||||
|
||||
return makeRequest({
|
||||
url,
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(body)
|
||||
}).then(function (res) {
|
||||
if (res.ok) {
|
||||
return res.json();
|
||||
} else {
|
||||
throw new Error('Failed to upadte newsletter preferences');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async updateEmailAddress({email}) {
|
||||
const identity = await api.member.identity();
|
||||
const url = endpointFor({type: 'members', resource: 'member/email'});
|
||||
|
|
Loading…
Add table
Reference in a new issue