Bugfix - Safes added via URL not stored when changing name (#2310)

* Fix Types

* Fix adding a safe to the list after it was accessed via URL

Co-authored-by: Daniel Sanchez <daniel.sanchez@gnosis.pm>
This commit is contained in:
Mati Dastugue 2021-05-25 05:41:13 -03:00 committed by GitHub
parent 55b40f91be
commit 3e4c9cf7d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 48 additions and 22 deletions

View File

@ -1,3 +1,4 @@
import { Store } from 'redux'
import { saveDefaultSafe, saveSafes } from 'src/logic/safe/utils' import { saveDefaultSafe, saveSafes } from 'src/logic/safe/utils'
import { ADD_SAFE_OWNER } from 'src/logic/safe/store/actions/addSafeOwner' import { ADD_SAFE_OWNER } from 'src/logic/safe/store/actions/addSafeOwner'
import { EDIT_SAFE_OWNER } from 'src/logic/safe/store/actions/editSafeOwner' import { EDIT_SAFE_OWNER } from 'src/logic/safe/store/actions/editSafeOwner'
@ -12,6 +13,7 @@ import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
import { checksumAddress } from 'src/utils/checksumAddress' import { checksumAddress } from 'src/utils/checksumAddress'
import { isValidAddressBookName } from 'src/logic/addressBook/utils' import { isValidAddressBookName } from 'src/logic/addressBook/utils'
import { addOrUpdateAddressBookEntry } from 'src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry' import { addOrUpdateAddressBookEntry } from 'src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry'
import { SafeRecord } from '../models/safe'
const watchedActions = [ const watchedActions = [
UPDATE_SAFE, UPDATE_SAFE,
@ -24,7 +26,16 @@ const watchedActions = [
SET_DEFAULT_SAFE, SET_DEFAULT_SAFE,
] ]
export const safeStorageMiddleware = (store) => (next) => async (action) => { type SafeProps = {
safe: SafeRecord
}
export const safeStorageMiddleware = (store: Store) => (
next: (arg0: { type: string; payload: string | SafeProps | { address: string; name: string } }) => any,
) => async (action: {
type: string
payload: string | SafeProps | { name: string; address: string }
}): Promise<any> => {
const handledAction = next(action) const handledAction = next(action)
if (watchedActions.includes(action.type)) { if (watchedActions.includes(action.type)) {
@ -35,8 +46,8 @@ export const safeStorageMiddleware = (store) => (next) => async (action) => {
switch (action.type) { switch (action.type) {
case ADD_OR_UPDATE_SAFE: { case ADD_OR_UPDATE_SAFE: {
const { safe } = action.payload const { safe } = action.payload as SafeProps
safe.owners.forEach((owner) => { safe.owners.forEach((owner: { address: string; name: any }) => {
const checksumEntry = makeAddressBookEntry({ address: checksumAddress(owner.address), name: owner.name }) const checksumEntry = makeAddressBookEntry({ address: checksumAddress(owner.address), name: owner.name })
if (isValidAddressBookName(checksumEntry.name)) { if (isValidAddressBookName(checksumEntry.name)) {
dispatch(addOrUpdateAddressBookEntry(checksumEntry)) dispatch(addOrUpdateAddressBookEntry(checksumEntry))
@ -51,7 +62,7 @@ export const safeStorageMiddleware = (store) => (next) => async (action) => {
break break
} }
case UPDATE_SAFE: { case UPDATE_SAFE: {
const { name, address } = action.payload const { name, address } = action.payload as { name: string; address: string }
if (name) { if (name) {
dispatch(addOrUpdateAddressBookEntry(makeAddressBookEntry({ name, address }))) dispatch(addOrUpdateAddressBookEntry(makeAddressBookEntry({ name, address })))
} }
@ -59,7 +70,7 @@ export const safeStorageMiddleware = (store) => (next) => async (action) => {
} }
case SET_DEFAULT_SAFE: { case SET_DEFAULT_SAFE: {
if (action.payload) { if (action.payload) {
saveDefaultSafe(action.payload) saveDefaultSafe(action.payload as string)
} }
break break
} }

View File

@ -80,16 +80,16 @@ export default handleActions<AppReduxState['safes'], Payloads>(
const safeAddress = safe.address const safeAddress = safe.address
const shouldUpdate = shouldSafeStoreBeUpdated(safe, state.getIn(['safes', safeAddress])) const shouldUpdate = shouldSafeStoreBeUpdated(safe, state.getIn(['safes', safeAddress]))
let loadedViaUrl = safe.loadedViaUrl
if (!state.hasIn(['safes', safeAddress])) {
loadedViaUrl = !safe?.name || safe?.name === LOADED_SAFE_KEY
}
return shouldUpdate return shouldUpdate
? state.updateIn( ? state.updateIn(
['safes', safeAddress], ['safes', safeAddress],
makeSafe({ name: safe?.name || LOADED_SAFE_KEY, address: safeAddress, loadedViaUrl }), // This intermediate value is used as prevSafe if no previous state. Else is not used
makeSafe({
name: safe?.name || LOADED_SAFE_KEY,
address: safeAddress,
loadedViaUrl: !safe?.name || safe?.name === LOADED_SAFE_KEY,
}),
(prevSafe) => updateSafeProps(prevSafe, safe), (prevSafe) => updateSafeProps(prevSafe, safe),
) )
: state : state
@ -106,7 +106,12 @@ export default handleActions<AppReduxState['safes'], Payloads>(
return shouldUpdate return shouldUpdate
? state.updateIn( ? state.updateIn(
['safes', safeAddress], ['safes', safeAddress],
makeSafe({ name: safe?.name || LOADED_SAFE_KEY, address: safeAddress, loadedViaUrl: !!safe?.name }), // This intermediate value is used as prevSafe if no previous state. Else is not used
makeSafe({
name: safe?.name || LOADED_SAFE_KEY,
address: safeAddress,
loadedViaUrl: !safe?.name || safe?.name === LOADED_SAFE_KEY,
}),
(prevSafe) => updateSafeProps(prevSafe, safe), (prevSafe) => updateSafeProps(prevSafe, safe),
) )
: state : state

View File

@ -106,7 +106,7 @@ const ReviewComponent = ({ userAddress, values }: Props): React.ReactElement =>
<Hairline /> <Hairline />
{owners.map((address, index) => ( {owners.map((address, index) => (
<> <>
<Row className={classes.owner}> <Row className={classes.owner} testId={'load-safe-review-owner-name-' + index}>
<Col align="center" xs={12}> <Col align="center" xs={12}>
<EthHashInfo <EthHashInfo
hash={address} hash={address}

View File

@ -1,3 +1,4 @@
import { List } from 'immutable'
import * as React from 'react' import * as React from 'react'
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import { ETHEREUM_NETWORK } from 'src/config/networks/network.d' import { ETHEREUM_NETWORK } from 'src/config/networks/network.d'
@ -13,7 +14,6 @@ import { SAFELIST_ADDRESS } from 'src/routes/routes'
import { buildSafe } from 'src/logic/safe/store/actions/fetchSafe' import { buildSafe } from 'src/logic/safe/store/actions/fetchSafe'
import { history } from 'src/store' import { history } from 'src/store'
import { SafeOwner, SafeRecordProps } from 'src/logic/safe/store/models/safe' import { SafeOwner, SafeRecordProps } from 'src/logic/safe/store/models/safe'
import { List } from 'immutable'
import { checksumAddress } from 'src/utils/checksumAddress' import { checksumAddress } from 'src/utils/checksumAddress'
import { networkSelector, providerNameSelector, userAccountSelector } from 'src/logic/wallets/store/selectors' import { networkSelector, providerNameSelector, userAccountSelector } from 'src/logic/wallets/store/selectors'
import { addOrUpdateSafe } from 'src/logic/safe/store/actions/addOrUpdateSafe' import { addOrUpdateSafe } from 'src/logic/safe/store/actions/addOrUpdateSafe'
@ -26,6 +26,9 @@ export const loadSafe = async (
): Promise<void> => { ): Promise<void> => {
const safeProps = await buildSafe(safeAddress, safeName) const safeProps = await buildSafe(safeAddress, safeName)
safeProps.owners = owners safeProps.owners = owners
// We are manually adding the safe. We enforce this state in case the safe was previously
// accessed by URL
safeProps.loadedViaUrl = false
const storedSafes = (await loadStoredSafes()) || {} const storedSafes = (await loadStoredSafes()) || {}

View File

@ -1,6 +1,8 @@
import { Icon, Link, Text } from '@gnosis.pm/safe-react-components'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import styled from 'styled-components'
import { styles } from './style' import { styles } from './style'
@ -22,8 +24,6 @@ import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
import { UpdateSafeModal } from 'src/routes/safe/components/Settings/UpdateSafeModal' import { UpdateSafeModal } from 'src/routes/safe/components/Settings/UpdateSafeModal'
import { grantedSelector } from 'src/routes/safe/container/selector' import { grantedSelector } from 'src/routes/safe/container/selector'
import { updateSafe } from 'src/logic/safe/store/actions/updateSafe' 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 { import {
latestMasterContractVersionSelector, latestMasterContractVersionSelector,
@ -35,6 +35,7 @@ import {
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics' import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
import { fetchMasterCopies, MasterCopy, MasterCopyDeployer } from 'src/logic/contracts/api/masterCopies' import { fetchMasterCopies, MasterCopy, MasterCopyDeployer } from 'src/logic/contracts/api/masterCopies'
import { getMasterCopyAddressFromProxyAddress } from 'src/logic/contracts/safeContracts' import { getMasterCopyAddressFromProxyAddress } from 'src/logic/contracts/safeContracts'
import { LOADED_SAFE_KEY } from 'src/utils/constants'
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'
@ -70,7 +71,10 @@ const SafeDetails = (): React.ReactElement => {
} }
const handleSubmit = (values) => { const handleSubmit = (values) => {
dispatch(updateSafe({ address: safeAddress, name: values.safeName })) // In case they set a name we assume the safe want to be stored even if it was opened via URL
dispatch(
updateSafe({ address: safeAddress, name: values.safeName, loadedViaUrl: values.safeName === LOADED_SAFE_KEY }),
)
const notification = getNotificationsFromTxType(TX_NOTIFICATION_TYPES.SAFE_NAME_CHANGE_TX) const notification = getNotificationsFromTxType(TX_NOTIFICATION_TYPES.SAFE_NAME_CHANGE_TX)
dispatch(enqueueSnackbar(enhanceSnackbarForAction(notification.afterExecution.noMoreConfirmationsNeeded))) dispatch(enqueueSnackbar(enhanceSnackbarForAction(notification.afterExecution.noMoreConfirmationsNeeded)))

View File

@ -26,7 +26,7 @@ import Row from 'src/components/layout/Row'
import Span from 'src/components/layout/Span' import Span from 'src/components/layout/Span'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
import { grantedSelector } from 'src/routes/safe/container/selector' import { grantedSelector } from 'src/routes/safe/container/selector'
import { safeNeedsUpdateSelector, safeOwnersSelector } from 'src/logic/safe/store/selectors' import { safeLoadedViaUrlSelector, safeNeedsUpdateSelector, safeOwnersSelector } from 'src/logic/safe/store/selectors'
export const OWNERS_SETTINGS_TAB_TEST_ID = 'owner-settings-tab' export const OWNERS_SETTINGS_TAB_TEST_ID = 'owner-settings-tab'
@ -41,6 +41,7 @@ const Settings: React.FC = () => {
const classes = useStyles() const classes = useStyles()
const [state, setState] = useState(INITIAL_STATE) const [state, setState] = useState(INITIAL_STATE)
const owners = useSelector(safeOwnersSelector) const owners = useSelector(safeOwnersSelector)
const isSafeLoadedViaUrl = useSelector(safeLoadedViaUrlSelector)
const needsUpdate = useSelector(safeNeedsUpdateSelector) const needsUpdate = useSelector(safeNeedsUpdateSelector)
const granted = useSelector(grantedSelector) const granted = useSelector(grantedSelector)
const addressBook = useSelector(addressBookSelector) const addressBook = useSelector(addressBookSelector)
@ -66,10 +67,12 @@ const Settings: React.FC = () => {
) : ( ) : (
<> <>
<Row className={classes.message}> <Row className={classes.message}>
<ButtonLink className={classes.removeSafeBtn} color="error" onClick={onShow('RemoveSafe')} size="lg"> {!isSafeLoadedViaUrl && (
<Span className={classes.links}>Remove Safe</Span> <ButtonLink className={classes.removeSafeBtn} color="error" onClick={onShow('RemoveSafe')} size="lg">
<Img alt="Trash Icon" className={classes.removeSafeIcon} src={RemoveSafeIcon} /> <Span className={classes.links}>Remove Safe</Span>
</ButtonLink> <Img alt="Trash Icon" className={classes.removeSafeIcon} src={RemoveSafeIcon} />
</ButtonLink>
)}
<RemoveSafeModal isOpen={showRemoveSafe} onClose={onHide('RemoveSafe')} /> <RemoveSafeModal isOpen={showRemoveSafe} onClose={onHide('RemoveSafe')} />
</Row> </Row>
<Block className={classes.root}> <Block className={classes.root}>