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 SearchIcon from '@material-ui/icons/Search'
|
||||
import SearchBar from 'material-ui-search-bar'
|
||||
import * as React from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
|
||||
import SafeList from './SafeList'
|
||||
|
@ -16,12 +16,11 @@ import Link from 'src/components/layout/Link'
|
|||
import Row from 'src/components/layout/Row'
|
||||
import { WELCOME_ADDRESS } from 'src/routes/routes'
|
||||
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 { AppReduxState } from 'src/store'
|
||||
|
||||
const { useEffect, useMemo, useState } = React
|
||||
|
||||
export const SafeListSidebarContext = React.createContext({
|
||||
isOpen: false,
|
||||
toggleSidebar: () => {},
|
||||
|
@ -39,12 +38,7 @@ const SafeListSidebar = ({ children, currentSafe, defaultSafe, safes, setDefault
|
|||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [filter, setFilter] = useState('')
|
||||
const classes = useSidebarStyles()
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
setFilter('')
|
||||
}, 300)
|
||||
}, [isOpen])
|
||||
const { trackEvent } = useAnalytics()
|
||||
|
||||
const searchClasses = {
|
||||
input: classes.searchInput,
|
||||
|
@ -54,6 +48,9 @@ const SafeListSidebar = ({ children, currentSafe, defaultSafe, safes, setDefault
|
|||
}
|
||||
|
||||
const toggleSidebar = () => {
|
||||
if (!isOpen) {
|
||||
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Safe List Sidebar' })
|
||||
}
|
||||
setIsOpen((prevIsOpen) => !prevIsOpen)
|
||||
}
|
||||
|
||||
|
@ -73,6 +70,12 @@ const SafeListSidebar = ({ children, currentSafe, defaultSafe, safes, setDefault
|
|||
|
||||
const filteredSafes = useMemo(() => filterBy(filter, safes), [safes, filter])
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
setFilter('')
|
||||
}, 300)
|
||||
}, [isOpen])
|
||||
|
||||
return (
|
||||
<SafeListSidebarContext.Provider value={{ isOpen, toggleSidebar }}>
|
||||
<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 { addressBookQueryParamsSelector, safesListSelector } from 'src/logic/safe/store/selectors'
|
||||
import { checksumAddress } from 'src/utils/checksumAddress'
|
||||
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
|
||||
|
||||
const AddressBookTable = ({ classes }) => {
|
||||
const columns = generateColumns()
|
||||
|
@ -53,6 +54,11 @@ const AddressBookTable = ({ classes }) => {
|
|||
const [editCreateEntryModalOpen, setEditCreateEntryModalOpen] = useState(false)
|
||||
const [deleteEntryModalOpen, setDeleteEntryModalOpen] = useState(false)
|
||||
const [sendFundsModalOpen, setSendFundsModalOpen] = useState(false)
|
||||
const { trackEvent } = useAnalytics()
|
||||
|
||||
useEffect(() => {
|
||||
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'AddressBook' })
|
||||
}, [trackEvent])
|
||||
|
||||
useEffect(() => {
|
||||
if (entryAddressToEditOrCreateNew) {
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
import { isSameURL } from 'src/utils/url'
|
||||
import { useIframeMessageHandler } from './hooks/useIframeMessageHandler'
|
||||
import ConfirmTransactionModal from './components/ConfirmTransactionModal'
|
||||
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
|
||||
|
||||
const centerCSS = css`
|
||||
display: flex;
|
||||
|
@ -64,6 +65,7 @@ const Apps = (): React.ReactElement => {
|
|||
)
|
||||
const iframeRef = useRef<HTMLIFrameElement>()
|
||||
|
||||
const { trackEvent } = useAnalytics()
|
||||
const granted = useSelector(grantedSelector)
|
||||
const safeAddress = useSelector(safeParamAddressFromStateSelector)
|
||||
const safeName = useSelector(safeNameSelector)
|
||||
|
@ -111,6 +113,7 @@ const Apps = (): React.ReactElement => {
|
|||
[selectedAppId],
|
||||
)
|
||||
|
||||
// Auto Select app first App
|
||||
useEffect(() => {
|
||||
const selectFirstEnabledApp = () => {
|
||||
const firstEnabledApp = appList.find((a) => !a.disabled)
|
||||
|
@ -124,7 +127,14 @@ const Apps = (): React.ReactElement => {
|
|||
if (initialSelect || currentAppWasDisabled) {
|
||||
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 iframe = iframeRef.current
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import TableCell from '@material-ui/core/TableCell'
|
||||
import TableContainer from '@material-ui/core/TableContainer'
|
||||
import TableRow from '@material-ui/core/TableRow'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { List } from 'immutable'
|
||||
import React from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
import { styles } from './styles'
|
||||
|
@ -29,6 +29,7 @@ import {
|
|||
} from 'src/routes/safe/components/Balances/dataFetcher'
|
||||
import { extendedSafeTokensSelector, grantedSelector } from 'src/routes/safe/container/selector'
|
||||
import { Skeleton } from '@material-ui/lab'
|
||||
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
|
||||
|
||||
const useStyles = makeStyles(styles as any)
|
||||
|
||||
|
@ -61,6 +62,11 @@ const Coins = (props: Props): React.ReactElement => {
|
|||
const currencyValues = useSelector(safeFiatBalancesListSelector)
|
||||
const granted = useSelector(grantedSelector)
|
||||
const [filteredData, setFilteredData] = React.useState<List<BalanceData>>(List())
|
||||
const { trackEvent } = useAnalytics()
|
||||
|
||||
useEffect(() => {
|
||||
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Coins' })
|
||||
}, [trackEvent])
|
||||
|
||||
React.useMemo(() => {
|
||||
setFilteredData(getBalanceData(activeTokens, selectedCurrency, currencyValues, currencyRate))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import Card from '@material-ui/core/Card'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import React from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
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 { safeSelector } from 'src/logic/safe/store/selectors'
|
||||
import { fontColor, lg, screenSm, screenXs } from 'src/theme/variables'
|
||||
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
|
||||
|
||||
const useStyles = makeStyles({
|
||||
cardInner: {
|
||||
|
@ -74,13 +75,18 @@ const useStyles = makeStyles({
|
|||
},
|
||||
} as any)
|
||||
|
||||
const Collectibles = () => {
|
||||
const Collectibles = (): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
const [selectedToken, setSelectedToken] = React.useState({})
|
||||
const [sendNFTsModalOpen, setSendNFTsModalOpen] = React.useState(false)
|
||||
const { address, ethBalance, name } = useSelector(safeSelector)
|
||||
const nftTokens = useSelector(nftTokensSelector)
|
||||
const activeAssetsList = useSelector(activeNftAssetsListSelector)
|
||||
const { trackEvent } = useAnalytics()
|
||||
|
||||
useEffect(() => {
|
||||
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Collectibles' })
|
||||
}, [trackEvent])
|
||||
|
||||
const handleItemSend = (nftToken) => {
|
||||
setSelectedToken(nftToken)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Loader, Text, theme, Title } from '@gnosis.pm/safe-react-components'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import React from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
import styled from 'styled-components'
|
||||
|
||||
|
@ -10,6 +10,7 @@ import ModulesTable from './ModulesTable'
|
|||
|
||||
import Block from 'src/components/layout/Block'
|
||||
import { safeModulesSelector, safeNonceSelector } from 'src/logic/safe/store/selectors'
|
||||
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
|
@ -39,10 +40,14 @@ const LoadingModules = (): React.ReactElement => {
|
|||
|
||||
const Advanced = (): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
|
||||
const nonce = useSelector(safeNonceSelector)
|
||||
const modules = useSelector(safeModulesSelector)
|
||||
const moduleData = getModuleData(modules) ?? null
|
||||
const { trackEvent } = useAnalytics()
|
||||
|
||||
useEffect(() => {
|
||||
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Settings', label: 'Advanced' })
|
||||
}, [trackEvent])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -33,7 +33,7 @@ export const SAVE_OWNER_CHANGES_BTN_TEST_ID = 'save-owner-changes-btn'
|
|||
const useStyles = makeStyles(styles)
|
||||
|
||||
type OwnProps = {
|
||||
isOpen: true
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
ownerAddress: string
|
||||
selectedOwnerName: string
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
import TableCell from '@material-ui/core/TableCell'
|
||||
import TableContainer from '@material-ui/core/TableContainer'
|
||||
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 React from 'react'
|
||||
import { List } from 'immutable'
|
||||
|
||||
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 Row from 'src/components/layout/Row'
|
||||
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 REMOVE_OWNER_BTN_TEST_ID = 'remove-owner-btn'
|
||||
|
@ -35,166 +39,167 @@ export const ADD_OWNER_BTN_TEST_ID = 'add-owner-btn'
|
|||
export const REPLACE_OWNER_BTN_TEST_ID = 'replace-owner-btn'
|
||||
export const OWNERS_ROW_TEST_ID = 'owners-row'
|
||||
|
||||
class ManageOwners extends React.Component<any, any> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
this.state = {
|
||||
selectedOwnerAddress: undefined,
|
||||
selectedOwnerName: undefined,
|
||||
showAddOwner: false,
|
||||
showRemoveOwner: false,
|
||||
showReplaceOwner: false,
|
||||
showEditOwner: false,
|
||||
}
|
||||
}
|
||||
|
||||
onShow = (action, row?: any) => () => {
|
||||
this.setState({
|
||||
[`show${action}`]: true,
|
||||
selectedOwnerAddress: row && row.address,
|
||||
selectedOwnerName: row && row.name,
|
||||
})
|
||||
}
|
||||
|
||||
onHide = (action) => () => {
|
||||
this.setState({
|
||||
[`show${action}`]: false,
|
||||
selectedOwnerAddress: undefined,
|
||||
selectedOwnerName: undefined,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { addressBook, classes, granted, owners } = this.props
|
||||
const {
|
||||
selectedOwnerAddress,
|
||||
selectedOwnerName,
|
||||
showAddOwner,
|
||||
showEditOwner,
|
||||
showRemoveOwner,
|
||||
showReplaceOwner,
|
||||
} = this.state
|
||||
const columns = generateColumns()
|
||||
const autoColumns = columns.filter((c) => !c.custom)
|
||||
const ownersAdbk = getOwnersWithNameFromAddressBook(addressBook, owners)
|
||||
const ownerData = getOwnerData(ownersAdbk)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Block className={classes.formContainer}>
|
||||
<Heading className={classes.title} tag="h2">
|
||||
Manage Safe Owners
|
||||
</Heading>
|
||||
<Paragraph className={classes.annotation}>
|
||||
Add, remove and replace owners or rename existing owners. Owner names are only stored locally and never
|
||||
shared with Gnosis or any third parties.
|
||||
</Paragraph>
|
||||
<TableContainer>
|
||||
<Table
|
||||
columns={columns}
|
||||
data={ownerData}
|
||||
defaultFixed
|
||||
defaultOrderBy={OWNERS_TABLE_NAME_ID}
|
||||
disablePagination
|
||||
label="Owners"
|
||||
noBorder
|
||||
size={ownerData.size}
|
||||
>
|
||||
{(sortedData) =>
|
||||
sortedData.map((row, index) => (
|
||||
<TableRow
|
||||
className={cn(classes.hide, index >= 3 && index === sortedData.size - 1 && classes.noBorderBottom)}
|
||||
data-testid={OWNERS_ROW_TEST_ID}
|
||||
key={index}
|
||||
tabIndex={-1}
|
||||
>
|
||||
{autoColumns.map((column: any) => (
|
||||
<TableCell align={column.align} component="td" key={column.id} style={cellWidth(column.width)}>
|
||||
{column.id === OWNERS_TABLE_ADDRESS_ID ? (
|
||||
<OwnerAddressTableCell address={row[column.id]} showLinks />
|
||||
) : (
|
||||
row[column.id]
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
<TableCell component="td">
|
||||
<Row align="end" className={classes.actions}>
|
||||
<Img
|
||||
alt="Edit owner"
|
||||
className={classes.editOwnerIcon}
|
||||
onClick={this.onShow('EditOwner', row)}
|
||||
src={RenameOwnerIcon}
|
||||
testId={RENAME_OWNER_BTN_TEST_ID}
|
||||
/>
|
||||
{granted && (
|
||||
<>
|
||||
<Img
|
||||
alt="Replace owner"
|
||||
className={classes.replaceOwnerIcon}
|
||||
onClick={this.onShow('ReplaceOwner', row)}
|
||||
src={ReplaceOwnerIcon}
|
||||
testId={REPLACE_OWNER_BTN_TEST_ID}
|
||||
/>
|
||||
{ownerData.size > 1 && (
|
||||
<Img
|
||||
alt="Remove owner"
|
||||
className={classes.removeOwnerIcon}
|
||||
onClick={this.onShow('RemoveOwner', row)}
|
||||
src={RemoveOwnerIcon}
|
||||
testId={REMOVE_OWNER_BTN_TEST_ID}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
}
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Block>
|
||||
{granted && (
|
||||
<>
|
||||
<Hairline />
|
||||
<Row align="end" className={classes.controlsRow} grow>
|
||||
<Col end="xs">
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={this.onShow('AddOwner')}
|
||||
size="small"
|
||||
testId={ADD_OWNER_BTN_TEST_ID}
|
||||
variant="contained"
|
||||
>
|
||||
Add new owner
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
)}
|
||||
<AddOwnerModal isOpen={showAddOwner} onClose={this.onHide('AddOwner')} />
|
||||
<RemoveOwnerModal
|
||||
isOpen={showRemoveOwner}
|
||||
onClose={this.onHide('RemoveOwner')}
|
||||
ownerAddress={selectedOwnerAddress}
|
||||
ownerName={selectedOwnerName}
|
||||
/>
|
||||
<ReplaceOwnerModal
|
||||
isOpen={showReplaceOwner}
|
||||
onClose={this.onHide('ReplaceOwner')}
|
||||
ownerAddress={selectedOwnerAddress}
|
||||
ownerName={selectedOwnerName}
|
||||
/>
|
||||
<EditOwnerModal
|
||||
isOpen={showEditOwner}
|
||||
onClose={this.onHide('EditOwner')}
|
||||
ownerAddress={selectedOwnerAddress}
|
||||
selectedOwnerName={selectedOwnerName}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
type Props = {
|
||||
addressBook: unknown
|
||||
granted: boolean
|
||||
owners: List<SafeOwner>
|
||||
}
|
||||
|
||||
export default withStyles(styles as any)(ManageOwners)
|
||||
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,
|
||||
showRemoveOwner: false,
|
||||
showReplaceOwner: false,
|
||||
showEditOwner: false,
|
||||
})
|
||||
|
||||
const onShow = (action, row?: any) => () => {
|
||||
setModalStatus((prevState) => ({
|
||||
...prevState,
|
||||
[`show${action}`]: !prevState[`show${action}`],
|
||||
}))
|
||||
setSelectedOwnerAddress(row && row.address)
|
||||
setSelectedOwnerName(row && row.name)
|
||||
}
|
||||
|
||||
const onHide = (action) => () => {
|
||||
setModalStatus((prevState) => ({
|
||||
...prevState,
|
||||
[`show${action}`]: !Boolean(prevState[`show${action}`]),
|
||||
}))
|
||||
setSelectedOwnerAddress(undefined)
|
||||
setSelectedOwnerName(undefined)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Settings', label: 'Owners' })
|
||||
}, [trackEvent])
|
||||
|
||||
const columns = generateColumns()
|
||||
const autoColumns = columns.filter((c) => !c.custom)
|
||||
const ownersAdbk = getOwnersWithNameFromAddressBook(addressBook as AddressBookEntryProps, owners)
|
||||
const ownerData = getOwnerData(ownersAdbk)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Block className={classes.formContainer}>
|
||||
<Heading className={classes.title} tag="h2">
|
||||
Manage Safe Owners
|
||||
</Heading>
|
||||
<Paragraph className={classes.annotation}>
|
||||
Add, remove and replace owners or rename existing owners. Owner names are only stored locally and never shared
|
||||
with Gnosis or any third parties.
|
||||
</Paragraph>
|
||||
<TableContainer>
|
||||
<Table
|
||||
columns={columns}
|
||||
data={ownerData}
|
||||
defaultFixed
|
||||
defaultOrderBy={OWNERS_TABLE_NAME_ID}
|
||||
disablePagination
|
||||
label="Owners"
|
||||
noBorder
|
||||
size={ownerData.size}
|
||||
>
|
||||
{(sortedData) =>
|
||||
sortedData.map((row, index) => (
|
||||
<TableRow
|
||||
className={cn(classes.hide, index >= 3 && index === sortedData.size - 1 && classes.noBorderBottom)}
|
||||
data-testid={OWNERS_ROW_TEST_ID}
|
||||
key={index}
|
||||
>
|
||||
{autoColumns.map((column: any) => (
|
||||
<TableCell align={column.align} component="td" key={column.id} style={cellWidth(column.width)}>
|
||||
{column.id === OWNERS_TABLE_ADDRESS_ID ? (
|
||||
<OwnerAddressTableCell address={row[column.id]} showLinks />
|
||||
) : (
|
||||
row[column.id]
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
<TableCell component="td">
|
||||
<Row align="end" className={classes.actions}>
|
||||
<Img
|
||||
alt="Edit owner"
|
||||
className={classes.editOwnerIcon}
|
||||
onClick={onShow('EditOwner', row)}
|
||||
src={RenameOwnerIcon}
|
||||
testId={RENAME_OWNER_BTN_TEST_ID}
|
||||
/>
|
||||
{granted && (
|
||||
<>
|
||||
<Img
|
||||
alt="Replace owner"
|
||||
className={classes.replaceOwnerIcon}
|
||||
onClick={onShow('ReplaceOwner', row)}
|
||||
src={ReplaceOwnerIcon}
|
||||
testId={REPLACE_OWNER_BTN_TEST_ID}
|
||||
/>
|
||||
{ownerData.size > 1 && (
|
||||
<Img
|
||||
alt="Remove owner"
|
||||
className={classes.removeOwnerIcon}
|
||||
onClick={onShow('RemoveOwner', row)}
|
||||
src={RemoveOwnerIcon}
|
||||
testId={REMOVE_OWNER_BTN_TEST_ID}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
}
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Block>
|
||||
{granted && (
|
||||
<>
|
||||
<Hairline />
|
||||
<Row align="end" className={classes.controlsRow} grow>
|
||||
<Col end="xs">
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={onShow('AddOwner')}
|
||||
size="small"
|
||||
testId={ADD_OWNER_BTN_TEST_ID}
|
||||
variant="contained"
|
||||
>
|
||||
Add new owner
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
)}
|
||||
<AddOwnerModal isOpen={modalsStatus.showAddOwner} onClose={onHide('AddOwner')} />
|
||||
<RemoveOwnerModal
|
||||
isOpen={modalsStatus.showRemoveOwner}
|
||||
onClose={onHide('RemoveOwner')}
|
||||
ownerAddress={selectedOwnerAddress}
|
||||
ownerName={selectedOwnerName}
|
||||
/>
|
||||
<ReplaceOwnerModal
|
||||
isOpen={modalsStatus.showReplaceOwner}
|
||||
onClose={onHide('ReplaceOwner')}
|
||||
ownerAddress={selectedOwnerAddress}
|
||||
ownerName={selectedOwnerName}
|
||||
/>
|
||||
<EditOwnerModal
|
||||
isOpen={modalsStatus.showEditOwner}
|
||||
onClose={onHide('EditOwner')}
|
||||
ownerAddress={selectedOwnerAddress}
|
||||
selectedOwnerName={selectedOwnerName}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ManageOwners
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { lg, sm } from 'src/theme/variables'
|
||||
import { createStyles } from '@material-ui/core'
|
||||
|
||||
export const styles = () => ({
|
||||
export const styles = createStyles({
|
||||
formContainer: {
|
||||
minHeight: '420px',
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import React from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
|
||||
import { styles } from './style'
|
||||
|
@ -29,6 +29,7 @@ import {
|
|||
safeNeedsUpdateSelector,
|
||||
safeParamAddressFromStateSelector,
|
||||
} 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_SUBMIT_BTN_TEST_ID = 'change-safe-name-btn'
|
||||
|
@ -44,6 +45,7 @@ const SafeDetails = (): React.ReactElement => {
|
|||
const safeName = useSelector(safeNameSelector)
|
||||
const safeNeedsUpdate = useSelector(safeNeedsUpdateSelector)
|
||||
const safeCurrentVersion = useSelector(safeCurrentVersionSelector)
|
||||
const { trackEvent } = useAnalytics()
|
||||
|
||||
const [isModalOpen, setModalOpen] = React.useState(false)
|
||||
const safeAddress = useSelector(safeParamAddressFromStateSelector)
|
||||
|
@ -63,6 +65,10 @@ const SafeDetails = (): React.ReactElement => {
|
|||
setModalOpen(true)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Settings', label: 'Details' })
|
||||
}, [trackEvent])
|
||||
|
||||
return (
|
||||
<>
|
||||
<GnoForm onSubmit={handleSubmit}>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { withStyles } from '@material-ui/core/styles'
|
||||
import { withSnackbar } from 'notistack'
|
||||
import React, { useState } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
|
||||
import ChangeThreshold from './ChangeThreshold'
|
||||
|
@ -22,6 +22,7 @@ import {
|
|||
safeParamAddressFromStateSelector,
|
||||
safeThresholdSelector,
|
||||
} from 'src/logic/safe/store/selectors'
|
||||
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
|
||||
|
||||
const ThresholdSettings = ({ classes, closeSnackbar, enqueueSnackbar }) => {
|
||||
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 (
|
||||
<>
|
||||
<Block className={classes.container}>
|
||||
|
|
|
@ -7,7 +7,7 @@ import { withStyles } from '@material-ui/core/styles'
|
|||
import ExpandLess from '@material-ui/icons/ExpandLess'
|
||||
import ExpandMore from '@material-ui/icons/ExpandMore'
|
||||
import cn from 'classnames'
|
||||
import React, { useState } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
import ExpandedTxComponent from './ExpandedTx'
|
||||
|
@ -21,6 +21,7 @@ import Block from 'src/components/layout/Block'
|
|||
import Row from 'src/components/layout/Row'
|
||||
import { safeCancellationTransactionsSelector } from 'src/logic/safe/store/selectors'
|
||||
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'
|
||||
|
||||
|
@ -28,6 +29,11 @@ const TxsTable = ({ classes }) => {
|
|||
const [expandedTx, setExpandedTx] = useState(null)
|
||||
const cancellationTransactions = useSelector(safeCancellationTransactionsSelector)
|
||||
const transactions = useSelector(extendedTransactionsSelector)
|
||||
const { trackEvent } = useAnalytics()
|
||||
|
||||
useEffect(() => {
|
||||
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Transactions' })
|
||||
}, [trackEvent])
|
||||
|
||||
const handleTxExpand = (safeTxHash) => {
|
||||
setExpandedTx((prevTx) => (prevTx === safeTxHash ? null : safeTxHash))
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { useCallback, useEffect, useState } from 'react'
|
||||
import GoogleAnalytics from 'react-ga'
|
||||
import GoogleAnalytics, { EventArgs } from 'react-ga'
|
||||
|
||||
import { getGoogleAnalyticsTrackingID } from 'src/config'
|
||||
import { COOKIES_KEY } from 'src/logic/cookies/model/cookie'
|
||||
import { loadFromCookie } from 'src/logic/cookies/utils'
|
||||
|
||||
export const SAFE_NAVIGATION_EVENT = 'Safe Navigation'
|
||||
|
||||
let analyticsLoaded = false
|
||||
export const loadGoogleAnalytics = () => {
|
||||
export const loadGoogleAnalytics = (): void => {
|
||||
if (analyticsLoaded) {
|
||||
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)
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -37,18 +44,24 @@ export const useAnalytics = () => {
|
|||
}, [])
|
||||
|
||||
const trackPage = useCallback(
|
||||
(page, options = {}) => {
|
||||
(page) => {
|
||||
if (!analyticsAllowed || !analyticsLoaded) {
|
||||
return
|
||||
}
|
||||
GoogleAnalytics.set({
|
||||
page,
|
||||
...options,
|
||||
})
|
||||
GoogleAnalytics.pageview(page)
|
||||
},
|
||||
[analyticsAllowed],
|
||||
)
|
||||
|
||||
return { trackPage }
|
||||
const trackEvent = useCallback(
|
||||
(event: EventArgs) => {
|
||||
if (!analyticsAllowed || !analyticsLoaded) {
|
||||
return
|
||||
}
|
||||
GoogleAnalytics.event(event)
|
||||
},
|
||||
[analyticsAllowed],
|
||||
)
|
||||
|
||||
return { trackPage, trackEvent }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue