Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ghostery Plus & Insights Promo Modals #464

Merged
merged 13 commits into from Oct 28, 2019
Merged
Changes from 1 commit
Commits
File filter
Filter file types
Jump to
Jump to file
Failed to load files.

Always

Just for now

Gh 1813/feature/in app plus promo upgrade (#463)

* Show plus promo modal on first view of Home view of each Hub session

* Stub out intro hub plus promo modal layout and implement its buttons

* Remove some unnecessary plus promo modal related CSS

* Continue adding styling for plus promo modal

* Continue styling plus promo modal

* Refactor plus promo modal CSS to improve alignment of elements

* Continue CSS work for plus promo modal

* Implement recommended gold banner in plus promo modal

* Update and tweak plus promo modal design

* Fix path bug in i18n-checker tool. Consolidate redundant entries in messages. Add check icon to plus promo modal. Modify plus promo modal css.

* Additional consolidation of redundant and unused entries in messages

* Factor plus promo modal rendering out to shared component. Implement rendering in panel. Implement display context CSS adjustments.

* Move Plus Promo modal rendering to a PlusPromoModal shared component. Begin to implement conditional panel display logic

* Create ModalPromos background class responsible for modal promo related business logic and state management

* Add local state to Panel to make sure component rerenders after user dismisses promo panel. Send record of promo interaction to background

* Make PromoModals code more robust

* Factor PlusPromoModal rendering out to helper in Panel. Extend conditional rendering logic to account for upgrade version of modal

* Mark PromoModals methods as static. Refine PlusPromoModal implementation and add debug code

* Add logic to hide plus promo modal from Insights subscribers. Cleanup and comments.

* Clean up. Finish string localization in PlusPromoModal

* Add gold ghostie badge svg. Begin fleshing out upgrade plus promo modal design

* Pull non-shared upgrade version of plus promo modal out of shared PlusPromoModal component and into Panel to simplify

* Flesh out upgrade promo modal UI and styles

* Implement panel to tab link for the upgrade to plus button

* Add no thanks link click handling

* Localization-proof upgrade modal CSS. Finalize UI and display logic.

* Implement ability to turn off promotions from inside plus upgrade promo modal
  • Loading branch information
wlycdgr committed Oct 24, 2019
commit e960dc7d91342d7624a3010134506a310921a7f5
@@ -2143,5 +2143,25 @@
},
"new_color_themes": {
"message": "New Color Themes"
},
"upgrade_cta_TEXT": {
"message": "Unlock historical tracker insights, priority support access, and new color themes by upgrading to Ghostery Plus for only $2 per month.",
"description": "Body text in a non-responsive modal. Character limit: 135."
},
"upgrade_your_ghostery_experience": {
"message": "Upgrade your Ghostery experience",
"description": "Header text for a non-responsive modal. Character limit: 40."
},
"upgrade_to_plus": {
"message": "Upgrade to Plus",
"description": "Button text in a non-responsive modal. Character limit: 35."
},
"no_thanks_turn_promos_off": {
"message": "No thanks, turn promos off",
"description": "Text link in a non-responsive modal. Character limit: 28."
},
"already_subscribed_sign_in": {
"message": "Already subscribed? Sign In",
"description": "Character limit: 28."
}
}
@@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg" width="177" height="94" viewBox="0 0 177 94">
<defs>
<linearGradient id="a" x1="80.357%" x2="35.84%" y1="34.91%" y2="70.747%">
<stop offset="0%" stop-color="#ECD868"/>
<stop offset="100%" stop-color="#D4AF37"/>
</linearGradient>
<linearGradient id="b" x1="78.916%" x2="78.916%" y1="17.554%" y2="79.445%">
<stop offset="0%" stop-color="#ECD868"/>
<stop offset="100%" stop-color="#D4AF37"/>
</linearGradient>
<linearGradient id="c" x1="87.172%" x2="12.828%" y1="0%" y2="100%">
<stop offset="0%" stop-color="#ECD868"/>
<stop offset="100%" stop-color="#D4AF37"/>
</linearGradient>
</defs>
<g fill="none" fill-rule="evenodd">
<path fill="#D4AF37" d="M137.254 85.351l-.647.646.647.647a.5.5 0 1 1-.708.706l-.647-.646-.647.646a.5.5 0 1 1-.707-.706l.647-.647-.647-.646a.5.5 0 0 1 .707-.707l.647.647.647-.647a.5.5 0 0 1 .708.707z"/>
<path fill="#D4AF37" fill-rule="nonzero" d="M158.953 46.619a.501.501 0 1 0 0-1.003 5.019 5.019 0 0 1-5.013-5.013.501.501 0 0 0-1.002 0 5.019 5.019 0 0 1-4.977 5.013h-.036a.501.501 0 1 0 0 1.003 5.019 5.019 0 0 1 5.013 5.012.501.501 0 0 0 1.002 0 5.019 5.019 0 0 1 4.977-5.012h.036zm-5.411 2.711a6.053 6.053 0 0 0-3.11-3.11c1.39-.61 2.5-1.72 3.11-3.11.61 1.39 1.72 2.5 3.11 3.11-1.39.61-2.5 1.72-3.11 3.11zM18.806 83.652a8.66 8.66 0 0 1-8.651-8.65.501.501 0 0 0-1.003 0 8.66 8.66 0 0 1-8.65 8.65.501.501 0 1 0 0 1.002 8.66 8.66 0 0 1 8.65 8.65.501.501 0 0 0 1.003 0 8.66 8.66 0 0 1 8.65-8.65.501.501 0 1 0 0-1.002zm-9.218 6.515a9.698 9.698 0 0 0-6.079-6.08 9.698 9.698 0 0 0 6.08-6.078 9.698 9.698 0 0 0 6.079 6.079 9.698 9.698 0 0 0-6.08 6.079zM118.85 19v2.005h2.006a.501.501 0 1 1 0 1.003h-2.006v2.005a.501.501 0 0 1-1.002 0v-2.005h-2.005a.501.501 0 1 1 0-1.003h2.005v-2.005a.501.501 0 0 1 1.002 0zm-74.543.694l1.15-1.15-1.15-1.15a.501.501 0 0 1 .709-.708l1.15 1.15 1.149-1.15a.501.501 0 0 1 .709.709l-1.15 1.15 1.15 1.149a.501.501 0 1 1-.71.708l-1.149-1.149-1.15 1.15a.501.501 0 1 1-.708-.71zm-41.4 43.317l-.649.648.649.648a.501.501 0 1 1-.71.709l-.647-.648-.649.648a.501.501 0 1 1-.708-.709l.648-.648-.648-.648a.501.501 0 0 1 .708-.709l.649.648.648-.648a.501.501 0 0 1 .709.709zm173.449 16.04l-.648.649.648.648a.501.501 0 1 1-.709.709l-.648-.648-.648.648a.501.501 0 1 1-.709-.709l.648-.648-.648-.648a.501.501 0 0 1 .709-.71l.648.65.648-.65a.501.501 0 0 1 .709.71zM43.303 88.36l-.648-.648-.648.648a.501.501 0 1 1-.709-.71l.648-.647-.648-.648a.501.501 0 0 1 .709-.71l.648.649.648-.648a.501.501 0 0 1 .71.709l-.649.648.648.648a.501.501 0 1 1-.709.709zM8.563 35.59v4.01h4.011a.501.501 0 1 1 0 1.002h-4.01v4.01a.501.501 0 0 1-1.003 0v-4.01h-4.01a.501.501 0 1 1 0-1.002h4.01v-4.01a.501.501 0 0 1 1.003 0zM141.91 3.008a.501.501 0 0 1-.501.5h-2.005v2.006a.501.501 0 0 1-1.003 0V3.509h-2.005a.501.501 0 1 1 0-1.003h2.005V.501a.501.501 0 0 1 1.003 0v2.005h2.005a.501.501 0 0 1 .501.502z"/>
<g fill-rule="nonzero">
<path fill="url(#a)" d="M106.777 18.884H85.743c-.204-1.486-.618-2.95-1.525-4.232-1.665-2.36-4.367-3.31-6.752-4.148-1.435-.504-2.79-.981-3.704-1.666-.893-.67-1.739-1.841-2.636-3.08-1.515-2.097-3.231-4.472-5.959-5.385-2.626-.878-5.319-.026-7.693.727-1.486.47-2.889.915-4.085.915-1.194 0-2.598-.444-4.084-.915-2.375-.753-5.07-1.605-7.693-.728-2.729.914-4.448 3.29-5.963 5.387-.894 1.238-1.74 2.408-2.632 3.078-.914.685-2.269 1.162-3.704 1.666-2.384.837-5.088 1.788-6.752 4.146-.907 1.283-1.32 2.748-1.525 4.233H0L6.872 37.75 0 56.615h21.035c.203 1.486.618 2.949 1.523 4.233 1.666 2.359 4.369 3.309 6.752 4.147 1.436.505 2.792.98 3.705 1.666.891.67 1.737 1.84 2.633 3.079 1.515 2.096 3.234 4.473 5.963 5.387 2.62.877 5.316.024 7.693-.727 1.486-.47 2.888-.915 4.084-.915 1.196 0 2.599.444 4.085.915 1.659.526 3.472 1.1 5.308 1.1.794 0 1.592-.108 2.384-.373 2.728-.913 4.445-3.288 5.96-5.385.896-1.239 1.742-2.41 2.636-3.08.914-.685 2.269-1.162 3.704-1.666 2.385-.838 5.088-1.788 6.752-4.148.907-1.284 1.32-2.747 1.523-4.232h21.037L99.905 37.75l6.872-18.866zM6.873 51.707l5.085-13.957-5.085-13.957h13.919c-.036 1.087-.118 2.094-.375 2.911-.33 1.049-1.136 2.22-1.987 3.46-1.476 2.145-3.15 4.58-3.15 7.586 0 3.008 1.674 5.44 3.15 7.586.851 1.24 1.657 2.411 1.987 3.46.257.817.34 1.824.375 2.911H6.872zm77.592-9.21c-1.021 1.487-2.079 3.023-2.633 4.783-.577 1.834-.607 3.766-.637 5.637-.032 2.038-.063 3.963-.825 5.044-.779 1.104-2.557 1.729-4.439 2.39-1.708.601-3.474 1.221-4.97 2.344-1.477 1.108-2.582 2.638-3.651 4.115-1.197 1.655-2.327 3.218-3.615 3.65-1.195.398-2.96-.16-4.823-.75-1.757-.557-3.574-1.132-5.484-1.132-1.909 0-3.726.575-5.482 1.131-1.869.591-3.632 1.151-4.826.75-1.286-.43-2.418-1.995-3.617-3.651-1.067-1.478-2.172-3.006-3.648-4.113-1.496-1.123-3.262-1.743-4.97-2.343-1.882-.662-3.66-1.287-4.44-2.39-.763-1.083-.793-3.009-.825-5.046-.028-1.87-.06-3.802-.636-5.635-.554-1.76-1.61-3.296-2.632-4.781-1.168-1.7-2.271-3.304-2.271-4.749 0-1.443 1.103-3.05 2.27-4.748 1.022-1.485 2.08-3.022 2.633-4.782.577-1.832.606-3.764.637-5.633.031-2.04.063-3.964.826-5.047.778-1.103 2.557-1.728 4.44-2.39 1.707-.6 3.472-1.221 4.968-2.344 1.476-1.107 2.58-2.636 3.648-4.112 1.199-1.657 2.33-3.221 3.617-3.652.28-.094.594-.134.931-.134 1.099 0 2.466.433 3.895.885 1.757.557 3.573 1.132 5.483 1.132 1.91 0 3.727-.575 5.484-1.132 1.869-.59 3.63-1.148 4.824-.75 1.286.431 2.417 1.996 3.614 3.65 1.069 1.478 2.174 3.007 3.652 4.114 1.496 1.122 3.261 1.743 4.97 2.344 1.881.662 3.66 1.287 4.438 2.39.765 1.083.795 3.007.826 5.045.028 1.869.059 3.802.637 5.634.554 1.761 1.61 3.298 2.632 4.784 1.168 1.698 2.271 3.303 2.271 4.746-.001 1.442-1.105 3.047-2.272 4.746zm15.439 9.21h-13.92c.038-1.087.118-2.094.376-2.911.33-1.049 1.135-2.22 1.988-3.46 1.475-2.146 3.148-4.58 3.148-7.586 0-3.007-1.673-5.439-3.148-7.584-.853-1.24-1.658-2.411-1.988-3.462-.258-.817-.34-1.824-.375-2.911h13.918L94.819 37.75l5.085 13.957z" transform="translate(35 9)"/>
<path fill="url(#b)" d="M53.389 12.117c-13.84 0-25.098 11.498-25.098 25.634 0 14.134 11.259 25.632 25.098 25.632 13.838 0 25.096-11.498 25.096-25.632 0-14.134-11.258-25.634-25.096-25.634zm0 47.075c-11.576 0-20.993-9.618-20.993-21.441 0-11.823 9.417-21.442 20.993-21.442 11.574 0 20.991 9.619 20.991 21.442S64.964 59.192 53.39 59.192z" transform="translate(35 9)"/>
</g>
<path fill="url(#c)" d="M64.058 45.76c-1.022-2.384-1.197-4.403-1.225-5.197v-6.53c0-4.99-4.005-9.033-8.945-9.033s-8.946 4.044-8.946 9.033v6.624c-.038.854-.239 2.812-1.22 5.104-1.321 3.078-.229 2.711.75 2.458.979-.252 3.164-1.24 3.847-.023.682 1.218 1.251 2.275 2.845 1.586 1.593-.69 2.344-.92 2.571-.92h.31c.227 0 .978.23 2.57.92 1.594.69 2.163-.368 2.846-1.586.682-1.218 2.868-.23 3.846.023.979.253 2.071.62.751-2.458M51.127 29.925c.963 0 1.745 1.248 1.745 2.788 0 1.54-.782 2.788-1.745 2.788-.964 0-1.745-1.248-1.745-2.788 0-1.54.781-2.788 1.745-2.788m2.761 11.144c-2.123 0-3.91-2.11-4.449-4.458 1.04 1.444 2.646 2.372 4.449 2.372s3.409-.928 4.449-2.372c-.539 2.348-2.326 4.458-4.449 4.458m2.761-5.568c-.964 0-1.745-1.248-1.745-2.788 0-1.54.781-2.788 1.745-2.788.964 0 1.745 1.248 1.745 2.788 0 1.54-.78 2.788-1.745 2.788" transform="translate(35 9)"/>
</g>
</svg>
@@ -19,10 +19,12 @@ import { handleClickOnNewTabLink } from '../../utils/msg';
* @memberof PanelBuildingBlocks
*/
const PanelToTabLink = (props) => {
const { href, children } = props;
const { className, href, children } = props;

const classes = className || '';

return (
<a href={href} onClick={handleClickOnNewTabLink}>{children}</a>
<a className={classes} href={href} onClick={handleClickOnNewTabLink}>{children}</a>
);
};

