Commit b17a38d9 authored by Lynn's avatar Lynn Committed by GitHub

feat: abstract analytics logging (#3892)

* fix: init commit

* fix: replace ReactGA.event with GoogleAnalyticsProvider.sentEvent

* fix: use GoogleAnalyticsProvider for all other ReactGA usages

* fix: add missing GoogleAnalyticsProvider import

* fix: incorporate zzmp's suggestions

* fix: add import I forgot

* fix: add another import I forgot

* fix: respond to zzmp comments
Co-authored-by: default avatarLynn Yu <lynn.yu@uniswap.org>
parent 22136b27
import { Trans } from '@lingui/macro'
import { sendEvent } from 'components/analytics'
import React, { ErrorInfo } from 'react'
import ReactGA from 'react-ga4'
import styled from 'styled-components/macro'
import store, { AppState } from '../../state'
......@@ -85,7 +85,10 @@ export default class ErrorBoundary extends React.Component<unknown, ErrorBoundar
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
ReactGA.event('exception', { description: error.toString() + errorInfo.toString(), fatal: true })
sendEvent('exception', {
description: error.toString() + errorInfo.toString(),
fatal: true,
})
}
render() {
......
import { Trans } from '@lingui/macro'
import { Currency } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
import { sendEvent } from 'components/analytics'
import { ButtonGray } from 'components/Button'
import Card from 'components/Card'
import { AutoColumn } from 'components/Column'
......@@ -10,8 +11,7 @@ import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution'
import { PoolState, usePools } from 'hooks/usePools'
import usePrevious from 'hooks/usePrevious'
import { DynamicSection } from 'pages/AddLiquidity/styled'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import ReactGA from 'react-ga4'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Box } from 'rebass'
import styled, { keyframes } from 'styled-components/macro'
import { ThemedText } from 'theme'
......@@ -101,7 +101,7 @@ export default function FeeSelector({
const handleFeePoolSelectWithEvent = useCallback(
(fee: FeeAmount) => {
ReactGA.event({
sendEvent({
category: 'FeePoolSelect',
action: 'Manual',
})
......@@ -122,7 +122,7 @@ export default function FeeSelector({
setShowOptions(false)
recommended.current = true
ReactGA.event({
sendEvent({
category: 'FeePoolSelect',
action: ' Recommended',
})
......
import { Trans } from '@lingui/macro'
import { Currency, Price, Token } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
import { sendEvent } from 'components/analytics'
import { AutoColumn, ColumnCenter } from 'components/Column'
import Loader from 'components/Loader'
import { format } from 'd3'
......@@ -9,7 +10,6 @@ import useTheme from 'hooks/useTheme'
import { saturate } from 'polished'
import React, { ReactNode, useCallback, useMemo } from 'react'
import { BarChart2, CloudOff, Inbox } from 'react-feather'
import ReactGA from 'react-ga4'
import { batch } from 'react-redux'
import { Bound } from 'state/mint/v3/actions'
import styled from 'styled-components/macro'
......@@ -158,7 +158,7 @@ export default function LiquidityChartRangeInput({
)
if (isError) {
ReactGA.event('exception', { description: error.toString(), fatal: false })
sendEvent('exception', { description: error.toString(), fatal: false })
}
return (
......
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { sendEvent } from 'components/analytics'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useCallback, useEffect } from 'react'
import { Heart, X } from 'react-feather'
import ReactGA from 'react-ga4'
import styled, { keyframes } from 'styled-components/macro'
import tokenLogo from '../../assets/images/token-logo.png'
......@@ -65,7 +65,7 @@ export default function ClaimPopup() {
const showClaimModal = useModalOpen(ApplicationModal.SELF_CLAIM)
const toggleSelfClaimModal = useToggleSelfClaimModal()
const handleToggleSelfClaimModal = useCallback(() => {
ReactGA.event({
sendEvent({
category: 'MerkleDrop',
action: 'Toggle self claim modal',
})
......@@ -79,7 +79,7 @@ export default function ClaimPopup() {
// listen for available claim and show popup if needed
useEffect(() => {
if (userHasAvailableclaim) {
ReactGA.event({
sendEvent({
category: 'MerkleDrop',
action: 'Show claim popup',
})
......
import { Trans } from '@lingui/macro'
import { sendEvent } from 'components/analytics'
import { AutoColumn } from 'components/Column'
import { RowFixed } from 'components/Row'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import { useEffect } from 'react'
import { MessageCircle, X } from 'react-feather'
import ReactGA from 'react-ga4'
import { useShowSurveyPopup } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { ExternalLink, ThemedText, Z_INDEX } from 'theme'
......@@ -62,7 +62,7 @@ export default function SurveyPopup() {
if (Math.random() < 0.01) {
setShowSurveyPopup(true)
// log a case of succesful view
ReactGA.event({
sendEvent({
category: 'Survey',
action: 'Saw Survey',
})
......@@ -80,7 +80,7 @@ export default function SurveyPopup() {
<Wrapper gap="10px">
<WrappedCloseIcon
onClick={() => {
ReactGA.event({
sendEvent({
category: 'Survey',
action: 'Clicked Survey Link',
})
......
import { Trans } from '@lingui/macro'
import { sendEvent } from 'components/analytics'
import Card, { DarkGreyCard } from 'components/Card'
import Row, { AutoRow, RowBetween } from 'components/Row'
import { useEffect, useRef } from 'react'
import { ArrowDown, Info, X } from 'react-feather'
import ReactGA from 'react-ga4'
import styled from 'styled-components/macro'
import { ExternalLink, ThemedText } from 'theme'
import { isMobile } from 'utils/userAgent'
......@@ -87,7 +87,7 @@ export function PrivacyPolicyModal() {
useEffect(() => {
if (!open) return
ReactGA.event({
sendEvent({
category: 'Modal',
action: 'Show Legal',
})
......
import { Trans } from '@lingui/macro'
import { sendEvent } from 'components/analytics'
import { ButtonOutlined } from 'components/Button'
import { AutoRow } from 'components/Row'
import React from 'react'
import ReactGA from 'react-ga4'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
......@@ -20,7 +20,7 @@ export default function PresetsButtons({ setFullRange }: { setFullRange: () => v
<Button
onClick={() => {
setFullRange()
ReactGA.event({
sendEvent({
category: 'Liquidity',
action: 'Full Range Clicked',
})
......
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
import { Currency, Token } from '@uniswap/sdk-core'
import { sendEvent } from 'components/analytics'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import useDebounce from 'hooks/useDebounce'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
......@@ -11,7 +12,6 @@ import { getTokenFilter } from 'lib/hooks/useTokenList/filtering'
import { tokenComparator, useSortTokensByQuery } from 'lib/hooks/useTokenList/sorting'
import { KeyboardEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Edit } from 'react-feather'
import ReactGA from 'react-ga4'
import AutoSizer from 'react-virtualized-auto-sizer'
import { FixedSizeList } from 'react-window'
import { Text } from 'rebass'
......@@ -93,7 +93,7 @@ export function CurrencySearch({
useEffect(() => {
if (isAddressSearch) {
ReactGA.event({
sendEvent({
category: 'Currency Select',
action: 'Search by address',
label: isAddressSearch,
......
import { Trans } from '@lingui/macro'
import { TokenList } from '@uniswap/token-lists'
import { sendEvent } from 'components/analytics'
import { ButtonPrimary } from 'components/Button'
import Card from 'components/Card'
import { AutoColumn } from 'components/Column'
......@@ -11,7 +12,6 @@ import useTheme from 'hooks/useTheme'
import { transparentize } from 'polished'
import { useCallback, useState } from 'react'
import { AlertTriangle, ArrowLeft } from 'react-feather'
import ReactGA from 'react-ga4'
import { useAppDispatch } from 'state/hooks'
import { enableList, removeList } from 'state/lists/actions'
import { useAllLists } from 'state/lists/hooks'
......@@ -54,7 +54,7 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
setAddError(null)
fetchList(listURL)
.then(() => {
ReactGA.event({
sendEvent({
category: 'Lists',
action: 'Add List',
label: listURL,
......@@ -66,7 +66,7 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
setModalView(CurrencyModalView.manage)
})
.catch((error) => {
ReactGA.event({
sendEvent({
category: 'Lists',
action: 'Add List Failed',
label: listURL,
......
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
import { TokenList } from '@uniswap/token-lists'
import { sendEvent } from 'components/analytics'
import Card from 'components/Card'
import { UNSUPPORTED_LIST_URLS } from 'constants/lists'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
......@@ -9,7 +10,6 @@ import parseENSAddress from 'lib/utils/parseENSAddress'
import uriToHttp from 'lib/utils/uriToHttp'
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { CheckCircle, Settings } from 'react-feather'
import ReactGA from 'react-ga4'
import { usePopper } from 'react-popper'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import styled from 'styled-components/macro'
......@@ -126,7 +126,7 @@ const ListRow = memo(function ListRow({ listUrl }: { listUrl: string }) {
const handleAcceptListUpdate = useCallback(() => {
if (!pending) return
ReactGA.event({
sendEvent({
category: 'Lists',
action: 'Update List from List Select',
label: listUrl,
......@@ -135,13 +135,13 @@ const ListRow = memo(function ListRow({ listUrl }: { listUrl: string }) {
}, [dispatch, listUrl, pending])
const handleRemoveList = useCallback(() => {
ReactGA.event({
sendEvent({
category: 'Lists',
action: 'Start Remove List',
label: listUrl,
})
if (window.prompt(t`Please confirm you would like to remove this list by typing REMOVE`) === `REMOVE`) {
ReactGA.event({
sendEvent({
category: 'Lists',
action: 'Confirm Remove List',
label: listUrl,
......@@ -151,7 +151,7 @@ const ListRow = memo(function ListRow({ listUrl }: { listUrl: string }) {
}, [dispatch, listUrl])
const handleEnableList = useCallback(() => {
ReactGA.event({
sendEvent({
category: 'Lists',
action: 'Enable List',
label: listUrl,
......@@ -160,7 +160,7 @@ const ListRow = memo(function ListRow({ listUrl }: { listUrl: string }) {
}, [dispatch, listUrl])
const handleDisableList = useCallback(() => {
ReactGA.event({
sendEvent({
category: 'Lists',
action: 'Disable List',
label: listUrl,
......
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
import { Percent } from '@uniswap/sdk-core'
import { sendEvent } from 'components/analytics'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { AUTO_ROUTER_SUPPORTED_CHAINS } from 'lib/hooks/routing/clientSideSmartOrderRouter'
import { useContext, useRef, useState } from 'react'
import { Settings, X } from 'react-feather'
import ReactGA from 'react-ga4'
import { Text } from 'rebass'
import styled, { ThemeContext } from 'styled-components/macro'
......@@ -211,7 +211,7 @@ export default function SettingsTab({ placeholderSlippage }: { placeholderSlippa
id="toggle-optimized-router-button"
isActive={!clientSideRouter}
toggle={() => {
ReactGA.event({
sendEvent({
category: 'Routing',
action: clientSideRouter ? 'enable routing API' : 'disable routing API',
})
......
import { Trans } from '@lingui/macro'
import { sendEvent } from 'components/analytics'
import { AutoColumn } from 'components/Column'
import { PrivacyPolicy } from 'components/PrivacyPolicy'
import Row, { AutoRow } from 'components/Row'
import { useCallback, useEffect, useState } from 'react'
import { ArrowLeft } from 'react-feather'
import ReactGA from 'react-ga4'
import styled from 'styled-components/macro'
import { AbstractConnector } from 'web3-react-abstract-connector'
import { UnsupportedChainIdError, useWeb3React } from 'web3-react-core'
......@@ -169,7 +169,7 @@ export default function WalletModal({
return true
})
// log selected wallet
ReactGA.event({
sendEvent({
category: 'Wallet',
action: 'Change Wallet',
label: name,
......
import ReactGA from 'react-ga4'
import { GaOptions, InitOptions, UaEventOptions } from 'react-ga4/types/ga4'
/**
* Google Analytics Provider containing all methods used throughout app to log events to Google Analytics.
*/
export default class GoogleAnalyticsProvider {
public sendEvent(event: string | UaEventOptions, params?: any) {
ReactGA.event(event, params)
}
public initialize(
GA_MEASUREMENT_ID: InitOptions[] | string,
options?: {
legacyDimensionMetric?: boolean
nonce?: string
testMode?: boolean
gaOptions?: GaOptions | any
gtagOptions?: any
}
) {
ReactGA.initialize(GA_MEASUREMENT_ID, options)
}
public set(fieldsObject: any) {
ReactGA.set(fieldsObject)
}
public outboundLink(
{
label,
}: {
label: string
},
hitCallback: () => unknown
) {
ReactGA.outboundLink({ label }, hitCallback)
}
public pageview(path?: string, _?: string[], title?: string) {
ReactGA.pageview(path, _, title)
}
public ga(...args: any[]) {
ReactGA.ga(...args)
}
public gaCommandSendTiming(timingCategory: any, timingVar: any, timingValue: any, timingLabel: any) {
ReactGA._gaCommandSendTiming(timingCategory, timingVar, timingValue, timingLabel)
}
}
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useEffect } from 'react'
import ReactGA from 'react-ga4'
import { RouteComponentProps } from 'react-router-dom'
import { getCLS, getFCP, getFID, getLCP, Metric } from 'web-vitals'
import { GOOGLE_ANALYTICS_CLIENT_ID_STORAGE_KEY } from './index'
function reportWebVitals({ name, delta, id }: Metric) {
ReactGA._gaCommandSendTiming('Web Vitals', name, Math.round(name === 'CLS' ? delta * 1000 : delta), id)
}
// tracks web vitals and pageviews
export default function GoogleAnalyticsReporter({ location: { pathname, search } }: RouteComponentProps): null {
useEffect(() => {
getFCP(reportWebVitals)
getFID(reportWebVitals)
getLCP(reportWebVitals)
getCLS(reportWebVitals)
}, [])
const { chainId } = useActiveWeb3React()
useEffect(() => {
// cd1 - custom dimension 1 - chainId
ReactGA.set({ cd1: chainId ?? 0 })
}, [chainId])
useEffect(() => {
ReactGA.pageview(`${pathname}${search}`)
}, [pathname, search])
useEffect(() => {
// typed as 'any' in react-ga4 -.-
ReactGA.ga((tracker: any) => {
if (!tracker) return
const clientId = tracker.get('clientId')
window.localStorage.setItem(GOOGLE_ANALYTICS_CLIENT_ID_STORAGE_KEY, clientId)
})
}, [])
return null
}
import ReactGA from 'react-ga4'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useEffect } from 'react'
import { UaEventOptions } from 'react-ga4/types/ga4'
import { RouteComponentProps } from 'react-router-dom'
import { isMobile } from 'utils/userAgent'
import { getCLS, getFCP, getFID, getLCP, Metric } from 'web-vitals'
import GoogleAnalyticsProvider from './GoogleAnalyticsProvider'
export const GOOGLE_ANALYTICS_CLIENT_ID_STORAGE_KEY = 'ga_client_id'
const GOOGLE_ANALYTICS_ID: string | undefined = process.env.REACT_APP_GOOGLE_ANALYTICS_ID
const storedClientId = window.localStorage.getItem(GOOGLE_ANALYTICS_CLIENT_ID_STORAGE_KEY)
const googleAnalytics = new GoogleAnalyticsProvider()
export function sendEvent(event: string | UaEventOptions, params?: any) {
return googleAnalytics.sendEvent(event, params)
}
export function outboundLink(
{
label,
}: {
label: string
},
hitCallback: () => unknown
) {
return googleAnalytics.outboundLink({ label }, hitCallback)
}
export function sendTiming(timingCategory: any, timingVar: any, timingValue: any, timingLabel: any) {
return googleAnalytics.gaCommandSendTiming(timingCategory, timingVar, timingValue, timingLabel)
}
if (typeof GOOGLE_ANALYTICS_ID === 'string') {
ReactGA.initialize(GOOGLE_ANALYTICS_ID, {
googleAnalytics.initialize(GOOGLE_ANALYTICS_ID, {
gaOptions: {
storage: 'none',
storeGac: false,
clientId: storedClientId ?? undefined,
},
})
ReactGA.set({
googleAnalytics.set({
anonymizeIp: true,
customBrowserType: !isMobile
? 'desktop'
......@@ -23,5 +50,39 @@ if (typeof GOOGLE_ANALYTICS_ID === 'string') {
: 'mobileRegular',
})
} else {
ReactGA.initialize('test', { gtagOptions: { debug_mode: true } })
googleAnalytics.initialize('test', { gtagOptions: { debug_mode: true } })
}
function reportWebVitals({ name, delta, id }: Metric) {
sendTiming('Web Vitals', name, Math.round(name === 'CLS' ? delta * 1000 : delta), id)
}
// tracks web vitals and pageviews
export function useAnalyticsReporter({ pathname, search }: RouteComponentProps['location']) {
useEffect(() => {
getFCP(reportWebVitals)
getFID(reportWebVitals)
getLCP(reportWebVitals)
getCLS(reportWebVitals)
}, [])
const { chainId } = useActiveWeb3React()
useEffect(() => {
// cd1 - custom dimension 1 - chainId
googleAnalytics.set({ cd1: chainId ?? 0 })
}, [chainId])
useEffect(() => {
googleAnalytics.pageview(`${pathname}${search}`)
}, [pathname, search])
useEffect(() => {
// typed as 'any' in react-ga4 -.-
googleAnalytics.ga((tracker: any) => {
if (!tracker) return
const clientId = tracker.get('clientId')
window.localStorage.setItem(GOOGLE_ANALYTICS_CLIENT_ID_STORAGE_KEY, clientId)
})
}, [])
}
import { Trans } from '@lingui/macro'
import { Currency, TradeType } from '@uniswap/sdk-core'
import { sendEvent } from 'components/analytics'
import { AutoColumn } from 'components/Column'
import { LoadingOpacityContainer } from 'components/Loader/styled'
import { RowFixed } from 'components/Row'
import { MouseoverTooltipContent } from 'components/Tooltip'
import ReactGA from 'react-ga4'
import { InterfaceTrade } from 'state/routing/types'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
......@@ -85,7 +85,7 @@ export default function GasEstimateBadge({
}
placement="bottom"
onOpen={() =>
ReactGA.event({
sendEvent({
category: 'Gas',
action: 'Gas Details Tooltip Open',
})
......
import { sendEvent } from 'components/analytics'
import { useEffect } from 'react'
import ReactGA from 'react-ga4'
import { ApplicationModal, setOpenModal } from 'state/application/reducer'
import { useAppDispatch } from 'state/hooks'
......@@ -18,7 +18,7 @@ export default function useAccountRiskCheck(account: string | null | undefined)
.then((data) => {
if (data.block) {
dispatch(setOpenModal(ApplicationModal.BLOCKED_ACCOUNT))
ReactGA.event({
sendEvent({
category: 'Address Screening',
action: 'blocked',
label: account,
......
import { skipToken } from '@reduxjs/toolkit/query/react'
import { Currency, Token } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
import { sendEvent } from 'components/analytics'
import useBlockNumber from 'lib/hooks/useBlockNumber'
import ms from 'ms.macro'
import { useMemo } from 'react'
import ReactGA from 'react-ga4'
import { useFeeTierDistributionQuery } from 'state/data/enhanced'
import { FeeTierDistributionQuery } from 'state/data/generated'
......@@ -112,7 +112,7 @@ function usePoolTVL(token0: Token | undefined, token1: Token | undefined) {
}
if (latestBlock - (_meta?.block?.number ?? 0) > MAX_DATA_BLOCK_AGE) {
ReactGA.event('exception', { description: `Graph stale (latest block: ${latestBlock})` })
sendEvent('exception', { description: `Graph stale (latest block: ${latestBlock})` })
return {
isLoading,
......
import { sendEvent } from 'components/analytics'
import { SupportedLocale } from 'constants/locales'
import { LocationDescriptor } from 'history'
import useParsedQueryString from 'hooks/useParsedQueryString'
import { stringify } from 'qs'
import { useMemo } from 'react'
import ReactGA from 'react-ga4'
import { useLocation } from 'react-router-dom'
import { useActiveLocale } from './useActiveLocale'
......@@ -26,7 +26,7 @@ export function useLocationLinkProps(locale: SupportedLocale | null): {
search: stringify({ ...qs, lng: locale }),
},
onClick: () => {
ReactGA.event({
sendEvent({
category: 'Localization',
action: 'Switch Locale',
label: `${activeLocale} -> ${locale}`,
......
......@@ -3,12 +3,12 @@ import { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
import { FeeAmount, NonfungiblePositionManager } from '@uniswap/v3-sdk'
import { sendEvent } from 'components/analytics'
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import useParsedQueryString from 'hooks/useParsedQueryString'
import { useCallback, useContext, useEffect, useState } from 'react'
import { AlertTriangle } from 'react-feather'
import ReactGA from 'react-ga4'
import { RouteComponentProps } from 'react-router-dom'
import { Text } from 'rebass'
import {
......@@ -305,7 +305,7 @@ export default function AddLiquidity({
feeAmount: position.pool.fee,
})
setTxHash(response.hash)
ReactGA.event({
sendEvent({
category: 'Liquidity',
action: 'Add',
label: [currencies[Field.CURRENCY_A]?.symbol, currencies[Field.CURRENCY_B]?.symbol].join('/'),
......
......@@ -2,12 +2,12 @@ import { BigNumber } from '@ethersproject/bignumber'
import { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
import { sendEvent } from 'components/analytics'
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
import { SwitchLocaleLink } from 'components/SwitchLocaleLink'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useCallback, useContext, useState } from 'react'
import { Plus } from 'react-feather'
import ReactGA from 'react-ga4'
import { RouteComponentProps } from 'react-router-dom'
import { Text } from 'rebass'
import { ThemeContext } from 'styled-components/macro'
......@@ -201,7 +201,7 @@ export default function AddLiquidity({
setTxHash(response.hash)
ReactGA.event({
sendEvent({
category: 'Liquidity',
action: 'Add',
label: [currencies[Field.CURRENCY_A]?.symbol, currencies[Field.CURRENCY_B]?.symbol].join('/'),
......
......@@ -3,11 +3,10 @@ import TopLevelModals from 'components/TopLevelModals'
import ApeModeQueryParamReader from 'hooks/useApeModeQueryParamReader'
import { lazy, Suspense } from 'react'
import { useEffect } from 'react'
import { useHistory } from 'react-router-dom'
import { Redirect, Route, Switch } from 'react-router-dom'
import { Redirect, Route, Switch, useHistory, useLocation } from 'react-router-dom'
import styled from 'styled-components/macro'
import GoogleAnalyticsReporter from '../components/analytics/GoogleAnalyticsReporter'
import { useAnalyticsReporter } from '../components/analytics'
import ErrorBoundary from '../components/ErrorBoundary'
import Header from '../components/Header'
import Polling from '../components/Header/Polling'
......@@ -67,6 +66,7 @@ const Marginer = styled.div`
export default function App() {
const history = useHistory()
useAnalyticsReporter(useLocation())
useEffect(() => {
const unlisten = history.listen(() => {
......@@ -79,7 +79,6 @@ export default function App() {
return (
<ErrorBoundary>
<Route component={GoogleAnalyticsReporter} />
<Route component={DarkModeQueryParamReader} />
<Route component={ApeModeQueryParamReader} />
<Web3ReactManager>
......
......@@ -3,6 +3,7 @@ import { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Fraction, Percent, Price, Token } from '@uniswap/sdk-core'
import { FeeAmount, Pool, Position, priceToClosestTick, TickMath } from '@uniswap/v3-sdk'
import { sendEvent } from 'components/analytics'
import Badge, { BadgeVariant } from 'components/Badge'
import { ButtonConfirmed } from 'components/Button'
import { BlueCard, DarkGreyCard, LightCard, YellowCard } from 'components/Card'
......@@ -23,7 +24,6 @@ import JSBI from 'jsbi'
import { NEVER_RELOAD, useSingleCallResult } from 'lib/hooks/multicall'
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
import { AlertCircle, AlertTriangle, ArrowDown } from 'react-feather'
import ReactGA from 'react-ga4'
import { Redirect, RouteComponentProps } from 'react-router'
import { Text } from 'rebass'
import { useAppDispatch } from 'state/hooks'
......@@ -336,7 +336,7 @@ function V2PairMigration({
return migrator
.multicall(data, { gasLimit: calculateGasMargin(gasEstimate) })
.then((response: TransactionResponse) => {
ReactGA.event({
sendEvent({
category: 'Migrate',
action: `${isNotUniswap ? 'SushiSwap' : 'V2'}->V3`,
label: `${currency0.symbol}/${currency1.symbol}`,
......
......@@ -3,6 +3,7 @@ import { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Fraction, Percent, Price, Token } from '@uniswap/sdk-core'
import { NonfungiblePositionManager, Pool, Position } from '@uniswap/v3-sdk'
import { sendEvent } from 'components/analytics'
import Badge from 'components/Badge'
import { ButtonConfirmed, ButtonGray, ButtonPrimary } from 'components/Button'
import { DarkCard, LightCard } from 'components/Card'
......@@ -25,7 +26,6 @@ import { useV3PositionFromTokenId } from 'hooks/useV3Positions'
import { useSingleCallResult } from 'lib/hooks/multicall'
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import { useCallback, useMemo, useRef, useState } from 'react'
import ReactGA from 'react-ga4'
import { Link, RouteComponentProps } from 'react-router-dom'
import { Bound } from 'state/mint/v3/actions'
import { useIsTransactionPending, useTransactionAdder } from 'state/transactions/hooks'
......@@ -470,7 +470,7 @@ export function PositionPage({
setCollectMigrationHash(response.hash)
setCollecting(false)
ReactGA.event({
sendEvent({
category: 'Liquidity',
action: 'CollectV3',
label: [currency0ForFeeCollectionPurposes.symbol, currency1ForFeeCollectionPurposes.symbol].join('/'),
......
......@@ -3,6 +3,7 @@ import { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Percent } from '@uniswap/sdk-core'
import { NonfungiblePositionManager } from '@uniswap/v3-sdk'
import { sendEvent } from 'components/analytics'
import RangeBadge from 'components/Badge/RangeBadge'
import { ButtonConfirmed, ButtonPrimary } from 'components/Button'
import { LightCard } from 'components/Card'
......@@ -24,7 +25,6 @@ import useTransactionDeadline from 'hooks/useTransactionDeadline'
import { useV3PositionFromTokenId } from 'hooks/useV3Positions'
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import { useCallback, useMemo, useState } from 'react'
import ReactGA from 'react-ga4'
import { Redirect, RouteComponentProps } from 'react-router-dom'
import { Text } from 'rebass'
import { useBurnV3ActionHandlers, useBurnV3State, useDerivedV3BurnInfo } from 'state/burn/v3/hooks'
......@@ -149,7 +149,7 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
.getSigner()
.sendTransaction(newTxn)
.then((response: TransactionResponse) => {
ReactGA.event({
sendEvent({
category: 'Liquidity',
action: 'RemoveV3',
label: [liquidityValue0.currency.symbol, liquidityValue1.currency.symbol].join('/'),
......
......@@ -3,11 +3,11 @@ import { Contract } from '@ethersproject/contracts'
import { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import { Currency, Percent } from '@uniswap/sdk-core'
import { sendEvent } from 'components/analytics'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useV2LiquidityTokenPermit } from 'hooks/useV2LiquidityTokenPermit'
import { useCallback, useContext, useMemo, useState } from 'react'
import { ArrowDown, Plus } from 'react-feather'
import ReactGA from 'react-ga4'
import { RouteComponentProps } from 'react-router'
import { Text } from 'rebass'
import { ThemeContext } from 'styled-components/macro'
......@@ -275,7 +275,7 @@ export default function RemoveLiquidity({
setTxHash(response.hash)
ReactGA.event({
sendEvent({
category: 'Liquidity',
action: 'Remove',
label: [currencyA.symbol, currencyB.symbol].join('/'),
......
......@@ -3,6 +3,7 @@ import { Trade } from '@uniswap/router-sdk'
import { Currency, CurrencyAmount, Token, TradeType } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import { sendEvent } from 'components/analytics'
import { NetworkAlert } from 'components/NetworkAlert/NetworkAlert'
import SwapDetailsDropdown from 'components/swap/SwapDetailsDropdown'
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
......@@ -13,7 +14,6 @@ import useTransactionDeadline from 'hooks/useTransactionDeadline'
import JSBI from 'jsbi'
import { useCallback, useContext, useEffect, useMemo, useState } from 'react'
import { ArrowDown, CheckCircle, HelpCircle } from 'react-feather'
import ReactGA from 'react-ga4'
import { RouteComponentProps } from 'react-router-dom'
import { Text } from 'rebass'
import { TradeState } from 'state/routing/types'
......@@ -237,7 +237,7 @@ export default function Swap({ history }: RouteComponentProps) {
} else {
await approveCallback()
ReactGA.event({
sendEvent({
category: 'Swap',
action: 'Approve',
label: [approvalOptimizedTradeString, approvalOptimizedTrade?.inputAmount?.currency.symbol].join('/'),
......@@ -286,12 +286,12 @@ export default function Swap({ history }: RouteComponentProps) {
swapCallback()
.then((hash) => {
setSwapState({ attemptingTxn: false, tradeToConfirm, showConfirm, swapErrorMessage: undefined, txHash: hash })
ReactGA.event({
sendEvent({
category: 'Swap',
action: 'transaction hash',
label: hash,
})
ReactGA.event({
sendEvent({
category: 'Swap',
action:
recipient === null
......@@ -378,7 +378,7 @@ export default function Swap({ history }: RouteComponentProps) {
const handleMaxInput = useCallback(() => {
maxInputAmount && onUserInput(Field.INPUT, maxInputAmount.toExact())
ReactGA.event({
sendEvent({
category: 'Swap',
action: 'Max',
})
......
import { skipToken } from '@reduxjs/toolkit/query/react'
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { IMetric, MetricLoggerUnit, setGlobalMetric } from '@uniswap/smart-order-router'
import { sendTiming } from 'components/analytics'
import { useStablecoinAmountFromFiatValue } from 'hooks/useUSDCPrice'
import { useRoutingAPIArguments } from 'lib/hooks/routing/useRoutingAPIArguments'
import useIsValidBlock from 'lib/hooks/useIsValidBlock'
import ms from 'ms.macro'
import { useMemo } from 'react'
import ReactGA from 'react-ga4'
import { useGetQuoteQuery } from 'state/routing/slice'
import { useClientSideRouter } from 'state/user/hooks'
......@@ -127,7 +127,7 @@ class GAMetric extends IMetric {
}
putMetric(key: string, value: number, unit?: MetricLoggerUnit) {
ReactGA._gaCommandSendTiming('Routing API', `${key} | ${unit}`, value, 'client')
sendTiming('Routing API', `${key} | ${unit}`, value, 'client')
}
}
......
import { outboundLink } from 'components/analytics'
import React, { HTMLProps } from 'react'
import { ArrowLeft, ExternalLink as LinkIconFeather, Trash, X } from 'react-feather'
import ReactGA from 'react-ga4'
import { Link } from 'react-router-dom'
import styled, { keyframes } from 'styled-components/macro'
......@@ -175,13 +175,13 @@ function handleClickExternalLink(event: React.MouseEvent<HTMLAnchorElement>) {
// don't prevent default, don't redirect if it's a new tab
if (target === '_blank' || event.ctrlKey || event.metaKey) {
ReactGA.outboundLink({ label: anonymizedHref }, () => {
outboundLink({ label: anonymizedHref }, () => {
console.debug('Fired outbound link event', anonymizedHref)
})
} else {
event.preventDefault()
// send a ReactGA event and then trigger a location change
ReactGA.outboundLink({ label: anonymizedHref }, () => {
outboundLink({ label: anonymizedHref }, () => {
window.location.href = anonymizedHref
})
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment