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

Feature/rewards pings integration #69

Merged
Merged
Changes from all commits
Commits
File filter
Filter file types
Jump to
Jump to file
Failed to load files.

Always

Just for now

@@ -28,11 +28,11 @@ class Notification extends Component {
this.closeNotification = this.closeNotification.bind(this);
}

closeNotification(confirm = null) {
closeNotification(confirm) {
this.setState({
closed: true
});
if (typeof this.props.data.closeCallback === 'function' && confirm !== null) {
if (typeof this.props.data.closeCallback === 'function') {
this.props.data.closeCallback(confirm);
}
}
@@ -67,9 +67,7 @@ class Notification extends Component {
target="_blank"
onClick={() => {
if (this.props.data.textLink.callback) {
// this.props.data.textLink.callback();
} else {
// this.props.data.closeCallback();
this.props.data.textLink.callback();
}
}}
>
@@ -67,7 +67,10 @@ class OfferCard extends Component {
textLink: {
href: 'https://www.ghostery.com/faqs',
text: t('rewards_learn_more'),
callback: () => this.sendSignal('offer_first_learn')
callback: () => {
this.sendSignal('offer_first_learn');
sendMessage('ping', 'rewards_learn');
},
},
closeCallback: (option) => { this.handlePrompt(1, option); },
},
@@ -83,10 +86,11 @@ class OfferCard extends Component {
buttons: false,
message: t('rewards_disable_notification'),
textLink: {
text: t('rewards_disable_confirm')
text: t('rewards_disable_confirm'),
callback: this.closeOfferCard,
},
closeCallback: this.closeOfferCard,
}
},
];

const { reward } = props;
@@ -149,6 +153,7 @@ class OfferCard extends Component {

disableRewards() {
this.sendSignal('rewards_off');
sendMessage('ping', 'rewards_off');
this.messageBackground('rewardsDisabled');
}

@@ -163,20 +168,24 @@ class OfferCard extends Component {
// @TODO update user settings
if (promptNumber === 1) {
if (!option) {
sendMessage('ping', 'rewards_first_reject');
this.setState({
showPrompt: 2
});
return;
}
this.sendSignal('offer_first_optin');
sendMessage('ping', 'rewards_first_accept');
} else if (promptNumber === 2) {
if (option) {
this.sendSignal('offer_first_optout');
sendMessage('ping', 'rewards_first_reject_optout');
this.disableRewards();
this.closeOfferCard();
return;
}
this.sendSignal('offer_first_optlater');
sendMessage('ping', 'rewards_first_reject_optin');
}
this.setState({
showPrompt: false
@@ -24,14 +24,8 @@ class DetailMenu extends React.Component {
switch (itemName) {
case 'showBlocking':
return 'list_dash';
case 'showHistory':
return 'history_dash';
case 'showPerformance':
return 'performance_dash';
case 'showRewards':
return 'rewards_dash';
case 'showPremium':
return 'premium_dash';
default:
return '';
}
@@ -43,10 +37,7 @@ class DetailMenu extends React.Component {
this.state = {
menu: {
showBlocking: true,
showHistory: false,
showPerformance: false,
showRewards: false,
showPremium: false,
},
};

@@ -49,6 +49,10 @@ class Panel extends React.Component {
reload: true,
});
}

if (data.enable_offers && data.unread_offer_ids.length > 0) {
sendMessage('ping', 'engaged_offer');
}
});
}

@@ -115,6 +115,7 @@ class Rewards extends React.Component {
});
this.props.actions.toggleOffersEnabled(!enable_offers);
this.props.actions.sendSignal(enable_offers ? 'rewards_off' : 'rewards_on');
sendMessage('ping', enable_offers ? 'rewards_off' : 'rewards_on');
}

