diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx index 8b50ae681f..fa9b73f54b 100644 --- a/src/components/App/index.tsx +++ b/src/components/App/index.tsx @@ -11,7 +11,7 @@ import ErrorIcon from 'src/assets/icons/error.svg' import InfoIcon from 'src/assets/icons/info.svg' import AppLayout from 'src/components/AppLayout' -import SafeListSidebarProvider, { SafeListSidebarContext } from 'src/components/SafeListSidebar' +import { SafeListSidebar, SafeListSidebarContext } from 'src/components/SafeListSidebar' import CookiesBanner from 'src/components/CookiesBanner' import Notifier from 'src/components/Notifier' import Backdrop from 'src/components/layout/Backdrop' @@ -159,9 +159,9 @@ const App: React.FC = ({ children }) => { } const WrapperAppWithSidebar: React.FC = ({ children }) => ( - + {children} - + ) export default WrapperAppWithSidebar diff --git a/src/components/SafeListSidebar/SafeList/AddresWrapper.tsx b/src/components/SafeListSidebar/SafeList/AddressWrapper.tsx similarity index 85% rename from src/components/SafeListSidebar/SafeList/AddresWrapper.tsx rename to src/components/SafeListSidebar/SafeList/AddressWrapper.tsx index 8bf621a3df..f21874aa3e 100644 --- a/src/components/SafeListSidebar/SafeList/AddresWrapper.tsx +++ b/src/components/SafeListSidebar/SafeList/AddressWrapper.tsx @@ -6,9 +6,10 @@ import { sameAddress } from 'src/logic/wallets/ethAddresses' import DefaultBadge from './DefaultBadge' import { SafeRecordProps } from 'src/logic/safe/store/models/safe' import { DefaultSafe } from 'src/routes/safe/store/reducer/types/safe' -import { SetDefaultSafe } from 'src/logic/safe/store/actions/setDefaultSafe' +import setDefaultSafe from 'src/logic/safe/store/actions/setDefaultSafe' import { makeStyles } from '@material-ui/core/styles' import { getNetworkInfo } from 'src/config' +import { useDispatch } from 'react-redux' const StyledButtonLink = styled(ButtonLink)` visibility: hidden; @@ -46,14 +47,18 @@ const useStyles = makeStyles({ type Props = { safe: SafeRecordProps defaultSafe: DefaultSafe - setDefaultSafe: SetDefaultSafe } const { nativeCoin } = getNetworkInfo() export const AddressWrapper = (props: Props): React.ReactElement => { const classes = useStyles() - const { safe, defaultSafe, setDefaultSafe } = props + const { safe, defaultSafe } = props + const dispatch = useDispatch() + + const setDefaultSafeAction = (safeAddress: string) => { + dispatch(setDefaultSafe(safeAddress)) + } return (
@@ -68,7 +73,7 @@ export const AddressWrapper = (props: Props): React.ReactElement => { className="safeListMakeDefaultButton" textSize="sm" onClick={() => { - setDefaultSafe(safe.address) + setDefaultSafeAction(safe.address) }} color="primary" > diff --git a/src/components/SafeListSidebar/SafeList/index.tsx b/src/components/SafeListSidebar/SafeList/index.tsx index 57b60238da..3a664d56a2 100644 --- a/src/components/SafeListSidebar/SafeList/index.tsx +++ b/src/components/SafeListSidebar/SafeList/index.tsx @@ -6,12 +6,11 @@ import * as React from 'react' import styled from 'styled-components' import { SafeRecord } from 'src/logic/safe/store/models/safe' import { DefaultSafe } from 'src/routes/safe/store/reducer/types/safe' -import { SetDefaultSafe } from 'src/logic/safe/store/actions/setDefaultSafe' import Hairline from 'src/components/layout/Hairline' import Link from 'src/components/layout/Link' import { sameAddress } from 'src/logic/wallets/ethAddresses' import { SAFELIST_ADDRESS } from 'src/routes/routes' -import { AddressWrapper } from './AddresWrapper' +import { AddressWrapper } from 'src/components/SafeListSidebar/SafeList/AddressWrapper' export const SIDEBAR_SAFELIST_ROW_TESTID = 'SIDEBAR_SAFELIST_ROW_TESTID' const StyledIcon = styled(Icon)` @@ -46,10 +45,9 @@ type Props = { defaultSafe: DefaultSafe safes: SafeRecord[] onSafeClick: () => void - setDefaultSafe: SetDefaultSafe } -const SafeList = ({ currentSafe, defaultSafe, onSafeClick, safes, setDefaultSafe }: Props): React.ReactElement => { +export const SafeList = ({ currentSafe, defaultSafe, onSafeClick, safes }: Props): React.ReactElement => { const classes = useStyles() return ( @@ -67,7 +65,7 @@ const SafeList = ({ currentSafe, defaultSafe, onSafeClick, safes, setDefaultSafe ) : (
placeholder
)} - + @@ -76,5 +74,3 @@ const SafeList = ({ currentSafe, defaultSafe, onSafeClick, safes, setDefaultSafe ) } - -export default SafeList diff --git a/src/components/SafeListSidebar/index.tsx b/src/components/SafeListSidebar/index.tsx index 8aeeb77b3f..9846f170c0 100644 --- a/src/components/SafeListSidebar/index.tsx +++ b/src/components/SafeListSidebar/index.tsx @@ -1,10 +1,10 @@ -import React, { useEffect, useMemo, useState } from 'react' +import React, { useEffect, useMemo, useState, ReactElement } from 'react' import Drawer from '@material-ui/core/Drawer' import SearchIcon from '@material-ui/icons/Search' import SearchBar from 'material-ui-search-bar' -import { connect } from 'react-redux' +import { useSelector } from 'react-redux' -import SafeList from './SafeList' +import { SafeList } from './SafeList' import { sortedSafeListSelector } from './selectors' import useSidebarStyles from './style' @@ -15,11 +15,9 @@ import Hairline from 'src/components/layout/Hairline' 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' export const SafeListSidebarContext = React.createContext({ isOpen: false, @@ -34,9 +32,17 @@ const filterBy = (filter, safes) => safe.name.toLowerCase().includes(filter.toLowerCase()), ) -const SafeListSidebar = ({ children, currentSafe, defaultSafe, safes, setDefaultSafeAction }) => { +type Props = { + children: ReactElement +} + +export const SafeListSidebar = ({ children }: Props): ReactElement => { const [isOpen, setIsOpen] = useState(false) const [filter, setFilter] = useState('') + const safes = useSelector(sortedSafeListSelector) + const defaultSafe = useSelector(defaultSafeSelector) + const currentSafe = useSelector(safeParamAddressFromStateSelector) + const classes = useSidebarStyles() const { trackEvent } = useAnalytics() @@ -118,19 +124,9 @@ const SafeListSidebar = ({ children, currentSafe, defaultSafe, safes, setDefault defaultSafe={defaultSafe} onSafeClick={toggleSidebar} safes={filteredSafes} - setDefaultSafe={setDefaultSafeAction} /> {children} ) } - -export default connect( - (state: AppReduxState) => ({ - safes: sortedSafeListSelector(state), - defaultSafe: defaultSafeSelector(state), - currentSafe: safeParamAddressFromStateSelector(state), - }), - { setDefaultSafeAction: setDefaultSafe }, -)(SafeListSidebar) diff --git a/src/logic/safe/store/actions/removeLocalSafe.ts b/src/logic/safe/store/actions/removeLocalSafe.ts new file mode 100644 index 0000000000..64bd42847d --- /dev/null +++ b/src/logic/safe/store/actions/removeLocalSafe.ts @@ -0,0 +1,11 @@ +import { Action, Dispatch } from 'redux' +import { loadStoredSafes } from 'src/logic/safe/utils' +import removeSafe from 'src/logic/safe/store/actions/removeSafe' + +export const removeLocalSafe = (safeAddress: string) => async (dispatch: Dispatch): Promise => { + const storedSafes = await loadStoredSafes() + if (storedSafes) { + delete storedSafes[safeAddress] + } + dispatch(removeSafe(safeAddress)) +} diff --git a/src/logic/safe/store/reducer/safe.ts b/src/logic/safe/store/reducer/safe.ts index 5381b38a79..b5997f68f9 100644 --- a/src/logic/safe/store/reducer/safe.ts +++ b/src/logic/safe/store/reducer/safe.ts @@ -126,7 +126,14 @@ export default handleActions( [REMOVE_SAFE]: (state: SafeReducerMap, action) => { const safeAddress = action.payload - return state.deleteIn(['safes', safeAddress]) + const currentDefaultSafe = state.get('defaultSafe') + + let newState = state.deleteIn(['safes', safeAddress]) + if (sameAddress(safeAddress, currentDefaultSafe)) { + newState = newState.set('defaultSafe', DEFAULT_SAFE_INITIAL_STATE) + } + + return newState }, [ADD_SAFE_OWNER]: (state: SafeReducerMap, action) => { const { ownerAddress, ownerName, safeAddress } = action.payload diff --git a/src/routes/open/components/Layout.tsx b/src/routes/open/components/Layout.tsx index 45385f05e4..2bce38460e 100644 --- a/src/routes/open/components/Layout.tsx +++ b/src/routes/open/components/Layout.tsx @@ -17,7 +17,7 @@ import { getOwnerAddressBy, getOwnerNameBy, } from 'src/routes/open/components/fields' -import Welcome from 'src/routes/welcome/components/Layout' +import { WelcomeLayout } from 'src/routes/welcome/components/index' import { history } from 'src/store' import { secondary, sm } from 'src/theme/variables' import { networkSelector, providerNameSelector, userAccountSelector } from 'src/logic/wallets/store/selectors' @@ -138,7 +138,7 @@ export const Layout = (props: LayoutProps): React.ReactElement => { ) : ( - + )} ) diff --git a/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.tsx b/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.tsx index 26157c4b2d..914da3c25c 100644 --- a/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.tsx +++ b/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.tsx @@ -40,17 +40,18 @@ type OwnProps = { selectedOwnerName: string } -const EditOwnerComponent = ({ isOpen, onClose, ownerAddress, selectedOwnerName }: OwnProps): React.ReactElement => { +export const EditOwnerModal = ({ isOpen, onClose, ownerAddress, selectedOwnerName }: OwnProps): React.ReactElement => { const classes = useStyles() const dispatch = useDispatch() - const safeAddress = useSelector(safeParamAddressFromStateSelector) as string - const handleSubmit = (values) => { - const { ownerName } = values - - dispatch(editSafeOwner({ safeAddress, ownerAddress, ownerName })) - dispatch(addOrUpdateAddressBookEntry(makeAddressBookEntry({ address: ownerAddress, name: ownerName }))) - dispatch(enqueueSnackbar(NOTIFICATIONS.OWNER_NAME_CHANGE_EXECUTED_MSG)) + const safeAddress = useSelector(safeParamAddressFromStateSelector) + const handleSubmit = ({ ownerName }: { ownerName: string }): void => { + // Update the value only if the ownerName really changed + if (ownerName !== selectedOwnerName) { + dispatch(editSafeOwner({ safeAddress, ownerAddress, ownerName })) + dispatch(addOrUpdateAddressBookEntry(makeAddressBookEntry({ address: ownerAddress, name: ownerName }))) + dispatch(enqueueSnackbar(NOTIFICATIONS.OWNER_NAME_CHANGE_EXECUTED_MSG)) + } onClose() } @@ -71,54 +72,56 @@ const EditOwnerComponent = ({ isOpen, onClose, ownerAddress, selectedOwnerName } - - {() => ( - <> - - - - - - - - - {ownerAddress} - - - - + + {(...args) => { + const pristine = args[2].pristine + return ( + <> + + + + + + + + + {ownerAddress} + + + + + + + + + + - - - - - - - - )} + + ) + }} ) } - -export default EditOwnerComponent diff --git a/src/routes/safe/components/Settings/ManageOwners/index.tsx b/src/routes/safe/components/Settings/ManageOwners/index.tsx index be55767adb..df255ca53e 100644 --- a/src/routes/safe/components/Settings/ManageOwners/index.tsx +++ b/src/routes/safe/components/Settings/ManageOwners/index.tsx @@ -9,7 +9,7 @@ import { List } from 'immutable' import RemoveOwnerIcon from '../assets/icons/bin.svg' import AddOwnerModal from './AddOwnerModal' -import EditOwnerModal from './EditOwnerModal' +import { EditOwnerModal } from './EditOwnerModal' import OwnerAddressTableCell from './OwnerAddressTableCell' import RemoveOwnerModal from './RemoveOwnerModal' import ReplaceOwnerModal from './ReplaceOwnerModal' diff --git a/src/routes/safe/components/Settings/RemoveSafeModal/index.tsx b/src/routes/safe/components/Settings/RemoveSafeModal/index.tsx index 5524ecd31d..4d2f8b728a 100644 --- a/src/routes/safe/components/Settings/RemoveSafeModal/index.tsx +++ b/src/routes/safe/components/Settings/RemoveSafeModal/index.tsx @@ -18,9 +18,16 @@ import Hairline from 'src/components/layout/Hairline' import Link from 'src/components/layout/Link' import Paragraph from 'src/components/layout/Paragraph' import Row from 'src/components/layout/Row' -import removeSafe from 'src/logic/safe/store/actions/removeSafe' -import { safeNameSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors' +import { + defaultSafeSelector, + safeNameSelector, + safeParamAddressFromStateSelector, +} from 'src/logic/safe/store/selectors' import { md, secondary } from 'src/theme/variables' +import { WELCOME_ADDRESS } from 'src/routes/routes' +import { removeLocalSafe } from 'src/logic/safe/store/actions/removeLocalSafe' +import { sameAddress } from 'src/logic/wallets/ethAddresses' +import { saveDefaultSafe } from 'src/logic/safe/utils' const openIconStyle = { height: md, @@ -29,14 +36,33 @@ const openIconStyle = { const useStyles = makeStyles(styles) -const RemoveSafeComponent = ({ isOpen, onClose }) => { +type RemoveSafeModalProps = { + isOpen: boolean + onClose: () => void +} + +export const RemoveSafeModal = ({ isOpen, onClose }: RemoveSafeModalProps): React.ReactElement => { const classes = useStyles() - const safeAddress = useSelector(safeParamAddressFromStateSelector) as string + const safeAddress = useSelector(safeParamAddressFromStateSelector) const safeName = useSelector(safeNameSelector) + const defaultSafe = useSelector(defaultSafeSelector) const dispatch = useDispatch() const explorerInfo = getExplorerInfo(safeAddress) const { url } = explorerInfo() + const onRemoveSafeHandler = async () => { + await dispatch(removeLocalSafe(safeAddress)) + if (sameAddress(safeAddress, defaultSafe)) { + await saveDefaultSafe('') + } + + onClose() + // using native redirect in order to avoid problems in several components + // trying to access references of the removed safe. + const relativePath = window.location.href.split('/#/')[0] + window.location.href = `${relativePath}/#/${WELCOME_ADDRESS}` + } + return ( {