mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-03-06 13:03:10 +08:00
Remove “reportUserEvent” calls, remnants of analytics integration
This commit is contained in:
parent
89bd1909ae
commit
40caf341b4
26 changed files with 9 additions and 649 deletions
|
@ -297,7 +297,6 @@ class TemplateStore extends MailspringStore {
|
|||
templateBody + signature,
|
||||
session.draft().body
|
||||
);
|
||||
Actions.recordUserEvent('Email Template Inserted');
|
||||
session.changes.add({ body: draftHtml });
|
||||
}
|
||||
});
|
||||
|
|
|
@ -66,10 +66,6 @@ class TranslateButton extends React.Component {
|
|||
const draftHtml = this.props.draft.body;
|
||||
const text = QuotedHTMLTransformer.removeQuotedHTML(draftHtml);
|
||||
|
||||
Actions.recordUserEvent('Email Translated', {
|
||||
language: YandexLanguages[lang],
|
||||
});
|
||||
|
||||
const queryParams = new URLSearchParams();
|
||||
queryParams.set('key', YandexTranslationKey);
|
||||
queryParams.set('lang', YandexLanguages[lang]);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { shell } from 'electron';
|
||||
import { Actions, React, PropTypes } from 'mailspring-exports';
|
||||
import { React, PropTypes } from 'mailspring-exports';
|
||||
import { RetinaImg, KeyCommandsRegion } from 'mailspring-component-kit';
|
||||
|
||||
import GithubStore from './github-store';
|
||||
|
@ -140,7 +140,6 @@ export default class ViewOnGithubButton extends React.Component {
|
|||
* request.
|
||||
*/
|
||||
_openLink = () => {
|
||||
Actions.recordUserEvent('Github Thread Opened', { pageUrl: this.state.link });
|
||||
if (this.state.link) {
|
||||
shell.openExternal(this.state.link);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { shell } from 'electron';
|
||||
import { RetinaImg } from 'mailspring-component-kit';
|
||||
import { Actions, React, ReactDOM, PropTypes } from 'mailspring-exports';
|
||||
import { React, ReactDOM, PropTypes } from 'mailspring-exports';
|
||||
|
||||
import OnboardingActions from '../onboarding-actions';
|
||||
import { finalizeAndValidateAccount } from '../onboarding-helpers';
|
||||
|
@ -120,11 +120,6 @@ const CreatePageForForm = FormComponent => {
|
|||
OnboardingActions.finishAndAddAccount(validated);
|
||||
})
|
||||
.catch(err => {
|
||||
Actions.recordUserEvent('Email Account Auth Failed', {
|
||||
errorMessage: err.message,
|
||||
provider: account.provider,
|
||||
});
|
||||
|
||||
const errorFieldNames = [];
|
||||
|
||||
if (err.message.includes('Authentication Error')) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ipcRenderer, shell, clipboard } from 'electron';
|
||||
import { React, PropTypes, Actions } from 'mailspring-exports';
|
||||
import { React, PropTypes } from 'mailspring-exports';
|
||||
import { RetinaImg } from 'mailspring-component-kit';
|
||||
import FormErrorMessage from './form-error-message';
|
||||
|
||||
|
@ -75,10 +75,6 @@ export default class OAuthSignInPage extends React.Component {
|
|||
_handleError(err) {
|
||||
this.setState({ authStage: 'error', errorMessage: err.message });
|
||||
AppEnv.reportError(err);
|
||||
Actions.recordUserEvent('Email Account Auth Failed', {
|
||||
errorMessage: err.message,
|
||||
provider: 'gmail',
|
||||
});
|
||||
}
|
||||
|
||||
startPollingForResponse() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { AccountStore, Account, Actions, IdentityStore } from 'mailspring-exports';
|
||||
import { AccountStore, Account, IdentityStore } from 'mailspring-exports';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import MailspringStore from 'mailspring-store';
|
||||
|
||||
|
@ -136,15 +136,8 @@ class OnboardingStore extends MailspringStore {
|
|||
|
||||
AppEnv.displayWindow();
|
||||
|
||||
Actions.recordUserEvent('Email Account Auth Succeeded', {
|
||||
provider: account.provider,
|
||||
});
|
||||
|
||||
if (isFirstAccount) {
|
||||
this._onMoveToPage('initial-preferences');
|
||||
Actions.recordUserEvent('First Account Linked', {
|
||||
provider: account.provider,
|
||||
});
|
||||
} else {
|
||||
// let them see the "success" screen for a moment
|
||||
// before the window is closed.
|
||||
|
|
|
@ -6,15 +6,6 @@ export default class WelcomePage extends React.Component {
|
|||
static displayName = 'WelcomePage';
|
||||
|
||||
_onContinue = () => {
|
||||
// We don't have a NylasId yet and therefore can't track the "Welcome
|
||||
// Page Finished" event.
|
||||
//
|
||||
// If a user already has a Nylas ID and gets to this page (which
|
||||
// happens if they sign out of all of their accounts), then it would
|
||||
// properly fire. This is a rare case though and we don't want
|
||||
// analytics users thinking it's part of the full funnel.
|
||||
//
|
||||
// Actions.recordUserEvent('Welcome Page Finished');
|
||||
OnboardingActions.moveToPage('tutorial');
|
||||
};
|
||||
|
||||
|
|
|
@ -75,12 +75,6 @@ class SendLaterButton extends Component {
|
|||
}
|
||||
|
||||
this.setState({ saving: true });
|
||||
const sendInSec = Math.round((new Date(sendLaterDate).valueOf() - Date.now()) / 1000);
|
||||
Actions.recordUserEvent('Draft Send Later', {
|
||||
timeInSec: sendInSec,
|
||||
timeInLog10Sec: Math.log10(sendInSec),
|
||||
label: dateLabel,
|
||||
});
|
||||
}
|
||||
|
||||
this.props.session.changes.addPluginMetadata(PLUGIN_ID, {
|
||||
|
|
|
@ -30,13 +30,6 @@ async function incrementMetadataUse(model, expiration) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (expiration) {
|
||||
const seconds = Math.round((new Date(expiration).getTime() - Date.now()) / 1000);
|
||||
Actions.recordUserEvent('Set Reminder', {
|
||||
seconds: seconds,
|
||||
secondsLog10: Math.log10(seconds),
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import _ from 'underscore';
|
||||
import {
|
||||
Actions,
|
||||
Thread,
|
||||
DatabaseStore,
|
||||
SearchQueryParser,
|
||||
|
@ -119,31 +118,8 @@ class SearchQuerySubscription extends MutableQuerySubscription {
|
|||
return;
|
||||
}
|
||||
|
||||
let timeToFirstServerResults = null;
|
||||
let timeToFirstThreadSelected = null;
|
||||
const timeInsideSearch = Math.round((Date.now() - this._searchStartedAt) / 1000);
|
||||
const numItems = this._focusedThreadCount;
|
||||
const didSelectAnyThreads = numItems > 0;
|
||||
// Not Implemented
|
||||
|
||||
if (this._firstThreadSelectedAt) {
|
||||
timeToFirstThreadSelected = Math.round(
|
||||
(this._firstThreadSelectedAt - this._searchStartedAt) / 1000
|
||||
);
|
||||
}
|
||||
if (this._resultsReceivedAt) {
|
||||
timeToFirstServerResults = Math.round(
|
||||
(this._resultsReceivedAt - this._searchStartedAt) / 1000
|
||||
);
|
||||
}
|
||||
|
||||
const data = {
|
||||
numItems,
|
||||
timeInsideSearch,
|
||||
didSelectAnyThreads,
|
||||
timeToFirstServerResults,
|
||||
timeToFirstThreadSelected,
|
||||
};
|
||||
Actions.recordUserEvent('Search Performed', data);
|
||||
this.resetData();
|
||||
}
|
||||
|
||||
|
|
|
@ -29,20 +29,6 @@ class SnoozeStore extends MailspringStore {
|
|||
this.unsubscribers.forEach(unsub => unsub());
|
||||
}
|
||||
|
||||
_recordSnoozeEvent(threads, snoozeDate, label) {
|
||||
try {
|
||||
const timeInSec = Math.round((new Date(snoozeDate).valueOf() - Date.now()) / 1000);
|
||||
Actions.recordUserEvent('Threads Snoozed', {
|
||||
timeInSec: timeInSec,
|
||||
timeInLog10Sec: Math.log10(timeInSec),
|
||||
label: label,
|
||||
numItems: threads.length,
|
||||
});
|
||||
} catch (e) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
_onSnoozeThreads = async (threads, snoozeDate, label) => {
|
||||
try {
|
||||
// ensure the user is authorized to use this feature
|
||||
|
@ -52,9 +38,6 @@ class SnoozeStore extends MailspringStore {
|
|||
iconUrl: 'mailspring://thread-snooze/assets/ic-snooze-modal@2x.png',
|
||||
});
|
||||
|
||||
// log to analytics
|
||||
this._recordSnoozeEvent(threads, snoozeDate, label);
|
||||
|
||||
// move the threads to the snoozed folder
|
||||
await moveThreads(threads, {
|
||||
snooze: true,
|
||||
|
|
|
@ -1,291 +0,0 @@
|
|||
/* eslint no-param-reassign: 0 */
|
||||
const assert = require('assert');
|
||||
const crypto = require('crypto');
|
||||
const validate = require('@segment/loosely-validate-event');
|
||||
const debug = require('debug')('analytics-node');
|
||||
const version = `3.0.0`;
|
||||
|
||||
// BG: Dependencies of analytics-node I lifted in
|
||||
|
||||
// https://github.com/segmentio/crypto-token/blob/master/lib/index.js
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||
function uid(length, fn) {
|
||||
const str = bytes => {
|
||||
const res = [];
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
res.push(chars[bytes[i] % chars.length]);
|
||||
}
|
||||
return res.join('');
|
||||
};
|
||||
|
||||
if (typeof length === 'function') {
|
||||
fn = length;
|
||||
length = null;
|
||||
}
|
||||
if (!length) {
|
||||
length = 10;
|
||||
}
|
||||
if (!fn) {
|
||||
return str(crypto.randomBytes(length));
|
||||
}
|
||||
|
||||
crypto.randomBytes(length, (err, bytes) => {
|
||||
if (err) {
|
||||
fn(err);
|
||||
return;
|
||||
}
|
||||
fn(null, str(bytes));
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
// https://github.com/stephenmathieson/remove-trailing-slash/blob/master/index.js
|
||||
function removeSlash(str) {
|
||||
return String(str).replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
const setImmediate = global.setImmediate || process.nextTick.bind(process);
|
||||
const noop = () => {};
|
||||
|
||||
export default class Analytics {
|
||||
/**
|
||||
* Initialize a new `Analytics` with your Segment project's `writeKey` and an
|
||||
* optional dictionary of `options`.
|
||||
*
|
||||
* @param {String} writeKey
|
||||
* @param {Object} [options] (optional)
|
||||
* @property {Number} flushAt (default: 20)
|
||||
* @property {Number} flushInterval (default: 10000)
|
||||
* @property {String} host (default: 'https://api.segment.io')
|
||||
*/
|
||||
|
||||
constructor(writeKey, options) {
|
||||
options = options || {};
|
||||
|
||||
assert(writeKey, "You must pass your Segment project's write key.");
|
||||
|
||||
this.queue = [];
|
||||
this.writeKey = writeKey;
|
||||
this.host = removeSlash(options.host || 'https://api.segment.io');
|
||||
this.flushAt = Math.max(options.flushAt, 1) || 20;
|
||||
this.flushInterval = options.flushInterval || 10000;
|
||||
this.flushed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an identify `message`.
|
||||
*
|
||||
* @param {Object} message
|
||||
* @param {Function} [callback] (optional)
|
||||
* @return {Analytics}
|
||||
*/
|
||||
|
||||
identify(message, callback) {
|
||||
validate(message, 'identify');
|
||||
this.enqueue('identify', message, callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a group `message`.
|
||||
*
|
||||
* @param {Object} message
|
||||
* @param {Function} [callback] (optional)
|
||||
* @return {Analytics}
|
||||
*/
|
||||
|
||||
group(message, callback) {
|
||||
validate(message, 'group');
|
||||
this.enqueue('group', message, callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a track `message`.
|
||||
*
|
||||
* @param {Object} message
|
||||
* @param {Function} [callback] (optional)
|
||||
* @return {Analytics}
|
||||
*/
|
||||
|
||||
track(message, callback) {
|
||||
validate(message, 'track');
|
||||
this.enqueue('track', message, callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a page `message`.
|
||||
*
|
||||
* @param {Object} message
|
||||
* @param {Function} [callback] (optional)
|
||||
* @return {Analytics}
|
||||
*/
|
||||
|
||||
page(message, callback) {
|
||||
validate(message, 'page');
|
||||
this.enqueue('page', message, callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a screen `message`.
|
||||
*
|
||||
* @param {Object} message
|
||||
* @param {Function} fn (optional)
|
||||
* @return {Analytics}
|
||||
*/
|
||||
|
||||
screen(message, callback) {
|
||||
validate(message, 'screen');
|
||||
this.enqueue('screen', message, callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an alias `message`.
|
||||
*
|
||||
* @param {Object} message
|
||||
* @param {Function} [callback] (optional)
|
||||
* @return {Analytics}
|
||||
*/
|
||||
|
||||
alias(message, callback) {
|
||||
validate(message, 'alias');
|
||||
this.enqueue('alias', message, callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a `message` of type `type` to the queue and
|
||||
* check whether it should be flushed.
|
||||
*
|
||||
* @param {String} type
|
||||
* @param {Object} message
|
||||
* @param {Functino} [callback] (optional)
|
||||
* @api private
|
||||
*/
|
||||
|
||||
enqueue(type, message, callback) {
|
||||
callback = callback || noop;
|
||||
|
||||
message = Object.assign({}, message);
|
||||
message.type = type;
|
||||
message.context = Object.assign(
|
||||
{
|
||||
library: {
|
||||
name: 'analytics-node',
|
||||
version,
|
||||
},
|
||||
},
|
||||
message.context
|
||||
);
|
||||
|
||||
message._metadata = Object.assign(
|
||||
{
|
||||
nodeVersion: process.versions.node,
|
||||
},
|
||||
message._metadata
|
||||
);
|
||||
|
||||
if (!message.timestamp) {
|
||||
message.timestamp = new Date();
|
||||
}
|
||||
|
||||
if (!message.messageId) {
|
||||
message.messageId = `node-${uid(32)}`;
|
||||
}
|
||||
|
||||
debug('%s: %o', type, message);
|
||||
|
||||
this.queue.push({ message, callback });
|
||||
|
||||
if (!this.flushed) {
|
||||
this.flushed = true;
|
||||
this.flush();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.queue.length >= this.flushAt) {
|
||||
this.flush();
|
||||
}
|
||||
|
||||
if (this.flushInterval && !this.timer) {
|
||||
this.timer = setTimeout(this.flush.bind(this), this.flushInterval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the current queue
|
||||
*
|
||||
* @param {Function} [callback] (optional)
|
||||
* @return {Analytics}
|
||||
*/
|
||||
|
||||
async flush(callback) {
|
||||
callback = callback || noop;
|
||||
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
|
||||
if (!this.queue.length) {
|
||||
setImmediate(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
const items = this.queue.splice(0, this.flushAt);
|
||||
const callbacks = items.map(item => item.callback);
|
||||
const messages = items.map(item => item.message);
|
||||
|
||||
const data = {
|
||||
batch: messages,
|
||||
timestamp: new Date(),
|
||||
sentAt: new Date(),
|
||||
};
|
||||
|
||||
debug('flush: %o', data);
|
||||
|
||||
const options = {
|
||||
body: JSON.stringify(data),
|
||||
credentials: 'include',
|
||||
headers: new Headers(),
|
||||
method: 'POST',
|
||||
};
|
||||
options.headers.set('Accept', 'application/json');
|
||||
options.headers.set('Authorization', `Basic ${btoa(`${this.writeKey}:`)}`);
|
||||
options.headers.set('Content-Type', 'application/json');
|
||||
|
||||
const runCallbacks = err => {
|
||||
callbacks.forEach(cb => cb(err));
|
||||
callback(err, data);
|
||||
debug('flushed: %o', data);
|
||||
};
|
||||
|
||||
let resp = null;
|
||||
try {
|
||||
resp = await fetch(`${this.host}/v1/batch`, options);
|
||||
} catch (err) {
|
||||
runCallbacks(err, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!resp.ok) {
|
||||
runCallbacks(new Error(`${resp.statusCode}: ${resp.statusText}`), null);
|
||||
return;
|
||||
}
|
||||
|
||||
const body = await resp.json();
|
||||
if (body.error) {
|
||||
runCallbacks(new Error(body.error.message), null);
|
||||
return;
|
||||
}
|
||||
runCallbacks(null, {
|
||||
status: resp.statusCode,
|
||||
body: body,
|
||||
ok: true,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
This folder contains a modified version of analytics-node. The original version uses `superagent`, which is both unnecessary (since we have both Browser and Node APIs and can use `fetch` and also added ~2.1MB of JavaScript to Mailspring.)
|
|
@ -1,154 +0,0 @@
|
|||
import _ from 'underscore';
|
||||
import MailspringStore from 'mailspring-store';
|
||||
import {
|
||||
IdentityStore,
|
||||
Actions,
|
||||
AccountStore,
|
||||
FocusedPerspectiveStore,
|
||||
MailspringAPIRequest,
|
||||
} from 'mailspring-exports';
|
||||
|
||||
import AnalyticsSink from '../analytics-electron';
|
||||
|
||||
/**
|
||||
* We wait 15 seconds to give the alias time to register before we send
|
||||
* any events.
|
||||
*/
|
||||
const DEBOUNCE_TIME = 5 * 1000;
|
||||
|
||||
class AnalyticsStore extends MailspringStore {
|
||||
activate() {
|
||||
// Allow requests to be grouped together if they're fired back-to-back,
|
||||
// but generally report each event as it happens. This segment library
|
||||
// is intended for a server where the user doesn't quit...
|
||||
this.analytics = new AnalyticsSink('mailspring', {
|
||||
host: `${MailspringAPIRequest.rootURLForServer('identity')}/api/s`,
|
||||
flushInterval: 500,
|
||||
flushAt: 5,
|
||||
});
|
||||
this.launchTime = Date.now();
|
||||
|
||||
const identifySoon = _.debounce(this.identify, DEBOUNCE_TIME);
|
||||
identifySoon();
|
||||
|
||||
// also ping the server every hour to make sure someone running
|
||||
// the app for days has up-to-date traits.
|
||||
setInterval(identifySoon, 60 * 60 * 1000); // 60 min
|
||||
|
||||
this.listenTo(IdentityStore, identifySoon);
|
||||
this.listenTo(Actions.recordUserEvent, (eventName, eventArgs) => {
|
||||
this.track(eventName, eventArgs);
|
||||
});
|
||||
}
|
||||
|
||||
// Properties applied to all events (only).
|
||||
eventState() {
|
||||
// Get a bit of context about the current perspective
|
||||
// so we can assess usage of unified inbox, etc.
|
||||
const perspective = FocusedPerspectiveStore.current();
|
||||
let currentProvider = null;
|
||||
|
||||
if (perspective && perspective.accountIds.length > 1) {
|
||||
currentProvider = 'Unified';
|
||||
} else {
|
||||
// Warning: when you auth a new account there's a single moment where the account cannot be found
|
||||
const account = perspective
|
||||
? AccountStore.accountForId(perspective.accountIds[0])
|
||||
: AccountStore.accounts()[0];
|
||||
currentProvider = account && account.displayProvider();
|
||||
}
|
||||
|
||||
return {
|
||||
currentProvider,
|
||||
};
|
||||
}
|
||||
|
||||
// Properties applied to all events and all people during an identify.
|
||||
superTraits() {
|
||||
const theme = AppEnv.themes ? AppEnv.themes.getActiveTheme() : null;
|
||||
|
||||
return {
|
||||
version: AppEnv.getVersion().split('-')[0],
|
||||
platform: process.platform,
|
||||
activeTheme: theme ? theme.name : null,
|
||||
workspaceMode: AppEnv.config.get('core.workspace.mode'),
|
||||
};
|
||||
}
|
||||
|
||||
baseTraits() {
|
||||
return Object.assign({}, this.superTraits(), {
|
||||
firstDaySeen: this.firstDaySeen(),
|
||||
timeSinceLaunch: (Date.now() - this.launchTime) / 1000,
|
||||
accountCount: AccountStore.accounts().length,
|
||||
providers: AccountStore.accounts().map(a => a.displayProvider()),
|
||||
});
|
||||
}
|
||||
|
||||
personalTraits() {
|
||||
const identity = IdentityStore.identity();
|
||||
if (!(identity && identity.id)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
email: identity.emailAddress,
|
||||
firstName: identity.firstName,
|
||||
lastName: identity.lastName,
|
||||
};
|
||||
}
|
||||
|
||||
track(eventName, eventArgs = {}) {
|
||||
// if (AppEnv.inDevMode()) { return }
|
||||
|
||||
const identity = IdentityStore.identity();
|
||||
if (!(identity && identity.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.analytics.track({
|
||||
event: eventName,
|
||||
userId: identity.id,
|
||||
properties: Object.assign({}, eventArgs, this.eventState(), this.superTraits()),
|
||||
});
|
||||
}
|
||||
|
||||
firstDaySeen() {
|
||||
let firstDaySeen = AppEnv.config.get('firstDaySeen');
|
||||
if (!firstDaySeen) {
|
||||
const [y, m, d] = new Date().toISOString().split(/[-|T]/);
|
||||
firstDaySeen = `${m}/${d}/${y}`;
|
||||
AppEnv.config.set('firstDaySeen', firstDaySeen);
|
||||
}
|
||||
return firstDaySeen;
|
||||
}
|
||||
|
||||
identify = () => {
|
||||
if (!AppEnv.isMainWindow()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const identity = IdentityStore.identity();
|
||||
if (!(identity && identity.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.analytics.identify({
|
||||
userId: identity.id,
|
||||
traits: this.baseTraits(),
|
||||
integrations: { All: true },
|
||||
});
|
||||
|
||||
// Ensure we never send PI anywhere but Mixpanel
|
||||
|
||||
this.analytics.identify({
|
||||
userId: identity.id,
|
||||
traits: Object.assign({}, this.baseTraits(), this.personalTraits()),
|
||||
integrations: {
|
||||
All: false,
|
||||
Mixpanel: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default new AnalyticsStore();
|
|
@ -1,5 +0,0 @@
|
|||
import AnalyticsStore from './analytics-store';
|
||||
|
||||
export function activate() {
|
||||
AnalyticsStore.activate();
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"name": "analytics",
|
||||
"version": "0.1.0",
|
||||
"main": "./lib/main",
|
||||
"description": "Analytics",
|
||||
"license": "Proprietary",
|
||||
"private": true,
|
||||
"windowTypes": {
|
||||
"default": true
|
||||
},
|
||||
"engines": {
|
||||
"mailspring": "*"
|
||||
}
|
||||
}
|
|
@ -54,7 +54,7 @@ export default class ThreadSharingPopover extends React.Component {
|
|||
}
|
||||
|
||||
_onToggleShared = async () => {
|
||||
const { thread, accountId } = this.props;
|
||||
const { thread } = this.props;
|
||||
const { shared } = this.state;
|
||||
|
||||
this.setState({ saving: true });
|
||||
|
@ -64,9 +64,6 @@ export default class ThreadSharingPopover extends React.Component {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!shared === true) {
|
||||
Actions.recordUserEvent('Thread Sharing Enabled', { accountId, threadId: thread.id });
|
||||
}
|
||||
Actions.queueTask(
|
||||
SyncbackMetadataTask.forSaving({
|
||||
model: thread,
|
||||
|
|
|
@ -27,7 +27,7 @@ describe "ActionBridge", ->
|
|||
|
||||
it "should not rebroadcast mainWindow actions since it is the main window", ->
|
||||
spyOn(@bridge, 'onRebroadcast')
|
||||
testAction = Actions.recordUserEvent
|
||||
testAction = Actions.queueTask
|
||||
testAction('bla')
|
||||
expect(@bridge.onRebroadcast).not.toHaveBeenCalled()
|
||||
|
||||
|
@ -57,7 +57,7 @@ describe "ActionBridge", ->
|
|||
|
||||
it "should rebroadcast mainWindow actions", ->
|
||||
spyOn(@bridge, 'onRebroadcast')
|
||||
testAction = Actions.recordUserEvent
|
||||
testAction = Actions.queueTask
|
||||
testAction('bla')
|
||||
expect(@bridge.onRebroadcast).toHaveBeenCalled()
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {
|
||||
React,
|
||||
PropTypes,
|
||||
Actions,
|
||||
MailspringAPIRequest,
|
||||
APIError,
|
||||
FeatureUsageStore,
|
||||
|
@ -87,10 +86,8 @@ export default class MetadataComposerToggleButton extends React.Component {
|
|||
}
|
||||
|
||||
_onClick = async () => {
|
||||
const { pluginName, pluginId } = this.props;
|
||||
const { pluginId } = this.props;
|
||||
let nextEnabled = !this._isEnabled();
|
||||
const dir = nextEnabled ? 'Enabled' : 'Disabled';
|
||||
|
||||
if (this.state.pending) {
|
||||
return;
|
||||
}
|
||||
|
@ -114,7 +111,6 @@ export default class MetadataComposerToggleButton extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
Actions.recordUserEvent(`${pluginName} ${dir}`);
|
||||
AppEnv.config.set(this._configKey(), nextEnabled);
|
||||
this._setEnabled(nextEnabled);
|
||||
};
|
||||
|
|
|
@ -468,11 +468,6 @@ class Actions {
|
|||
*/
|
||||
static pushSheet = ActionScopeWindow;
|
||||
|
||||
/*
|
||||
Public: Publish a user event to any analytics services linked to N1.
|
||||
*/
|
||||
static recordUserEvent = ActionScopeMainWindow;
|
||||
|
||||
static addMailRule = ActionScopeWindow;
|
||||
static reorderMailRule = ActionScopeWindow;
|
||||
static updateMailRule = ActionScopeWindow;
|
||||
|
|
|
@ -182,7 +182,6 @@ class DraftStore extends MailspringStore {
|
|||
};
|
||||
|
||||
_onComposeReply = ({ thread, threadId, message, messageId, popout, type, behavior }) => {
|
||||
Actions.recordUserEvent('Draft Created', { type });
|
||||
return Promise.props(this._modelifyContext({ thread, threadId, message, messageId }))
|
||||
.then(({ message: m, thread: t }) => {
|
||||
return DraftFactory.createOrUpdateDraftForReply({ message: m, thread: t, type, behavior });
|
||||
|
@ -193,7 +192,6 @@ class DraftStore extends MailspringStore {
|
|||
};
|
||||
|
||||
_onComposeForward = async ({ thread, threadId, message, messageId, popout }) => {
|
||||
Actions.recordUserEvent('Draft Created', { type: 'forward' });
|
||||
return Promise.props(this._modelifyContext({ thread, threadId, message, messageId }))
|
||||
.then(({ thread: t, message: m }) => {
|
||||
return DraftFactory.createDraftForForward({ thread: t, message: m });
|
||||
|
@ -275,7 +273,6 @@ class DraftStore extends MailspringStore {
|
|||
};
|
||||
|
||||
_onPopoutBlankDraft = async () => {
|
||||
Actions.recordUserEvent('Draft Created', { type: 'new' });
|
||||
const draft = await DraftFactory.createDraft();
|
||||
const { headerMessageId } = await this._finalizeAndPersistNewMessage(draft);
|
||||
await this._onPopoutDraft(headerMessageId, { newDraft: true });
|
||||
|
|
|
@ -103,12 +103,8 @@ class WorkspaceStore extends MailspringStore
|
|||
throw new Error("Actions.toggleWorkspaceLocationHidden - pass a WorkspaceStore.Location")
|
||||
|
||||
if @_hiddenLocations[location.id]
|
||||
if location is @Location.MessageListSidebar
|
||||
Actions.recordUserEvent("Sidebar Opened")
|
||||
delete @_hiddenLocations[location.id]
|
||||
else
|
||||
if location is @Location.MessageListSidebar
|
||||
Actions.recordUserEvent("Sidebar Closed")
|
||||
@_hiddenLocations[location.id] = location
|
||||
|
||||
AppEnv.config.set('core.workspace.hiddenLocations', @_hiddenLocations)
|
||||
|
|
|
@ -44,17 +44,4 @@ export default class ChangeStarredTask extends ChangeMailTask {
|
|||
task.starred = !this.starred;
|
||||
return task;
|
||||
}
|
||||
|
||||
recordUserEvent() {
|
||||
if (this.source === 'Mail Rules') {
|
||||
return;
|
||||
}
|
||||
const eventName = this.starred ? 'Starred' : 'Unstarred';
|
||||
Actions.recordUserEvent(`Threads ${eventName}`, {
|
||||
source: this.source,
|
||||
numThreads: this.threadIds.length,
|
||||
description: this.description(),
|
||||
isUndo: this.isUndo,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,6 @@ export default class SendDraftTask extends Task {
|
|||
}
|
||||
|
||||
onSuccess() {
|
||||
Actions.recordUserEvent('Draft Sent');
|
||||
Actions.draftDeliverySucceeded({
|
||||
headerMessageId: this.draft.headerMessageId,
|
||||
accountId: this.draft.accountId,
|
||||
|
@ -143,9 +142,6 @@ export default class SendDraftTask extends Task {
|
|||
errorMessage,
|
||||
errorDetail,
|
||||
});
|
||||
Actions.recordUserEvent('Draft Sending Errored', {
|
||||
key: key,
|
||||
});
|
||||
}
|
||||
|
||||
// note - this code must match what is used for send-later!
|
||||
|
|
|
@ -191,7 +191,6 @@ lazyLoad(`SanitizeTransformer`, 'services/sanitize-transformer');
|
|||
lazyLoad(`QuotedHTMLTransformer`, 'services/quoted-html-transformer');
|
||||
lazyLoad(`InlineStyleTransformer`, 'services/inline-style-transformer');
|
||||
lazyLoad(`SearchableComponentMaker`, 'searchable-components/searchable-component-maker');
|
||||
lazyLoad(`BatteryStatusManager`, 'services/battery-status-manager');
|
||||
|
||||
// Errors
|
||||
lazyLoadWithGetter(`APIError`, () => require('../flux/errors').APIError);
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
import moment from 'moment-timezone';
|
||||
import Actions from '../flux/actions';
|
||||
|
||||
class BatteryStatusManager {
|
||||
constructor() {
|
||||
this._callbacks = [];
|
||||
this._battery = null;
|
||||
this._lastChangeTime = Date.now();
|
||||
this.activate();
|
||||
}
|
||||
|
||||
async activate() {
|
||||
if (this._battery) {
|
||||
return;
|
||||
}
|
||||
this._battery = await navigator.getBattery();
|
||||
this._battery.addEventListener('chargingchange', this._onChargingChange);
|
||||
}
|
||||
|
||||
deactivate() {
|
||||
if (!this._battery) {
|
||||
return;
|
||||
}
|
||||
this._battery.removeEventListener('chargingchange', this._onChargingChange);
|
||||
this._battery = null;
|
||||
}
|
||||
|
||||
_onChargingChange = () => {
|
||||
const changeTime = Date.now();
|
||||
Actions.recordUserEvent('Battery State Changed', {
|
||||
oldState: this.isBatteryCharging() ? 'battery' : 'ac',
|
||||
oldStateDuration: Math.min(
|
||||
changeTime - this._lastChangeTime,
|
||||
moment.duration(12, 'hours').asMilliseconds()
|
||||
),
|
||||
});
|
||||
this._lastChangeTime = changeTime;
|
||||
this._callbacks.forEach(cb => cb());
|
||||
};
|
||||
|
||||
onChange(callback) {
|
||||
this._callbacks.push(callback);
|
||||
}
|
||||
|
||||
isBatteryCharging() {
|
||||
if (!this._battery) {
|
||||
return false;
|
||||
}
|
||||
return this._battery.charging;
|
||||
}
|
||||
}
|
||||
|
||||
export default new BatteryStatusManager();
|
Loading…
Reference in a new issue