Gaëtan Renaudeau
7 years ago
committed by
GitHub
39 changed files with 420 additions and 86 deletions
@ -0,0 +1,10 @@ |
|||
#!/bin/bash |
|||
|
|||
if [ -z $ANALYTICS_KEY ]; then |
|||
echo 'ANALYTICS_KEY must be set' |
|||
exit 1 |
|||
fi |
|||
|
|||
cd `dirname $0`/.. |
|||
|
|||
wget https://cdn.segment.com/analytics.js/v1/$ANALYTICS_KEY/analytics.min.js -O static/analytics.min.js |
@ -0,0 +1,29 @@ |
|||
import { PureComponent } from 'react' |
|||
import { track } from './segment' |
|||
|
|||
class Track extends PureComponent<{ |
|||
onMount?: boolean, |
|||
onUnmount?: boolean, |
|||
onUpdate?: boolean, |
|||
event: string, |
|||
properties?: Object, |
|||
}> { |
|||
componentDidMount() { |
|||
if (this.props.onMount) this.track() |
|||
} |
|||
componentDidUpdate() { |
|||
if (this.props.onUpdate) this.track() |
|||
} |
|||
componentWillUnmount() { |
|||
if (this.props.onUnmount) this.track() |
|||
} |
|||
track = () => { |
|||
const { event, properties } = this.props |
|||
track(event, properties) |
|||
} |
|||
render() { |
|||
return null |
|||
} |
|||
} |
|||
|
|||
export default Track |
@ -0,0 +1,14 @@ |
|||
import { PureComponent } from 'react' |
|||
import { page } from './segment' |
|||
|
|||
class TrackPage extends PureComponent<{ category: string, name?: string, properties?: Object }> { |
|||
componentDidMount() { |
|||
const { category, name, properties } = this.props |
|||
page(category, name, properties) |
|||
} |
|||
render() { |
|||
return null |
|||
} |
|||
} |
|||
|
|||
export default TrackPage |
@ -0,0 +1,18 @@ |
|||
/* eslint-disable */ |
|||
import { getPath } from 'helpers/staticPath' |
|||
|
|||
// prettier-ignore
|
|||
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var e=analytics.methods[t];analytics[e]=analytics.factory(e)};analytics.SNIPPET_VERSION="4.1.0"; |
|||
}}(); |
|||
|
|||
let loaded = false |
|||
export const load = () => { |
|||
if (loaded) return |
|||
loaded = true |
|||
var n = document.createElement('script') |
|||
n.type = 'text/javascript' |
|||
n.async = !0 |
|||
n.src = getPath('analytics.min.js') |
|||
var a = document.getElementsByTagName('script')[0] |
|||
a.parentNode.insertBefore(n, a) |
|||
} |
@ -0,0 +1,100 @@ |
|||
// @flow
|
|||
|
|||
import uuid from 'uuid/v4' |
|||
import logger from 'logger' |
|||
import invariant from 'invariant' |
|||
import user from 'helpers/user' |
|||
import { DEBUG_ANALYTICS } from 'config/constants' |
|||
import { langAndRegionSelector } from 'reducers/settings' |
|||
import { getSystemLocale } from 'helpers/systemLocale' |
|||
import { load } from './inject-in-window' |
|||
|
|||
invariant(typeof window !== 'undefined', 'analytics/segment must be called on renderer thread') |
|||
|
|||
const sessionId = uuid() |
|||
|
|||
const getContext = store => { |
|||
const state = store.getState() |
|||
const { language, region } = langAndRegionSelector(state) |
|||
const systemLocale = getSystemLocale() |
|||
return { |
|||
ip: '0.0.0.0', |
|||
appVersion: __APP_VERSION__, |
|||
language, |
|||
region, |
|||
environment: __DEV__ ? 'development' : 'production', |
|||
systemLanguage: systemLocale.language, |
|||
systemRegion: systemLocale.region, |
|||
sessionId, |
|||
} |
|||
} |
|||
|
|||
let storeInstance // is the redux store. it's also used as a flag to know if analytics is on or off.
|
|||
|
|||
export const start = (store: *) => { |
|||
storeInstance = store |
|||
const { analytics } = window |
|||
if (typeof analytics === 'undefined') { |
|||
logger.error('analytics is not available') |
|||
return |
|||
} |
|||
const { id } = user() |
|||
load() |
|||
analytics.identify( |
|||
id, |
|||
{}, |
|||
{ |
|||
context: getContext(store), |
|||
}, |
|||
) |
|||
if (DEBUG_ANALYTICS) { |
|||
logger.log(`analytics: start() with user id ${id}`) |
|||
} |
|||
} |
|||
|
|||
export const stop = () => { |
|||
storeInstance = null |
|||
const { analytics } = window |
|||
if (typeof analytics === 'undefined') { |
|||
logger.error('analytics is not available') |
|||
return |
|||
} |
|||
analytics.reset() |
|||
if (DEBUG_ANALYTICS) { |
|||
logger.log(`analytics: stop()`) |
|||
} |
|||
} |
|||
|
|||
export const track = (event: string, properties: ?Object) => { |
|||
if (!storeInstance) { |
|||
return |
|||
} |
|||
const { analytics } = window |
|||
if (typeof analytics === 'undefined') { |
|||
logger.error('analytics is not available') |
|||
return |
|||
} |
|||
analytics.track(event, properties, { |
|||
context: getContext(storeInstance), |
|||
}) |
|||
if (DEBUG_ANALYTICS) { |
|||
logger.log(`analytics: track(${event},`, properties) |
|||
} |
|||
} |
|||
|
|||
export const page = (category: string, name: ?string, properties: ?Object) => { |
|||
if (!storeInstance) { |
|||
return |
|||
} |
|||
const { analytics } = window |
|||
if (typeof analytics === 'undefined') { |
|||
logger.error('analytics is not available') |
|||
return |
|||
} |
|||
analytics.page(category, name, properties, { |
|||
context: getContext(storeInstance), |
|||
}) |
|||
if (DEBUG_ANALYTICS) { |
|||
logger.log(`analytics: page(${category}, ${name || ''},`, properties) |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
import React, { Component, Fragment } from 'react' |
|||
import TrackPage from 'analytics/TrackPage' |
|||
import StepConnectDevice from '../StepConnectDevice' |
|||
|
|||
class ReceiveStepConnectDevice extends Component<*> { |
|||
render() { |
|||
return ( |
|||
<Fragment> |
|||
<TrackPage category="Receive" name="Step2" /> |
|||
<StepConnectDevice {...this.props} /> |
|||
</Fragment> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default ReceiveStepConnectDevice |
@ -0,0 +1,16 @@ |
|||
import React, { Component, Fragment } from 'react' |
|||
import TrackPage from 'analytics/TrackPage' |
|||
import StepConnectDevice from '../StepConnectDevice' |
|||
|
|||
class SendStepConnectDevice extends Component<*> { |
|||
render() { |
|||
return ( |
|||
<Fragment> |
|||
<TrackPage category="Send" name="Step2" /> |
|||
<StepConnectDevice {...this.props} /> |
|||
</Fragment> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default SendStepConnectDevice |
@ -0,0 +1,11 @@ |
|||
// @flow
|
|||
import memoize from 'lodash/memoize' |
|||
|
|||
const parse = memoize(navLang => { |
|||
const localeSplit = (navLang || '').split('-') |
|||
const language = (localeSplit[0] || '').toLowerCase() || null |
|||
const region = (localeSplit[1] || '').toUpperCase() || null |
|||
return { language, region } |
|||
}) |
|||
|
|||
export const getSystemLocale = () => parse(window.navigator.language) |
@ -0,0 +1,18 @@ |
|||
import { shareAnalyticsSelector } from 'reducers/settings' |
|||
import { start, stop } from 'analytics/segment' |
|||
|
|||
let isAnalyticsStarted = false |
|||
|
|||
export default store => next => action => { |
|||
next(action) |
|||
const state = store.getState() |
|||
const shareAnalytics = shareAnalyticsSelector(state) |
|||
if (shareAnalytics !== isAnalyticsStarted) { |
|||
isAnalyticsStarted = shareAnalytics |
|||
if (shareAnalytics) { |
|||
start(store) |
|||
} else { |
|||
stop() |
|||
} |
|||
} |
|||
} |
File diff suppressed because one or more lines are too long
@ -1,2 +1,3 @@ |
|||
system: Use system locale |
|||
en: English |
|||
fr: French |
|||
|
Loading…
Reference in new issue