From d7a2d6de51d0dde4b4bcb38e5fb96c0a5d32a4df Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 5 Mar 2020 17:04:30 -0500 Subject: [PATCH 01/15] Design spring-plus promo modal --- _locales/en/messages.json | 3 + app/images/panel/spring-plus-logo.svg | 1 + .../BuildingBlocks/ModalExitButton.jsx | 18 +- app/panel/components/Panel.jsx | 57 ++++-- app/panel/components/PlusPromoModal.jsx | 94 ++++++++++ app/scss/panel.scss | 1 + app/scss/partials/_colors.scss | 3 + app/scss/partials/_modal_exit_button.scss | 7 +- app/scss/partials/_plus_promo_modal.scss | 175 ++++++++++++++++++ src/background.js | 4 + src/classes/PromoModals.js | 14 +- 11 files changed, 354 insertions(+), 23 deletions(-) create mode 100644 app/images/panel/spring-plus-logo.svg create mode 100644 app/panel/components/PlusPromoModal.jsx create mode 100644 app/scss/partials/_plus_promo_modal.scss diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 4a850e047..616a57ece 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -2349,6 +2349,9 @@ "seven_day_free_trial": { "message": "7 Day Free Trial ($14/mo)" }, + "spring_has_sprung": { + "message": "Spring has sprung!" + }, "full_coverage_protection_promise": { "message": "Get full-coverage protection across all browsers & apps on your device" }, diff --git a/app/images/panel/spring-plus-logo.svg b/app/images/panel/spring-plus-logo.svg new file mode 100644 index 000000000..0234c7c5b --- /dev/null +++ b/app/images/panel/spring-plus-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/panel/components/BuildingBlocks/ModalExitButton.jsx b/app/panel/components/BuildingBlocks/ModalExitButton.jsx index 92dad90e3..f0984837d 100644 --- a/app/panel/components/BuildingBlocks/ModalExitButton.jsx +++ b/app/panel/components/BuildingBlocks/ModalExitButton.jsx @@ -14,6 +14,7 @@ import React from 'react'; import PropTypes from 'prop-types'; +import ClassNames from 'classnames'; /** * A Functional React component for a Exit Button @@ -22,11 +23,16 @@ import PropTypes from 'prop-types'; */ const ModalExitButton = (props) => { const { - toggleModal + toggleModal, + border } = props; + const borderClassNames = ClassNames('ModalExitButton__exit flex-container align-middle', { + 'spring-green': border === 'spring-green' + }); + return ( - ); @@ -34,7 +40,13 @@ const ModalExitButton = (props) => { // PropTypes ensure we pass required props of the correct type ModalExitButton.propTypes = { - toggleModal: PropTypes.func.isRequired + toggleModal: PropTypes.func.isRequired, + border: PropTypes.string, +}; + +// Default props used in the App +ModalExitButton.defaultProps = { + border: 'grey' }; export default ModalExitButton; diff --git a/app/panel/components/Panel.jsx b/app/panel/components/Panel.jsx index c13fbb5da..88a60a3ad 100644 --- a/app/panel/components/Panel.jsx +++ b/app/panel/components/Panel.jsx @@ -16,6 +16,7 @@ import { NavLink } from 'react-router-dom'; import Header from '../containers/HeaderContainer'; import { PremiumPromoModal } from '../../shared-components'; import InsightsPromoModal from './InsightsPromoModal'; +import PlusPromoModal from './PlusPromoModal'; import { DynamicUIPortContext } from '../contexts/DynamicUIPortContext'; import { sendMessage } from '../utils/msg'; import { setTheme } from '../utils/utils'; @@ -373,29 +374,57 @@ class Panel extends React.Component { ); } + /** + * @returns {null|JSX} + * @private + * Renders the Insights promo modal if the user is not already an Insights subscriber + */ + _renderPlusPromoModal = () => { + // if (this._plusSubscriber()) return null; + if (this._plusSubscriber()) { + console.log('test'); + } + + // sendMessage('promoModals.sawPlusPromo', {}); + + // sendMessage('ping', 'promo_modals_show_plus'); + + return ( + + ); + } + + _renderPromoModal = () => this._renderPlusPromoModal(); + /** * @returns {null|JSX} * @private * Renders either the Insights or the Premium promo modal */ - _renderPromoModal = () => { - const { - promoModal, - isPromoModalHidden, - } = this.props; + // _renderPromoModal = () => { + // const { + // promoModal, + // isPromoModalHidden, + // } = this.props; - if (isPromoModalHidden) return null; + // if (isPromoModalHidden) return null; - if (promoModal === 'insights') { - return this._renderInsightsPromoModal(); - } + // if (promoModal === 'insights') { + // return this._renderInsightsPromoModal(); + // } - if (promoModal === 'premium') { - return this._renderPremiumPromoModal(); - } + // if (promoModal === 'premium') { + // return this._renderPremiumPromoModal(); + // } - return null; - } + // return null; + // } /** * React's required render function. Returns JSX diff --git a/app/panel/components/PlusPromoModal.jsx b/app/panel/components/PlusPromoModal.jsx new file mode 100644 index 000000000..976066a43 --- /dev/null +++ b/app/panel/components/PlusPromoModal.jsx @@ -0,0 +1,94 @@ +/** + * PlusPromo Modal Component + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2019 Ghostery, Inc. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import ClassNames from 'classnames'; +import Modal from '../../shared-components/Modal'; +import ModalExitButton from './BuildingBlocks/ModalExitButton'; + +/** + * A functional React component for a PlusPromo Modal that may be displayed in the Hub and/or Panel + * @return {JSX} JSX for rendering a PlusPromo Modal + * @memberof SharedComponents + */ +const PlusPromoModal = (props) => { + const { + show, + location, + isPlus, + handleTryMidnightClick, + handleGetPlusClick, + handleKeepBasicClick, + handleGoAwayClick, + handleXClick, + } = props; + + const contentClassNames = ClassNames( + 'PlusPromoModal__content', + 'flex-container', + 'flex-dir-column', + 'align-middle', + ); + + return ( + +
+ +
+
+
+
+
Spring has sprung!
+
+
+
Support us and unlock a new spring theme, personal tracking insights, and other special perks by upgrading to Ghostery Plus for $2 per month.
+
+
+
+
+
+ +
+
+ Already a plus subscriber? + {t('no_thanks_turn_promos_off')} +
+
+
+ + ); +}; + + +// PropTypes ensure we pass required props of the correct type +// PlusPromoModal.propTypes = { +// show: PropTypes.bool.isRequired, +// isPlus: PropTypes.bool.isRequired, +// handleTryMidnightClick: PropTypes.func.isRequired, +// handleGetPlusClick: PropTypes.func.isRequired, +// handleKeepBasicClick: PropTypes.func, +// handleGoAwayClick: PropTypes.func, +// handleXClick: PropTypes.func, +// }; + +// const noop = () => {}; +// PlusPromoModal.defaultProps = { +// handleKeepBasicClick: noop, +// handleGoAwayClick: noop, +// handleXClick: noop, +// }; + +export default PlusPromoModal; diff --git a/app/scss/panel.scss b/app/scss/panel.scss index eaef66ef7..b58c2e8aa 100644 --- a/app/scss/panel.scss +++ b/app/scss/panel.scss @@ -76,6 +76,7 @@ html body { @import './partials/_stats_graph'; @import './partials/_modal_exit_button'; @import './partials/insights_promo_modal.scss'; +@import './partials/plus_promo_modal.scss'; // Imports from ../shared-components directory @import '../shared-components/Modal/Modal.scss'; diff --git a/app/scss/partials/_colors.scss b/app/scss/partials/_colors.scss index d52e7f0ca..4695aab80 100644 --- a/app/scss/partials/_colors.scss +++ b/app/scss/partials/_colors.scss @@ -39,6 +39,9 @@ $link-blue: #2092BF; //primary-color $button-primary: #3AA2CF; $dark-cyan-blue: #325e97; //insights modal border +/* GREENS */ +$spring-green: #6aa103; + /* MARKETING COLORS */ $red: #E74055; $purple: #720174; diff --git a/app/scss/partials/_modal_exit_button.scss b/app/scss/partials/_modal_exit_button.scss index 955fdfaa1..3b4e739e2 100644 --- a/app/scss/partials/_modal_exit_button.scss +++ b/app/scss/partials/_modal_exit_button.scss @@ -19,9 +19,14 @@ width: 26px; height: 26px; border-radius: 15px; - border: solid 0.8px #325e97; background-color: #f7f7f7; @include transition(background-color 0.2s); + &.grey { + border: solid 0.8px #325e97; + } + &.spring-green { + border: solid 2px $spring-green; + } } .ModalExitButton__exit:hover { background-color: #efefef; diff --git a/app/scss/partials/_plus_promo_modal.scss b/app/scss/partials/_plus_promo_modal.scss new file mode 100644 index 000000000..f08f1b46a --- /dev/null +++ b/app/scss/partials/_plus_promo_modal.scss @@ -0,0 +1,175 @@ +/** + * Plus Promo Modal Sass + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2019 Ghostery, Inc. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + +// Plus Promo Modal +$standard-font-family: Roboto, "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; +$condensed-font-family: Roboto Condensed, "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + +.PlusPromoModal__content { + position: relative; + z-index: 10; + width: 456px; + height: 410px; + border: solid 1.9px $spring-green; + background-color: #f7f7f7; +} + +.PlusPromoModal__plus-logo { + width: 216px; + height: 92px; + margin: 22px 0px 6px; + background-image: url('/app/images/panel/spring-plus-logo.svg'); +} + +.PlusPromoModal__main-content-container { + height: 210px; + display: flex; + // flex-direction: column; + // align-items: center; + justify-content: center; +} +.PlusPromoModal__header { + text-align: center; + font-weight: bold; + font-family: $standard-font-family; + .title { + font-size: 20px; + margin-top: 8px; + } + .description { + font-size: 18px; + margin-top: 16px; + font-weight: 500; + color: $tundora; + width: 350px; + } +} + +.PlusPromoModal__header-beta-icon { + float: right; + margin-left: 5px; + width: 20px; + height: 12px; + background-image: url('/app/images/panel/midnight-beta-icon.svg'); +} + +.PlusPromoModal__sub-header { + width: 438px; + margin-bottom: 12px; + text-align: center; + font-size: 18px; + font-weight: bold; + line-height: 1.5; + color: #4A4A4A; +} + +.PlusPromoModal__features-container { + display: flex; + width: 100%; +} + +.PlusPromoModal__feature-column { + &:nth-child(odd) { + margin-left: 20px; + width: 55%; + } + &:nth-child(even) { + margin-right: 25px; + width: 45%; + } +} + +.PlusPromoModal__feature { + display: flex; + align-items: center; + &:nth-child(1) { + margin-bottom: 4px; + } +} + +.PlusPromoModal__feature-text { + font-family: $standard-font-family; + font-size: 14px; + color: $medium-gray; + padding-bottom: 5px; + line-height: 16px; +} + +.PlusPromoModal__checked-circle-icon { + flex: none; + height: 18px; + width: 18px; + margin-right: 8px; + background-image: url('/app/images/panel/midnight-check-icon.svg'); + align-self: flex-start; +} + +.PlusPromoModal__call-to-action-container { + background-color: #e7ecee; + margin-bottom: 3px; + width: 99%; + height: 107px; + z-index: -1; + display: flex; + flex-direction: column; + justify-content: space-around; // Edge does not support space-evenly + position: absolute; + bottom: 1px; +} + +.PlusPromoModal__button-container { + display: flex; + justify-content: center; + margin-top: 10px; +} + +.PlusPromoModal__download-button { + height: 36px; + width: 176px; + border-radius: 2px; + padding: 0px 20px 0px; + 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; + + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + + font-family: $condensed-font-family; + font-size: 14px; + font-weight: bold; + letter-spacing: 0.5px; + text-align: center; + color: #ffffff; + + transition: background-color 0.25s ease-out, color 0.25s ease-out; + + text-transform: uppercase; +} + +.PlusPromoModal__text-link-container { + display: flex; + justify-content: space-around; // Edge does not support space-evenly +} + +.PlusPromoModal__link { + margin-left: 10px; + margin-right: 10px; + font-size: 15px; + font-family: $standard-font-family; + color: #4a4a4a; + text-decoration: underline; + text-align: center; + cursor: pointer; +} diff --git a/src/background.js b/src/background.js index 68cbb0d02..4eeee8f8f 100644 --- a/src/background.js +++ b/src/background.js @@ -985,6 +985,10 @@ function onMessageHandler(request, sender, callback) { promoModals.recordInsightsPromoSighting(); return false; } + if (name === 'promoModals.sawPlusPromo') { + promoModals.recordPlusPromoSighting(); + return false; + } if (name === 'promoModals.turnOffPromos') { promoModals.turnOffPromos(); return false; diff --git a/src/classes/PromoModals.js b/src/classes/PromoModals.js index dd9306ec9..b5ece6bf8 100644 --- a/src/classes/PromoModals.js +++ b/src/classes/PromoModals.js @@ -17,7 +17,8 @@ import panelData from './PanelData'; const DAYS_BETWEEN_PROMOS = { premium: globals.DEBUG ? 0.0005 : 30, // 40 seconds on staging - insights: globals.DEBUG ? 0.0005 : 30 // 40 seconds on staging + insights: globals.DEBUG ? 0.0005 : 30, // 40 seconds on staging + plus: globals.DEBUG ? 0.0005 : 30 // 40 seconds on staging }; const WEEKLY_INSIGHTS_TARGET = globals.DEBUG ? 1 : 3; const DAILY_INSIGHTS_TARGET = globals.DEBUG ? 7 : 3; @@ -25,6 +26,7 @@ const DAILY_INSIGHTS_TARGET = globals.DEBUG ? 7 : 3; const MSECS_IN_DAY = 86400000; // 1000 msecs-in-sec * 60 secs-in-min * 60 mins-in-hour * 24 hours-in-day const PREMIUM = 'premium'; const INSIGHTS = 'insights'; +const PLUS = 'plus'; const PROMO_MODAL_LAST_SEEN = 'promo_modal_last_seen'; /** @@ -40,9 +42,9 @@ class PromoModals { */ static whichPromoModalShouldWeDisplay() { // The order is important - // Insights takes priority over Premium + // Insights takes priority over Plus if (this._isTimeForAPromo(INSIGHTS)) return INSIGHTS; - if (this._isTimeForAPromo(PREMIUM)) return PREMIUM; + if (this._isTimeForAPromo(PLUS)) return PLUS; return null; } @@ -50,6 +52,8 @@ class PromoModals { static recordInsightsPromoSighting() { this._recordPromoSighting(INSIGHTS); } + static recordPlusPromoSighting() { this._recordPromoSighting(PLUS); } + static turnOffPromos() { panelData.set({ notify_promotions: false }); } /** @@ -61,9 +65,9 @@ class PromoModals { static _isTimeForAPromo(type) { if (conf.notify_promotions === false) { return false; } - const lastSeenPremiumPromo = conf[`${PREMIUM}_${PROMO_MODAL_LAST_SEEN}`]; + const lastSeenPlusPromo = conf[`${PLUS}_${PROMO_MODAL_LAST_SEEN}`]; const lastSeenInsightsPromo = conf[`${INSIGHTS}_${PROMO_MODAL_LAST_SEEN}`]; - const lastSeenPromo = Math.max(lastSeenPremiumPromo, lastSeenInsightsPromo); + const lastSeenPromo = Math.max(lastSeenPlusPromo, lastSeenInsightsPromo); if (type === INSIGHTS && !this._hasEngagedFrequently()) { return false; From 95280ff9dc9e5c0255eaeb0226e5d97fa384e815 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 9 Mar 2020 09:51:50 -0400 Subject: [PATCH 02/15] Add click events to promo modal --- .../BuildingBlocks/ModalExitButton.jsx | 2 +- app/panel/components/InsightsPromoModal.jsx | 3 +- app/panel/components/Panel.jsx | 60 +++++++++++-------- app/panel/components/PlusPromoModal.jsx | 46 ++++++-------- .../containers/PlusPromoModalContainer.js | 28 +++++++++ app/scss/partials/_modal_exit_button.scss | 2 +- app/scss/partials/_plus_promo_modal.scss | 12 +++- src/background.js | 1 + src/classes/Metrics.js | 12 +++- src/classes/PromoModals.js | 2 + 10 files changed, 110 insertions(+), 58 deletions(-) create mode 100644 app/panel/containers/PlusPromoModalContainer.js diff --git a/app/panel/components/BuildingBlocks/ModalExitButton.jsx b/app/panel/components/BuildingBlocks/ModalExitButton.jsx index f0984837d..81c13b2e1 100644 --- a/app/panel/components/BuildingBlocks/ModalExitButton.jsx +++ b/app/panel/components/BuildingBlocks/ModalExitButton.jsx @@ -28,7 +28,7 @@ const ModalExitButton = (props) => { } = props; const borderClassNames = ClassNames('ModalExitButton__exit flex-container align-middle', { - 'spring-green': border === 'spring-green' + green: border === 'green' }); return ( diff --git a/app/panel/components/InsightsPromoModal.jsx b/app/panel/components/InsightsPromoModal.jsx index 5b1e54b7e..7c53b464e 100644 --- a/app/panel/components/InsightsPromoModal.jsx +++ b/app/panel/components/InsightsPromoModal.jsx @@ -29,8 +29,9 @@ class InsightsPromoModal extends React.Component { handleXClick = () => { this.props.handleXClick(INSIGHTS); } render() { + const { show } = this.props; return ( - +
diff --git a/app/panel/components/Panel.jsx b/app/panel/components/Panel.jsx index 88a60a3ad..3c87eb9ed 100644 --- a/app/panel/components/Panel.jsx +++ b/app/panel/components/Panel.jsx @@ -16,7 +16,7 @@ import { NavLink } from 'react-router-dom'; import Header from '../containers/HeaderContainer'; import { PremiumPromoModal } from '../../shared-components'; import InsightsPromoModal from './InsightsPromoModal'; -import PlusPromoModal from './PlusPromoModal'; +import PlusPromoModal from '../containers/PlusPromoModalContainer'; import { DynamicUIPortContext } from '../contexts/DynamicUIPortContext'; import { sendMessage } from '../utils/msg'; import { setTheme } from '../utils/utils'; @@ -246,6 +246,21 @@ class Panel extends React.Component { }); } + /** + * @private + * Handle clicks on the download button in the Premium promo modals + */ + _handlePromoTryPlusClick = () => { + this.props.actions.togglePromoModal(); + + // Add utm params + const url = `https://checkout.${DOMAIN}.com/plus?utm_source=gbe&utm_campaign=in_app_spring2020`; + sendMessage('openNewTab', { + url, + become_active: true, + }); + } + /** * @private * Handle clicks on the 'Get Plus' option in the Premium modals @@ -287,6 +302,7 @@ class Panel extends React.Component { * Handle clicks on the 'X' close icon in promo modals */ _handlePromoXClick = (modal) => { + console.log('handling x click'); this.props.actions.togglePromoModal(); if (modal === 'insights') { @@ -370,6 +386,7 @@ class Panel extends React.Component { handleSignInClick={this._handlePromoSignInClick} handleSubscribeClick={this._handlePromoSubscribeClick} handleXClick={this._handlePromoXClick} + show /> ); } @@ -380,51 +397,46 @@ class Panel extends React.Component { * Renders the Insights promo modal if the user is not already an Insights subscriber */ _renderPlusPromoModal = () => { - // if (this._plusSubscriber()) return null; - if (this._plusSubscriber()) { - console.log('test'); - } - - // sendMessage('promoModals.sawPlusPromo', {}); + if (this._plusSubscriber() || this._premiumSubscriber()) { return null; } - // sendMessage('ping', 'promo_modals_show_plus'); + sendMessage('promoModals.sawPlusPromo', {}); + sendMessage('ping', 'promo_modals_show_plus'); return ( ); } - _renderPromoModal = () => this._renderPlusPromoModal(); - /** * @returns {null|JSX} * @private * Renders either the Insights or the Premium promo modal */ - // _renderPromoModal = () => { - // const { - // promoModal, - // isPromoModalHidden, - // } = this.props; + _renderPromoModal = () => { + const { + promoModal, + isPromoModalHidden, + } = this.props; - // if (isPromoModalHidden) return null; + if (isPromoModalHidden) return null; - // if (promoModal === 'insights') { - // return this._renderInsightsPromoModal(); - // } + if (promoModal === 'insights') { + return this._renderInsightsPromoModal(); + } - // if (promoModal === 'premium') { - // return this._renderPremiumPromoModal(); - // } + if (promoModal === 'plus') { + return this._renderPlusPromoModal(); + } - // return null; - // } + return null; + } /** * React's required render function. Returns JSX diff --git a/app/panel/components/PlusPromoModal.jsx b/app/panel/components/PlusPromoModal.jsx index 976066a43..b5c9ef28c 100644 --- a/app/panel/components/PlusPromoModal.jsx +++ b/app/panel/components/PlusPromoModal.jsx @@ -1,5 +1,5 @@ /** - * PlusPromo Modal Component + * PlusPromoModal Component * * Ghostery Browser Extension * https://www.ghostery.com/ @@ -25,13 +25,11 @@ import ModalExitButton from './BuildingBlocks/ModalExitButton'; const PlusPromoModal = (props) => { const { show, - location, - isPlus, - handleTryMidnightClick, - handleGetPlusClick, - handleKeepBasicClick, + handleTryPlusClick, + handleSignInClick, handleGoAwayClick, handleXClick, + loggedIn } = props; const contentClassNames = ClassNames( @@ -44,7 +42,7 @@ const PlusPromoModal = (props) => { return (
- +
@@ -58,13 +56,15 @@ const PlusPromoModal = (props) => {
-
-
- Already a plus subscriber? - {t('no_thanks_turn_promos_off')} +
+ {!loggedIn && + Already a plus subscriber? + } + {t('no_thanks_turn_promos_off')}
@@ -74,21 +74,13 @@ const PlusPromoModal = (props) => { // PropTypes ensure we pass required props of the correct type -// PlusPromoModal.propTypes = { -// show: PropTypes.bool.isRequired, -// isPlus: PropTypes.bool.isRequired, -// handleTryMidnightClick: PropTypes.func.isRequired, -// handleGetPlusClick: PropTypes.func.isRequired, -// handleKeepBasicClick: PropTypes.func, -// handleGoAwayClick: PropTypes.func, -// handleXClick: PropTypes.func, -// }; - -// const noop = () => {}; -// PlusPromoModal.defaultProps = { -// handleKeepBasicClick: noop, -// handleGoAwayClick: noop, -// handleXClick: noop, -// }; +PlusPromoModal.propTypes = { + show: PropTypes.bool.isRequired, + handleTryPlusClick: PropTypes.func.isRequired, + handleSignInClick: PropTypes.func.isRequired, + handleGoAwayClick: PropTypes.func.isRequired, + handleXClick: PropTypes.func.isRequired, + loggedIn: PropTypes.bool.isRequired, +}; export default PlusPromoModal; diff --git a/app/panel/containers/PlusPromoModalContainer.js b/app/panel/containers/PlusPromoModalContainer.js new file mode 100644 index 000000000..c01b3d97d --- /dev/null +++ b/app/panel/containers/PlusPromoModalContainer.js @@ -0,0 +1,28 @@ +/** + * PlusPromoModal Container + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2019 Ghostery, Inc. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + +import { connect } from 'react-redux'; +import { withRouter } from 'react-router-dom'; +import PlusPromoModal from '../components/PlusPromoModal'; +/** + * Map redux store state properties to Panel view component own properties. + * @memberOf PanelContainers + * @param {Object} state entire Redux store's state + * @param {Object} ownProps props passed to the connected component + * @return {function} this function returns plain object, which will be merged into Panel view component props + * @todo We are not using ownProps, so we better not specify it explicitly, + * in this case it won't be passed by React (see https://github.com/reactjs/react-redux/blob/master/docs/api.md). + */ +const mapStateToProps = state => Object.assign({}, state.account, {}); + +export default withRouter(connect(mapStateToProps)(PlusPromoModal)); diff --git a/app/scss/partials/_modal_exit_button.scss b/app/scss/partials/_modal_exit_button.scss index 3b4e739e2..82aade2b4 100644 --- a/app/scss/partials/_modal_exit_button.scss +++ b/app/scss/partials/_modal_exit_button.scss @@ -24,7 +24,7 @@ &.grey { border: solid 0.8px #325e97; } - &.spring-green { + &.green { border: solid 2px $spring-green; } } diff --git a/app/scss/partials/_plus_promo_modal.scss b/app/scss/partials/_plus_promo_modal.scss index f08f1b46a..9f7fdfa0e 100644 --- a/app/scss/partials/_plus_promo_modal.scss +++ b/app/scss/partials/_plus_promo_modal.scss @@ -171,5 +171,15 @@ $condensed-font-family: Roboto Condensed, "Open Sans", "Helvetica Neue", Helveti color: #4a4a4a; text-decoration: underline; text-align: center; - cursor: pointer; + cursor: pointer; + + &.sign-in { + float: left; + left: 10px; + } + + &.turn-promos-off { + float: right; + right: 10px; + } } diff --git a/src/background.js b/src/background.js index 4eeee8f8f..b5e145d8e 100644 --- a/src/background.js +++ b/src/background.js @@ -990,6 +990,7 @@ function onMessageHandler(request, sender, callback) { return false; } if (name === 'promoModals.turnOffPromos') { + console.log('turning off promos'); promoModals.turnOffPromos(); return false; } diff --git a/src/classes/Metrics.js b/src/classes/Metrics.js index 51377cc82..e41a4d1b5 100644 --- a/src/classes/Metrics.js +++ b/src/classes/Metrics.js @@ -510,10 +510,16 @@ class Metrics { */ _getThemeValue() { const { current_theme } = conf; - if (current_theme === 'midnight-theme') { - return 1; + switch (current_theme) { + case 'midnight-theme': + return 1; + case 'leaf-theme': + return 2; + case 'palm-theme': + return 3; + default: + return 0; } - return 0; } /** diff --git a/src/classes/PromoModals.js b/src/classes/PromoModals.js index b5ece6bf8..a35b509fb 100644 --- a/src/classes/PromoModals.js +++ b/src/classes/PromoModals.js @@ -99,8 +99,10 @@ class PromoModals { */ static _hasEngagedFrequently() { const { engaged_daily_count } = conf.metrics || []; + console.log(engaged_daily_count); const very_engaged_days = engaged_daily_count.reduce((acc, count) => (count >= DAILY_INSIGHTS_TARGET ? ++acc : acc), 0); + console.log('very engaged days: ', very_engaged_days); return very_engaged_days >= WEEKLY_INSIGHTS_TARGET; } From 887f445bcd17094b19e49a0389c6c2214ef41a36 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 9 Mar 2020 09:59:05 -0400 Subject: [PATCH 03/15] Remove comments and console.logs --- app/panel/components/Panel.jsx | 2 -- app/scss/partials/_plus_promo_modal.scss | 2 -- src/background.js | 1 - src/classes/PromoModals.js | 2 -- 4 files changed, 7 deletions(-) diff --git a/app/panel/components/Panel.jsx b/app/panel/components/Panel.jsx index 3c87eb9ed..bbf37491a 100644 --- a/app/panel/components/Panel.jsx +++ b/app/panel/components/Panel.jsx @@ -253,7 +253,6 @@ class Panel extends React.Component { _handlePromoTryPlusClick = () => { this.props.actions.togglePromoModal(); - // Add utm params const url = `https://checkout.${DOMAIN}.com/plus?utm_source=gbe&utm_campaign=in_app_spring2020`; sendMessage('openNewTab', { url, @@ -302,7 +301,6 @@ class Panel extends React.Component { * Handle clicks on the 'X' close icon in promo modals */ _handlePromoXClick = (modal) => { - console.log('handling x click'); this.props.actions.togglePromoModal(); if (modal === 'insights') { diff --git a/app/scss/partials/_plus_promo_modal.scss b/app/scss/partials/_plus_promo_modal.scss index 9f7fdfa0e..7d43f0ab1 100644 --- a/app/scss/partials/_plus_promo_modal.scss +++ b/app/scss/partials/_plus_promo_modal.scss @@ -34,8 +34,6 @@ $condensed-font-family: Roboto Condensed, "Open Sans", "Helvetica Neue", Helveti .PlusPromoModal__main-content-container { height: 210px; display: flex; - // flex-direction: column; - // align-items: center; justify-content: center; } .PlusPromoModal__header { diff --git a/src/background.js b/src/background.js index b5e145d8e..4eeee8f8f 100644 --- a/src/background.js +++ b/src/background.js @@ -990,7 +990,6 @@ function onMessageHandler(request, sender, callback) { return false; } if (name === 'promoModals.turnOffPromos') { - console.log('turning off promos'); promoModals.turnOffPromos(); return false; } diff --git a/src/classes/PromoModals.js b/src/classes/PromoModals.js index a35b509fb..b5ece6bf8 100644 --- a/src/classes/PromoModals.js +++ b/src/classes/PromoModals.js @@ -99,10 +99,8 @@ class PromoModals { */ static _hasEngagedFrequently() { const { engaged_daily_count } = conf.metrics || []; - console.log(engaged_daily_count); const very_engaged_days = engaged_daily_count.reduce((acc, count) => (count >= DAILY_INSIGHTS_TARGET ? ++acc : acc), 0); - console.log('very engaged days: ', very_engaged_days); return very_engaged_days >= WEEKLY_INSIGHTS_TARGET; } From c14cf550eec5887f1a196283820362f67c304a50 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 9 Mar 2020 10:36:07 -0400 Subject: [PATCH 04/15] Add translations --- _locales/en/messages.json | 6 ++++++ app/panel/components/PlusPromoModal.jsx | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 616a57ece..79b371697 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1807,6 +1807,9 @@ "subscribe_pitch": { "message": "While Ghostery is free, you can choose to support us through a small subscription of $2 per month in exchange for cool perks, such as color themes, priority help service, and more. Join our mission and subscribe!" }, + "subscribe_pitch_spring": { + "message": "Support us and unlock a new spring theme, personal tracking insights, and other special perks by upgrading to Ghostery Plus for $2 per month." + }, "subscribe_pitch_learn_more": { "message": "Learn more" }, @@ -2336,6 +2339,9 @@ "message": "Already subscribed? Sign In", "description": "Character limit (including spaces and punctuation): 28." }, + "already_subscribed_to_plus_sign_in": { + "message": "Already a plus subscriber?" + }, "promos_turned_off_notification": { "message": "Promos turned off. You can turn them back on in", "description": "translation must take into account that 'Settings' is appended to this string" diff --git a/app/panel/components/PlusPromoModal.jsx b/app/panel/components/PlusPromoModal.jsx index b5c9ef28c..4b7047cd3 100644 --- a/app/panel/components/PlusPromoModal.jsx +++ b/app/panel/components/PlusPromoModal.jsx @@ -47,22 +47,22 @@ const PlusPromoModal = (props) => {
-
Spring has sprung!
+
{t('spring_has_sprung')}
-
Support us and unlock a new spring theme, personal tracking insights, and other special perks by upgrading to Ghostery Plus for $2 per month.
+
{!loggedIn && - Already a plus subscriber? + {t('already_subscribed_to_plus_sign_in')} } {t('no_thanks_turn_promos_off')}
From 6301df35e56637f3ff17c930b002a7457319bad2 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 31 Mar 2020 07:10:24 -0400 Subject: [PATCH 05/15] Unfinished PromoModal superclass --- app/hub/Views/HomeView/HomeViewContainer.jsx | 6 +- app/panel/components/Panel.jsx | 19 +++-- .../PromoModal/PromoModal.jsx | 77 +++++++++++++++++++ app/shared-components/PromoModal/index.js | 16 ++++ src/classes/PromoModals.js | 2 +- 5 files changed, 111 insertions(+), 9 deletions(-) create mode 100644 app/shared-components/PromoModal/PromoModal.jsx create mode 100644 app/shared-components/PromoModal/index.js diff --git a/app/hub/Views/HomeView/HomeViewContainer.jsx b/app/hub/Views/HomeView/HomeViewContainer.jsx index babcbf1c1..c98e55b78 100644 --- a/app/hub/Views/HomeView/HomeViewContainer.jsx +++ b/app/hub/Views/HomeView/HomeViewContainer.jsx @@ -15,11 +15,12 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import QueryString from 'query-string'; import HomeView from './HomeView'; -import { PremiumPromoModal } from '../../../shared-components'; +import PromoModal from '../../../shared-components/PromoModal'; import { sendMessage } from '../../utils'; import globals from '../../../../src/classes/Globals'; const DOMAIN = globals.DEBUG ? 'ghosterystage' : 'ghostery'; +const PREMIUM = 'premium'; /** * @class Implement the Home View for the Ghostery Hub @@ -137,7 +138,8 @@ class HomeViewContainer extends Component { return (
- ); } @@ -379,7 +384,8 @@ class Panel extends React.Component { sendMessage('ping', 'promo_modals_show_insights'); return ( - { if (this._plusSubscriber() || this._premiumSubscriber()) { return null; } @@ -401,7 +407,8 @@ class Panel extends React.Component { sendMessage('ping', 'promo_modals_show_plus'); return ( - { this.props.handleGoAwayClick(this.props.type); } + + handleSubscribeClick = () => { this.props.handleSubscribeClick(this.props.type); } + + handleXClick = () => { this.props.handleXClick(this.props.type); } + + render() { + const { type } = this.props; + switch (type) { + case INSIGHTS: + return ; + case PLUS: + return ; + case PREMIUM: + return ; + default: + return ; + } + } +} + +// PropTypes ensure we pass required props of the correct type +PromoModal.propTypes = { + type: PropTypes.string.isRequired, + handleGoAwayClick: PropTypes.func, + handleSignInClick: PropTypes.func, + handleSubscribeClick: PropTypes.func, + handleXClick: PropTypes.func, + handleTryMidnightClick: PropTypes.func, + handleGetPlusClick: PropTypes.func, + handleKeepBasicClick: PropTypes.func, + location: PropTypes.string, + isPlus: PropTypes.bool, +}; + +const noop = () => {}; +PromoModal.defaultProps = { + handleGoAwayClick: noop, + handleSignInClick: noop, + handleSubscribeClick: noop, + handleXClick: noop, + handleTryMidnightClick: noop, + handleGetPlusClick: noop, + handleKeepBasicClick: noop, + location: 'panel', + isPlus: false, +}; + +export default PromoModal; diff --git a/app/shared-components/PromoModal/index.js b/app/shared-components/PromoModal/index.js new file mode 100644 index 000000000..1cd699cc2 --- /dev/null +++ b/app/shared-components/PromoModal/index.js @@ -0,0 +1,16 @@ +/** + * Point of entry index.js file for PromoModal + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2019 Ghostery, Inc. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + +import PromoModal from './PromoModal'; + +export default PromoModal; diff --git a/src/classes/PromoModals.js b/src/classes/PromoModals.js index b5ece6bf8..1a06bcd10 100644 --- a/src/classes/PromoModals.js +++ b/src/classes/PromoModals.js @@ -18,7 +18,7 @@ import panelData from './PanelData'; const DAYS_BETWEEN_PROMOS = { premium: globals.DEBUG ? 0.0005 : 30, // 40 seconds on staging insights: globals.DEBUG ? 0.0005 : 30, // 40 seconds on staging - plus: globals.DEBUG ? 0.0005 : 30 // 40 seconds on staging + plus: globals.DEBUG ? 0.0005 : 2 // 40 seconds on staging }; const WEEKLY_INSIGHTS_TARGET = globals.DEBUG ? 1 : 3; const DAILY_INSIGHTS_TARGET = globals.DEBUG ? 7 : 3; From bedad100a01667f524b73785916469b74e93593c Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 1 Apr 2020 03:40:53 -0400 Subject: [PATCH 06/15] Add base class for promo modals --- app/panel/components/InsightsPromoModal.jsx | 118 ++++++++---------- app/panel/components/Panel.jsx | 14 +-- .../PromoModal/PromoModal.jsx | 36 +++--- 3 files changed, 76 insertions(+), 92 deletions(-) diff --git a/app/panel/components/InsightsPromoModal.jsx b/app/panel/components/InsightsPromoModal.jsx index 7c53b464e..b37956c8d 100644 --- a/app/panel/components/InsightsPromoModal.jsx +++ b/app/panel/components/InsightsPromoModal.jsx @@ -15,77 +15,69 @@ import React from 'react'; import Modal from '../../shared-components/Modal'; import ModalExitButton from './BuildingBlocks/ModalExitButton'; -const INSIGHTS = 'insights'; - -/** - * @class Implements the Insights Promo Modal - * @memberof PanelClasses - */ -class InsightsPromoModal extends React.Component { - handleGoAwayClick = () => { this.props.handleGoAwayClick(INSIGHTS); } - - handleSubscribeClick = () => { this.props.handleSubscribeClick(INSIGHTS); } - - handleXClick = () => { this.props.handleXClick(INSIGHTS); } - - render() { - const { show } = this.props; - return ( - -
- -
-
- {t('panel_insights_promotion_header')} -
-
- {t('panel_insights_promotion_description')} -
-
-
-
- -
- { t('panel_insights_audit_tags') } -
-
-
- -
- { t('panel_insights_promotion_trace_poor_performance') } -
+const InsightsPromoModal = (props) => { + const { + handleGoAwayClick, + handleSignInClick, + handleSubscribeClick, + handleXClick, + show + } = props; + return ( + +
+ +
+
+ {t('panel_insights_promotion_header')} +
+
+ {t('panel_insights_promotion_description')} +
+
+
+
+ +
+ {t('panel_insights_audit_tags')}
-
-
- -
- { t('panel_insights_promotion_watch_pings') } -
-
-
- -
- { t('panel_insights_promotion_explore_trends') } -
+
+ +
+ {t('panel_insights_promotion_trace_poor_performance')}
-
-
- - {t('panel_insights_promotion_call_to_action')} - +
+
+ +
+ {t('panel_insights_promotion_watch_pings')} +
-
- {t('subscribe_pitch_sign_in')} - {t('no_thanks_turn_promos_off')} +
+ +
+ {t('panel_insights_promotion_explore_trends')} +
- - ); - } -} +
+
+ + {t('panel_insights_promotion_call_to_action')} + +
+
+ {t('subscribe_pitch_sign_in')} + {t('no_thanks_turn_promos_off')} +
+
+
+ + ); +}; export default InsightsPromoModal; diff --git a/app/panel/components/Panel.jsx b/app/panel/components/Panel.jsx index 0416c4a30..000c30c27 100644 --- a/app/panel/components/Panel.jsx +++ b/app/panel/components/Panel.jsx @@ -15,9 +15,6 @@ import React from 'react'; import { NavLink } from 'react-router-dom'; import Header from '../containers/HeaderContainer'; import PromoModal from '../../shared-components/PromoModal'; -import { PremiumPromoModal } from '../../shared-components'; -import InsightsPromoModal from './InsightsPromoModal'; -import PlusPromoModal from '../containers/PlusPromoModalContainer'; import { DynamicUIPortContext } from '../contexts/DynamicUIPortContext'; import { sendMessage } from '../utils/msg'; import { setTheme } from '../utils/utils'; @@ -386,10 +383,10 @@ class Panel extends React.Component { return ( this._handlePromoGoAwayClick(INSIGHTS)} + handleSignInClick={() => this._handlePromoSignInClick} + handleSubscribeClick={() => this._handlePromoSubscribeClick(INSIGHTS)} + handleXClick={() => this._handlePromoXClick(INSIGHTS)} show /> ); @@ -406,14 +403,15 @@ class Panel extends React.Component { sendMessage('promoModals.sawPlusPromo', {}); sendMessage('ping', 'promo_modals_show_plus'); + const { loggedIn } = this.props; return ( ); diff --git a/app/shared-components/PromoModal/PromoModal.jsx b/app/shared-components/PromoModal/PromoModal.jsx index 347338758..442cc0719 100644 --- a/app/shared-components/PromoModal/PromoModal.jsx +++ b/app/shared-components/PromoModal/PromoModal.jsx @@ -20,32 +20,26 @@ import PremiumPromoModal from '../PremiumPromoModal'; const INSIGHTS = 'insights'; const PLUS = 'plus'; const PREMIUM = 'premium'; + /** - * A superclass for Promo Modals + * A base functional component for Promo Modals * @return {JSX} * @memberof HubComponents */ -class PromoModal extends React.Component { - handleGoAwayClick = () => { this.props.handleGoAwayClick(this.props.type); } - - handleSubscribeClick = () => { this.props.handleSubscribeClick(this.props.type); } - handleXClick = () => { this.props.handleXClick(this.props.type); } - - render() { - const { type } = this.props; - switch (type) { - case INSIGHTS: - return ; - case PLUS: - return ; - case PREMIUM: - return ; - default: - return ; - } +const PromoModal = (props) => { + const { type } = props; + switch (type) { + case INSIGHTS: + return ; + case PLUS: + return ; + case PREMIUM: + return ; + default: + return ; } -} +}; // PropTypes ensure we pass required props of the correct type PromoModal.propTypes = { @@ -61,7 +55,7 @@ PromoModal.propTypes = { isPlus: PropTypes.bool, }; -const noop = () => {}; +const noop = () => { }; PromoModal.defaultProps = { handleGoAwayClick: noop, handleSignInClick: noop, From 04a976040f369eac558059365493876bc53740e0 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 1 Apr 2020 05:07:05 -0400 Subject: [PATCH 07/15] Insights promo modal broken --- app/panel/components/Panel.jsx | 116 +++++++++--------- .../PromoModal/PromoModal.jsx | 82 +++++++++++-- .../PromoModal/PromoModalContainer.js | 48 ++++++++ src/classes/PromoModals.js | 2 +- 4 files changed, 178 insertions(+), 70 deletions(-) create mode 100644 app/shared-components/PromoModal/PromoModalContainer.js diff --git a/app/panel/components/Panel.jsx b/app/panel/components/Panel.jsx index 000c30c27..e4a4dc1c0 100644 --- a/app/panel/components/Panel.jsx +++ b/app/panel/components/Panel.jsx @@ -14,11 +14,11 @@ import React from 'react'; import { NavLink } from 'react-router-dom'; import Header from '../containers/HeaderContainer'; -import PromoModal from '../../shared-components/PromoModal'; +// import PromoModal from '../../shared-components/PromoModal'; +import PromoModalContainer from '../../shared-components/PromoModal/PromoModalContainer'; import { DynamicUIPortContext } from '../contexts/DynamicUIPortContext'; import { sendMessage } from '../utils/msg'; import { setTheme } from '../utils/utils'; -import history from '../utils/history'; import globals from '../../../src/classes/Globals'; const DOMAIN = globals.DEBUG ? 'ghosterystage' : 'ghostery'; @@ -222,16 +222,16 @@ class Panel extends React.Component { }); }; - /** - * @private - * Handle clicks on sign in links in promo modals - */ - _handlePromoSignInClick = () => { - this.props.actions.togglePromoModal(); - history.push({ - pathname: '/login', - }); - }; + // /** + // * @private + // * Handle clicks on sign in links in promo modals + // */ + // _handlePromoSignInClick = () => { + // this.props.actions.togglePromoModal(); + // history.push({ + // pathname: '/login', + // }); + // }; /** * @private @@ -275,26 +275,26 @@ class Panel extends React.Component { }); }; - /** - * @private - * Handle click action when user selects Subscribe button in the Insights modal - * @param {string} modal Modal type (insights or plus) - */ - _handlePromoSubscribeClick = (modal) => { - this.props.actions.togglePromoModal(); + // /** + // * @private + // * Handle click action when user selects Subscribe button in the Insights modal + // * @param {string} modal Modal type (insights or plus) + // */ + // _handlePromoSubscribeClick = (modal) => { + // this.props.actions.togglePromoModal(); - let url = `https://checkout.${DOMAIN}.com/`; + // let url = `https://checkout.${DOMAIN}.com/`; - if (modal === 'insights') { - sendMessage('ping', 'promo_modals_insights_upgrade_cta'); - url += 'insights?utm_source=gbe&utm_campaign=in_app_upgrade'; - } + // if (modal === 'insights') { + // sendMessage('ping', 'promo_modals_insights_upgrade_cta'); + // url += 'insights?utm_source=gbe&utm_campaign=in_app_upgrade'; + // } - sendMessage('openNewTab', { - url, - become_active: true, - }); - }; + // sendMessage('openNewTab', { + // url, + // become_active: true, + // }); + // }; /** * @param modal 'insights' or 'premium' @@ -355,16 +355,17 @@ class Panel extends React.Component { const isPlus = this._plusSubscriber(); return ( - + // +
); } @@ -379,16 +380,18 @@ class Panel extends React.Component { sendMessage('promoModals.sawInsightsPromo', {}); sendMessage('ping', 'promo_modals_show_insights'); + console.log('the props: ', this.props); return ( - this._handlePromoGoAwayClick(INSIGHTS)} - handleSignInClick={() => this._handlePromoSignInClick} - handleSubscribeClick={() => this._handlePromoSubscribeClick(INSIGHTS)} - handleXClick={() => this._handlePromoXClick(INSIGHTS)} - show - /> + + // this._handlePromoGoAwayClick(INSIGHTS)} + // handleSignInClick={() => this._handlePromoSignInClick} + // handleSubscribeClick={() => this._handlePromoSubscribeClick(INSIGHTS)} + // handleXClick={() => this._handlePromoXClick(INSIGHTS)} + // show + // /> ); } @@ -405,15 +408,16 @@ class Panel extends React.Component { const { loggedIn } = this.props; return ( - + // +
); } diff --git a/app/shared-components/PromoModal/PromoModal.jsx b/app/shared-components/PromoModal/PromoModal.jsx index 442cc0719..09d5b4dd0 100644 --- a/app/shared-components/PromoModal/PromoModal.jsx +++ b/app/shared-components/PromoModal/PromoModal.jsx @@ -1,5 +1,5 @@ /** - * Super component for all of the Promo Modals + * Base component for all of the Promo Modals * * Ghostery Browser Extension * https://www.ghostery.com/ @@ -16,7 +16,11 @@ import PropTypes from 'prop-types'; import InsightsPromoModal from '../../panel/components/InsightsPromoModal'; import PlusPromoModal from '../../panel/components/PlusPromoModal'; import PremiumPromoModal from '../PremiumPromoModal'; +import history from '../../panel/utils/history'; +import { sendMessage } from '../../panel/utils/msg'; +import globals from '../../../src/classes/Globals'; +const DOMAIN = globals.DEBUG ? 'ghosterystage' : 'ghostery'; const INSIGHTS = 'insights'; const PLUS = 'plus'; const PREMIUM = 'premium'; @@ -27,19 +31,71 @@ const PREMIUM = 'premium'; * @memberof HubComponents */ -const PromoModal = (props) => { - const { type } = props; - switch (type) { - case INSIGHTS: - return ; - case PLUS: - return ; - case PREMIUM: - return ; - default: - return ; +class PromoModal extends React.Component { + /** + * @private + * Handle click action when user selects Subscribe button in the Insights modal + * @param {string} modal Modal type (insights or plus) + */ + _handlePromoSubscribeClick = (modal) => { + this.props.actions.togglePromoModal(); + + let url = `https://checkout.${DOMAIN}.com/`; + + if (modal === 'insights') { + sendMessage('ping', 'promo_modals_insights_upgrade_cta'); + url += 'insights?utm_source=gbe&utm_campaign=in_app_upgrade'; + } + + sendMessage('openNewTab', { + url, + become_active: true, + }); + }; + + /** + * @private + * Handle clicks on sign in links in promo modals + */ + _handlePromoSignInClick = () => { + console.log('here'); + this.props.actions.togglePromoModal(); + history.push({ + pathname: '/login', + }); + }; + + _handleGoAwayClick = (type) => { this.props.handleGoAwayClick(type); } + + _handleSubscribeClick = (type) => { this.props.handleSubscribeClick(type); } + + _handlePromoXClick = (type) => { + console.log('test'); + this.props.handleXClick(type); } -}; + + render() { + const { type } = this.props; + switch (type) { + case INSIGHTS: + return ( + this._handlePromoSubscribeClick(type)} + handleXClick={() => this._handlePromoXClick(type)} + show + /> + ); + case PLUS: + return ; + case PREMIUM: + return ; + default: + return ; + } + } +} // PropTypes ensure we pass required props of the correct type PromoModal.propTypes = { diff --git a/app/shared-components/PromoModal/PromoModalContainer.js b/app/shared-components/PromoModal/PromoModalContainer.js new file mode 100644 index 000000000..77d2bec64 --- /dev/null +++ b/app/shared-components/PromoModal/PromoModalContainer.js @@ -0,0 +1,48 @@ +/** + * Promo Modal Container + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2019 Ghostery, Inc. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ +/** + * @namespace PanelContainers + */ +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import PromoModal from './PromoModal'; +import { togglePromoModal } from '../../panel/actions/PanelActions'; // get shared actions from Panel + +/** + * Map redux store state properties to PromoModal component own properties. + * @memberOf PanelContainers + * @param {Object} state entire Redux store's state + * @param {Object} ownProps props passed to the connected component + * @return {function} this function returns plain object, which will be merged into PromoModal props + * @todo We are not using ownProps, so we better not specify it explicitly, + * in this case it won't be passed by React (see https://github.com/reactjs/react-redux/blob/master/docs/api.md). + */ +// const mapStateToProps = state => Object.assign({}, { +// togglePromoModal: state.panel.togglePromoModal, +// }); + +/** + * Bind Login view component action creators using Redux's bindActionCreators + * @memberOf PanelContainers + * @param {function} dispatch redux store method which dispatches actions + * @param {Object} ownProps Login view component own props + * @return {function} to be used as an argument in redux connect call + */ +const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(Object.assign({ togglePromoModal }), dispatch) }); +/** + * Connects PromoModal component to the Redux store. + * @param {function} mapStateToProps [description] + * + * @return {Object} A higher-order React component class that passes state into PromoModal. Used by React framework. + */ +export default connect(null, mapDispatchToProps)(PromoModal); diff --git a/src/classes/PromoModals.js b/src/classes/PromoModals.js index 1a06bcd10..8d753694a 100644 --- a/src/classes/PromoModals.js +++ b/src/classes/PromoModals.js @@ -21,7 +21,7 @@ const DAYS_BETWEEN_PROMOS = { plus: globals.DEBUG ? 0.0005 : 2 // 40 seconds on staging }; const WEEKLY_INSIGHTS_TARGET = globals.DEBUG ? 1 : 3; -const DAILY_INSIGHTS_TARGET = globals.DEBUG ? 7 : 3; +const DAILY_INSIGHTS_TARGET = globals.DEBUG ? 2 : 3; const MSECS_IN_DAY = 86400000; // 1000 msecs-in-sec * 60 secs-in-min * 60 mins-in-hour * 24 hours-in-day const PREMIUM = 'premium'; From 31121c1665a8fae5305109364f6654bb8aa4c84a Mon Sep 17 00:00:00 2001 From: Ethan Gooding Date: Thu, 2 Apr 2020 12:53:04 -0400 Subject: [PATCH 08/15] Fix handlePromoXClick, comment in PromoModal structure --- .../PromoModal/PromoModal.jsx | 35 +++++++++++++++++-- .../PromoModal/PromoModalContainer.js | 4 ++- manifest.json | 3 +- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/app/shared-components/PromoModal/PromoModal.jsx b/app/shared-components/PromoModal/PromoModal.jsx index 09d5b4dd0..223630981 100644 --- a/app/shared-components/PromoModal/PromoModal.jsx +++ b/app/shared-components/PromoModal/PromoModal.jsx @@ -58,7 +58,6 @@ class PromoModal extends React.Component { * Handle clicks on sign in links in promo modals */ _handlePromoSignInClick = () => { - console.log('here'); this.props.actions.togglePromoModal(); history.push({ pathname: '/login', @@ -70,11 +69,41 @@ class PromoModal extends React.Component { _handleSubscribeClick = (type) => { this.props.handleSubscribeClick(type); } _handlePromoXClick = (type) => { - console.log('test'); - this.props.handleXClick(type); + this.props.actions.togglePromoModal(); + + if (type === 'insights') { + sendMessage('ping', 'promo_modals_decline_insights_upgrade'); + } + } + + renderModalContent() { + const { type } = this.props; + switch (type) { + case INSIGHTS: + return ( + this._handlePromoSubscribeClick(type)} + show + /> + ); + case PLUS: + return ; + case PREMIUM: + return ; + default: + return ; + } } render() { + // return ( + // + //
+ // {this.renderModalContent()} + //
+ // + // ); + const { type } = this.props; switch (type) { case INSIGHTS: diff --git a/app/shared-components/PromoModal/PromoModalContainer.js b/app/shared-components/PromoModal/PromoModalContainer.js index 77d2bec64..819fd9dec 100644 --- a/app/shared-components/PromoModal/PromoModalContainer.js +++ b/app/shared-components/PromoModal/PromoModalContainer.js @@ -38,7 +38,9 @@ import { togglePromoModal } from '../../panel/actions/PanelActions'; // get shar * @param {Object} ownProps Login view component own props * @return {function} to be used as an argument in redux connect call */ -const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(Object.assign({ togglePromoModal }), dispatch) }); +const mapDispatchToProps = dispatch => ({ + actions: bindActionCreators(Object.assign({ togglePromoModal }), dispatch) +}); /** * Connects PromoModal component to the Redux store. * @param {function} mapStateToProps [description] diff --git a/manifest.json b/manifest.json index b718e86ac..48078e881 100644 --- a/manifest.json +++ b/manifest.json @@ -104,5 +104,6 @@ "cliqz/offers-cc/index.html", "cliqz/offers-reminder/index.html", "cliqz/popup-notification/images/*" - ] + ], + "debug": true } From 2b54972e666a51ac3042666cbc33a907c9affa8b Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 6 Apr 2020 05:38:03 -0400 Subject: [PATCH 09/15] Merge insights, plus, and premium PromoModal methods --- app/panel/components/InsightsPromoModal.jsx | 83 --------- .../components/InsightsPromoModalContent.jsx | 78 ++++++++ app/panel/components/Panel.jsx | 157 ++-------------- app/panel/components/PlusPromoModal.jsx | 86 --------- .../components/PlusPromoModalContent.jsx | 80 +++++++++ app/scss/hub.scss | 2 +- app/scss/panel.scss | 2 +- .../PremiumPromoModal/PremiumPromoModal.jsx | 138 --------------- .../PremiumPromoModalContent.jsx | 129 ++++++++++++++ .../PremiumPromoModalContent.scss} | 0 .../index.js | 4 +- .../PromoModal/PromoModal.jsx | 167 +++++++++++------- .../PromoModal/PromoModalContainer.js | 9 +- app/shared-components/index.js | 4 +- src/classes/PromoModals.js | 6 +- 15 files changed, 419 insertions(+), 526 deletions(-) delete mode 100644 app/panel/components/InsightsPromoModal.jsx create mode 100644 app/panel/components/InsightsPromoModalContent.jsx delete mode 100644 app/panel/components/PlusPromoModal.jsx create mode 100644 app/panel/components/PlusPromoModalContent.jsx delete mode 100644 app/shared-components/PremiumPromoModal/PremiumPromoModal.jsx create mode 100644 app/shared-components/PremiumPromoModalContent/PremiumPromoModalContent.jsx rename app/shared-components/{PremiumPromoModal/PremiumPromoModal.scss => PremiumPromoModalContent/PremiumPromoModalContent.scss} (100%) rename app/shared-components/{PremiumPromoModal => PremiumPromoModalContent}/index.js (78%) diff --git a/app/panel/components/InsightsPromoModal.jsx b/app/panel/components/InsightsPromoModal.jsx deleted file mode 100644 index b37956c8d..000000000 --- a/app/panel/components/InsightsPromoModal.jsx +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Insights Promo Modal Component - * - * Ghostery Browser Extension - * https://www.ghostery.com/ - * - * Copyright 2019 Ghostery, Inc. All rights reserved. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0 - */ - -import React from 'react'; -import Modal from '../../shared-components/Modal'; -import ModalExitButton from './BuildingBlocks/ModalExitButton'; - -const InsightsPromoModal = (props) => { - const { - handleGoAwayClick, - handleSignInClick, - handleSubscribeClick, - handleXClick, - show - } = props; - return ( - -
- -
-
- {t('panel_insights_promotion_header')} -
-
- {t('panel_insights_promotion_description')} -
-
-
-
- -
- {t('panel_insights_audit_tags')} -
-
-
- -
- {t('panel_insights_promotion_trace_poor_performance')} -
-
-
-
-
- -
- {t('panel_insights_promotion_watch_pings')} -
-
-
- -
- {t('panel_insights_promotion_explore_trends')} -
-
-
-
-
-
- - {t('panel_insights_promotion_call_to_action')} - -
-
- {t('subscribe_pitch_sign_in')} - {t('no_thanks_turn_promos_off')} -
-
-
- - ); -}; - -export default InsightsPromoModal; diff --git a/app/panel/components/InsightsPromoModalContent.jsx b/app/panel/components/InsightsPromoModalContent.jsx new file mode 100644 index 000000000..0debb3dfc --- /dev/null +++ b/app/panel/components/InsightsPromoModalContent.jsx @@ -0,0 +1,78 @@ +/** + * Insights Promo Modal Component + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2019 Ghostery, Inc. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + +import React from 'react'; + +const InsightsPromoModalContent = (props) => { + const { + handleGoAwayClick, + handleSignInClick, + handleTryInsightsClick, + XButton, + } = props; + return ( +
+ {XButton} +
+
+ {t('panel_insights_promotion_header')} +
+
+ {t('panel_insights_promotion_description')} +
+
+
+
+ +
+ {t('panel_insights_audit_tags')} +
+
+
+ +
+ {t('panel_insights_promotion_trace_poor_performance')} +
+
+
+
+
+ +
+ {t('panel_insights_promotion_watch_pings')} +
+
+
+ +
+ {t('panel_insights_promotion_explore_trends')} +
+
+
+
+
+
+ + {t('panel_insights_promotion_call_to_action')} + +
+
+ {t('subscribe_pitch_sign_in')} + {t('no_thanks_turn_promos_off')} +
+
+
+ ); +}; + +export default InsightsPromoModalContent; diff --git a/app/panel/components/Panel.jsx b/app/panel/components/Panel.jsx index e4a4dc1c0..210a486e3 100644 --- a/app/panel/components/Panel.jsx +++ b/app/panel/components/Panel.jsx @@ -14,14 +14,11 @@ import React from 'react'; import { NavLink } from 'react-router-dom'; import Header from '../containers/HeaderContainer'; -// import PromoModal from '../../shared-components/PromoModal'; import PromoModalContainer from '../../shared-components/PromoModal/PromoModalContainer'; import { DynamicUIPortContext } from '../contexts/DynamicUIPortContext'; import { sendMessage } from '../utils/msg'; import { setTheme } from '../utils/utils'; -import globals from '../../../src/classes/Globals'; -const DOMAIN = globals.DEBUG ? 'ghosterystage' : 'ghostery'; const INSIGHTS = 'insights'; const PLUS = 'plus'; const PREMIUM = 'premium'; @@ -197,118 +194,9 @@ class Panel extends React.Component { ); } - return false; } - /** - * @param modal 'insights' or 'premium' - * @private - * Handle clicks on the link to turn off promos in the promo modals - */ - _handlePromoGoAwayClick = (modal) => { - this.props.actions.togglePromoModal(); - - sendMessage('promoModals.turnOffPromos', {}); - - if (modal === 'insights') { - sendMessage('ping', 'promo_modals_decline_insights_upgrade'); - } - - this.props.actions.showNotification({ - classes: 'warning', - reload: false, - text: t('promos_turned_off_notification'), - }); - }; - - // /** - // * @private - // * Handle clicks on sign in links in promo modals - // */ - // _handlePromoSignInClick = () => { - // this.props.actions.togglePromoModal(); - // history.push({ - // pathname: '/login', - // }); - // }; - - /** - * @private - * Handle clicks on the download button in the Premium promo modals - */ - _handlePromoTryMidnightClick = () => { - this.props.actions.togglePromoModal(); - - const url = 'https://ghostery.com/thanks-for-downloading-midnight?utm_source=gbe&utm_campaign=in_app'; - sendMessage('openNewTab', { - url, - become_active: true, - }); - } - - /** - * @private - * Handle clicks on the download button in the Premium promo modals - */ - _handlePromoTryPlusClick = () => { - this.props.actions.togglePromoModal(); - - const url = `https://checkout.${DOMAIN}.com/plus?utm_source=gbe&utm_campaign=in_app_spring2020`; - sendMessage('openNewTab', { - url, - become_active: true, - }); - } - - /** - * @private - * Handle clicks on the 'Get Plus' option in the Premium modals - */ - _handlePromoGetPlusClick = () => { - this.props.actions.togglePromoModal(); - - const url = `https://checkout.${DOMAIN}.com/plus?utm_source=gbe&utm_campaign=in_app`; - sendMessage('openNewTab', { - url, - become_active: true, - }); - }; - - // /** - // * @private - // * Handle click action when user selects Subscribe button in the Insights modal - // * @param {string} modal Modal type (insights or plus) - // */ - // _handlePromoSubscribeClick = (modal) => { - // this.props.actions.togglePromoModal(); - - // let url = `https://checkout.${DOMAIN}.com/`; - - // if (modal === 'insights') { - // sendMessage('ping', 'promo_modals_insights_upgrade_cta'); - // url += 'insights?utm_source=gbe&utm_campaign=in_app_upgrade'; - // } - - // sendMessage('openNewTab', { - // url, - // become_active: true, - // }); - // }; - - /** - * @param modal 'insights' or 'premium' - * @private - * Handle clicks on the 'X' close icon in promo modals - */ - _handlePromoXClick = (modal) => { - this.props.actions.togglePromoModal(); - - if (modal === 'insights') { - sendMessage('ping', 'promo_modals_decline_insights_upgrade'); - } - }; - /** * @returns {bool} * @private @@ -355,17 +243,11 @@ class Panel extends React.Component { const isPlus = this._plusSubscriber(); return ( - // -
+ ); } @@ -378,20 +260,12 @@ class Panel extends React.Component { if (this._insightsSubscriber()) return null; sendMessage('promoModals.sawInsightsPromo', {}); - sendMessage('ping', 'promo_modals_show_insights'); - console.log('the props: ', this.props); return ( - - // this._handlePromoGoAwayClick(INSIGHTS)} - // handleSignInClick={() => this._handlePromoSignInClick} - // handleSubscribeClick={() => this._handlePromoSubscribeClick(INSIGHTS)} - // handleXClick={() => this._handlePromoXClick(INSIGHTS)} - // show - // /> + ); } @@ -404,20 +278,13 @@ class Panel extends React.Component { if (this._plusSubscriber() || this._premiumSubscriber()) { return null; } sendMessage('promoModals.sawPlusPromo', {}); - sendMessage('ping', 'promo_modals_show_plus'); const { loggedIn } = this.props; return ( - // -
+ ); } diff --git a/app/panel/components/PlusPromoModal.jsx b/app/panel/components/PlusPromoModal.jsx deleted file mode 100644 index 4b7047cd3..000000000 --- a/app/panel/components/PlusPromoModal.jsx +++ /dev/null @@ -1,86 +0,0 @@ -/** - * PlusPromoModal Component - * - * Ghostery Browser Extension - * https://www.ghostery.com/ - * - * Copyright 2019 Ghostery, Inc. All rights reserved. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0 - */ - -import React from 'react'; -import PropTypes from 'prop-types'; -import ClassNames from 'classnames'; -import Modal from '../../shared-components/Modal'; -import ModalExitButton from './BuildingBlocks/ModalExitButton'; - -/** - * A functional React component for a PlusPromo Modal that may be displayed in the Hub and/or Panel - * @return {JSX} JSX for rendering a PlusPromo Modal - * @memberof SharedComponents - */ -const PlusPromoModal = (props) => { - const { - show, - handleTryPlusClick, - handleSignInClick, - handleGoAwayClick, - handleXClick, - loggedIn - } = props; - - const contentClassNames = ClassNames( - 'PlusPromoModal__content', - 'flex-container', - 'flex-dir-column', - 'align-middle', - ); - - return ( - -
- -
-
-
-
-
{t('spring_has_sprung')}
-
-
-
-
-
-
-
-
- -
-
- {!loggedIn && - {t('already_subscribed_to_plus_sign_in')} - } - {t('no_thanks_turn_promos_off')} -
-
-
- - ); -}; - - -// PropTypes ensure we pass required props of the correct type -PlusPromoModal.propTypes = { - show: PropTypes.bool.isRequired, - handleTryPlusClick: PropTypes.func.isRequired, - handleSignInClick: PropTypes.func.isRequired, - handleGoAwayClick: PropTypes.func.isRequired, - handleXClick: PropTypes.func.isRequired, - loggedIn: PropTypes.bool.isRequired, -}; - -export default PlusPromoModal; diff --git a/app/panel/components/PlusPromoModalContent.jsx b/app/panel/components/PlusPromoModalContent.jsx new file mode 100644 index 000000000..81bca0b0f --- /dev/null +++ b/app/panel/components/PlusPromoModalContent.jsx @@ -0,0 +1,80 @@ +/** + * PlusPromoModal Component + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2019 Ghostery, Inc. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import ClassNames from 'classnames'; + +/** + * A functional React component for a PlusPromo Modal that may be displayed in the Hub and/or Panel + * @return {JSX} JSX for rendering a PlusPromo Modal + * @memberof SharedComponents + */ +const PlusPromoModalContent = (props) => { + const { + handleGoAwayClick, + handleTryPlusClick, + handleSignInClick, + loggedIn, + XButton + } = props; + + const contentClassNames = ClassNames( + 'PlusPromoModal__content', + 'flex-container', + 'flex-dir-column', + 'align-middle', + ); + + return ( +
+ {XButton} +
+
+
+
+
{t('spring_has_sprung')}
+
+
+
+
+
+
+
+
+ +
+
+ {!loggedIn && + {t('already_subscribed_to_plus_sign_in')} + } + {t('no_thanks_turn_promos_off')} +
+
+
+ ); +}; + + +// PropTypes ensure we pass required props of the correct type +PlusPromoModalContent.propTypes = { + handleTryPlusClick: PropTypes.func.isRequired, + handleSignInClick: PropTypes.func.isRequired, + handleGoAwayClick: PropTypes.func.isRequired, + loggedIn: PropTypes.bool.isRequired, + XButton: PropTypes.element.isRequired, +}; + +export default PlusPromoModalContent; diff --git a/app/scss/hub.scss b/app/scss/hub.scss index 9cc1a696b..2ed71890e 100644 --- a/app/scss/hub.scss +++ b/app/scss/hub.scss @@ -76,7 +76,7 @@ html, body, #root { // Imports from ../shared-components directory @import '../shared-components/ExitButton/ExitButton.scss'; @import '../shared-components/Modal/Modal.scss'; -@import '../shared-components/PremiumPromoModal/PremiumPromoModal.scss'; +@import '../shared-components/PremiumPromoModalContent/PremiumPromoModalContent.scss'; @import '../shared-components/SteppedNavigation/SteppedNavigation.scss'; @import '../shared-components/ToastMessage/ToastMessage.scss'; @import '../shared-components/ToggleCheckbox/ToggleCheckbox.scss'; diff --git a/app/scss/panel.scss b/app/scss/panel.scss index b58c2e8aa..7f6807340 100644 --- a/app/scss/panel.scss +++ b/app/scss/panel.scss @@ -80,4 +80,4 @@ html body { // Imports from ../shared-components directory @import '../shared-components/Modal/Modal.scss'; -@import '../shared-components/PremiumPromoModal/PremiumPromoModal.scss'; +@import '../shared-components/PremiumPromoModalContent/PremiumPromoModalContent.scss'; diff --git a/app/shared-components/PremiumPromoModal/PremiumPromoModal.jsx b/app/shared-components/PremiumPromoModal/PremiumPromoModal.jsx deleted file mode 100644 index b1fa7018f..000000000 --- a/app/shared-components/PremiumPromoModal/PremiumPromoModal.jsx +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Premium Promo Modal Component - * - * Ghostery Browser Extension - * https://www.ghostery.com/ - * - * Copyright 2019 Ghostery, Inc. All rights reserved. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0 - */ - -import React from 'react'; -import PropTypes from 'prop-types'; -import ClassNames from 'classnames'; -import Modal from '../Modal/Modal'; -import ModalExitButton from '../../panel/components/BuildingBlocks/ModalExitButton'; - -/** - * A functional React component for a Premium Promo Modal that may be displayed in the Hub and/or Panel - * @return {JSX} JSX for rendering a Premium Promo Modal - * @memberof SharedComponents - */ -const PremiumPromoModal = (props) => { - const { - show, - location, - isPlus, - handleTryMidnightClick, - handleGetPlusClick, - handleKeepBasicClick, - handleGoAwayClick, - handleXClick, - } = props; - - const isInHub = location === 'hub'; - const isInPanel = location === 'panel'; - - const contentClassNames = ClassNames( - 'PremiumPromoModal__content', - 'flex-container', - 'flex-dir-column', - 'align-middle', - ); - - return ( - -
- {isInPanel && ( - - )} -
-
-
-
- {t('try_ghostery_midnight')} -
-
-
-
{t('full_coverage_protection_promise')}
-
-
-
- -
- {t('system_wide_tracker_and_ad_blocking')} -
-
-
- -
- {t('built_in_vpn')} -
-
-
-
-
- -
- {t('historical_tracking_insights')} -
-
-
- -
-
-
-
-
-
-
-
- {t('download_for_free')} -
-
-
- {!isPlus && ( -
- )} - {isInHub && ( -
- {t('no_thanks_continue_with_basic')} -
- )} - {isInPanel && ( -
- {t('no_thanks_turn_promos_off')} -
- )} -
-
-
- - ); -}; - - -// PropTypes ensure we pass required props of the correct type -PremiumPromoModal.propTypes = { - show: PropTypes.bool.isRequired, - location: PropTypes.string.isRequired, - isPlus: PropTypes.bool.isRequired, - handleTryMidnightClick: PropTypes.func.isRequired, - handleGetPlusClick: PropTypes.func.isRequired, - handleKeepBasicClick: PropTypes.func, - handleGoAwayClick: PropTypes.func, - handleXClick: PropTypes.func, -}; - -const noop = () => {}; -PremiumPromoModal.defaultProps = { - handleKeepBasicClick: noop, - handleGoAwayClick: noop, - handleXClick: noop, -}; - -export default PremiumPromoModal; diff --git a/app/shared-components/PremiumPromoModalContent/PremiumPromoModalContent.jsx b/app/shared-components/PremiumPromoModalContent/PremiumPromoModalContent.jsx new file mode 100644 index 000000000..d18bbd5dd --- /dev/null +++ b/app/shared-components/PremiumPromoModalContent/PremiumPromoModalContent.jsx @@ -0,0 +1,129 @@ +/** + * Premium Promo Modal Component + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2019 Ghostery, Inc. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import ClassNames from 'classnames'; + +/** + * A functional React component for a Premium Promo Modal that may be displayed in the Hub and/or Panel + * @return {JSX} JSX for rendering a Premium Promo Modal + * @memberof SharedComponents + */ +const PremiumPromoModalContent = (props) => { + const { + isPlus, + handleTryMidnightClick, + handleGetPlusClick, + handleKeepBasicClick, + handleGoAwayClick, + location, + XButton + } = props; + + const contentClassNames = ClassNames( + 'PremiumPromoModal__content', + 'flex-container', + 'flex-dir-column', + 'align-middle', + ); + + const isInHub = location === 'hub'; + const isInPanel = location === 'panel'; + + return ( +
+ {XButton} +
+
+
+
+ {t('try_ghostery_midnight')} +
+
+
+
{t('full_coverage_protection_promise')}
+
+
+
+ +
+ {t('system_wide_tracker_and_ad_blocking')} +
+
+
+ +
+ {t('built_in_vpn')} +
+
+
+
+
+ +
+ {t('historical_tracking_insights')} +
+
+
+ +
+
+
+
+
+
+
+
+ {t('download_for_free')} +
+
+
+ {!isPlus && ( +
+ )} + {isInHub && ( +
+ {t('no_thanks_continue_with_basic')} +
+ )} + {isInPanel && ( +
+ {t('no_thanks_turn_promos_off')} +
+ )} +
+
+
+ ); +}; + + +// PropTypes ensure we pass required props of the correct type +PremiumPromoModalContent.propTypes = { + location: PropTypes.string.isRequired, + isPlus: PropTypes.bool.isRequired, + handleTryMidnightClick: PropTypes.func.isRequired, + handleGetPlusClick: PropTypes.func.isRequired, + handleKeepBasicClick: PropTypes.func, + handleGoAwayClick: PropTypes.func, + XButton: PropTypes.element.isRequired +}; + +const noop = () => { }; +PremiumPromoModalContent.defaultProps = { + handleKeepBasicClick: noop, + handleGoAwayClick: noop, +}; + +export default PremiumPromoModalContent; diff --git a/app/shared-components/PremiumPromoModal/PremiumPromoModal.scss b/app/shared-components/PremiumPromoModalContent/PremiumPromoModalContent.scss similarity index 100% rename from app/shared-components/PremiumPromoModal/PremiumPromoModal.scss rename to app/shared-components/PremiumPromoModalContent/PremiumPromoModalContent.scss diff --git a/app/shared-components/PremiumPromoModal/index.js b/app/shared-components/PremiumPromoModalContent/index.js similarity index 78% rename from app/shared-components/PremiumPromoModal/index.js rename to app/shared-components/PremiumPromoModalContent/index.js index 9d353dc6c..ee9bf24d2 100644 --- a/app/shared-components/PremiumPromoModal/index.js +++ b/app/shared-components/PremiumPromoModalContent/index.js @@ -11,6 +11,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ -import PremiumPromoModal from './PremiumPromoModal'; +import PremiumPromoModalContent from './PremiumPromoModalContent'; -export default PremiumPromoModal; +export default PremiumPromoModalContent; diff --git a/app/shared-components/PromoModal/PromoModal.jsx b/app/shared-components/PromoModal/PromoModal.jsx index 223630981..86281bb09 100644 --- a/app/shared-components/PromoModal/PromoModal.jsx +++ b/app/shared-components/PromoModal/PromoModal.jsx @@ -13,12 +13,14 @@ import React from 'react'; import PropTypes from 'prop-types'; -import InsightsPromoModal from '../../panel/components/InsightsPromoModal'; -import PlusPromoModal from '../../panel/components/PlusPromoModal'; -import PremiumPromoModal from '../PremiumPromoModal'; +import Modal from '../Modal'; +import InsightsPromoModalContent from '../../panel/components/InsightsPromoModalContent'; +import PlusPromoModalContent from '../../panel/components/PlusPromoModalContent'; +import PremiumPromoModalContent from '../PremiumPromoModalContent'; import history from '../../panel/utils/history'; import { sendMessage } from '../../panel/utils/msg'; import globals from '../../../src/classes/Globals'; +import ModalExitButton from '../../panel/components/BuildingBlocks/ModalExitButton'; const DOMAIN = globals.DEBUG ? 'ghosterystage' : 'ghostery'; const INSIGHTS = 'insights'; @@ -33,25 +35,65 @@ const PREMIUM = 'premium'; class PromoModal extends React.Component { /** + * @param modal 'insights' or 'premium' * @private - * Handle click action when user selects Subscribe button in the Insights modal - * @param {string} modal Modal type (insights or plus) + * Handle clicks on the link to turn off promos in the promo modals */ - _handlePromoSubscribeClick = (modal) => { + _handlePromoGoAwayClick = (modal) => { this.props.actions.togglePromoModal(); - let url = `https://checkout.${DOMAIN}.com/`; + sendMessage('promoModals.turnOffPromos', {}); - if (modal === 'insights') { - sendMessage('ping', 'promo_modals_insights_upgrade_cta'); - url += 'insights?utm_source=gbe&utm_campaign=in_app_upgrade'; + switch (modal) { + case INSIGHTS: + sendMessage('ping', 'promo_modals_decline_insights_upgrade'); + break; + case PLUS: + sendMessage('ping', 'promo_modals_decline_plus_upgrade'); + break; + case PREMIUM: + sendMessage('ping', 'promo_modals_decline_insights_upgrade'); + break; + default: + break; + } + + this.props.actions.showNotification({ + classes: 'warning', + reload: false, + text: t('promos_turned_off_notification'), + }); + }; + + /** + * @private + * Handle clicks on the download buttons + */ + _handlePromoTryProductClick = (product, utm_campaign) => { + this.props.actions.togglePromoModal(); + + let url; + switch (product) { + case PLUS: + // Should we send a plus ping here? + // sendMessage('ping', 'promo_modals_plus_upgrade_cta'); + url = `https://checkout.${DOMAIN}.com/plus?utm_source=gbe&utm_campaign=${utm_campaign}`; + break; + case PREMIUM: + url = `https://ghostery.com/thanks-for-downloading-midnight?utm_source=gbe&utm_campaign=${utm_campaign}`; + break; + case INSIGHTS: + sendMessage('ping', 'promo_modals_insights_upgrade_cta'); + url = `https://checkout.${DOMAIN}.com/insights?utm_source=gbe&utm_campaign=${utm_campaign}`; + break; + default: } sendMessage('openNewTab', { url, become_active: true, }); - }; + } /** * @private @@ -64,91 +106,88 @@ class PromoModal extends React.Component { }); }; - _handleGoAwayClick = (type) => { this.props.handleGoAwayClick(type); } - - _handleSubscribeClick = (type) => { this.props.handleSubscribeClick(type); } - _handlePromoXClick = (type) => { this.props.actions.togglePromoModal(); - if (type === 'insights') { + if (type === INSIGHTS) { sendMessage('ping', 'promo_modals_decline_insights_upgrade'); } + + if (type === PLUS) { + sendMessage('ping', 'promo_modals_decline_plus_upgrade'); + } } renderModalContent() { - const { type } = this.props; + const { type, loggedIn } = this.props; switch (type) { case INSIGHTS: return ( - this._handlePromoSubscribeClick(type)} - show + this._handlePromoGoAwayClick(INSIGHTS)} + handleTryInsightsClick={() => this._handlePromoTryProductClick(INSIGHTS, 'in_app_upgrade')} + handleSignInClick={this._handlePromoSignInClick} + XButton={( + this._handlePromoXClick(INSIGHTS)} + /> + )} + {...this.props} /> ); case PLUS: - return ; - case PREMIUM: - return ; - default: - return ; - } - } - - render() { - // return ( - // - //
- // {this.renderModalContent()} - //
- // - // ); - - const { type } = this.props; - switch (type) { - case INSIGHTS: return ( - this._handlePromoGoAwayClick(PLUS)} + handleTryPlusClick={() => this._handlePromoTryProductClick(PLUS, 'in_app_spring2020')} handleSignInClick={this._handlePromoSignInClick} - handleSubscribeClick={() => this._handlePromoSubscribeClick(type)} - handleXClick={() => this._handlePromoXClick(type)} - show + XButton={( + this._handlePromoXClick(PLUS)} + /> + )} + loggedIn={loggedIn} /> ); - case PLUS: - return ; case PREMIUM: - return ; + return ( + this._handlePromoGoAwayClick(PREMIUM)} + handleTryMidnightClick={() => this._handlePromoTryProductClick(PREMIUM, 'in_app')} + handleGetPlusClick={() => this._handlePromoTryProductClick(PLUS, 'in_app')} + XButton={( + this._handlePromoXClick(PREMIUM)} + /> + )} + {...this.props} + /> + ); default: - return ; + return ; } } + + render() { + return ( + + {this.renderModalContent()} + + ); + } } // PropTypes ensure we pass required props of the correct type PromoModal.propTypes = { type: PropTypes.string.isRequired, - handleGoAwayClick: PropTypes.func, - handleSignInClick: PropTypes.func, - handleSubscribeClick: PropTypes.func, - handleXClick: PropTypes.func, - handleTryMidnightClick: PropTypes.func, - handleGetPlusClick: PropTypes.func, - handleKeepBasicClick: PropTypes.func, location: PropTypes.string, isPlus: PropTypes.bool, }; -const noop = () => { }; PromoModal.defaultProps = { - handleGoAwayClick: noop, - handleSignInClick: noop, - handleSubscribeClick: noop, - handleXClick: noop, - handleTryMidnightClick: noop, - handleGetPlusClick: noop, - handleKeepBasicClick: noop, location: 'panel', isPlus: false, }; diff --git a/app/shared-components/PromoModal/PromoModalContainer.js b/app/shared-components/PromoModal/PromoModalContainer.js index 819fd9dec..50585c692 100644 --- a/app/shared-components/PromoModal/PromoModalContainer.js +++ b/app/shared-components/PromoModal/PromoModalContainer.js @@ -16,7 +16,7 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import PromoModal from './PromoModal'; -import { togglePromoModal } from '../../panel/actions/PanelActions'; // get shared actions from Panel +import { togglePromoModal, showNotification } from '../../panel/actions/PanelActions'; // get shared actions from Panel /** * Map redux store state properties to PromoModal component own properties. @@ -39,7 +39,12 @@ import { togglePromoModal } from '../../panel/actions/PanelActions'; // get shar * @return {function} to be used as an argument in redux connect call */ const mapDispatchToProps = dispatch => ({ - actions: bindActionCreators(Object.assign({ togglePromoModal }), dispatch) + actions: bindActionCreators( + Object.assign({ + togglePromoModal, + showNotification + }), dispatch + ) }); /** * Connects PromoModal component to the Redux store. diff --git a/app/shared-components/index.js b/app/shared-components/index.js index 8cea5f1ae..a9d0e5d80 100644 --- a/app/shared-components/index.js +++ b/app/shared-components/index.js @@ -17,7 +17,7 @@ import ExitButton from './ExitButton'; import Modal from './Modal'; -import PremiumPromoModal from './PremiumPromoModal'; +import PremiumPromoModalContent from './PremiumPromoModalContent'; import SteppedNavigation from './SteppedNavigation'; import ToastMessage from './ToastMessage'; import ToggleCheckbox from './ToggleCheckbox'; @@ -26,7 +26,7 @@ import ToggleSwitch from './ToggleSwitch'; export { ExitButton, Modal, - PremiumPromoModal, + PremiumPromoModalContent, SteppedNavigation, ToastMessage, ToggleCheckbox, diff --git a/src/classes/PromoModals.js b/src/classes/PromoModals.js index 8d753694a..8baca2551 100644 --- a/src/classes/PromoModals.js +++ b/src/classes/PromoModals.js @@ -18,7 +18,7 @@ import panelData from './PanelData'; const DAYS_BETWEEN_PROMOS = { premium: globals.DEBUG ? 0.0005 : 30, // 40 seconds on staging insights: globals.DEBUG ? 0.0005 : 30, // 40 seconds on staging - plus: globals.DEBUG ? 0.0005 : 2 // 40 seconds on staging + plus: globals.DEBUG ? 0.0005 : 7 // 40 seconds on staging }; const WEEKLY_INSIGHTS_TARGET = globals.DEBUG ? 1 : 3; const DAILY_INSIGHTS_TARGET = globals.DEBUG ? 2 : 3; @@ -54,7 +54,9 @@ class PromoModals { static recordPlusPromoSighting() { this._recordPromoSighting(PLUS); } - static turnOffPromos() { panelData.set({ notify_promotions: false }); } + static turnOffPromos() { + panelData.set({ notify_promotions: false }); + } /** * Check Conf values to determine if the enough time has From 70c9fa115ed20bab56c382881a2f67880486371a Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 7 Apr 2020 16:11:00 -0400 Subject: [PATCH 10/15] Fix error --- app/panel/containers/PlusPromoModalContainer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/panel/containers/PlusPromoModalContainer.js b/app/panel/containers/PlusPromoModalContainer.js index c01b3d97d..f54e64bad 100644 --- a/app/panel/containers/PlusPromoModalContainer.js +++ b/app/panel/containers/PlusPromoModalContainer.js @@ -13,7 +13,7 @@ import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; -import PlusPromoModal from '../components/PlusPromoModal'; +import PlusPromoModal from '../components/PlusPromoModalContent'; /** * Map redux store state properties to Panel view component own properties. * @memberOf PanelContainers From 56d07164f4252ec8ccefc99c62d2cc82f4fff8f3 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 7 Apr 2020 18:18:14 -0400 Subject: [PATCH 11/15] Refactor X button out to PromoModal.jsx. Move Insights and Plus PromoModalContent out to shared-components --- app/panel/components/Panel.jsx | 4 ++ .../containers/PlusPromoModalContainer.js | 2 +- app/scss/panel.scss | 4 +- .../InsightsPromoModalContent.jsx | 4 +- .../InsightsPromoModalContent.scss} | 5 ++ .../InsightsPromoModalContent/index.js | 16 ++++++ .../PlusPromoModalContent.jsx | 13 +---- .../PlusPromoModalContent.scss} | 0 .../PlusPromoModalContent/index.js | 16 ++++++ .../PremiumPromoModalContent.jsx | 13 +---- .../PremiumPromoModalContent.scss | 0 .../PremiumPromoModalContent/index.js | 0 .../PromoModal/PromoModal.jsx | 53 ++++++++++--------- manifest.json | 1 - 14 files changed, 75 insertions(+), 56 deletions(-) rename app/{panel/components => shared-components/ModalContent/InsightsPromoModalContent}/InsightsPromoModalContent.jsx (95%) rename app/{scss/partials/_insights_promo_modal.scss => shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.scss} (97%) create mode 100644 app/shared-components/ModalContent/InsightsPromoModalContent/index.js rename app/{panel/components => shared-components/ModalContent/PlusPromoModalContent}/PlusPromoModalContent.jsx (88%) rename app/{scss/partials/_plus_promo_modal.scss => shared-components/ModalContent/PlusPromoModalContent/PlusPromoModalContent.scss} (100%) create mode 100644 app/shared-components/ModalContent/PlusPromoModalContent/index.js rename app/shared-components/{ => ModalContent}/PremiumPromoModalContent/PremiumPromoModalContent.jsx (93%) rename app/shared-components/{ => ModalContent}/PremiumPromoModalContent/PremiumPromoModalContent.scss (100%) rename app/shared-components/{ => ModalContent}/PremiumPromoModalContent/index.js (100%) diff --git a/app/panel/components/Panel.jsx b/app/panel/components/Panel.jsx index 210a486e3..7c1606d83 100644 --- a/app/panel/components/Panel.jsx +++ b/app/panel/components/Panel.jsx @@ -309,6 +309,10 @@ class Panel extends React.Component { return this._renderPlusPromoModal(); } + if (promoModal === 'premium') { + return this._renderPremiumPromoModal(); + } + return null; } diff --git a/app/panel/containers/PlusPromoModalContainer.js b/app/panel/containers/PlusPromoModalContainer.js index f54e64bad..953ab22bf 100644 --- a/app/panel/containers/PlusPromoModalContainer.js +++ b/app/panel/containers/PlusPromoModalContainer.js @@ -13,7 +13,7 @@ import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; -import PlusPromoModal from '../components/PlusPromoModalContent'; +import PlusPromoModal from '../../shared-components/ModalContent/PlusPromoModalContent/PlusPromoModalContent'; /** * Map redux store state properties to Panel view component own properties. * @memberOf PanelContainers diff --git a/app/scss/panel.scss b/app/scss/panel.scss index 7f6807340..18e1bb053 100644 --- a/app/scss/panel.scss +++ b/app/scss/panel.scss @@ -75,9 +75,9 @@ html body { @import './partials/_stats'; @import './partials/_stats_graph'; @import './partials/_modal_exit_button'; -@import './partials/insights_promo_modal.scss'; -@import './partials/plus_promo_modal.scss'; // Imports from ../shared-components directory @import '../shared-components/Modal/Modal.scss'; @import '../shared-components/PremiumPromoModalContent/PremiumPromoModalContent.scss'; +@import '../shared-components/PlusPromoModalContent/PlusPromoModalContent.scss'; +@import '../shared-components/InsightsPromoModalContent/InsightsPromoModalContent.scss'; diff --git a/app/panel/components/InsightsPromoModalContent.jsx b/app/shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.jsx similarity index 95% rename from app/panel/components/InsightsPromoModalContent.jsx rename to app/shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.jsx index 0debb3dfc..d2f4b7190 100644 --- a/app/panel/components/InsightsPromoModalContent.jsx +++ b/app/shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.jsx @@ -18,11 +18,9 @@ const InsightsPromoModalContent = (props) => { handleGoAwayClick, handleSignInClick, handleTryInsightsClick, - XButton, } = props; return ( -
- {XButton} +
{t('panel_insights_promotion_header')} diff --git a/app/scss/partials/_insights_promo_modal.scss b/app/shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.scss similarity index 97% rename from app/scss/partials/_insights_promo_modal.scss rename to app/shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.scss index 6ef2c2adb..4de8768ff 100644 --- a/app/scss/partials/_insights_promo_modal.scss +++ b/app/shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.scss @@ -26,6 +26,11 @@ $condensed-font-family: Roboto Condensed, "Open Sans", "Helvetica Neue", Helveti z-index: 10; } +.InsightsModal__container { + min-height: 450px; + justify-content: space-between; +} + .InsightsModal__image { height: 94px; width: 177px; diff --git a/app/shared-components/ModalContent/InsightsPromoModalContent/index.js b/app/shared-components/ModalContent/InsightsPromoModalContent/index.js new file mode 100644 index 000000000..9708db36b --- /dev/null +++ b/app/shared-components/ModalContent/InsightsPromoModalContent/index.js @@ -0,0 +1,16 @@ +/** + * Point of entry index.js file for InsightsPromoModalContent Component + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2019 Ghostery, Inc. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + +import InsightsPromoModalContent from './InsightsPromoModalContent'; + +export default InsightsPromoModalContent; diff --git a/app/panel/components/PlusPromoModalContent.jsx b/app/shared-components/ModalContent/PlusPromoModalContent/PlusPromoModalContent.jsx similarity index 88% rename from app/panel/components/PlusPromoModalContent.jsx rename to app/shared-components/ModalContent/PlusPromoModalContent/PlusPromoModalContent.jsx index 81bca0b0f..b938559e5 100644 --- a/app/panel/components/PlusPromoModalContent.jsx +++ b/app/shared-components/ModalContent/PlusPromoModalContent/PlusPromoModalContent.jsx @@ -13,7 +13,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import ClassNames from 'classnames'; /** * A functional React component for a PlusPromo Modal that may be displayed in the Hub and/or Panel @@ -26,19 +25,10 @@ const PlusPromoModalContent = (props) => { handleTryPlusClick, handleSignInClick, loggedIn, - XButton } = props; - const contentClassNames = ClassNames( - 'PlusPromoModal__content', - 'flex-container', - 'flex-dir-column', - 'align-middle', - ); - return ( -
- {XButton} +
@@ -74,7 +64,6 @@ PlusPromoModalContent.propTypes = { handleSignInClick: PropTypes.func.isRequired, handleGoAwayClick: PropTypes.func.isRequired, loggedIn: PropTypes.bool.isRequired, - XButton: PropTypes.element.isRequired, }; export default PlusPromoModalContent; diff --git a/app/scss/partials/_plus_promo_modal.scss b/app/shared-components/ModalContent/PlusPromoModalContent/PlusPromoModalContent.scss similarity index 100% rename from app/scss/partials/_plus_promo_modal.scss rename to app/shared-components/ModalContent/PlusPromoModalContent/PlusPromoModalContent.scss diff --git a/app/shared-components/ModalContent/PlusPromoModalContent/index.js b/app/shared-components/ModalContent/PlusPromoModalContent/index.js new file mode 100644 index 000000000..06264f719 --- /dev/null +++ b/app/shared-components/ModalContent/PlusPromoModalContent/index.js @@ -0,0 +1,16 @@ +/** + * Point of entry index.js file for PlusPromoModalContent Component + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2019 Ghostery, Inc. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + +import PlusPromoModalContent from './PlusPromoModalContent'; + +export default PlusPromoModalContent; diff --git a/app/shared-components/PremiumPromoModalContent/PremiumPromoModalContent.jsx b/app/shared-components/ModalContent/PremiumPromoModalContent/PremiumPromoModalContent.jsx similarity index 93% rename from app/shared-components/PremiumPromoModalContent/PremiumPromoModalContent.jsx rename to app/shared-components/ModalContent/PremiumPromoModalContent/PremiumPromoModalContent.jsx index d18bbd5dd..b4f925cfe 100644 --- a/app/shared-components/PremiumPromoModalContent/PremiumPromoModalContent.jsx +++ b/app/shared-components/ModalContent/PremiumPromoModalContent/PremiumPromoModalContent.jsx @@ -13,7 +13,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import ClassNames from 'classnames'; /** * A functional React component for a Premium Promo Modal that may be displayed in the Hub and/or Panel @@ -28,22 +27,13 @@ const PremiumPromoModalContent = (props) => { handleKeepBasicClick, handleGoAwayClick, location, - XButton } = props; - const contentClassNames = ClassNames( - 'PremiumPromoModal__content', - 'flex-container', - 'flex-dir-column', - 'align-middle', - ); - const isInHub = location === 'hub'; const isInPanel = location === 'panel'; return ( -
- {XButton} +
@@ -117,7 +107,6 @@ PremiumPromoModalContent.propTypes = { handleGetPlusClick: PropTypes.func.isRequired, handleKeepBasicClick: PropTypes.func, handleGoAwayClick: PropTypes.func, - XButton: PropTypes.element.isRequired }; const noop = () => { }; diff --git a/app/shared-components/PremiumPromoModalContent/PremiumPromoModalContent.scss b/app/shared-components/ModalContent/PremiumPromoModalContent/PremiumPromoModalContent.scss similarity index 100% rename from app/shared-components/PremiumPromoModalContent/PremiumPromoModalContent.scss rename to app/shared-components/ModalContent/PremiumPromoModalContent/PremiumPromoModalContent.scss diff --git a/app/shared-components/PremiumPromoModalContent/index.js b/app/shared-components/ModalContent/PremiumPromoModalContent/index.js similarity index 100% rename from app/shared-components/PremiumPromoModalContent/index.js rename to app/shared-components/ModalContent/PremiumPromoModalContent/index.js diff --git a/app/shared-components/PromoModal/PromoModal.jsx b/app/shared-components/PromoModal/PromoModal.jsx index 86281bb09..8675c3e51 100644 --- a/app/shared-components/PromoModal/PromoModal.jsx +++ b/app/shared-components/PromoModal/PromoModal.jsx @@ -13,10 +13,11 @@ import React from 'react'; import PropTypes from 'prop-types'; +import ClassNames from 'classnames'; import Modal from '../Modal'; -import InsightsPromoModalContent from '../../panel/components/InsightsPromoModalContent'; -import PlusPromoModalContent from '../../panel/components/PlusPromoModalContent'; -import PremiumPromoModalContent from '../PremiumPromoModalContent'; +import InsightsPromoModalContent from '../ModalContent/InsightsPromoModalContent'; +import PlusPromoModalContent from '../ModalContent/PlusPromoModalContent'; +import PremiumPromoModalContent from '../ModalContent/PremiumPromoModalContent'; import history from '../../panel/utils/history'; import { sendMessage } from '../../panel/utils/msg'; import globals from '../../../src/classes/Globals'; @@ -111,13 +112,24 @@ class PromoModal extends React.Component { if (type === INSIGHTS) { sendMessage('ping', 'promo_modals_decline_insights_upgrade'); - } - - if (type === PLUS) { + } else if (type === PLUS) { sendMessage('ping', 'promo_modals_decline_plus_upgrade'); } } + _renderXButton = (type) => { + const XButtonClass = type === PLUS + ? 'PlusPromoModal__exitButton' + : 'InsightsModal__exitButton'; + + return ( + this._handlePromoXClick(type)} + /> + ); + }; + renderModalContent() { const { type, loggedIn } = this.props; switch (type) { @@ -127,12 +139,6 @@ class PromoModal extends React.Component { handleGoAwayClick={() => this._handlePromoGoAwayClick(INSIGHTS)} handleTryInsightsClick={() => this._handlePromoTryProductClick(INSIGHTS, 'in_app_upgrade')} handleSignInClick={this._handlePromoSignInClick} - XButton={( - this._handlePromoXClick(INSIGHTS)} - /> - )} {...this.props} /> ); @@ -142,12 +148,6 @@ class PromoModal extends React.Component { handleGoAwayClick={() => this._handlePromoGoAwayClick(PLUS)} handleTryPlusClick={() => this._handlePromoTryProductClick(PLUS, 'in_app_spring2020')} handleSignInClick={this._handlePromoSignInClick} - XButton={( - this._handlePromoXClick(PLUS)} - /> - )} loggedIn={loggedIn} /> ); @@ -157,12 +157,6 @@ class PromoModal extends React.Component { handleGoAwayClick={() => this._handlePromoGoAwayClick(PREMIUM)} handleTryMidnightClick={() => this._handlePromoTryProductClick(PREMIUM, 'in_app')} handleGetPlusClick={() => this._handlePromoTryProductClick(PLUS, 'in_app')} - XButton={( - this._handlePromoXClick(PREMIUM)} - /> - )} {...this.props} /> ); @@ -172,9 +166,18 @@ class PromoModal extends React.Component { } render() { + const { type } = this.props; + const modalContentClassNames = ClassNames('', { + InsightsModal__content: type === INSIGHTS, + PlusPromoModal__content: type === PLUS, + PremiumPromoModal__content: type === PREMIUM, + }); return ( - {this.renderModalContent()} +
+ {this._renderXButton(type)} + {this.renderModalContent()} +
); } diff --git a/manifest.json b/manifest.json index 48078e881..af7390984 100644 --- a/manifest.json +++ b/manifest.json @@ -105,5 +105,4 @@ "cliqz/offers-reminder/index.html", "cliqz/popup-notification/images/*" ], - "debug": true } From 4f187afe5181930d0816d725db985b5dc610cd34 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 7 Apr 2020 18:32:53 -0400 Subject: [PATCH 12/15] Fix imports and remove debug flag from manifest.json --- app/scss/hub.scss | 2 +- app/scss/panel.scss | 6 +++--- app/shared-components/index.js | 6 +++++- manifest.json | 3 +-- src/classes/PromoModals.js | 4 ++-- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/app/scss/hub.scss b/app/scss/hub.scss index 2ed71890e..ca5c92252 100644 --- a/app/scss/hub.scss +++ b/app/scss/hub.scss @@ -76,7 +76,7 @@ html, body, #root { // Imports from ../shared-components directory @import '../shared-components/ExitButton/ExitButton.scss'; @import '../shared-components/Modal/Modal.scss'; -@import '../shared-components/PremiumPromoModalContent/PremiumPromoModalContent.scss'; +@import '../shared-components/ModalContent/PremiumPromoModalContent/PremiumPromoModalContent.scss'; @import '../shared-components/SteppedNavigation/SteppedNavigation.scss'; @import '../shared-components/ToastMessage/ToastMessage.scss'; @import '../shared-components/ToggleCheckbox/ToggleCheckbox.scss'; diff --git a/app/scss/panel.scss b/app/scss/panel.scss index 18e1bb053..91a8bba1c 100644 --- a/app/scss/panel.scss +++ b/app/scss/panel.scss @@ -78,6 +78,6 @@ html body { // Imports from ../shared-components directory @import '../shared-components/Modal/Modal.scss'; -@import '../shared-components/PremiumPromoModalContent/PremiumPromoModalContent.scss'; -@import '../shared-components/PlusPromoModalContent/PlusPromoModalContent.scss'; -@import '../shared-components/InsightsPromoModalContent/InsightsPromoModalContent.scss'; +@import '../shared-components/ModalContent/PremiumPromoModalContent/PremiumPromoModalContent.scss'; +@import '../shared-components/ModalContent/PlusPromoModalContent/PlusPromoModalContent.scss'; +@import '../shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.scss'; diff --git a/app/shared-components/index.js b/app/shared-components/index.js index a9d0e5d80..e7afa1809 100644 --- a/app/shared-components/index.js +++ b/app/shared-components/index.js @@ -17,7 +17,9 @@ import ExitButton from './ExitButton'; import Modal from './Modal'; -import PremiumPromoModalContent from './PremiumPromoModalContent'; +import PremiumPromoModalContent from './ModalContent/PremiumPromoModalContent'; +import PlusPromoModalContent from './ModalContent/PlusPromoModalContent'; +import InsightsPromoModalContent from './ModalContent/InsightsPromoModalContent'; import SteppedNavigation from './SteppedNavigation'; import ToastMessage from './ToastMessage'; import ToggleCheckbox from './ToggleCheckbox'; @@ -27,6 +29,8 @@ export { ExitButton, Modal, PremiumPromoModalContent, + PlusPromoModalContent, + InsightsPromoModalContent, SteppedNavigation, ToastMessage, ToggleCheckbox, diff --git a/manifest.json b/manifest.json index af7390984..d7f5401ee 100644 --- a/manifest.json +++ b/manifest.json @@ -7,7 +7,6 @@ "version_name": "8.4.7", "default_locale": "en", "description": "__MSG_short_description__", - "debug": true, "log": true, "icons": { "16": "app/images/icon16.png", @@ -104,5 +103,5 @@ "cliqz/offers-cc/index.html", "cliqz/offers-reminder/index.html", "cliqz/popup-notification/images/*" - ], + ] } diff --git a/src/classes/PromoModals.js b/src/classes/PromoModals.js index 8baca2551..2f49320a9 100644 --- a/src/classes/PromoModals.js +++ b/src/classes/PromoModals.js @@ -18,10 +18,10 @@ import panelData from './PanelData'; const DAYS_BETWEEN_PROMOS = { premium: globals.DEBUG ? 0.0005 : 30, // 40 seconds on staging insights: globals.DEBUG ? 0.0005 : 30, // 40 seconds on staging - plus: globals.DEBUG ? 0.0005 : 7 // 40 seconds on staging + plus: globals.DEBUG ? 0.0005 : 30 // 40 seconds on staging }; const WEEKLY_INSIGHTS_TARGET = globals.DEBUG ? 1 : 3; -const DAILY_INSIGHTS_TARGET = globals.DEBUG ? 2 : 3; +const DAILY_INSIGHTS_TARGET = globals.DEBUG ? 7 : 3; const MSECS_IN_DAY = 86400000; // 1000 msecs-in-sec * 60 secs-in-min * 60 mins-in-hour * 24 hours-in-day const PREMIUM = 'premium'; From ea8f83f89e16b2a94c581bcef53da2041c40fdc9 Mon Sep 17 00:00:00 2001 From: Ethan Gooding Date: Wed, 8 Apr 2020 12:04:25 -0400 Subject: [PATCH 13/15] Use React.Fragment, update class names, factor out repeat code, update lint file --- .eslintrc.js | 1 + app/panel/components/Rewards.jsx | 6 +-- .../InsightsPromoModalContent.jsx | 42 +++++++++---------- .../InsightsPromoModalContent.scss | 27 +++++------- .../PromoModal/PromoModal.jsx | 19 +++++---- 5 files changed, 47 insertions(+), 48 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 1a07c41f3..d1af6c8b8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -83,6 +83,7 @@ module.exports = { 'react/no-access-state-in-setstate': [0], // TODO: enable this check 'react/no-danger': [0], 'react/prop-types': [0], + 'react/jsx-fragments': [1, 'element'], 'react/sort-comp': [2, { order: [ "static-variables", diff --git a/app/panel/components/Rewards.jsx b/app/panel/components/Rewards.jsx index 977e47615..cd3bb844f 100644 --- a/app/panel/components/Rewards.jsx +++ b/app/panel/components/Rewards.jsx @@ -11,7 +11,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ -import React from 'react'; +import React, { Fragment } from 'react'; import ClassNames from 'classnames'; import { Route } from 'react-router-dom'; import { ToggleSlider } from './BuildingBlocks'; @@ -266,7 +266,7 @@ class Rewards extends React.Component { const src = chrome.runtime.getURL('cliqz/offers-cc/index.html?cross-origin'); const text = t(`panel_rewards_view__reward${rewardsCount === 1 ? '' : 's'}`); return ( - <> + {is_expanded && (
{rewardsCount}
@@ -281,7 +281,7 @@ class Rewards extends React.Component { height={iframeHeight} title="myoffrz-rewards" /> - + ); } diff --git a/app/shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.jsx b/app/shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.jsx index d2f4b7190..129a88e45 100644 --- a/app/shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.jsx +++ b/app/shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.jsx @@ -11,7 +11,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ -import React from 'react'; +import React, { Fragment } from 'react'; const InsightsPromoModalContent = (props) => { const { @@ -20,56 +20,56 @@ const InsightsPromoModalContent = (props) => { handleTryInsightsClick, } = props; return ( -
-
-
+ +
+
{t('panel_insights_promotion_header')}
-
+
{t('panel_insights_promotion_description')}
-
+
- -
+ +
{t('panel_insights_audit_tags')}
- -
+ +
{t('panel_insights_promotion_trace_poor_performance')}
-
+
- -
+ +
{t('panel_insights_promotion_watch_pings')}
- -
+ +
{t('panel_insights_promotion_explore_trends')}
-
+
- + {t('panel_insights_promotion_call_to_action')}
-
- {t('subscribe_pitch_sign_in')} - {t('no_thanks_turn_promos_off')} +
+ {t('subscribe_pitch_sign_in')} + {t('no_thanks_turn_promos_off')}
-
+ ); }; diff --git a/app/shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.scss b/app/shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.scss index 4de8768ff..7f1d192bb 100644 --- a/app/shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.scss +++ b/app/shared-components/ModalContent/InsightsPromoModalContent/InsightsPromoModalContent.scss @@ -15,7 +15,7 @@ $standard-font-family: Roboto, "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; $condensed-font-family: Roboto Condensed, "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; -.InsightsModal__content { +.InsightsPromoModal__content { background-color: $alabaster; position: relative; width: 518px; @@ -26,19 +26,14 @@ $condensed-font-family: Roboto Condensed, "Open Sans", "Helvetica Neue", Helveti z-index: 10; } -.InsightsModal__container { - min-height: 450px; - justify-content: space-between; -} - -.InsightsModal__image { +.InsightsPromoModal__image { height: 94px; width: 177px; margin-top: 20px; background-image: url('/app/images/panel/insights-ribbon.svg'); } -.InsightsModal__header { +.InsightsPromoModal__header { font-family: $standard-font-family; height: 27.1px; font-size: 20px; @@ -48,7 +43,7 @@ $condensed-font-family: Roboto Condensed, "Open Sans", "Helvetica Neue", Helveti margin-bottom: 10px; } -.InsightsModal__description { +.InsightsPromoModal__description { width: 400px; font-size: 18px; font-weight: 500; @@ -57,7 +52,7 @@ $condensed-font-family: Roboto Condensed, "Open Sans", "Helvetica Neue", Helveti font-family: $standard-font-family; } -.InsightsModal__features { +.InsightsPromoModal__features { &:nth-child(odd) { margin-left: 25px; width: 50%; @@ -69,7 +64,7 @@ $condensed-font-family: Roboto Condensed, "Open Sans", "Helvetica Neue", Helveti } } -.InsightsModal__feature-text { +.InsightsPromoModal__feature-text { font-family: $standard-font-family; font-size: 14px; color: $medium-gray; @@ -77,7 +72,7 @@ $condensed-font-family: Roboto Condensed, "Open Sans", "Helvetica Neue", Helveti line-height: 16px; } -.InsightsModal__checked-circle-icon { +.InsightsPromoModal__checked-circle-icon { flex: none; height: 18px; width: 18px; @@ -86,7 +81,7 @@ $condensed-font-family: Roboto Condensed, "Open Sans", "Helvetica Neue", Helveti align-self: flex-start; } -.InsightsModal__call-to-action-container { +.InsightsPromoModal__call-to-action-container { height: 95px; width: 99%; margin-top: 10px; @@ -96,7 +91,7 @@ $condensed-font-family: Roboto Condensed, "Open Sans", "Helvetica Neue", Helveti flex-direction: column; } -.InsightsModal__call-to-action { +.InsightsPromoModal__call-to-action { cursor: pointer; display: flex; justify-content: center; @@ -116,7 +111,7 @@ $condensed-font-family: Roboto Condensed, "Open Sans", "Helvetica Neue", Helveti } } -.InsightsModal__link { +.InsightsPromoModal__link { font-family: $standard-font-family; font-size: 13px; color: $tundora; @@ -126,7 +121,7 @@ $condensed-font-family: Roboto Condensed, "Open Sans", "Helvetica Neue", Helveti } } -.InsightsModal__other-options-container { +.InsightsPromoModal__other-options-container { margin-top: 10px; padding: 0 10.5px; text-decoration: underline; diff --git a/app/shared-components/PromoModal/PromoModal.jsx b/app/shared-components/PromoModal/PromoModal.jsx index 8675c3e51..ef39b64e0 100644 --- a/app/shared-components/PromoModal/PromoModal.jsx +++ b/app/shared-components/PromoModal/PromoModal.jsx @@ -118,9 +118,7 @@ class PromoModal extends React.Component { } _renderXButton = (type) => { - const XButtonClass = type === PLUS - ? 'PlusPromoModal__exitButton' - : 'InsightsModal__exitButton'; + const XButtonClass = ClassNames({ PlusPromoModal__exitButton: type === PLUS }); return (
From e3140f55977a5e5f9ac0136719e444ea7a58db8b Mon Sep 17 00:00:00 2001 From: Ethan Gooding Date: Wed, 8 Apr 2020 12:14:37 -0400 Subject: [PATCH 14/15] Update InsightsPromoModal class name and fix display --- app/shared-components/PromoModal/PromoModal.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/shared-components/PromoModal/PromoModal.jsx b/app/shared-components/PromoModal/PromoModal.jsx index ef39b64e0..265a47826 100644 --- a/app/shared-components/PromoModal/PromoModal.jsx +++ b/app/shared-components/PromoModal/PromoModal.jsx @@ -170,7 +170,7 @@ class PromoModal extends React.Component { 'flex-dir-column', 'align-middle', { - InsightsModal__content: type === INSIGHTS, + InsightsPromoModal__content: type === INSIGHTS, PlusPromoModal__content: type === PLUS, PremiumPromoModal__content: type === PREMIUM, } From 406fec5e9d2b4d1fb31ee2ef35b2699505142cd7 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 15 Apr 2020 20:08:06 -0400 Subject: [PATCH 15/15] Fix premium promo modal in hub. Refactor promo modal pings. Style modal exit button. --- .../BuildingBlocks/ModalExitButton.jsx | 5 +-- app/panel/components/Panel.jsx | 3 ++ .../containers/PlusPromoModalContainer.js | 28 ----------------- .../PromoModal/PromoModal.jsx | 31 ++++++++----------- 4 files changed, 17 insertions(+), 50 deletions(-) delete mode 100644 app/panel/containers/PlusPromoModalContainer.js diff --git a/app/panel/components/BuildingBlocks/ModalExitButton.jsx b/app/panel/components/BuildingBlocks/ModalExitButton.jsx index 81c13b2e1..50b7a604e 100644 --- a/app/panel/components/BuildingBlocks/ModalExitButton.jsx +++ b/app/panel/components/BuildingBlocks/ModalExitButton.jsx @@ -14,7 +14,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import ClassNames from 'classnames'; /** * A Functional React component for a Exit Button @@ -27,9 +26,7 @@ const ModalExitButton = (props) => { border } = props; - const borderClassNames = ClassNames('ModalExitButton__exit flex-container align-middle', { - green: border === 'green' - }); + const borderClassNames = `ModalExitButton__exit flex-container align-middle ${border}`; return (