[Address book v2] Address book update events (#2293)

* Remove UNKNOWN from owner list

* Fetch Safe Name from address book

* Update Safe name in address book when updating from settings > safe details

* Fix lint issue
This commit is contained in:
Daniel Sanchez 2021-05-17 17:06:08 +02:00 committed by GitHub
parent 839c947e82
commit 7b00ee170b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 69 additions and 56 deletions

View File

@ -22,13 +22,13 @@ import { networkSelector } from 'src/logic/wallets/store/selectors'
import { SAFELIST_ADDRESS, WELCOME_ADDRESS } from 'src/routes/routes'
import {
safeTotalFiatBalanceSelector,
safeNameSelector,
safeParamAddressFromStateSelector,
safeLoadedViaUrlSelector,
} from 'src/logic/safe/store/selectors'
import { currentCurrencySelector } from 'src/logic/currencyValues/store/selectors'
import Modal from 'src/components/Modal'
import SendModal from 'src/routes/safe/components/Balances/SendModal'
import { useSafeName } from 'src/logic/addressBook/hooks/useSafeName'
import { useLoadSafe } from 'src/logic/safe/hooks/useLoadSafe'
import { useSafeScheduledUpdates } from 'src/logic/safe/hooks/useSafeScheduledUpdates'
import useSafeActions from 'src/logic/safe/hooks/useSafeActions'
@ -72,7 +72,7 @@ const App: React.FC = ({ children }) => {
const matchSafe = useRouteMatch({ path: `${SAFELIST_ADDRESS}`, strict: false })
const history = useHistory()
const safeAddress = useSelector(safeParamAddressFromStateSelector)
const safeName = useSelector(safeNameSelector) ?? ''
const safeName = useSafeName(safeAddress)
const { safeActionsState, onShow, onHide, showSendFunds, hideSendFunds } = useSafeActions()
const currentSafeBalance = useSelector(safeTotalFiatBalanceSelector)
const currentCurrency = useSelector(currentCurrencySelector)

View File

@ -1,4 +1,4 @@
import React from 'react'
import React, { ReactElement } from 'react'
import styled from 'styled-components'
import { ButtonLink, EthHashInfo, Text } from '@gnosis.pm/safe-react-components'
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
@ -51,9 +51,8 @@ type Props = {
const { nativeCoin } = getNetworkInfo()
export const AddressWrapper = (props: Props): React.ReactElement => {
export const AddressWrapper = ({ safe, defaultSafe }: Props): ReactElement => {
const classes = useStyles()
const { safe, defaultSafe } = props
const dispatch = useDispatch()
const setDefaultSafeAction = (safeAddress: string) => {

View File

@ -1,3 +1,4 @@
import { List } from 'immutable'
import React, { useEffect, useMemo, useState, ReactElement } from 'react'
import Drawer from '@material-ui/core/Drawer'
import SearchIcon from '@material-ui/icons/Search'
@ -18,6 +19,9 @@ import { WELCOME_ADDRESS } from 'src/routes/routes'
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
import { defaultSafeSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { SafeRecord, SafeRecordProps } from 'src/logic/safe/store/models/safe'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
import { getNameFromAddressBook } from 'src/logic/addressBook/utils'
export const SafeListSidebarContext = React.createContext({
isOpen: false,
@ -31,6 +35,15 @@ const filterBy = (filter, safes) =>
safe.address.toLowerCase().includes(filter.toLowerCase()) ||
safe.name.toLowerCase().includes(filter.toLowerCase()),
)
const useAddressBookSafeNames = (safeList: List<SafeRecord>): List<SafeRecordProps> => {
const addressBook = useSelector(addressBookSelector)
return safeList.map((safeRecord) => {
const safe = safeRecord.toObject()
const name = getNameFromAddressBook(addressBook, safe.address) || ''
return { ...safe, name }
})
}
type Props = {
children: ReactElement
@ -39,9 +52,10 @@ type Props = {
export const SafeListSidebar = ({ children }: Props): ReactElement => {
const [isOpen, setIsOpen] = useState(false)
const [filter, setFilter] = useState('')
const safes = useSelector(sortedSafeListSelector).filter((safe) => !safe.loadedViaUrl)
const safesFromStore = useSelector(sortedSafeListSelector).filter((safe) => !safe.loadedViaUrl)
const defaultSafe = useSelector(defaultSafeSelector)
const currentSafe = useSelector(safeParamAddressFromStateSelector)
const safes = useAddressBookSafeNames(safesFromStore)
const classes = useSidebarStyles()
const { trackEvent } = useAnalytics()

View File

@ -0,0 +1,10 @@
import { useSelector } from 'react-redux'
import { getNameFromAddressBook } from 'src/logic/addressBook/utils'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
export const useSafeName = (safeAddress: string): string => {
const addressBook = useSelector(addressBookSelector)
return getNameFromAddressBook(addressBook, safeAddress) || ''
}

View File

@ -94,7 +94,7 @@ export const getOwnersWithNameFromAddressBook = (
const ownerName = getNameFromAddressBook(addressBook, owner.address)
return {
address: owner.address,
name: ownerName || owner.name,
name: ownerName || '',
}
})
}
@ -116,7 +116,7 @@ export const formatAddressListToAddressBookNames = (
}
/**
* If the safe is not loaded, the owner wasn't not deleted
* If the safe is not loaded, the owner wasn't deleted
* If the safe is already loaded and the owner has a valid name, will return true if the address is not already on the addressBook
* @param name
* @param address

View File

@ -83,8 +83,6 @@ export const safeFieldSelector = <K extends keyof SafeRecordProps>(field: K) =>
safe: SafeRecord,
): SafeRecordProps[K] | undefined => (safe ? safe.get(field, baseSafe.get(field)) : undefined)
export const safeNameSelector = createSelector(safeSelector, safeFieldSelector('name'))
export const safeEthBalanceSelector = createSelector(safeSelector, safeFieldSelector('ethBalance'))
export const safeNeedsUpdateSelector = createSelector(safeSelector, safeFieldSelector('needsUpdate'))

View File

@ -92,7 +92,7 @@ const DetailsForm = ({ errors, form }: DetailsFormProps): React.ReactElement =>
<Field
component={TextField}
name={FIELD_LOAD_NAME}
placeholder="Name of the Safe"
placeholder="Name of the Safe*"
text="Safe name"
type="text"
validate={composeValidators(required, minMaxLength(1, 50))}

View File

@ -6,11 +6,8 @@ import { useHistory } from 'react-router-dom'
import { useSelector } from 'react-redux'
import { INTERFACE_MESSAGES, Transaction, RequestId, LowercaseNetworks } from '@gnosis.pm/safe-apps-sdk-v1'
import {
safeEthBalanceSelector,
safeParamAddressFromStateSelector,
safeNameSelector,
} from 'src/logic/safe/store/selectors'
import { safeEthBalanceSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { useSafeName } from 'src/logic/addressBook/hooks/useSafeName'
import { grantedSelector } from 'src/routes/safe/container/selector'
import { getNetworkName, getTxServiceUrl } from 'src/config'
import { SAFELIST_ADDRESS } from 'src/routes/routes'
@ -86,7 +83,7 @@ const AppFrame = ({ appUrl }: Props): ReactElement => {
const granted = useSelector(grantedSelector)
const safeAddress = useSelector(safeParamAddressFromStateSelector)
const ethBalance = useSelector(safeEthBalanceSelector)
const safeName = useSelector(safeNameSelector)
const safeName = useSafeName(safeAddress)
const { trackEvent } = useAnalytics()
const history = useHistory()
const { consentReceived, onConsentReceipt } = useLegalConsent()

View File

@ -12,12 +12,10 @@ import {
} from '@gnosis.pm/safe-apps-sdk-v1'
import { useDispatch, useSelector } from 'react-redux'
import { useEffect, useCallback, MutableRefObject } from 'react'
import { getNetworkName, getTxServiceUrl } from 'src/config/'
import {
safeEthBalanceSelector,
safeNameSelector,
safeParamAddressFromStateSelector,
} from 'src/logic/safe/store/selectors'
import { useSafeName } from 'src/logic/addressBook/hooks/useSafeName'
import { safeEthBalanceSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { TransactionParams } from '../components/AppFrame'
import { SafeApp } from 'src/routes/safe/components/Apps/types.d'
@ -39,8 +37,8 @@ const useIframeMessageHandler = (
iframeRef: MutableRefObject<HTMLIFrameElement | null>,
): ReturnType => {
const { enqueueSnackbar, closeSnackbar } = useSnackbar()
const safeName = useSelector(safeNameSelector)
const safeAddress = useSelector(safeParamAddressFromStateSelector)
const safeName = useSafeName(safeAddress)
const ethBalance = useSelector(safeEthBalanceSelector)
const dispatch = useDispatch()

View File

@ -1,5 +1,5 @@
import { makeStyles } from '@material-ui/core/styles'
import React, { useEffect, useState } from 'react'
import React, { ReactElement, useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import ReceiveModal from 'src/components/App/ReceiveModal'
@ -13,11 +13,8 @@ import Row from 'src/components/layout/Row'
import { SAFELIST_ADDRESS } from 'src/routes/routes'
import SendModal from 'src/routes/safe/components/Balances/SendModal'
import { CurrencyDropdown } from 'src/routes/safe/components/CurrencyDropdown'
import {
safeFeaturesEnabledSelector,
safeNameSelector,
safeParamAddressFromStateSelector,
} from 'src/logic/safe/store/selectors'
import { useSafeName } from 'src/logic/addressBook/hooks/useSafeName'
import { safeFeaturesEnabledSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { wrapInSuspense } from 'src/utils/wrapInSuspense'
import { useFetchTokens } from 'src/logic/safe/hooks/useFetchTokens'
@ -45,13 +42,13 @@ export const COLLECTIBLES_LOCATION_REGEX = /\/balances\/collectibles$/
const useStyles = makeStyles(styles)
const Balances = (): React.ReactElement => {
const Balances = (): ReactElement => {
const classes = useStyles()
const [state, setState] = useState(INITIAL_STATE)
const address = useSelector(safeParamAddressFromStateSelector)
const featuresEnabled = useSelector(safeFeaturesEnabledSelector)
const safeName = useSelector(safeNameSelector) ?? ''
const safeName = useSafeName(address)
useFetchTokens(address)

View File

@ -1,7 +1,7 @@
import IconButton from '@material-ui/core/IconButton'
import { makeStyles } from '@material-ui/core/styles'
import Close from '@material-ui/icons/Close'
import React, { useEffect, useState } from 'react'
import React, { ReactElement, useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import { EthHashInfo } from '@gnosis.pm/safe-react-components'
@ -13,7 +13,8 @@ import Hairline from 'src/components/layout/Hairline'
import Paragraph from 'src/components/layout/Paragraph'
import Row from 'src/components/layout/Row'
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
import { safeNameSelector, safeOwnersSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { useSafeName } from 'src/logic/addressBook/hooks/useSafeName'
import { safeOwnersSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { TxParametersDetail } from 'src/routes/safe/components/Transactions/helpers/TxParametersDetail'
import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters'
import { EstimationStatus, useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas'
@ -34,11 +35,11 @@ type ReviewAddOwnerProps = {
values: OwnerValues
}
export const ReviewAddOwner = ({ onClickBack, onClose, onSubmit, values }: ReviewAddOwnerProps): React.ReactElement => {
export const ReviewAddOwner = ({ onClickBack, onClose, onSubmit, values }: ReviewAddOwnerProps): ReactElement => {
const classes = useStyles()
const [data, setData] = useState('')
const safeAddress = useSelector(safeParamAddressFromStateSelector) as string
const safeName = useSelector(safeNameSelector)
const safeName = useSafeName(safeAddress)
const owners = useSelector(safeOwnersSelector)
const [manualSafeTxGas, setManualSafeTxGas] = useState(0)
const [manualGasPrice, setManualGasPrice] = useState<string | undefined>()

View File

@ -14,7 +14,8 @@ import Hairline from 'src/components/layout/Hairline'
import Paragraph from 'src/components/layout/Paragraph'
import Row from 'src/components/layout/Row'
import { getGnosisSafeInstanceAt, SENTINEL_ADDRESS } from 'src/logic/contracts/safeContracts'
import { safeNameSelector, safeOwnersSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { safeOwnersSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { useSafeName } from 'src/logic/addressBook/hooks/useSafeName'
import { getOwnersWithNameFromAddressBook } from 'src/logic/addressBook/utils'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
import { TxParametersDetail } from 'src/routes/safe/components/Transactions/helpers/TxParametersDetail'
@ -50,7 +51,7 @@ export const ReviewRemoveOwnerModal = ({
const classes = useStyles()
const [data, setData] = useState('')
const safeAddress = useSelector(safeParamAddressFromStateSelector)
const safeName = useSelector(safeNameSelector)
const safeName = useSafeName(safeAddress)
const owners = useSelector(safeOwnersSelector)
const addressBook = useSelector(addressBookSelector)
const ownersWithAddressBookName = owners ? getOwnersWithNameFromAddressBook(addressBook, owners) : List([])

View File

@ -15,11 +15,11 @@ import Paragraph from 'src/components/layout/Paragraph'
import Row from 'src/components/layout/Row'
import { getGnosisSafeInstanceAt, SENTINEL_ADDRESS } from 'src/logic/contracts/safeContracts'
import {
safeNameSelector,
safeOwnersSelector,
safeParamAddressFromStateSelector,
safeThresholdSelector,
} from 'src/logic/safe/store/selectors'
import { useSafeName } from 'src/logic/addressBook/hooks/useSafeName'
import { getOwnersWithNameFromAddressBook } from 'src/logic/addressBook/utils'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
import { TxParametersDetail } from 'src/routes/safe/components/Transactions/helpers/TxParametersDetail'
@ -57,7 +57,7 @@ export const ReviewReplaceOwnerModal = ({
const classes = useStyles()
const [data, setData] = useState('')
const safeAddress = useSelector(safeParamAddressFromStateSelector)
const safeName = useSelector(safeNameSelector)
const safeName = useSafeName(safeAddress)
const owners = useSelector(safeOwnersSelector)
const threshold = useSelector(safeThresholdSelector) || 1
const addressBook = useSelector(addressBookSelector)

View File

@ -15,7 +15,7 @@ import { RemoveOwnerModal } from './RemoveOwnerModal'
import { ReplaceOwnerModal } from './ReplaceOwnerModal'
import RenameOwnerIcon from './assets/icons/rename-owner.svg'
import ReplaceOwnerIcon from './assets/icons/replace-owner.svg'
import { OWNERS_TABLE_ADDRESS_ID, OWNERS_TABLE_NAME_ID, generateColumns, getOwnerData } from './dataFetcher'
import { OWNERS_TABLE_ADDRESS_ID, generateColumns, getOwnerData } from './dataFetcher'
import { styles } from './style'
import { getExplorerInfo } from 'src/config'
@ -103,7 +103,7 @@ const ManageOwners = ({ addressBook, granted, owners }: Props): React.ReactEleme
columns={columns}
data={ownerData}
defaultFixed
defaultOrderBy={OWNERS_TABLE_NAME_ID}
defaultOrderBy={OWNERS_TABLE_ADDRESS_ID}
disablePagination
label="Owners"
noBorder

View File

@ -1,3 +1,4 @@
import { EthHashInfo } from '@gnosis.pm/safe-react-components'
import IconButton from '@material-ui/core/IconButton'
import { makeStyles } from '@material-ui/core/styles'
import Close from '@material-ui/icons/Close'
@ -6,17 +7,13 @@ import { useDispatch, useSelector } from 'react-redux'
import { styles } from './style'
import { EthHashInfo } from '@gnosis.pm/safe-react-components'
import Modal from 'src/components/Modal'
import Block from 'src/components/layout/Block'
import Hairline from 'src/components/layout/Hairline'
import Paragraph from 'src/components/layout/Paragraph'
import Row from 'src/components/layout/Row'
import {
defaultSafeSelector,
safeNameSelector,
safeParamAddressFromStateSelector,
} from 'src/logic/safe/store/selectors'
import { useSafeName } from 'src/logic/addressBook/hooks/useSafeName'
import { defaultSafeSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { WELCOME_ADDRESS } from 'src/routes/routes'
import { removeLocalSafe } from 'src/logic/safe/store/actions/removeLocalSafe'
import { sameAddress } from 'src/logic/wallets/ethAddresses'
@ -36,7 +33,7 @@ type RemoveSafeModalProps = {
export const RemoveSafeModal = ({ isOpen, onClose }: RemoveSafeModalProps): React.ReactElement => {
const classes = useStyles()
const safeAddress = useSelector(safeParamAddressFromStateSelector)
const safeName = useSelector(safeNameSelector)
const safeName = useSafeName(safeAddress)
const defaultSafe = useSelector(defaultSafeSelector)
const dispatch = useDispatch()

View File

@ -1,5 +1,5 @@
import { makeStyles } from '@material-ui/core/styles'
import React, { useEffect, useState } from 'react'
import React, { ReactElement, useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { styles } from './style'
@ -15,20 +15,21 @@ import Col from 'src/components/layout/Col'
import Heading from 'src/components/layout/Heading'
import Paragraph from 'src/components/layout/Paragraph'
import Row from 'src/components/layout/Row'
import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
import { addOrUpdateAddressBookEntry } from 'src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry'
import enqueueSnackbar from 'src/logic/notifications/store/actions/enqueueSnackbar'
import { getNotificationsFromTxType, enhanceSnackbarForAction } from 'src/logic/notifications'
import { sameAddress } from 'src/logic/wallets/ethAddresses'
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
import { UpdateSafeModal } from 'src/routes/safe/components/Settings/UpdateSafeModal'
import { grantedSelector } from 'src/routes/safe/container/selector'
import { updateSafe } from 'src/logic/safe/store/actions/updateSafe'
import { Icon, Link, Text } from '@gnosis.pm/safe-react-components'
import styled from 'styled-components'
import { useSafeName } from 'src/logic/addressBook/hooks/useSafeName'
import {
latestMasterContractVersionSelector,
safeCurrentVersionSelector,
safeNameSelector,
safeNeedsUpdateSelector,
safeParamAddressFromStateSelector,
} from 'src/logic/safe/store/selectors'
@ -51,18 +52,18 @@ const StyledIcon = styled(Icon)`
left: 6px;
`
const SafeDetails = (): React.ReactElement => {
const SafeDetails = (): ReactElement => {
const classes = useStyles()
const isUserOwner = useSelector(grantedSelector)
const latestMasterContractVersion = useSelector(latestMasterContractVersionSelector)
const dispatch = useDispatch()
const safeName = useSelector(safeNameSelector)
const safeAddress = useSelector(safeParamAddressFromStateSelector)
const safeName = useSafeName(safeAddress)
const safeNeedsUpdate = useSelector(safeNeedsUpdateSelector)
const safeCurrentVersion = useSelector(safeCurrentVersionSelector)
const { trackEvent } = useAnalytics()
const [isModalOpen, setModalOpen] = React.useState(false)
const safeAddress = useSelector(safeParamAddressFromStateSelector)
const [isModalOpen, setModalOpen] = useState(false)
const [safeInfo, setSafeInfo] = useState<MasterCopy | undefined>()
const toggleModal = () => {
@ -70,7 +71,7 @@ const SafeDetails = (): React.ReactElement => {
}
const handleSubmit = (values) => {
dispatch(updateSafe({ address: safeAddress, name: values.safeName }))
dispatch(addOrUpdateAddressBookEntry(makeAddressBookEntry({ address: safeAddress, name: values.safeName })))
const notification = getNotificationsFromTxType(TX_NOTIFICATION_TYPES.SAFE_NAME_CHANGE_TX)
dispatch(enqueueSnackbar(enhanceSnackbarForAction(notification.afterExecution.noMoreConfirmationsNeeded)))