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:
parent
e9f7acff63
commit
f7d4cfe112
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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',
|
||||||
},
|
},
|
||||||
|
|
|
@ -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}>
|
||||||
|
|
|
@ -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}>
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue