Feature #272: Google Analytics (#299)

* Adds google analytics tracking for every route

* Adds cookies acceptance check before tracking

* Fix react-ga dependency

* Fix cookieStore deletion

* Merge with #189-cookie-banner

* Fixs react ga version
Refactored HOC with hooks

* Fix TYPO

* Fix path for cookies utils

* Fix imports

* remove flow type definition for polish

* Add GA ID log

* Fix load GA After cookies acceptance
This commit is contained in:
Agustin Pane 2019-12-05 05:54:42 -03:00 committed by Mikhail Mikheev
parent 932dcf7eb4
commit 85ff11796e
7 changed files with 402 additions and 25 deletions

282
flow-typed/npm/react-ga_vx.x.x.js vendored Normal file
View File

@ -0,0 +1,282 @@
// flow-typed signature: 07c4a3f5d999e123126e4525eece89d8
// flow-typed version: <<STUB>>/react-ga_vlatest/flow_v0.112.0
/**
* This is an autogenerated libdef stub for:
*
* 'react-ga'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'react-ga' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'react-ga/core' {
declare module.exports: any;
}
declare module 'react-ga/demo/app/Events' {
declare module.exports: any;
}
declare module 'react-ga/demo/app' {
declare module.exports: any;
}
declare module 'react-ga/demo/app/Router' {
declare module.exports: any;
}
declare module 'react-ga/demo/app/withTracker' {
declare module.exports: any;
}
declare module 'react-ga/demo' {
declare module.exports: any;
}
declare module 'react-ga/dist/esm/components/OutboundLink' {
declare module.exports: any;
}
declare module 'react-ga/dist/esm/core' {
declare module.exports: any;
}
declare module 'react-ga/dist/esm' {
declare module.exports: any;
}
declare module 'react-ga/dist/esm/utils/console/log' {
declare module.exports: any;
}
declare module 'react-ga/dist/esm/utils/console/warn' {
declare module.exports: any;
}
declare module 'react-ga/dist/esm/utils/format' {
declare module.exports: any;
}
declare module 'react-ga/dist/esm/utils/loadGA' {
declare module.exports: any;
}
declare module 'react-ga/dist/esm/utils/mightBeEmail' {
declare module.exports: any;
}
declare module 'react-ga/dist/esm/utils/removeLeadingSlash' {
declare module.exports: any;
}
declare module 'react-ga/dist/esm/utils/testModeAPI' {
declare module.exports: any;
}
declare module 'react-ga/dist/esm/utils/toTitleCase' {
declare module.exports: any;
}
declare module 'react-ga/dist/esm/utils/trim' {
declare module.exports: any;
}
declare module 'react-ga/dist/react-ga-core' {
declare module.exports: any;
}
declare module 'react-ga/dist/react-ga-core.min' {
declare module.exports: any;
}
declare module 'react-ga/dist/react-ga' {
declare module.exports: any;
}
declare module 'react-ga/dist/react-ga.min' {
declare module.exports: any;
}
declare module 'react-ga/src/components/OutboundLink' {
declare module.exports: any;
}
declare module 'react-ga/src/core' {
declare module.exports: any;
}
declare module 'react-ga/src' {
declare module.exports: any;
}
declare module 'react-ga/src/utils/console/log' {
declare module.exports: any;
}
declare module 'react-ga/src/utils/console/warn' {
declare module.exports: any;
}
declare module 'react-ga/src/utils/format' {
declare module.exports: any;
}
declare module 'react-ga/src/utils/loadGA' {
declare module.exports: any;
}
declare module 'react-ga/src/utils/mightBeEmail' {
declare module.exports: any;
}
declare module 'react-ga/src/utils/removeLeadingSlash' {
declare module.exports: any;
}
declare module 'react-ga/src/utils/testModeAPI' {
declare module.exports: any;
}
declare module 'react-ga/src/utils/toTitleCase' {
declare module.exports: any;
}
declare module 'react-ga/src/utils/trim' {
declare module.exports: any;
}
declare module 'react-ga/version-bower' {
declare module.exports: any;
}
// Filename aliases
declare module 'react-ga/core.js' {
declare module.exports: $Exports<'react-ga/core'>;
}
declare module 'react-ga/demo/app/Events.jsx' {
declare module.exports: $Exports<'react-ga/demo/app/Events'>;
}
declare module 'react-ga/demo/app/index' {
declare module.exports: $Exports<'react-ga/demo/app'>;
}
declare module 'react-ga/demo/app/index.jsx' {
declare module.exports: $Exports<'react-ga/demo/app'>;
}
declare module 'react-ga/demo/app/Router.jsx' {
declare module.exports: $Exports<'react-ga/demo/app/Router'>;
}
declare module 'react-ga/demo/app/withTracker.jsx' {
declare module.exports: $Exports<'react-ga/demo/app/withTracker'>;
}
declare module 'react-ga/demo/index' {
declare module.exports: $Exports<'react-ga/demo'>;
}
declare module 'react-ga/demo/index.jsx' {
declare module.exports: $Exports<'react-ga/demo'>;
}
declare module 'react-ga/dist/esm/components/OutboundLink.js' {
declare module.exports: $Exports<'react-ga/dist/esm/components/OutboundLink'>;
}
declare module 'react-ga/dist/esm/core.js' {
declare module.exports: $Exports<'react-ga/dist/esm/core'>;
}
declare module 'react-ga/dist/esm/index' {
declare module.exports: $Exports<'react-ga/dist/esm'>;
}
declare module 'react-ga/dist/esm/index.js' {
declare module.exports: $Exports<'react-ga/dist/esm'>;
}
declare module 'react-ga/dist/esm/utils/console/log.js' {
declare module.exports: $Exports<'react-ga/dist/esm/utils/console/log'>;
}
declare module 'react-ga/dist/esm/utils/console/warn.js' {
declare module.exports: $Exports<'react-ga/dist/esm/utils/console/warn'>;
}
declare module 'react-ga/dist/esm/utils/format.js' {
declare module.exports: $Exports<'react-ga/dist/esm/utils/format'>;
}
declare module 'react-ga/dist/esm/utils/loadGA.js' {
declare module.exports: $Exports<'react-ga/dist/esm/utils/loadGA'>;
}
declare module 'react-ga/dist/esm/utils/mightBeEmail.js' {
declare module.exports: $Exports<'react-ga/dist/esm/utils/mightBeEmail'>;
}
declare module 'react-ga/dist/esm/utils/removeLeadingSlash.js' {
declare module.exports: $Exports<'react-ga/dist/esm/utils/removeLeadingSlash'>;
}
declare module 'react-ga/dist/esm/utils/testModeAPI.js' {
declare module.exports: $Exports<'react-ga/dist/esm/utils/testModeAPI'>;
}
declare module 'react-ga/dist/esm/utils/toTitleCase.js' {
declare module.exports: $Exports<'react-ga/dist/esm/utils/toTitleCase'>;
}
declare module 'react-ga/dist/esm/utils/trim.js' {
declare module.exports: $Exports<'react-ga/dist/esm/utils/trim'>;
}
declare module 'react-ga/dist/react-ga-core.js' {
declare module.exports: $Exports<'react-ga/dist/react-ga-core'>;
}
declare module 'react-ga/dist/react-ga-core.min.js' {
declare module.exports: $Exports<'react-ga/dist/react-ga-core.min'>;
}
declare module 'react-ga/dist/react-ga.js' {
declare module.exports: $Exports<'react-ga/dist/react-ga'>;
}
declare module 'react-ga/dist/react-ga.min.js' {
declare module.exports: $Exports<'react-ga/dist/react-ga.min'>;
}
declare module 'react-ga/src/components/OutboundLink.js' {
declare module.exports: $Exports<'react-ga/src/components/OutboundLink'>;
}
declare module 'react-ga/src/core.js' {
declare module.exports: $Exports<'react-ga/src/core'>;
}
declare module 'react-ga/src/index' {
declare module.exports: $Exports<'react-ga/src'>;
}
declare module 'react-ga/src/index.js' {
declare module.exports: $Exports<'react-ga/src'>;
}
declare module 'react-ga/src/utils/console/log.js' {
declare module.exports: $Exports<'react-ga/src/utils/console/log'>;
}
declare module 'react-ga/src/utils/console/warn.js' {
declare module.exports: $Exports<'react-ga/src/utils/console/warn'>;
}
declare module 'react-ga/src/utils/format.js' {
declare module.exports: $Exports<'react-ga/src/utils/format'>;
}
declare module 'react-ga/src/utils/loadGA.js' {
declare module.exports: $Exports<'react-ga/src/utils/loadGA'>;
}
declare module 'react-ga/src/utils/mightBeEmail.js' {
declare module.exports: $Exports<'react-ga/src/utils/mightBeEmail'>;
}
declare module 'react-ga/src/utils/removeLeadingSlash.js' {
declare module.exports: $Exports<'react-ga/src/utils/removeLeadingSlash'>;
}
declare module 'react-ga/src/utils/testModeAPI.js' {
declare module.exports: $Exports<'react-ga/src/utils/testModeAPI'>;
}
declare module 'react-ga/src/utils/toTitleCase.js' {
declare module.exports: $Exports<'react-ga/src/utils/toTitleCase'>;
}
declare module 'react-ga/src/utils/trim.js' {
declare module.exports: $Exports<'react-ga/src/utils/trim'>;
}
declare module 'react-ga/version-bower.js' {
declare module.exports: $Exports<'react-ga/version-bower'>;
}

View File

@ -71,7 +71,8 @@
"reselect": "^4.0.0", "reselect": "^4.0.0",
"squarelink": "^1.1.3", "squarelink": "^1.1.3",
"web3": "1.2.4", "web3": "1.2.4",
"web3connect": "^1.0.0-beta.23" "web3connect": "^1.0.0-beta.23",
"react-ga": "^2.7.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "7.7.4", "@babel/cli": "7.7.4",

View File

@ -15,6 +15,7 @@ import { loadFromCookie, saveCookie } from '~/logic/cookies/utils'
import { cookieBannerOpen } from '~/logic/cookies/store/selectors' import { cookieBannerOpen } from '~/logic/cookies/store/selectors'
import { openCookieBanner } from '~/logic/cookies/store/actions/openCookieBanner' import { openCookieBanner } from '~/logic/cookies/store/actions/openCookieBanner'
import { loadIntercom } from '~/utils/intercom' import { loadIntercom } from '~/utils/intercom'
import { loadGoogleAnalytics } from '~/utils/googleAnalytics'
const useStyles = makeStyles({ const useStyles = makeStyles({
container: { container: {
@ -172,6 +173,7 @@ const CookiesBanner = () => {
if (showAnalytics) { if (showAnalytics) {
loadIntercom() loadIntercom()
loadGoogleAnalytics()
} }
return showBanner ? cookieBannerContent : null return showBanner ? cookieBannerContent : null

View File

@ -1,32 +1,45 @@
// @flow // @flow
import { ensureOnce } from '~/utils/singleton' import { ensureOnce } from "~/utils/singleton"
import { ETHEREUM_NETWORK } from '~/logic/wallets/getWeb3' import { ETHEREUM_NETWORK } from "~/logic/wallets/getWeb3"
import { TX_SERVICE_HOST, SIGNATURES_VIA_METAMASK, RELAY_API_URL } from '~/config/names' import {
import devConfig from './development' RELAY_API_URL,
import testConfig from './testing' SIGNATURES_VIA_METAMASK,
import stagingConfig from './staging' TX_SERVICE_HOST
import prodConfig from './production' } from "~/config/names"
import mainnetDevConfig from './development-mainnet' import devConfig from "./development"
import mainnetProdConfig from './production-mainnet' import testConfig from "./testing"
import mainnetStagingConfig from './staging-mainnet' import stagingConfig from "./staging"
import prodConfig from "./production"
import mainnetDevConfig from "./development-mainnet"
import mainnetProdConfig from "./production-mainnet"
import mainnetStagingConfig from "./staging-mainnet"
const configuration = () => { const configuration = () => {
if (process.env.NODE_ENV === 'test') { if (process.env.NODE_ENV === "test") {
return testConfig return testConfig
} }
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === "production") {
if (process.env.REACT_APP_NETWORK === 'mainnet') { if (process.env.REACT_APP_NETWORK === "mainnet") {
return process.env.REACT_APP_ENV === 'production' ? mainnetProdConfig : mainnetStagingConfig return process.env.REACT_APP_ENV === "production"
? mainnetProdConfig
: mainnetStagingConfig
} }
return process.env.REACT_APP_ENV === 'production' ? prodConfig : stagingConfig return process.env.REACT_APP_ENV === "production"
? prodConfig
: stagingConfig
} }
return process.env.REACT_APP_NETWORK === 'mainnet' ? mainnetDevConfig : devConfig return process.env.REACT_APP_NETWORK === "mainnet"
? mainnetDevConfig
: devConfig
} }
export const getNetwork = () => (process.env.REACT_APP_NETWORK === 'mainnet' ? ETHEREUM_NETWORK.MAINNET : ETHEREUM_NETWORK.RINKEBY) export const getNetwork = () =>
process.env.REACT_APP_NETWORK === "mainnet"
? ETHEREUM_NETWORK.MAINNET
: ETHEREUM_NETWORK.RINKEBY
const getConfig = ensureOnce(configuration) const getConfig = ensureOnce(configuration)
@ -36,7 +49,8 @@ export const getTxServiceHost = () => {
return config[TX_SERVICE_HOST] return config[TX_SERVICE_HOST]
} }
export const getTxServiceUriFrom = (safeAddress: string) => `safes/${safeAddress}/transactions/` export const getTxServiceUriFrom = (safeAddress: string) =>
`safes/${safeAddress}/transactions/`
export const getRelayUrl = () => getConfig()[RELAY_API_URL] export const getRelayUrl = () => getConfig()[RELAY_API_URL]
@ -46,4 +60,12 @@ export const signaturesViaMetamask = () => {
return config[SIGNATURES_VIA_METAMASK] return config[SIGNATURES_VIA_METAMASK]
} }
export const getIntercomId = () => (process.env.REACT_APP_ENV === 'production' ? process.env.REACT_APP_INTERCOM_ID : 'plssl1fl') export const getGoogleAnalyticsTrackingID = () =>
getNetwork() === ETHEREUM_NETWORK.MAINNET
? process.env.REACT_APP_GOOGLE_ANALYTICS_ID_MAINNET
: process.env.REACT_APP_GOOGLE_ANALYTICS_ID_RINKEBY
export const getIntercomId = () =>
process.env.REACT_APP_ENV === "production"
? process.env.REACT_APP_INTERCOM_ID
: "plssl1fl"

View File

@ -15,6 +15,7 @@ import {
OPENING_ADDRESS, OPENING_ADDRESS,
LOAD_ADDRESS, LOAD_ADDRESS,
} from './routes' } from './routes'
import { withTracker } from '~/utils/googleAnalytics'
const Safe = React.lazy(() => import('./safe/container')) const Safe = React.lazy(() => import('./safe/container'))
@ -62,11 +63,11 @@ const Routes = ({ defaultSafe, location }: RoutesProps) => {
return <Redirect to={WELCOME_ADDRESS} /> return <Redirect to={WELCOME_ADDRESS} />
}} }}
/> />
<Route exact path={WELCOME_ADDRESS} component={Welcome} /> <Route exact path={WELCOME_ADDRESS} component={withTracker(Welcome)} />
<Route exact path={OPEN_ADDRESS} component={Open} /> <Route exact path={OPEN_ADDRESS} component={withTracker(Open)} />
<Route path={SAFE_ADDRESS} component={Safe} /> <Route path={SAFE_ADDRESS} component={withTracker(Safe)} />
<Route exact path={OPENING_ADDRESS} component={Opening} /> <Route exact path={OPENING_ADDRESS} component={withTracker(Opening)} />
<Route exact path={LOAD_ADDRESS} component={Load} /> <Route exact path={LOAD_ADDRESS} component={withTracker(Load)} />
<Redirect to="/" /> <Redirect to="/" />
</Switch> </Switch>
) )

View File

@ -0,0 +1,64 @@
// @flow
import React, { useEffect, useState } from 'react'
import GoogleAnalytics from 'react-ga'
import { getGoogleAnalyticsTrackingID } from '~/config'
import { COOKIES_KEY } from '~/logic/cookies/model/cookie'
import type { CookiesProps } from '~/logic/cookies/model/cookie'
import type { RouterProps } from '~/routes/safe/store/selectors'
import { loadFromCookie } from '~/logic/cookies/utils'
let analyticsLoaded = false
export const loadGoogleAnalytics = () => {
if (analyticsLoaded) {
return
}
// eslint-disable-next-line no-console
console.log('Loading google analytics...')
const trackingID = getGoogleAnalyticsTrackingID()
if (!trackingID) {
console.error('[GoogleAnalytics] - In order to use google analytics you need to add an trackingID')
} else {
GoogleAnalytics.initialize(trackingID)
analyticsLoaded = true
}
}
export const withTracker = (WrappedComponent, options = {}) => {
const [useAnalytics, setUseAnalytics] = useState(false)
useEffect(() => {
async function fetchCookiesFromStorage() {
const cookiesState: CookiesProps = await loadFromCookie(COOKIES_KEY)
if (cookiesState) {
const { acceptedAnalytics } = cookiesState
setUseAnalytics(acceptedAnalytics)
}
}
fetchCookiesFromStorage()
}, [])
const trackPage = (page) => {
if (!useAnalytics || !analyticsLoaded) {
return
}
GoogleAnalytics.set({
page,
...options,
})
GoogleAnalytics.pageview(page)
}
const HOC = (props: RouterProps) => {
// eslint-disable-next-line react/prop-types
const { location } = props
useEffect(() => {
const page = location.pathname + location.search
trackPage(page)
}, [location.pathname])
return <WrappedComponent {...props} />
}
return HOC
}

View File

@ -15186,6 +15186,11 @@ react-focus-lock@^1.18.3:
prop-types "^15.6.2" prop-types "^15.6.2"
react-clientside-effect "^1.2.0" react-clientside-effect "^1.2.0"
react-ga@^2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/react-ga/-/react-ga-2.7.0.tgz#24328f157f31e8cffbf4de74a3396536679d8d7c"
integrity sha512-AjC7UOZMvygrWTc2hKxTDvlMXEtbmA0IgJjmkhgmQQ3RkXrWR11xEagLGFGaNyaPnmg24oaIiaNPnEoftUhfXA==
react-helmet-async@^1.0.2: react-helmet-async@^1.0.2:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.0.4.tgz#079ef10b7fefcaee6240fefd150711e62463cc97" resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.0.4.tgz#079ef10b7fefcaee6240fefd150711e62463cc97"