@@ -14,6 +14,7 @@
import React from 'react';
import ClassNames from 'classnames';
import Header from '../containers/HeaderContainer';
import PanelToTabLink from './BuildingBlocks/PanelToTabLink';
import { PlusPromoModal, Modal } from '../../shared-components';
import InsightsPromoModal from '../containers/InsightsPromoModalContainer';
import { DynamicUIPortContext } from '../contexts/DynamicUIPortContext';
@@ -208,21 +209,60 @@ class Panel extends React.Component {
});
}

_renderPlusPromoUpgradeModal() {
_handleNoThanksClick = () => {
sendMessage('promoModals.sawPlusPromo', {});
sendMessage('promoModals.turnOffPromos', {});
this.setState({
plusPromoModalShown: true
});
}

_handleSubscriberSignInClick = () => {
sendMessage('promoModals.sawPlusPromo', {});
this.setState({
plusPromoModalShown: true
});
this.props.history.push('/login');
}

_renderPlusPromoUpgradeModal(signedIn) {
const contentClassNames = ClassNames(
'PlusPromoModal__content',
'flex-container',
'flex-dir-column',
'align-middle',
'panel'
'panel',
'upgrade'
);

return (
<Modal show>
<div className={contentClassNames}>
<div className="PlusPromoModal__thanks-for-download">[Upgrade version of the Plus Promo modal]</div>
<div className="PlusPromoModal__button basic button" onClick={this._handlePlusPromoModalClicks}>
<span>Dismiss</span>
<div className="PlusPromoModal__buttons-background upgrade" />
<img className="PlusPromoModal__gold-ghostie-badge" src="/app/images/hub/home/gold-ghostie-badge.svg" />
<div className="PlusPromoModal__header">
{t('upgrade_your_ghostery_experience')}
</div>
<div className="PlusPromoModal__description cta">
{t('upgrade_cta_TEXT')}
</div>
<div className="PlusPromoModal__button-container" onClick={this._handlePlusPromoModalClicks}>
<PanelToTabLink className="PlusPromoModal__button upgrade" href="http://signon.ghostery.com/en/subscribe/">
<span className="button-text">{t('upgrade_to_plus')}</span>
</PanelToTabLink>
</div>
<div className="PlusPromoModal__text-link-container">
{
!signedIn &&
(
<div onClick={this._handleSubscriberSignInClick} className="PlusPromoModal__text-link">
{t('already_subscribed_sign_in')}
</div>
)
}
<div onClick={this._handleNoThanksClick} className="PlusPromoModal__text-link">
{t('no_thanks_turn_promos_off')}
</div>
</div>
</div>
</Modal>
@@ -233,12 +273,17 @@ class Panel extends React.Component {
const { plusPromoModalShown } = this.state;
const { account, haveSeenInitialPlusPromo, isTimeForAPlusPromo } = this.props;

if (account && account.user && account.user.subscriptionsPlus) return null; // don't show the promo to Plus subscribers!
if (account && account.user && account.user.scopes && account.user.scopes.includes('subscriptions:insights')) return null; // don't show the promo to Insights subscribers, either

// The business logic that controls how often promo modals should be shown lives in src/classes/PromoModals
if (plusPromoModalShown || !isTimeForAPlusPromo) return null;

if (haveSeenInitialPlusPromo) { return this._renderPlusPromoUpgradeModal(); }
// Check account status
const signedIn = account && account.user;
const plusSubscriber = signedIn && account.user.subscriptionsPlus;
const insightsSubscriber = signedIn && account.user.scopes && account.user.scopes.includes('subscriptions:insights');

if (plusSubscriber || insightsSubscriber) return null;

if (haveSeenInitialPlusPromo) { return this._renderPlusPromoUpgradeModal(signedIn); }

return (
<PlusPromoModal
@@ -44,7 +44,7 @@ const Subscribe = (props) => {
</div>
{(loggedIn === 'false') && (
<NavLink to="/login" className="pitch-already-subscriber">
<span>{t('subscribe_pitch_sign_here')}</span>
<span>{t('already_subscribed_sign_in')}</span>
</NavLink>
)}
</div>
@@ -31,6 +31,7 @@ function _renderInitialVersion(props) {
'flex-container',
'flex-dir-column',
'align-middle',
'initial',
locationClassName
);
const optionsContainerClassNames = ClassNames(
@@ -55,7 +56,7 @@ function _renderInitialVersion(props) {
return (
<Modal show={show}>
<div className={contentClassNames}>
<div className="PlusPromoModal__buttons-background" />
<div className="PlusPromoModal__buttons-background initial" />
{isInHub && (
<div className="PlusPromoModal__thanks-for-download">
{t('thanks_for_downloading_ghostery')}
@@ -81,7 +82,7 @@ function _renderInitialVersion(props) {
<div className="PlusPromoModal__option-description-item">{t('fast_browsing')}</div>
</div>
</div>
<div className="PlusPromoModal__button basic button" onClick={clickHandler}>
<div className="PlusPromoModal__button basic" onClick={clickHandler}>
<span>{t('select_basic')}</span>
</div>
</div>
@@ -114,7 +115,7 @@ function _renderInitialVersion(props) {
</div>
</div>
</div>
<a href="http://signon.ghostery.com/en/subscribe/" target="_blank" rel="noopener noreferrer" className="PlusPromoModal__button plus button" onClick={clickHandler}>
<a href="http://signon.ghostery.com/en/subscribe/" target="_blank" rel="noopener noreferrer" className="PlusPromoModal__button plus" onClick={clickHandler}>
<span>{t('select_plus')}</span>
</a>
</div>
@@ -1,17 +1,47 @@
$standard-font-family: Roboto, "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
This conversation was marked as resolved by christophertino

This comment has been minimized.

@christophertino

christophertino Oct 26, 2019
Member

Add a document header / license notice

This comment has been minimized.

@wlycdgr

wlycdgr Oct 28, 2019
Author Member

Done


.PlusPromoModal__gold-ghostie-badge {
margin-top: 21px;
margin-bottom: 16px;
}

.PlusPromoModal__header {
margin-bottom: 10px;
font-size: 20px;
font-weight: bold;
font-family: $standard-font-family;
}

.PlusPromoModal__description {
margin-bottom: 5px;
width: 350px;
text-align: center;
font-size: 18px;
font-weight: 500;
font-family: $standard-font-family
}

.PlusPromoModal__content {
position: relative;
background-color: #f7f7f7;
border: 1.9px solid #930194;
z-index: 10;

&.in-hub {
width: 646px;
height: 553px;
&.initial {
&.in-hub {
width: 646px;
height: 553px;
}

&.in-panel {
width: 556px;
height: 471px;
}
}

&.in-panel {
width: 556px;
height: 471px;
&.upgrade {
width: 434px;
height: 410px;
}
}

@@ -22,6 +52,9 @@
width: 99%;
height: 72px;
z-index: -1;

&.initial { height: 72px; }
&.upgrade { height: 107px; }
}

.PlusPromoModal__thanks-for-download {
@@ -162,39 +195,86 @@
justify-content: center;
}

.PlusPromoModal__button-container {
width: 100%;
display: flex;
justify-content: center;
position: absolute;
bottom: 50px;
}

.PlusPromoModal__button {
margin: 0;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: none;
align-items: center;
font-weight: bold;
font-family: $standard-font-family;
letter-spacing: 0.5px;
text-transform: uppercase;
cursor: pointer;
transition: background-color 0.25s ease-out, color 0.25s ease-out;
-webkit-appearance: none;

&.basic {
font-size: 11px;
width: 135px;
height: 40px;
border: solid 2px #15b4f2;
background-color: white;
color: #2cbcf4;
box-shadow: none;
}
&.basic:hover {
background-color: #2cbcf4;
color: white;
}

&.plus {
font-size: 11px;
width: 163px;
height: 38px;
border: none;
background-image: linear-gradient(to bottom, #2fdbfa, #15b4f2);
color: white;
font-weight: 600;
letter-spacing: 0.5px;
box-shadow: none;
}
&.plus:hover {
background-image: linear-gradient(to bottom, #1fcbea, #05a4e2);
}

&.upgrade {
font-size: 13px;
height: 36px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.24), 0 0 2px 0 rgba(0, 0, 0, 0.12);
background-color: #1dafed;
color: white;
}
&.upgrade:hover {
background-color: #0698d6;
}

.button-text {
padding: 0px 20px 0px;
color: white;
text-decoration: none;
}
}

.PlusPromoModal__text-link-container {
position: absolute;
bottom: 10px;
width: 100%;
display: flex;
justify-content: space-evenly;
}

.PlusPromoModal__text-link {
margin-left: 10px;
margin-right: 10px;
font-size: 15px;
font-family: $standard-font-family;
color: #4a4a4a;
text-decoration: underline;
cursor: pointer;
}
@@ -1086,6 +1086,10 @@ function onMessageHandler(request, sender, callback) {
promoModals.recordInsightsPromoSighting();
return true;
This conversation was marked as resolved by christophertino

This comment has been minimized.

@christophertino

christophertino Oct 26, 2019
Member

return false

This comment has been minimized.

@wlycdgr

wlycdgr Oct 28, 2019
Author Member

Done

}
if (name === 'promoModals.turnOffPromos') {
promoModals.turnOffPromos();
return true;
This conversation was marked as resolved by christophertino

This comment has been minimized.

@christophertino

christophertino Oct 26, 2019
Member

return false

This comment has been minimized.

@wlycdgr

wlycdgr Oct 28, 2019
Author Member

Done

}
}

/**
@@ -41,6 +41,8 @@ class PromoModals {

static recordInsightsPromoSighting() { this._recordPromoSighting(INSIGHTS); }

static turnOffPromos() { conf.notify_promotions = false; }

static _isTimeForAPromo(type) {
if (conf.notify_promotions === false) { return false; }

ProTip! Use n and p to navigate between commits in a pull request.