Track GA for safe actions (#1302)

* Track GA for safe actions

* Add tracking for safeListSidebar

* review changes

* review changes v2
This commit is contained in:
nicolas 2020-08-31 11:33:55 -03:00 committed by GitHub
parent e9f7acff63
commit f7d4cfe112
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 265 additions and 191 deletions

View File

@ -1,7 +1,7 @@
import React, { useEffect, useMemo, useState } from 'react'
import Drawer from '@material-ui/core/Drawer' import Drawer from '@material-ui/core/Drawer'
import SearchIcon from '@material-ui/icons/Search' import SearchIcon from '@material-ui/icons/Search'
import SearchBar from 'material-ui-search-bar' import SearchBar from 'material-ui-search-bar'
import * as React from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import SafeList from './SafeList' import SafeList from './SafeList'
@ -16,12 +16,11 @@ import Link from 'src/components/layout/Link'
import Row from 'src/components/layout/Row' import Row from 'src/components/layout/Row'
import { WELCOME_ADDRESS } from 'src/routes/routes' import { WELCOME_ADDRESS } from 'src/routes/routes'
import setDefaultSafe from 'src/logic/safe/store/actions/setDefaultSafe' import setDefaultSafe from 'src/logic/safe/store/actions/setDefaultSafe'
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
import { defaultSafeSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors' import { defaultSafeSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { AppReduxState } from 'src/store' import { AppReduxState } from 'src/store'
const { useEffect, useMemo, useState } = React
export const SafeListSidebarContext = React.createContext({ export const SafeListSidebarContext = React.createContext({
isOpen: false, isOpen: false,
toggleSidebar: () => {}, toggleSidebar: () => {},
@ -39,12 +38,7 @@ const SafeListSidebar = ({ children, currentSafe, defaultSafe, safes, setDefault
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false)
const [filter, setFilter] = useState('') const [filter, setFilter] = useState('')
const classes = useSidebarStyles() const classes = useSidebarStyles()
const { trackEvent } = useAnalytics()
useEffect(() => {
setTimeout(() => {
setFilter('')
}, 300)
}, [isOpen])
const searchClasses = { const searchClasses = {
input: classes.searchInput, input: classes.searchInput,
@ -54,6 +48,9 @@ const SafeListSidebar = ({ children, currentSafe, defaultSafe, safes, setDefault
} }
const toggleSidebar = () => { const toggleSidebar = () => {
if (!isOpen) {
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Safe List Sidebar' })
}
setIsOpen((prevIsOpen) => !prevIsOpen) setIsOpen((prevIsOpen) => !prevIsOpen)
} }
@ -73,6 +70,12 @@ const SafeListSidebar = ({ children, currentSafe, defaultSafe, safes, setDefault
const filteredSafes = useMemo(() => filterBy(filter, safes), [safes, filter]) const filteredSafes = useMemo(() => filterBy(filter, safes), [safes, filter])
useEffect(() => {
setTimeout(() => {
setFilter('')
}, 300)
}, [isOpen])
return ( return (
<SafeListSidebarContext.Provider value={{ isOpen, toggleSidebar }}> <SafeListSidebarContext.Provider value={{ isOpen, toggleSidebar }}>
<Drawer <Drawer

View File

@ -41,6 +41,7 @@ import RemoveOwnerIcon from 'src/routes/safe/components/Settings/assets/icons/bi
import RemoveOwnerIconDisabled from 'src/routes/safe/components/Settings/assets/icons/disabled-bin.svg' import RemoveOwnerIconDisabled from 'src/routes/safe/components/Settings/assets/icons/disabled-bin.svg'
import { addressBookQueryParamsSelector, safesListSelector } from 'src/logic/safe/store/selectors' import { addressBookQueryParamsSelector, safesListSelector } from 'src/logic/safe/store/selectors'
import { checksumAddress } from 'src/utils/checksumAddress' import { checksumAddress } from 'src/utils/checksumAddress'
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
const AddressBookTable = ({ classes }) => { const AddressBookTable = ({ classes }) => {
const columns = generateColumns() const columns = generateColumns()
@ -53,6 +54,11 @@ const AddressBookTable = ({ classes }) => {
const [editCreateEntryModalOpen, setEditCreateEntryModalOpen] = useState(false) const [editCreateEntryModalOpen, setEditCreateEntryModalOpen] = useState(false)
const [deleteEntryModalOpen, setDeleteEntryModalOpen] = useState(false) const [deleteEntryModalOpen, setDeleteEntryModalOpen] = useState(false)
const [sendFundsModalOpen, setSendFundsModalOpen] = useState(false) const [sendFundsModalOpen, setSendFundsModalOpen] = useState(false)
const { trackEvent } = useAnalytics()
useEffect(() => {
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'AddressBook' })
}, [trackEvent])
useEffect(() => { useEffect(() => {
if (entryAddressToEditOrCreateNew) { if (entryAddressToEditOrCreateNew) {

View File

@ -19,6 +19,7 @@ import {
import { isSameURL } from 'src/utils/url' import { isSameURL } from 'src/utils/url'
import { useIframeMessageHandler } from './hooks/useIframeMessageHandler' import { useIframeMessageHandler } from './hooks/useIframeMessageHandler'
import ConfirmTransactionModal from './components/ConfirmTransactionModal' import ConfirmTransactionModal from './components/ConfirmTransactionModal'
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
const centerCSS = css` const centerCSS = css`
display: flex; display: flex;
@ -64,6 +65,7 @@ const Apps = (): React.ReactElement => {
) )
const iframeRef = useRef<HTMLIFrameElement>() const iframeRef = useRef<HTMLIFrameElement>()
const { trackEvent } = useAnalytics()
const granted = useSelector(grantedSelector) const granted = useSelector(grantedSelector)
const safeAddress = useSelector(safeParamAddressFromStateSelector) const safeAddress = useSelector(safeParamAddressFromStateSelector)
const safeName = useSelector(safeNameSelector) const safeName = useSelector(safeNameSelector)
@ -111,6 +113,7 @@ const Apps = (): React.ReactElement => {
[selectedAppId], [selectedAppId],
) )
// Auto Select app first App
useEffect(() => { useEffect(() => {
const selectFirstEnabledApp = () => { const selectFirstEnabledApp = () => {
const firstEnabledApp = appList.find((a) => !a.disabled) const firstEnabledApp = appList.find((a) => !a.disabled)
@ -124,7 +127,14 @@ const Apps = (): React.ReactElement => {
if (initialSelect || currentAppWasDisabled) { if (initialSelect || currentAppWasDisabled) {
selectFirstEnabledApp() selectFirstEnabledApp()
} }
}, [appList, selectedApp, selectedAppId]) }, [appList, selectedApp, selectedAppId, trackEvent])
// track GA
useEffect(() => {
if (selectedApp) {
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Apps', label: selectedApp.name })
}
}, [selectedApp, trackEvent])
const handleIframeLoad = useCallback(() => { const handleIframeLoad = useCallback(() => {
const iframe = iframeRef.current const iframe = iframeRef.current

View File

@ -1,9 +1,9 @@
import React, { useEffect } from 'react'
import TableCell from '@material-ui/core/TableCell' import TableCell from '@material-ui/core/TableCell'
import TableContainer from '@material-ui/core/TableContainer' import TableContainer from '@material-ui/core/TableContainer'
import TableRow from '@material-ui/core/TableRow' import TableRow from '@material-ui/core/TableRow'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import { List } from 'immutable' import { List } from 'immutable'
import React from 'react'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { styles } from './styles' import { styles } from './styles'
@ -29,6 +29,7 @@ import {
} from 'src/routes/safe/components/Balances/dataFetcher' } from 'src/routes/safe/components/Balances/dataFetcher'
import { extendedSafeTokensSelector, grantedSelector } from 'src/routes/safe/container/selector' import { extendedSafeTokensSelector, grantedSelector } from 'src/routes/safe/container/selector'
import { Skeleton } from '@material-ui/lab' import { Skeleton } from '@material-ui/lab'
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
const useStyles = makeStyles(styles as any) const useStyles = makeStyles(styles as any)
@ -61,6 +62,11 @@ const Coins = (props: Props): React.ReactElement => {
const currencyValues = useSelector(safeFiatBalancesListSelector) const currencyValues = useSelector(safeFiatBalancesListSelector)
const granted = useSelector(grantedSelector) const granted = useSelector(grantedSelector)
const [filteredData, setFilteredData] = React.useState<List<BalanceData>>(List()) const [filteredData, setFilteredData] = React.useState<List<BalanceData>>(List())
const { trackEvent } = useAnalytics()
useEffect(() => {
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Coins' })
}, [trackEvent])
React.useMemo(() => { React.useMemo(() => {
setFilteredData(getBalanceData(activeTokens, selectedCurrency, currencyValues, currencyRate)) setFilteredData(getBalanceData(activeTokens, selectedCurrency, currencyValues, currencyRate))

View File

@ -1,6 +1,6 @@
import React, { useEffect } from 'react'
import Card from '@material-ui/core/Card' import Card from '@material-ui/core/Card'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import React from 'react'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import Item from './components/Item' import Item from './components/Item'
@ -10,6 +10,7 @@ import { activeNftAssetsListSelector, nftTokensSelector } from 'src/logic/collec
import SendModal from 'src/routes/safe/components/Balances/SendModal' import SendModal from 'src/routes/safe/components/Balances/SendModal'
import { safeSelector } from 'src/logic/safe/store/selectors' import { safeSelector } from 'src/logic/safe/store/selectors'
import { fontColor, lg, screenSm, screenXs } from 'src/theme/variables' import { fontColor, lg, screenSm, screenXs } from 'src/theme/variables'
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
const useStyles = makeStyles({ const useStyles = makeStyles({
cardInner: { cardInner: {
@ -74,13 +75,18 @@ const useStyles = makeStyles({
}, },
} as any) } as any)
const Collectibles = () => { const Collectibles = (): React.ReactElement => {
const classes = useStyles() const classes = useStyles()
const [selectedToken, setSelectedToken] = React.useState({}) const [selectedToken, setSelectedToken] = React.useState({})
const [sendNFTsModalOpen, setSendNFTsModalOpen] = React.useState(false) const [sendNFTsModalOpen, setSendNFTsModalOpen] = React.useState(false)
const { address, ethBalance, name } = useSelector(safeSelector) const { address, ethBalance, name } = useSelector(safeSelector)
const nftTokens = useSelector(nftTokensSelector) const nftTokens = useSelector(nftTokensSelector)
const activeAssetsList = useSelector(activeNftAssetsListSelector) const activeAssetsList = useSelector(activeNftAssetsListSelector)
const { trackEvent } = useAnalytics()
useEffect(() => {
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Collectibles' })
}, [trackEvent])
const handleItemSend = (nftToken) => { const handleItemSend = (nftToken) => {
setSelectedToken(nftToken) setSelectedToken(nftToken)

View File

@ -1,6 +1,6 @@
import { Loader, Text, theme, Title } from '@gnosis.pm/safe-react-components' import { Loader, Text, theme, Title } from '@gnosis.pm/safe-react-components'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import React from 'react' import React, { useEffect } from 'react'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import styled from 'styled-components' import styled from 'styled-components'
@ -10,6 +10,7 @@ import ModulesTable from './ModulesTable'
import Block from 'src/components/layout/Block' import Block from 'src/components/layout/Block'
import { safeModulesSelector, safeNonceSelector } from 'src/logic/safe/store/selectors' import { safeModulesSelector, safeNonceSelector } from 'src/logic/safe/store/selectors'
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
@ -39,10 +40,14 @@ const LoadingModules = (): React.ReactElement => {
const Advanced = (): React.ReactElement => { const Advanced = (): React.ReactElement => {
const classes = useStyles() const classes = useStyles()
const nonce = useSelector(safeNonceSelector) const nonce = useSelector(safeNonceSelector)
const modules = useSelector(safeModulesSelector) const modules = useSelector(safeModulesSelector)
const moduleData = getModuleData(modules) ?? null const moduleData = getModuleData(modules) ?? null
const { trackEvent } = useAnalytics()
useEffect(() => {
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Settings', label: 'Advanced' })
}, [trackEvent])
return ( return (
<> <>

View File

@ -33,7 +33,7 @@ export const SAVE_OWNER_CHANGES_BTN_TEST_ID = 'save-owner-changes-btn'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
type OwnProps = { type OwnProps = {
isOpen: true isOpen: boolean
onClose: () => void onClose: () => void
ownerAddress: string ownerAddress: string
selectedOwnerName: string selectedOwnerName: string

View File

@ -1,9 +1,10 @@
import React, { useState, useEffect } from 'react'
import TableCell from '@material-ui/core/TableCell' import TableCell from '@material-ui/core/TableCell'
import TableContainer from '@material-ui/core/TableContainer' import TableContainer from '@material-ui/core/TableContainer'
import TableRow from '@material-ui/core/TableRow' import TableRow from '@material-ui/core/TableRow'
import { withStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import cn from 'classnames' import cn from 'classnames'
import React from 'react' import { List } from 'immutable'
import RemoveOwnerIcon from '../assets/icons/bin.svg' import RemoveOwnerIcon from '../assets/icons/bin.svg'
@ -28,6 +29,9 @@ import Img from 'src/components/layout/Img'
import Paragraph from 'src/components/layout/Paragraph/index' import Paragraph from 'src/components/layout/Paragraph/index'
import Row from 'src/components/layout/Row' import Row from 'src/components/layout/Row'
import { getOwnersWithNameFromAddressBook } from 'src/logic/addressBook/utils' import { getOwnersWithNameFromAddressBook } from 'src/logic/addressBook/utils'
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
import { AddressBookEntryProps } from 'src/logic/addressBook/model/addressBook'
import { SafeOwner } from 'src/logic/safe/store/models/safe'
export const RENAME_OWNER_BTN_TEST_ID = 'rename-owner-btn' export const RENAME_OWNER_BTN_TEST_ID = 'rename-owner-btn'
export const REMOVE_OWNER_BTN_TEST_ID = 'remove-owner-btn' export const REMOVE_OWNER_BTN_TEST_ID = 'remove-owner-btn'
@ -35,49 +39,52 @@ export const ADD_OWNER_BTN_TEST_ID = 'add-owner-btn'
export const REPLACE_OWNER_BTN_TEST_ID = 'replace-owner-btn' export const REPLACE_OWNER_BTN_TEST_ID = 'replace-owner-btn'
export const OWNERS_ROW_TEST_ID = 'owners-row' export const OWNERS_ROW_TEST_ID = 'owners-row'
class ManageOwners extends React.Component<any, any> { const useStyles = makeStyles(styles)
constructor(props) {
super(props)
this.state = { type Props = {
selectedOwnerAddress: undefined, addressBook: unknown
selectedOwnerName: undefined, granted: boolean
owners: List<SafeOwner>
}
const ManageOwners = ({ addressBook, granted, owners }: Props): React.ReactElement => {
const { trackEvent } = useAnalytics()
const classes = useStyles()
const [selectedOwnerAddress, setSelectedOwnerAddress] = useState()
const [selectedOwnerName, setSelectedOwnerName] = useState()
const [modalsStatus, setModalStatus] = useState({
showAddOwner: false, showAddOwner: false,
showRemoveOwner: false, showRemoveOwner: false,
showReplaceOwner: false, showReplaceOwner: false,
showEditOwner: false, showEditOwner: false,
}
}
onShow = (action, row?: any) => () => {
this.setState({
[`show${action}`]: true,
selectedOwnerAddress: row && row.address,
selectedOwnerName: row && row.name,
}) })
const onShow = (action, row?: any) => () => {
setModalStatus((prevState) => ({
...prevState,
[`show${action}`]: !prevState[`show${action}`],
}))
setSelectedOwnerAddress(row && row.address)
setSelectedOwnerName(row && row.name)
} }
onHide = (action) => () => { const onHide = (action) => () => {
this.setState({ setModalStatus((prevState) => ({
[`show${action}`]: false, ...prevState,
selectedOwnerAddress: undefined, [`show${action}`]: !Boolean(prevState[`show${action}`]),
selectedOwnerName: undefined, }))
}) setSelectedOwnerAddress(undefined)
setSelectedOwnerName(undefined)
} }
render() { useEffect(() => {
const { addressBook, classes, granted, owners } = this.props trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Settings', label: 'Owners' })
const { }, [trackEvent])
selectedOwnerAddress,
selectedOwnerName,
showAddOwner,
showEditOwner,
showRemoveOwner,
showReplaceOwner,
} = this.state
const columns = generateColumns() const columns = generateColumns()
const autoColumns = columns.filter((c) => !c.custom) const autoColumns = columns.filter((c) => !c.custom)
const ownersAdbk = getOwnersWithNameFromAddressBook(addressBook, owners) const ownersAdbk = getOwnersWithNameFromAddressBook(addressBook as AddressBookEntryProps, owners)
const ownerData = getOwnerData(ownersAdbk) const ownerData = getOwnerData(ownersAdbk)
return ( return (
@ -87,8 +94,8 @@ class ManageOwners extends React.Component<any, any> {
Manage Safe Owners Manage Safe Owners
</Heading> </Heading>
<Paragraph className={classes.annotation}> <Paragraph className={classes.annotation}>
Add, remove and replace owners or rename existing owners. Owner names are only stored locally and never Add, remove and replace owners or rename existing owners. Owner names are only stored locally and never shared
shared with Gnosis or any third parties. with Gnosis or any third parties.
</Paragraph> </Paragraph>
<TableContainer> <TableContainer>
<Table <Table
@ -107,7 +114,6 @@ class ManageOwners extends React.Component<any, any> {
className={cn(classes.hide, index >= 3 && index === sortedData.size - 1 && classes.noBorderBottom)} className={cn(classes.hide, index >= 3 && index === sortedData.size - 1 && classes.noBorderBottom)}
data-testid={OWNERS_ROW_TEST_ID} data-testid={OWNERS_ROW_TEST_ID}
key={index} key={index}
tabIndex={-1}
> >
{autoColumns.map((column: any) => ( {autoColumns.map((column: any) => (
<TableCell align={column.align} component="td" key={column.id} style={cellWidth(column.width)}> <TableCell align={column.align} component="td" key={column.id} style={cellWidth(column.width)}>
@ -123,7 +129,7 @@ class ManageOwners extends React.Component<any, any> {
<Img <Img
alt="Edit owner" alt="Edit owner"
className={classes.editOwnerIcon} className={classes.editOwnerIcon}
onClick={this.onShow('EditOwner', row)} onClick={onShow('EditOwner', row)}
src={RenameOwnerIcon} src={RenameOwnerIcon}
testId={RENAME_OWNER_BTN_TEST_ID} testId={RENAME_OWNER_BTN_TEST_ID}
/> />
@ -132,7 +138,7 @@ class ManageOwners extends React.Component<any, any> {
<Img <Img
alt="Replace owner" alt="Replace owner"
className={classes.replaceOwnerIcon} className={classes.replaceOwnerIcon}
onClick={this.onShow('ReplaceOwner', row)} onClick={onShow('ReplaceOwner', row)}
src={ReplaceOwnerIcon} src={ReplaceOwnerIcon}
testId={REPLACE_OWNER_BTN_TEST_ID} testId={REPLACE_OWNER_BTN_TEST_ID}
/> />
@ -140,7 +146,7 @@ class ManageOwners extends React.Component<any, any> {
<Img <Img
alt="Remove owner" alt="Remove owner"
className={classes.removeOwnerIcon} className={classes.removeOwnerIcon}
onClick={this.onShow('RemoveOwner', row)} onClick={onShow('RemoveOwner', row)}
src={RemoveOwnerIcon} src={RemoveOwnerIcon}
testId={REMOVE_OWNER_BTN_TEST_ID} testId={REMOVE_OWNER_BTN_TEST_ID}
/> />
@ -162,7 +168,7 @@ class ManageOwners extends React.Component<any, any> {
<Col end="xs"> <Col end="xs">
<Button <Button
color="primary" color="primary"
onClick={this.onShow('AddOwner')} onClick={onShow('AddOwner')}
size="small" size="small"
testId={ADD_OWNER_BTN_TEST_ID} testId={ADD_OWNER_BTN_TEST_ID}
variant="contained" variant="contained"
@ -173,28 +179,27 @@ class ManageOwners extends React.Component<any, any> {
</Row> </Row>
</> </>
)} )}
<AddOwnerModal isOpen={showAddOwner} onClose={this.onHide('AddOwner')} /> <AddOwnerModal isOpen={modalsStatus.showAddOwner} onClose={onHide('AddOwner')} />
<RemoveOwnerModal <RemoveOwnerModal
isOpen={showRemoveOwner} isOpen={modalsStatus.showRemoveOwner}
onClose={this.onHide('RemoveOwner')} onClose={onHide('RemoveOwner')}
ownerAddress={selectedOwnerAddress} ownerAddress={selectedOwnerAddress}
ownerName={selectedOwnerName} ownerName={selectedOwnerName}
/> />
<ReplaceOwnerModal <ReplaceOwnerModal
isOpen={showReplaceOwner} isOpen={modalsStatus.showReplaceOwner}
onClose={this.onHide('ReplaceOwner')} onClose={onHide('ReplaceOwner')}
ownerAddress={selectedOwnerAddress} ownerAddress={selectedOwnerAddress}
ownerName={selectedOwnerName} ownerName={selectedOwnerName}
/> />
<EditOwnerModal <EditOwnerModal
isOpen={showEditOwner} isOpen={modalsStatus.showEditOwner}
onClose={this.onHide('EditOwner')} onClose={onHide('EditOwner')}
ownerAddress={selectedOwnerAddress} ownerAddress={selectedOwnerAddress}
selectedOwnerName={selectedOwnerName} selectedOwnerName={selectedOwnerName}
/> />
</> </>
) )
}
} }
export default withStyles(styles as any)(ManageOwners) export default ManageOwners

View File

@ -1,6 +1,7 @@
import { lg, sm } from 'src/theme/variables' import { lg, sm } from 'src/theme/variables'
import { createStyles } from '@material-ui/core'
export const styles = () => ({ export const styles = createStyles({
formContainer: { formContainer: {
minHeight: '420px', minHeight: '420px',
}, },

View File

@ -1,5 +1,5 @@
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import React from 'react' import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import { styles } from './style' import { styles } from './style'
@ -29,6 +29,7 @@ import {
safeNeedsUpdateSelector, safeNeedsUpdateSelector,
safeParamAddressFromStateSelector, safeParamAddressFromStateSelector,
} from 'src/logic/safe/store/selectors' } from 'src/logic/safe/store/selectors'
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
export const SAFE_NAME_INPUT_TEST_ID = 'safe-name-input' export const SAFE_NAME_INPUT_TEST_ID = 'safe-name-input'
export const SAFE_NAME_SUBMIT_BTN_TEST_ID = 'change-safe-name-btn' export const SAFE_NAME_SUBMIT_BTN_TEST_ID = 'change-safe-name-btn'
@ -44,6 +45,7 @@ const SafeDetails = (): React.ReactElement => {
const safeName = useSelector(safeNameSelector) const safeName = useSelector(safeNameSelector)
const safeNeedsUpdate = useSelector(safeNeedsUpdateSelector) const safeNeedsUpdate = useSelector(safeNeedsUpdateSelector)
const safeCurrentVersion = useSelector(safeCurrentVersionSelector) const safeCurrentVersion = useSelector(safeCurrentVersionSelector)
const { trackEvent } = useAnalytics()
const [isModalOpen, setModalOpen] = React.useState(false) const [isModalOpen, setModalOpen] = React.useState(false)
const safeAddress = useSelector(safeParamAddressFromStateSelector) const safeAddress = useSelector(safeParamAddressFromStateSelector)
@ -63,6 +65,10 @@ const SafeDetails = (): React.ReactElement => {
setModalOpen(true) setModalOpen(true)
} }
useEffect(() => {
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Settings', label: 'Details' })
}, [trackEvent])
return ( return (
<> <>
<GnoForm onSubmit={handleSubmit}> <GnoForm onSubmit={handleSubmit}>

View File

@ -1,6 +1,6 @@
import { withStyles } from '@material-ui/core/styles' import { withStyles } from '@material-ui/core/styles'
import { withSnackbar } from 'notistack' import { withSnackbar } from 'notistack'
import React, { useState } from 'react' import React, { useState, useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import ChangeThreshold from './ChangeThreshold' import ChangeThreshold from './ChangeThreshold'
@ -22,6 +22,7 @@ import {
safeParamAddressFromStateSelector, safeParamAddressFromStateSelector,
safeThresholdSelector, safeThresholdSelector,
} from 'src/logic/safe/store/selectors' } from 'src/logic/safe/store/selectors'
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
const ThresholdSettings = ({ classes, closeSnackbar, enqueueSnackbar }) => { const ThresholdSettings = ({ classes, closeSnackbar, enqueueSnackbar }) => {
const [isModalOpen, setModalOpen] = useState(false) const [isModalOpen, setModalOpen] = useState(false)
@ -52,6 +53,12 @@ const ThresholdSettings = ({ classes, closeSnackbar, enqueueSnackbar }) => {
) )
} }
const { trackEvent } = useAnalytics()
useEffect(() => {
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Settings', label: 'Owners' })
}, [trackEvent])
return ( return (
<> <>
<Block className={classes.container}> <Block className={classes.container}>

View File

@ -7,7 +7,7 @@ import { withStyles } from '@material-ui/core/styles'
import ExpandLess from '@material-ui/icons/ExpandLess' import ExpandLess from '@material-ui/icons/ExpandLess'
import ExpandMore from '@material-ui/icons/ExpandMore' import ExpandMore from '@material-ui/icons/ExpandMore'
import cn from 'classnames' import cn from 'classnames'
import React, { useState } from 'react' import React, { useState, useEffect } from 'react'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import ExpandedTxComponent from './ExpandedTx' import ExpandedTxComponent from './ExpandedTx'
@ -21,6 +21,7 @@ import Block from 'src/components/layout/Block'
import Row from 'src/components/layout/Row' import Row from 'src/components/layout/Row'
import { safeCancellationTransactionsSelector } from 'src/logic/safe/store/selectors' import { safeCancellationTransactionsSelector } from 'src/logic/safe/store/selectors'
import { extendedTransactionsSelector } from 'src/logic/safe/store/selectors/transactions' import { extendedTransactionsSelector } from 'src/logic/safe/store/selectors/transactions'
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
export const TRANSACTION_ROW_TEST_ID = 'transaction-row' export const TRANSACTION_ROW_TEST_ID = 'transaction-row'
@ -28,6 +29,11 @@ const TxsTable = ({ classes }) => {
const [expandedTx, setExpandedTx] = useState(null) const [expandedTx, setExpandedTx] = useState(null)
const cancellationTransactions = useSelector(safeCancellationTransactionsSelector) const cancellationTransactions = useSelector(safeCancellationTransactionsSelector)
const transactions = useSelector(extendedTransactionsSelector) const transactions = useSelector(extendedTransactionsSelector)
const { trackEvent } = useAnalytics()
useEffect(() => {
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Transactions' })
}, [trackEvent])
const handleTxExpand = (safeTxHash) => { const handleTxExpand = (safeTxHash) => {
setExpandedTx((prevTx) => (prevTx === safeTxHash ? null : safeTxHash)) setExpandedTx((prevTx) => (prevTx === safeTxHash ? null : safeTxHash))

View File

@ -1,12 +1,14 @@
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import GoogleAnalytics from 'react-ga' import GoogleAnalytics, { EventArgs } from 'react-ga'
import { getGoogleAnalyticsTrackingID } from 'src/config' import { getGoogleAnalyticsTrackingID } from 'src/config'
import { COOKIES_KEY } from 'src/logic/cookies/model/cookie' import { COOKIES_KEY } from 'src/logic/cookies/model/cookie'
import { loadFromCookie } from 'src/logic/cookies/utils' import { loadFromCookie } from 'src/logic/cookies/utils'
export const SAFE_NAVIGATION_EVENT = 'Safe Navigation'
let analyticsLoaded = false let analyticsLoaded = false
export const loadGoogleAnalytics = () => { export const loadGoogleAnalytics = (): void => {
if (analyticsLoaded) { if (analyticsLoaded) {
return return
} }
@ -22,7 +24,12 @@ export const loadGoogleAnalytics = () => {
} }
} }
export const useAnalytics = () => { type UseAnalyticsResponse = {
trackPage: (path: string) => void
trackEvent: (event: EventArgs) => void
}
export const useAnalytics = (): UseAnalyticsResponse => {
const [analyticsAllowed, setAnalyticsAllowed] = useState(false) const [analyticsAllowed, setAnalyticsAllowed] = useState(false)
useEffect(() => { useEffect(() => {
@ -37,18 +44,24 @@ export const useAnalytics = () => {
}, []) }, [])
const trackPage = useCallback( const trackPage = useCallback(
(page, options = {}) => { (page) => {
if (!analyticsAllowed || !analyticsLoaded) { if (!analyticsAllowed || !analyticsLoaded) {
return return
} }
GoogleAnalytics.set({
page,
...options,
})
GoogleAnalytics.pageview(page) GoogleAnalytics.pageview(page)
}, },
[analyticsAllowed], [analyticsAllowed],
) )
return { trackPage } const trackEvent = useCallback(
(event: EventArgs) => {
if (!analyticsAllowed || !analyticsLoaded) {
return
}
GoogleAnalytics.event(event)
},
[analyticsAllowed],
)
return { trackPage, trackEvent }
} }