Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Ghostery Plus & Insights Promo Modals #464

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

Always

Just for now

Prioritize showing Insights upgrade promo over Plus upgrade promo whe…

…n conditions for both are met
  • Loading branch information
wlycdgr committed Oct 24, 2019
commit 93369c8580a4a25f3dda78323f97188eb4dd951a
@@ -18,7 +18,7 @@ import {
TOGGLE_EXPERT,
SET_THEME,
CLEAR_THEME,
TOGGLE_INSIGHTS_MODAL
TOGGLE_PROMO_MODAL
} from '../constants/constants';
import { sendMessageInPromise } from '../utils/msg';

@@ -104,8 +104,8 @@ export const getTheme = name => dispatch => (
* Triggered when the user signs in through the Insights modal into an account that does not have an insights subscription, prompting to re-display the modal, requiring a re-render
* @return {Object}
*/
export function toggleInsightsModal() {
export function togglePromoModal() {
return {
type: TOGGLE_INSIGHTS_MODAL,
type: TOGGLE_PROMO_MODAL,
};
}
@@ -22,14 +22,14 @@ class InsightsPromoModal extends React.Component {
history.push({
pathname: '/login',
});
this.props.actions.toggleInsightsModal();
this.props.actions.togglePromoModal();
};

render() {
return (
<Modal show>
<div className="InsightsModal__content flex-container flex-dir-column align-middle">
<ModalExitButton className="InsightsModal__exitButton" toggleModal={this.props.actions.toggleInsightsModal} />
<ModalExitButton className="InsightsModal__exitButton" toggleModal={this.props.actions.togglePromoModal} />
<div className="InsightsModal__image" />
<div className="InsightsModal__header">
{t('panel_insights_promotion_header')}
@@ -75,7 +75,7 @@ class InsightsPromoModal extends React.Component {
</div>
<div className="InsightsModal__other-options-container flex-container align-justify">
<span onClick={this.clickSignIn} className="InsightsModal__link">{t('subscribe_pitch_sign_in')}</span>
<span onClick={this.props.actions.toggleInsightsModal} className="InsightsModal__link">{t('subscribe_pitch_no_thanks')}</span>
<span onClick={this.props.actions.togglePromoModal} className="InsightsModal__link">{t('subscribe_pitch_no_thanks')}</span>
</div>
</div>
</div>
@@ -73,7 +73,7 @@ class Login extends React.Component {
})
.finally(() => {
this.setState({ loading: false }, () => {
this.props.actions.toggleInsightsModal();
this.props.actions.togglePromoModal();
history.push({
pathname: this.props.is_expert ? '/detail/blocking' : '/'
});
@@ -27,10 +27,6 @@ import { setTheme } from '../utils/utils';
class Panel extends React.Component {
constructor(props) {
super(props);
this.state = {
insightsPromoModalShown: false,
plusPromoModalShown: false
};

// event bindings
this.closeNotification = this.closeNotification.bind(this);
@@ -202,26 +198,16 @@ class Panel extends React.Component {
}

_handlePlusPromoModalClicks = () => {
// TODO send appropriate metrics ping(s) for GH-1775
sendMessage('promoModals.sawPlusPromo', {});
this.setState({
plusPromoModalShown: true
});
this.props.actions.togglePromoModal();
}

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

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

@@ -269,22 +255,34 @@ class Panel extends React.Component {
);
}

_renderPlusPromoModal = () => {
const { plusPromoModalShown } = this.state;
const { account, haveSeenInitialPlusPromo, isTimeForAPlusPromo } = this.props;
_signedIn = () => {
const { account } = this.props;

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

_plusSubscriber = () => {
const { account } = this.props;

return this._signedIn() && account.user.subscriptionsPlus;
}

_insightsSubscriber = () => {
const { account } = this.props;

return this._signedIn() && account.user.scopes && account.user.scopes.includes('subscriptions:insights');
}

// Check account status
const signedIn = account && account.user;
const plusSubscriber = signedIn && account.user.subscriptionsPlus;
const insightsSubscriber = signedIn && account.user.scopes && account.user.scopes.includes('subscriptions:insights');
_renderPlusPromoModal = () => {
if (this._plusSubscriber() || this._insightsSubscriber()) return null;

if (plusSubscriber || insightsSubscriber) return null;
sendMessage('promoModals.sawPlusPromo', {});

if (haveSeenInitialPlusPromo) { return this._renderPlusPromoUpgradeModal(signedIn); }
if (this.props.promoModal === 'plus_upgrade') {
return this._renderPlusPromoUpgradeModal(this._signedIn());
}

// promoModal === 'plus_initial'
return (
<PlusPromoModal
show
@@ -295,18 +293,30 @@ class Panel extends React.Component {
}

_renderInsightsPromoModal = () => {
const { account, isTimeForInsightsPromo, isInsightsModalHidden } = this.props;
const { insightsPromoModalShown } = this.state;

if (isInsightsModalHidden) return null;
if (insightsPromoModalShown || !isTimeForInsightsPromo) return null;
if (account && account.user && account.user.scopes && account.user.scopes.includes('subscriptions:insights')) return null;
if (this._insightsSubscriber()) return null;

sendMessage('promoModals.sawInsightsPromo', '', 'metrics');

return (
<InsightsPromoModal />
);
return <InsightsPromoModal />;
}

_renderPromoModal = () => {
const {
promoModal,
isPromoModalHidden,
} = this.props;

if (isPromoModalHidden) return null;

if (promoModal === 'insights') {
return this._renderInsightsPromoModal();
}

if (promoModal === 'plus_initial' || promoModal === 'plus_upgrade') {
return this._renderPlusPromoModal();
}

return null;
}

/**
@@ -323,8 +333,7 @@ class Panel extends React.Component {

return (
<div id="panel">
{this._renderPlusPromoModal()}
{this._renderInsightsPromoModal()}
{this._renderPromoModal()}
<div className="callout-container">
<div className={`${(!notificationText ? 'hide ' : '') + this.props.notificationClasses} callout`}>
<svg onClick={this.closeNotification} width="15px" height="15px" viewBox="0 0 15 15" className="close-button">
@@ -18,7 +18,7 @@ export const SHOW_NOTIFICATION = 'SHOW_NOTIFICATION';
export const TOGGLE_CLIQZ_FEATURE = 'TOGGLE_CLIQZ_FEATURE';
export const CLOSE_NOTIFICATION = 'CLOSE_NOTIFICATION';
export const TOGGLE_EXPERT = 'TOGGLE_EXPERT';
export const TOGGLE_INSIGHTS_MODAL = 'TOGGLE_INSIGHTS_MODAL';
export const TOGGLE_PROMO_MODAL = 'TOGGLE_PROMO_MODAL';

// summary
export const UPDATE_SUMMARY_DATA = 'UPDATE_SUMMARY_DATA';
@@ -27,7 +27,7 @@ import {
TOGGLE_EXPANDED,
SET_THEME,
CLEAR_THEME,
TOGGLE_INSIGHTS_MODAL
TOGGLE_PROMO_MODAL
} from '../constants/constants';
import {
LOGIN_SUCCESS,
@@ -61,7 +61,7 @@ const initialState = {
email: '',
emailValidated: false,
current_theme: 'default',
isInsightsModalHidden: false,
isPromoModalHidden: false,
};
/**
* Default export for panel view reducer. Handles actions
@@ -262,10 +262,10 @@ export default (state = initialState, action) => {
}
return state;
}
case TOGGLE_INSIGHTS_MODAL: {
case TOGGLE_PROMO_MODAL: {
return {
...state,
isInsightsModalHidden: !state.isInsightsModalHidden
isPromoModalHidden: !state.isPromoModalHidden
};
}
default: return state;
@@ -115,7 +115,7 @@ class ConfData {
_initProperty('hide_alert_trusted', false);
_initProperty('ignore_first_party', true);
_initProperty('import_callout_dismissed', true);
_initProperty('insights_promo_modal_last_seen', null);
_initProperty('insights_promo_modal_last_seen', 0);
_initProperty('install_random_number', 0);
_initProperty('install_date', 0);
_initProperty('is_expanded', false);
@@ -125,7 +125,7 @@ class ConfData {
_initProperty('notify_promotions', true);
_initProperty('notify_upgrade_updates', true);
_initProperty('paid_subscription', false);
_initProperty('plus_promo_modal_last_seen', null);
_initProperty('plus_promo_modal_last_seen', 0);
_initProperty('rewards_accepted', false);
_initProperty('rewards_opted_in', false);
_initProperty('settings_last_imported', 0);
@@ -767,6 +767,10 @@ class Metrics {
conf.metrics.engaged_daily_count = engaged_daily_count;
conf.metrics.engaged_daily_velocity = engaged_daily_velocity;
this._sendReq('engaged', ['daily', 'weekly', 'monthly']);

console.error('IVZ Metrics#_recordEngaged');
console.error(`engaged_daily_count: ${engaged_daily_count}`);
console.error(`engaged_daily_velocity: ${engaged_daily_velocity}`);
}

/**
@@ -366,9 +366,7 @@ class PanelData {
is_expert,
is_android: globals.BROWSER_INFO.os === 'android',
language,
isTimeForInsightsPromo: promoModals.isTimeForInsightsPromo(),
isTimeForAPlusPromo: promoModals.isTimeForAPlusPromo(),
haveSeenInitialPlusPromo: promoModals.haveSeenInitialPlusPromo(),
promoModal: promoModals.whichPromoModalShouldWeDisplay(),
reload_banner_status,
tab_id,
trackers_banner_status,
@@ -18,8 +18,13 @@ const DAYS_BETWEEN_PROMOS = {
plus: globals.DEBUG ? 0.00025 : 30,
insights: globals.DEBUG ? 0.00025 : 30
};
const WEEKLY_INSIGHTS_TARGET = globals.DEBUG ? 1 : 3;
const DAILY_INSIGHTS_TARGET = 3;

const MSECS_IN_DAY = 86400000; // 1000 msecs-in-sec * 60 secs-in-min * 60 mins-in-hour * 24 hours-in-day
const PLUS = 'plus';
const PLUS_INITIAL = 'plus_initial';
const PLUS_UPGRADE = 'plus_upgrade';
const INSIGHTS = 'insights';
const PROMO_MODAL_LAST_SEEN = 'promo_modal_last_seen';

@@ -28,30 +33,36 @@ const PROMO_MODAL_LAST_SEEN = 'promo_modal_last_seen';
* @memberOf BackgroundClasses
*/
class PromoModals {
static haveSeenInitialPlusPromo() {
const lastSeenTime = conf[`${PLUS}_${PROMO_MODAL_LAST_SEEN}`];
return (lastSeenTime !== null);
}
static whichPromoModalShouldWeDisplay() {
if (this._isTimeForAPromo(INSIGHTS)) return INSIGHTS;

static isTimeForAPlusPromo() { return this._isTimeForAPromo(PLUS); }
if (this._isTimeForAPromo(PLUS)) {
if (this._haveSeenInitialPlusPromo()) return PLUS_UPGRADE;

static isTimeForInsightsPromo() { return this._isTimeForAPromo(INSIGHTS); }
return PLUS_INITIAL;
}

return null;
}

static recordPlusPromoSighting() { this._recordPromoSighting(PLUS); }

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

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

static _haveSeenInitialPlusPromo() {
const lastSeenTime = conf[`${PLUS}_${PROMO_MODAL_LAST_SEEN}`];
return (lastSeenTime !== 0);
}

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

const lastSeenPlusPromo = conf[`${PLUS}_${PROMO_MODAL_LAST_SEEN}`];
const lastSeenInsightsPromo = conf[`${INSIGHTS}_${PROMO_MODAL_LAST_SEEN}`];
const lastSeenPromo = lastSeenPlusPromo > lastSeenInsightsPromo ? lastSeenPlusPromo : lastSeenInsightsPromo;

if (lastSeenPromo === null) { return true; }

if (type === INSIGHTS && !this._hasEngagedFrequently()) {
return false;
}
@@ -68,14 +79,13 @@ class PromoModals {

static _hasEngagedFrequently() {
const { engaged_daily_count } = conf.metrics || [];
const DAILY_TARGET = 3;
const WEEKLY_TARGET = 3;

let very_engaged_days = 0;
engaged_daily_count.forEach((count) => {
very_engaged_days = count >= DAILY_TARGET ? ++very_engaged_days : very_engaged_days;
very_engaged_days = count >= DAILY_INSIGHTS_TARGET ? ++very_engaged_days : very_engaged_days;
});
if (very_engaged_days >= WEEKLY_TARGET) return true;

if (very_engaged_days >= WEEKLY_INSIGHTS_TARGET) return true;

return false;
}
ProTip! Use n and p to navigate between commits in a pull request.