/**
@@ -14,6 +14,7 @@
import React, { Component } from 'react';
import { debounce } from 'underscore';
import { Route } from 'react-router-dom';
import { sendMessage } from '../utils/msg';
import SettingsMenu from './Settings/SettingsMenu';
import GlobalBlocking from './Settings/GlobalBlocking';
import TrustAndRestrict from './Settings/TrustAndRestrict';
@@ -67,6 +68,7 @@ class Settings extends React.Component {
toggleCheckbox(event) {
if (event.currentTarget.name === 'enable_offers') {
this.props.actions.sendSignal(event.currentTarget.checked ? 'rewards_off' : 'rewards_on');
sendMessage('ping', !event.currentTarget.checked ? 'rewards_off' : 'rewards_on');
}
this.props.actions.toggleCheckbox({
event: event.currentTarget.name,
@@ -430,6 +430,7 @@ function handleRewards(name, message, tab_id, callback) {
button.update();
break;
case 'deleteReward':
rewards.markRewardRead(message.offerId);
rewards.deleteReward(message.offerId);
button.update();
break;
@@ -439,6 +440,9 @@ function handleRewards(name, message, tab_id, callback) {
case 'rewardsPromptAccepted':
conf.rewards_accepted = true;
break;
case 'ping':
metrics.ping(message);
break;
default:
break;
}
@@ -16,6 +16,7 @@ import conf from './Conf';
import { log, prefsSet, prefsGet } from '../utils/common';
import { processUrlQuery } from '../utils/utils';
import abtest from './ABTest';
import rewards from './Rewards';

// CONSTANTS
const FREQUENCIES = { // in milliseconds
@@ -26,6 +27,7 @@ const FREQUENCIES = { // in milliseconds
};
const CRITICAL_METRICS = ['install', 'install_complete', 'upgrade', 'active', 'engaged', 'uninstall'];
const CAMPAIGN_METRICS = ['install', 'active', 'uninstall'];
const FIRST_REWARD_METRICS = ['rewards_first_accept', 'rewards_first_reject', 'rewards_first_reject_optin', 'rewards_first_reject_optout', 'rewards_learn'];
const { METRICS_SUB_DOMAIN, EXTENSION_VERSION, BROWSER_INFO } = globals;
const IS_EDGE = (BROWSER_INFO.name === 'edge');
const MAX_DELAYED_PINGS = 100;
@@ -130,38 +132,47 @@ class Metrics {
break;

// Extension Usage
case 'live_scan':
case 'pause':
case 'resume':
case 'trust_site':
case 'restrict_site':
case 'live_scan':
case 'resume':
case 'sign_in':
case 'trust_site':
this._sendReq(type, ['all', 'daily', 'weekly']);
break;

// New
case 'list_dash':
case 'adblock_off':
case 'adblock_on':
case 'antitrack_off':
case 'antitrack_on':
case 'create_account_extension':
case 'create_account_setup':
case 'engaged_offer':
case 'history_dash':
case 'history_learn':
case 'list_dash':
case 'pause_snooze':
case 'performance_dash':
case 'performance_learn':
case 'rewards_dash':
case 'rewards_learn':
case 'premium_dash':
case 'premium_learn':
case 'antitrack_on':
case 'antitrack_off':
case 'adblock_on':
case 'adblock_off':
case 'smartblock_on':
case 'rewards_dash':
case 'rewards_first_accept':
case 'rewards_first_reject':
case 'rewards_first_reject_optin':
case 'rewards_first_reject_optout':
case 'rewards_learn':
case 'rewards_off':
case 'rewards_on':
case 'smartblock_off':
case 'pause_snooze':
case 'viewchange_from_simple':
case 'smartblock_on':
case 'viewchange_from_detailed':
case 'viewchange_from_expanded':
case 'create_account_extension':
case 'create_account_setup':
case 'viewchange_from_simple':
this._sendReq(type, ['all', 'daily', 'weekly', 'monthly']);
break;

// uncaught ping type
default:
log(`metrics ping() error: ping name ${type} not found`);
@@ -254,7 +265,9 @@ class Metrics {
// Type of blocking selected during setup
`&sb=${encodeURIComponent(conf.setup_block.toString())}` +
// Recency, days since last active daily ping
`&rc=${encodeURIComponent(this._getRecency(type, frequency).toString())}`;
`&rc=${encodeURIComponent(this._getRecency(type, frequency).toString())}` +
// Current number of rewards received
`&rr=${encodeURIComponent(this._getRewardsCount().toString())}`;

if (CAMPAIGN_METRICS.includes(type)) {
// only send campaign attribution when necessary
@@ -263,6 +276,11 @@ class Metrics {
`&us=${encodeURIComponent(this.utm_source)}` +
// Marketing campaign (Former utm_campaign)
`&uc=${encodeURIComponent(this.utm_campaign)}`;
} else if (FIRST_REWARD_METRICS.includes(type)) {
// metrics specific to the first reward instance
metrics_url +=
// Reward ID
`&rid=${encodeURIComponent(this._getRewardId().toString())}`;
}

return metrics_url;
@@ -332,6 +350,35 @@ class Metrics {
}
return -1;
}
/**
* Get the number of Rewards shown to the user.
*
* @private
*
* @return {string} number of rewards, grouped into ranges.
*/
_getRewardsCount() {
const numShown = rewards.totalOffersSeen;
if (numShown >= 6) {
return '6+';
} else if (numShown >= 2) {
return '2-5';
} else if (numShown === 1) {
return '1';
}
return '0';
}
/**
* Get the current Reward Id.
*
* @private
*
* @return {string} the current Reward Id
*/
_getRewardId() {
const currentOffer = rewards.currentOffer || { offer_id: 'no_id' };
return currentOffer.offer_id;
}
/**
* Calculate remaining scheduled time for a ping
*
@@ -19,7 +19,7 @@ import conf from './Conf';
import tabInfo from './TabInfo';
import Policy from './Policy';
import globals from './Globals';
import { log } from '../utils/common';
import { log, prefsGet, prefsSet } from '../utils/common';
import { sendMessage, injectScript } from '../utils/utils';
import * as accounts from '../utils/accounts';

@@ -31,22 +31,22 @@ const t = chrome.i18n.getMessage;
*/
class Rewards {
constructor() {
this.storedOffers = {};
this.unreadOfferIds = [];
this.getStoredOffers();
this.currentOffer = null;
this.ports = new Map();
this.channelsSupported = (typeof chrome.runtime.onConnect === 'object');
}

deleteReward(offerId) {
this.markRewardRead(offerId);
delete this.storedOffers[offerId];
// @TODO send signal?
this.updateStoredOffers();
}

markRewardRead(offerId) {
const rewardIdx = this.unreadOfferIds.indexOf(offerId);
this.unreadOfferIds.splice(rewardIdx, 1);
this.updateStoredOffers();
}

sendSignal(message) {
@@ -62,7 +62,24 @@ class Rewards {
cliqz.modules['offers-v2'].background.actions.processRealEstateMessage(signal);
}

getStoredOffers() {
return prefsGet('storedOffers', 'unreadOfferIds', 'totalOffersSeen')
.then((response) => {
this.storedOffers = response.storedOffers || {};
this.unreadOfferIds = response.unreadOfferIds || [];
this.totalOffersSeen = response.totalOffersSeen || 0;
});
}

updateStoredOffers() {
prefsSet({ storedOffers: this.storedOffers });
prefsSet({ unreadOfferIds: this.unreadOfferIds }).then(() => { button.update(); });
}

showHotDog(tab_id, offer) {
this.updateStoredOffers();
this.totalOffersSeen++;
prefsSet({ totalOffersSeen: this.totalOffersSeen });
this.currentOffer = offer;
const tab = tabInfo.getTabInfo(tab_id);

@@ -26,6 +26,11 @@ chrome.runtime.getManifest.returns({
debug: true
});

// Create Mock for the Cliqz dependencies
jest.mock('browser-core', () => {
return { App: class App {} }
});

// Initialization for Globals.js
Object.defineProperty(navigator, 'userAgent', {
value: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36",
ProTip! Use n and p to navigate between commits in a pull request.