From c2f3ffaca7c6b5bb485e941ef62dc704710d31c1 Mon Sep 17 00:00:00 2001 From: Steve Larson <9larsons@gmail.com> Date: Wed, 28 Feb 2024 13:57:19 -0600 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Prevented=20newsletter=20subscri?= =?UTF-8?q?ptions=20from=20getting=20out=20of=20sync=20in=20Portal=20(#197?= =?UTF-8?q?68)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refs https://linear.app/tryghost/issue/ENG-677 - UnsubscribePage is intended to be able to be used without logging in to Portal. The app context (member state) was not synchronized when logged in, causing conflicts in the client data vs. database. - Now when a logged in member is found, the member object is manually updated to reflect the API response(s). --- .../src/components/pages/UnsubscribePage.js | 80 +++++++++++-------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/apps/portal/src/components/pages/UnsubscribePage.js b/apps/portal/src/components/pages/UnsubscribePage.js index b9b6863cf4..8b4e467d25 100644 --- a/apps/portal/src/components/pages/UnsubscribePage.js +++ b/apps/portal/src/components/pages/UnsubscribePage.js @@ -41,9 +41,12 @@ async function updateMemberNewsletters({api, memberUuid, newsletters, enableComm } } +// NOTE: This modal is available even if not logged in, but because it's possible to also be logged in while making modifications, +// we need to update the member data in the context if logged in. export default function UnsubscribePage() { - const {site, pageData, onAction, t} = useContext(AppContext); + const {site, pageData, member: loggedInMember, onAction, t} = useContext(AppContext); const api = setupGhostApi({siteUrl: site.url}); + // member is the member data fetched from the API based on the uuid and its state is limited to just this modal, not all of Portal const [member, setMember] = useState(); const [loading, setLoading] = useState(true); const siteNewsletters = getSiteNewsletters({site}); @@ -56,6 +59,39 @@ export default function UnsubscribePage() { const {comments_enabled: commentsEnabled} = site; const {enable_comment_notifications: enableCommentNotifications = false} = member || {}; + const updateNewsletters = async (newsletters) => { + const updatedData = await updateMemberNewsletters({api, memberUuid: pageData.uuid, newsletters}); + setSubscribedNewsletters(updatedData.newsletters); + // Keep the member data in sync if logged in + if (loggedInMember) { + loggedInMember.newsletters = updatedData.newsletters; + } + }; + + const updateCommentNotifications = async (enabled) => { + const updatedData = await updateMemberNewsletters({api, memberUuid: pageData.uuid, enableCommentNotifications: enabled}); + setMember(updatedData); + // Keep the member data in sync if logged in + if (loggedInMember) { + loggedInMember.enable_comment_notifications = enabled; + } + }; + + const unsubscribeAll = async () => { + setSubscribedNewsletters([]); + onAction('showPopupNotification', { + action: 'updated:success', + message: t(`Unsubscribed from all emails.`) + }); + const updatedMember = await api.member.updateNewsletters({uuid: pageData.uuid, newsletters: [], enableCommentNotifications: false}); + setMember(updatedMember); + if (loggedInMember) { + loggedInMember.newsletters = []; + loggedInMember.enable_comment_notifications = false; + } + }; + + // This handles the url query param actions that ultimately launch this component/modal useEffect(() => { const ghostApi = setupGhostApi({siteUrl: site.url}); (async () => { @@ -80,31 +116,15 @@ export default function UnsubscribePage() { setSubscribedNewsletters(memberNewsletters); if (siteNewsletters?.length === 1 && !commentsEnabled && !pageData.newsletterUuid) { // Unsubscribe from all the newsletters, because we only have one - const updatedData = await updateMemberNewsletters({ - api: ghostApi, - memberUuid: pageData.uuid, - newsletters: [] - }); - setSubscribedNewsletters(updatedData.newsletters); + await updateNewsletters([]); } else if (pageData.newsletterUuid) { // Unsubscribe link for a specific newsletter - const updatedData = await updateMemberNewsletters({ - api: ghostApi, - memberUuid: pageData.uuid, - newsletters: memberNewsletters?.filter((d) => { - return d.uuid !== pageData.newsletterUuid; - }) - }); - setSubscribedNewsletters(updatedData.newsletters); + await updateNewsletters(memberNewsletters?.filter((d) => { + return d.uuid !== pageData.newsletterUuid; + })); } else if (pageData.comments && commentsEnabled) { // Unsubscribe link for comments - const updatedData = await updateMemberNewsletters({ - api: ghostApi, - memberUuid: pageData.uuid, - enableCommentNotifications: false - }); - - setMember(updatedData); + await updateCommentNotifications(false); } })(); }, [commentsEnabled, pageData.uuid, pageData.newsletterUuid, pageData.comments, site.url, siteNewsletters?.length]); @@ -226,23 +246,13 @@ export default function UnsubscribePage() { notification={HeaderNotification} subscribedNewsletters={subscribedNewsletters} updateSubscribedNewsletters={async (newsletters) => { - setSubscribedNewsletters(newsletters); + await updateNewsletters(newsletters); setHasInteracted(true); - await api.member.updateNewsletters({uuid: pageData.uuid, newsletters}); - }} - updateCommentNotifications={async (enabled) => { - const updatedMember = await api.member.updateNewsletters({uuid: pageData.uuid, enableCommentNotifications: enabled}); - setMember(updatedMember); }} + updateCommentNotifications={updateCommentNotifications} unsubscribeAll={async () => { + await unsubscribeAll(); setHasInteracted(true); - setSubscribedNewsletters([]); - onAction('showPopupNotification', { - action: 'updated:success', - message: t(`Unsubscribed from all emails.`) - }); - const updatedMember = await api.member.updateNewsletters({uuid: pageData.uuid, newsletters: [], enableCommentNotifications: false}); - setMember(updatedMember); }} isPaidMember={member?.status !== 'free'} isCommentsEnabled={commentsEnabled !== 'off'}