0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-04-01 02:41:39 -05:00

Adds brand new FAQs and links to Portal based on support documents

- Adds links and new modals for two new FAQs
- Has some checks for suppressions based on member property email_suppressions
- Using different technique for back buttons to simulate bigger stack

refs https://github.com/TryGhost/Team/issues/2348
This commit is contained in:
James Morris 2022-12-01 16:32:34 +00:00
parent 6b7d313415
commit 6b4b7f207f
14 changed files with 186 additions and 19 deletions

View file

@ -20,6 +20,7 @@ import {OfferPageStyles} from './pages/OfferPage';
import {FeedbackPageStyles} from './pages/FeedbackPage';
import EmailSuppressedPage from '!!raw-loader!./pages/EmailSuppressedPage.css';
import EmailSuppressionFAQ from '!!raw-loader!./pages/EmailSuppressionFAQ.css';
import EmailReceivingFAQ from '!!raw-loader!./pages/EmailReceivingFAQ.css';
// Global styles
const FrameStyles = `
@ -322,6 +323,12 @@ const FrameStyles = `
z-index: 9999;
}
.gh-portal-popup-container.large-size {
width: 720px;
justify-content: flex-start;
padding: 0;
}
.gh-portal-popup-container.full-size {
width: 100vw;
min-height: 100vh;
@ -499,6 +506,12 @@ const FrameStyles = `
margin-bottom: 20px;
}
.gh-portal-section.flex {
display: flex;
flex-direction: column;
gap: 2rem;
}
.gh-portal-detail-header {
position: relative;
display: flex;
@ -515,6 +528,8 @@ const FrameStyles = `
display: flex;
align-items: center;
justify-content: space-between;
flex-direction: column;
gap: 2rem;
}
.gh-portal-list-header {
@ -771,10 +786,14 @@ const FrameStyles = `
const MobileStyles = `
@media (max-width: 1440px) {
.gh-portal-popup-container:not(.full-size):not(.preview) {
.gh-portal-popup-container:not(.full-size):not(.large-size):not(.preview) {
width: 460px;
}
.gh-portal-popup-container.large-size {
width: 600px;
}
.gh-portal-input {
height: 42px;
margin-bottom: 16px;
@ -857,6 +876,10 @@ const MobileStyles = `
justify-content: flex-start;
}
.gh-portal-popup-container.large-size {
padding: 0 !important;
}
.gh-portal-popup-wrapper.account-home,
.gh-portal-popup-container.account-home {
background: var(--grey13);
@ -1177,6 +1200,7 @@ export function getFrameStyles({site}) {
MultipleProductsGlobalStyles +
FeedbackPageStyles +
EmailSuppressedPage +
EmailSuppressionFAQ;
EmailSuppressionFAQ +
EmailReceivingFAQ;
return FrameStyle;
}

View file

@ -20,6 +20,7 @@ export const GlobalStyles = `
--grey14: #fbfbfb;
--white: #fff;
--red: #f02525;
--darkerRed: #C50202;
--yellow: #FFDC15;
--green: #7FC724;
}
@ -77,6 +78,18 @@ export const GlobalStyles = `
letter-spacing: -0.019em;
}
h4 {
font-size: 19px;
font-weight: 700;
letter-spacing: -0.02em;
}
h5 {
font-size: 15px;
font-weight: 700;
letter-spacing: -0.02em;
}
p {
font-size: 15px;
line-height: 1.5em;
@ -107,6 +120,31 @@ export const GlobalStyles = `
line-height: 1.5em;
}
.gh-longform {
padding: 6vmin 10vmin 4vmin 6vmin;
}
.gh-longform h3 {
margin-top: 0;
margin-bottom: 1em;
}
.gh-longform h4 {
margin-top: 2.5em;
margin-bottom: 1.25em;
}
.gh-longform h5 {
margin-top: 0;
margin-bottom: 0.5em;
}
.gh-longform .gh-portal-btn {
width: calc(100% + 4vmin);
margin-top: 4rem;
margin-right: -4vmin;
}
@media (max-width: 1440px) {
h1 {
font-size: 32px;
@ -139,6 +177,14 @@ export const GlobalStyles = `
font-size: 24px;
letter-spacing: -0.019em;
}
.gh-longform {
padding: 10vmin 12vmin 10vmin 10vmin;
}
.gh-mobile-shortener {
display: none;
}
}
`;

View file

@ -185,6 +185,10 @@ class PopupContent extends React.Component {
}
}
if (page === 'emailSuppressionFAQ' || page === 'emailReceivingFAQ') {
pageClass += ' large-size';
}
let className = 'gh-portal-popup-container';
if (hasMode(['preview'])) {

View file

@ -29,6 +29,16 @@ export const ActionButtonStyles = `
border-color: var(--red);
}
.gh-feature-suppressions .gh-portal-btn-destructive:not(:disabled) {
color: var(--red);
border-color: var(--red);
}
.gh-feature-suppressions .gh-portal-btn-destructive:not(:disabled):hover {
color: var(--darkerRed);
border-color: var(--darkerRed);
}
.gh-portal-btn-text {
padding: 0;
font-weight: 500;

View file

@ -3,7 +3,7 @@ import CloseButton from '../common/CloseButton';
import BackButton from '../common/BackButton';
import {useContext, useState} from 'react';
import Switch from '../common/Switch';
import {getSiteNewsletters} from '../../utils/helpers';
import {getSiteNewsletters, hasMemberGotEmailSuppression} from '../../utils/helpers';
import ActionButton from '../common/ActionButton';
import {ReactComponent as CheckmarkIcon} from '../../images/icons/check-circle.svg';
@ -151,7 +151,7 @@ export default function NewsletterManagement({
isCommentsEnabled,
enableCommentNotifications
}) {
const {brandColor, site} = useContext(AppContext);
const {brandColor, onAction, member, site} = useContext(AppContext);
const isDisabled = !subscribedNewsletters?.length && ((isCommentsEnabled && !enableCommentNotifications) || !isCommentsEnabled);
const EmptyNotification = () => {
return null;
@ -162,7 +162,7 @@ export default function NewsletterManagement({
<CloseButton />
<AccountHeader />
<FinalNotification />
<div className='gh-portal-section'>
<div className='gh-portal-section flex'>
<div className='gh-portal-list'>
<NewsletterPrefs
subscribedNewsletters={subscribedNewsletters}
@ -182,7 +182,7 @@ export default function NewsletterManagement({
/>
</div>
</div>
<footer className='gh-portal-action-footer'>
<footer className={'gh-portal-action-footer' + (hasMemberGotEmailSuppression({member}) ? ' gh-feature-suppressions' : '')}>
<div style={{width: '100%'}}>
<ActionButton
isRunning={false}
@ -198,6 +198,13 @@ export default function NewsletterManagement({
/>
<ShowPaidMemberMessage isPaid={isPaidMember} site={site} />
</div>
{hasMemberGotEmailSuppression({member}) && !isDisabled &&
<button
className='gh-portal-btn-text gh-email-faq-page-button'
onClick={() => onAction('switchPage', {page: 'emailReceivingFAQ'})}
>
Not receiving emails? Learn more
</button>}
</footer>
</div>
);

View file

@ -73,6 +73,7 @@ footer.gh-portal-account-footer {
color: var(--red);
font-weight: 500;
font-size: 1.25rem;
letter-spacing: 0.2px;
}
.gh-portal-email-notice-icon {

View file

@ -17,7 +17,7 @@ function EmailPreferencesAction() {
? (
<p className="gh-portal-email-notice">
<EmailDeliveryFailedIcon className="gh-portal-email-notice-icon" />
You're currently not receiving emails
<span>You're <span className="gh-mobile-shortener">currently </span>not receiving emails</span>
</p>
)
: <p>Update your preferences</p>

View file

@ -0,0 +1,3 @@
.gh-email-receiving-faq {
}

View file

@ -0,0 +1,43 @@
import AppContext from 'AppContext';
import {useContext} from 'react';
import BackButton from 'components/common/BackButton';
import CloseButton from 'components/common/CloseButton';
import {getSupportAddress} from 'utils/helpers';
export default function EmailReceivingPage() {
const {brandColor, onAction, site} = useContext(AppContext);
const supportAddress = `mailto:${getSupportAddress({site})}`;
return (
<div className="gh-email-receiving-faq">
<header className='gh-portal-detail-header'>
<BackButton brandColor={brandColor} onClick={() => {
onAction('switchPage', {page: 'accountEmail', lastPage: 'accountHome'});
}} />
<CloseButton />
</header>
<div class="gh-longform">
<h3>Help! I'm not receiving subscription emails</h3>
<p>If you're not receiving the newsletter, the first course of action is to check your account settings to ensure your email address is correct and your email preferences are set to subscribed.</p>
<h4>Check the email on your account</h4>
<p>If you need to correct a typo in your email address or change the email address associated with your membership, click <b>Edit</b> next to your address when viewing your account settings.</p>
<p>Once saved, you'll need to confirm the change by clicking the confirmation link, which will arrive via email to your new email address's inbox.</p>
<h4>Check spam & promotional folders</h4>
<p>Once the email has been confirmed, the next course of action is to check your spam folder, and any promotional folders, set up by your mail provider.</p>
<h5>Spam</h5>
<p>If the newsletter landed in spam, you can mark the email as 'not spam' within your mail client this should ensure that future newsletters arrive in your inbox going forward.</p>
<h5>Promotional folder</h5>
<p>If the newsletter arrived in your promotional folder, move the email to your main inbox. This should teach your mail client to place the newsletter in your primary inbox going forward.</p>
<h5>Add to Contacts</h5>
<p>To help avoid future emails arriving in spam or promotional folders, you can also add the newsletter 'From' email address to your contact list.</p>
<h4>Check with your mail provider</h4>
<p>For those with corporate-based email addresses, you may need to reach out to your help desk or IT department to troubleshoot any potential reasons that may prevent you from receiving email newsletters.</p>
<p><a className='gh-portal-btn gh-portal-btn-branded' href={supportAddress} onClick={() => {
supportAddress && window.open(supportAddress);
}}>Need more help? Contact support</a></p>
</div>
</div>
);
}

View file

@ -15,10 +15,15 @@
color: var(--grey6);
}
.gh-email-suppressed-page-button {
color: var(--grey3);
.gh-email-faq-page-button {
color: var(--grey6);
cursor: pointer;
font-size: 1.5rem;
text-decoration: underline;
background: none;
transition: color linear 100ms;
}
.gh-email-faq-page-button:hover {
color: var(--grey5);
}

View file

@ -44,8 +44,8 @@ export default function EmailSuppressedPage() {
</p>
<button
className="gh-portal-btn-text gh-email-suppressed-page-button"
onClick={() => onAction('switchPage', {page: 'emailSuppressionFAQ'})}
className="gh-portal-btn-text gh-email-faq-page-button"
onClick={() => onAction('switchPage', {page: 'emailSuppressionFAQ', lastPage: 'emailSuppressed'})}
>
Learn more about why this happens
</button>

View file

@ -1,22 +1,37 @@
import AppContext from 'AppContext';
import {useContext} from 'react';
import BackButton from 'components/common/BackButton';
import CloseButton from 'components/common/CloseButton';
import {getSupportAddress} from 'utils/helpers';
export default function EmailSuppressedPage() {
const {onAction} = useContext(AppContext);
const {brandColor, onAction, site} = useContext(AppContext);
const onClose = () => {
onAction('switchPage', {page: 'emailSuppressed', lastPage: 'accountHome'});
};
const supportAddress = `mailto:${getSupportAddress({site})}`;
return (
<div className="gh-email-suppression-faq">
<header className='gh-portal-detail-header'>
<CloseButton onClick={onClose} />
<BackButton brandColor={brandColor} onClick={() => {
onAction('switchPage', {page: 'emailSuppressed', lastPage: 'accountHome'});
}} />
<CloseButton />
</header>
<div>
<h3>Why is my email disabled?</h3>
<div class="gh-longform">
<h3>Why has my email been disabled?</h3>
<p>Newsletters can be disabled on your account for two reasons: A previous email was marked as spam, or attempting to send an email resulted in a permanent failure (bounce).</p>
<h4>Spam complaints</h4>
<p>If a newsletter is flagged as spam, emails are automatically disabled for that address to make sure you no longer receive any unwanted messages.</p>
<p>If the spam complaint was accidental, or you would like to begin receiving emails again, you can resubscribe to emails by clicking the button on the previous screen.</p>
<p>Once resubscribed, if you still don't see emails in your inbox, check your spam folder. Some inbox providers keep a record of previous spam complaints and will continue to flag emails. If this happens, mark the latest newsletter as 'Not spam' to move it back to your primary inbox.</p>
<h4>Permanent failure (bounce)</h4>
<p>When an inbox fails to accept an email it is commonly called a bounce. In many cases, this can be temporary. However, in some cases, a bounced email can be returned as a permanent failure when an email address is invalid or non-existent.</p>
<p>In the event a permanent failure is received when attempting to send a newsletter, emails will be disabled on the account.</p>
<p>If you would like to start receiving emails again, the best next steps are to check your email address on file for any issues and then click resubscribe on the previous screen.</p>
<p><a className='gh-portal-btn gh-portal-btn-branded' href={supportAddress} onClick={() => {
supportAddress && window.open(supportAddress);
}}>Need more help? Contact support</a></p>
</div>
</div>
);

View file

@ -12,6 +12,7 @@ import UnsubscribePage from './components/pages/UnsubscribePage';
import FeedbackPage from './components/pages/FeedbackPage';
import EmailSuppressedPage from './components/pages/EmailSuppressedPage';
import EmailSuppressionFAQ from './components/pages/EmailSuppressionFAQ';
import EmailReceivingFAQ from './components/pages/EmailReceivingFAQ';
/** List of all available pages in Portal, mapped to their UI component
* Any new page added to portal needs to be mapped here
@ -30,7 +31,8 @@ const Pages = {
offer: OfferPage,
feedback: FeedbackPage,
emailSuppressed: EmailSuppressedPage,
emailSuppressionFAQ: EmailSuppressionFAQ
emailSuppressionFAQ: EmailSuppressionFAQ,
emailReceivingFAQ: EmailReceivingFAQ
};
/** Return page if valid, fallback to signup */

View file

@ -657,6 +657,13 @@ export const getMemberEmail = ({member}) => {
return member.email;
};
export const hasMemberGotEmailSuppression = ({member}) => {
if (!member) {
return '';
}
return member.email_suppression;
};
export const getFirstpromoterId = ({site}) => {
return (site && site.firstpromoter_account);
};