diff --git a/flow-typed/npm/react-ga_vx.x.x.js b/flow-typed/npm/react-ga_vx.x.x.js new file mode 100644 index 00000000..5d40089f --- /dev/null +++ b/flow-typed/npm/react-ga_vx.x.x.js @@ -0,0 +1,282 @@ +// flow-typed signature: 07c4a3f5d999e123126e4525eece89d8 +// flow-typed version: <>/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'>; +} diff --git a/package.json b/package.json index 784c1698..055e2fb2 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,8 @@ "reselect": "^4.0.0", "squarelink": "^1.1.3", "web3": "1.2.4", - "web3connect": "^1.0.0-beta.23" + "web3connect": "^1.0.0-beta.23", + "react-ga": "^2.7.0" }, "devDependencies": { "@babel/cli": "7.7.4", diff --git a/src/components/CookiesBanner/index.jsx b/src/components/CookiesBanner/index.jsx index 74441bfe..0f82cac2 100644 --- a/src/components/CookiesBanner/index.jsx +++ b/src/components/CookiesBanner/index.jsx @@ -15,6 +15,7 @@ import { loadFromCookie, saveCookie } from '~/logic/cookies/utils' import { cookieBannerOpen } from '~/logic/cookies/store/selectors' import { openCookieBanner } from '~/logic/cookies/store/actions/openCookieBanner' import { loadIntercom } from '~/utils/intercom' +import { loadGoogleAnalytics } from '~/utils/googleAnalytics' const useStyles = makeStyles({ container: { @@ -172,6 +173,7 @@ const CookiesBanner = () => { if (showAnalytics) { loadIntercom() + loadGoogleAnalytics() } return showBanner ? cookieBannerContent : null diff --git a/src/config/index.js b/src/config/index.js index dd5d6741..51e49108 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,32 +1,45 @@ // @flow -import { ensureOnce } from '~/utils/singleton' -import { ETHEREUM_NETWORK } from '~/logic/wallets/getWeb3' -import { TX_SERVICE_HOST, SIGNATURES_VIA_METAMASK, RELAY_API_URL } from '~/config/names' -import devConfig from './development' -import testConfig from './testing' -import stagingConfig from './staging' -import prodConfig from './production' -import mainnetDevConfig from './development-mainnet' -import mainnetProdConfig from './production-mainnet' -import mainnetStagingConfig from './staging-mainnet' +import { ensureOnce } from "~/utils/singleton" +import { ETHEREUM_NETWORK } from "~/logic/wallets/getWeb3" +import { + RELAY_API_URL, + SIGNATURES_VIA_METAMASK, + TX_SERVICE_HOST +} from "~/config/names" +import devConfig from "./development" +import testConfig from "./testing" +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 = () => { - if (process.env.NODE_ENV === 'test') { + if (process.env.NODE_ENV === "test") { return testConfig } - if (process.env.NODE_ENV === 'production') { - if (process.env.REACT_APP_NETWORK === 'mainnet') { - return process.env.REACT_APP_ENV === 'production' ? mainnetProdConfig : mainnetStagingConfig + if (process.env.NODE_ENV === "production") { + if (process.env.REACT_APP_NETWORK === "mainnet") { + 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) @@ -36,7 +49,8 @@ export const getTxServiceHost = () => { 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] @@ -46,4 +60,12 @@ export const signaturesViaMetamask = () => { 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" diff --git a/src/routes/index.js b/src/routes/index.js index ff3d3271..1b25ee85 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -15,6 +15,7 @@ import { OPENING_ADDRESS, LOAD_ADDRESS, } from './routes' +import { withTracker } from '~/utils/googleAnalytics' const Safe = React.lazy(() => import('./safe/container')) @@ -62,11 +63,11 @@ const Routes = ({ defaultSafe, location }: RoutesProps) => { return }} /> - - - - - + + + + + ) diff --git a/src/utils/googleAnalytics.js b/src/utils/googleAnalytics.js new file mode 100644 index 00000000..4d888c6d --- /dev/null +++ b/src/utils/googleAnalytics.js @@ -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 + } + + return HOC +} diff --git a/yarn.lock b/yarn.lock index 66c2a37b..632d5266 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15186,6 +15186,11 @@ react-focus-lock@^1.18.3: prop-types "^15.6.2" 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: version "1.0.4" resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.0.4.tgz#079ef10b7fefcaee6240fefd150711e62463cc97"