diff --git a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/index.jsx b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/index.jsx new file mode 100644 index 00000000..112dd654 --- /dev/null +++ b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/index.jsx @@ -0,0 +1,151 @@ +// @flow +import React, { useState, useEffect } from 'react' +import { List } from 'immutable' +import { SharedSnackbarConsumer } from '~/components/SharedSnackBar' +import Modal from '~/components/Modal' +import { type Safe } from '~/routes/safe/store/models/safe' +import { type Owner, makeOwner } from '~/routes/safe/store/models/owner' +import { setOwners } from '~/logic/safe/utils' +import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts' +import OwnerForm from './screens/OwnerForm' +import ReviewReplaceOwner from './screens/Review' +import { withStyles } from '@material-ui/core/styles' + +const styles = () => ({ + biggerModalWindow: { + width: '775px', + minHeight: '500px', + position: 'static', + }, +}) + +type Props = { + onClose: () => void, + classes: Object, + isOpen: boolean, + safeAddress: string, + safeName: string, + ownerAddress: string, + ownerName: string, + owners: List, + network: string, + userAddress: string, + createTransaction: Function, +} +type ActiveScreen = 'ownerForm' | 'reviewReplaceOwner' + +const SENTINEL_ADDRESS = '0x0000000000000000000000000000000000000001' + +export const sendReplaceOwner = async ( + values: Object, + safeAddress: string, + ownerAddressToRemove: string, + ownerNameToRemove: string, + owners: List, + openSnackbar: Function, + createTransaction: Function, +) => { + const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress) + const storedOwners = await gnosisSafe.getOwners() + const index = storedOwners.findIndex(ownerAddress => ownerAddress === ownerAddressToRemove) + const prevAddress = index === 0 ? SENTINEL_ADDRESS : storedOwners[index - 1] + const txData = gnosisSafe.contract.methods.swapOwner(prevAddress, ownerAddressToRemove, values.ownerAddress).encodeABI() + const text = `Replace Owner ${ownerNameToRemove} (${ownerAddressToRemove}) with ${values.ownerName} (${values.ownerAddress})` + + const ownersWithoutOldOwner = owners.filter(o => o.address !== ownerAddressToRemove) + const ownersWithNewOwner = ownersWithoutOldOwner.push(makeOwner({ name: values.ownerName, address: values.ownerAddress })) + + const txHash = createTransaction(safeAddress, safeAddress, 0, txData, openSnackbar) + if (txHash) { + setOwners(safeAddress, ownersWithNewOwner) + } +} + +const ReplaceOwner = ({ + onClose, + isOpen, + classes, + safeAddress, + safeName, + ownerAddress, + ownerName, + owners, + network, + userAddress, + createTransaction, +}: Props) => { + const [activeScreen, setActiveScreen] = useState('checkOwner') + const [values, setValues] = useState({}) + + useEffect( + () => () => { + setActiveScreen('checkOwner') + setValues({}) + }, + [isOpen], + ) + + const onClickBack = () => setActiveScreen('checkOwner') + + const ownerSubmitted = (newValues: Object) => { + values.ownerName = newValues.ownerName + values.ownerAddress = newValues.ownerAddress + setValues(values) + setActiveScreen('reviewReplaceOwner') + } + + return ( + + + {({ openSnackbar }) => { + const onReplaceOwner = () => { + onClose() + try { + sendReplaceOwner(values, safeAddress, ownerAddress, ownerName, owners, openSnackbar, createTransaction) + } catch (error) { + // eslint-disable-next-line + console.log('Error while removing an owner ' + error) + } + } + + return ( + + + {activeScreen === 'checkOwner' && ( + + )} + {activeScreen === 'reviewReplaceOwner' && ( + + )} + + + ) + }} + + + ) +} + +export default withStyles(styles)(ReplaceOwner) diff --git a/src/routes/safe/components/Settings/ManageOwners/index.jsx b/src/routes/safe/components/Settings/ManageOwners/index.jsx index bdc38b17..5b60cc24 100644 --- a/src/routes/safe/components/Settings/ManageOwners/index.jsx +++ b/src/routes/safe/components/Settings/ManageOwners/index.jsx @@ -21,6 +21,7 @@ import Hairline from '~/components/layout/Hairline' import Button from '~/components/layout/Button' import AddOwnerModal from './AddOwnerModal' import RemoveOwnerModal from './RemoveOwnerModal' +import ReplaceOwnerModal from './ReplaceOwnerModal' import OwnerAddressTableCell from './OwnerAddressTableCell' import type { Owner } from '~/routes/safe/store/models/owner' import { @@ -60,6 +61,7 @@ class ManageOwners extends React.Component { selectedOwnerName: undefined, showAddOwner: false, showRemoveOwner: false, + showReplaceOwner: false, } onShow = (action: Action, row?: Object) => () => { @@ -92,6 +94,7 @@ class ManageOwners extends React.Component { const { showAddOwner, showRemoveOwner, + showReplaceOwner, selectedOwnerName, selectedOwnerAddress, } = this.state @@ -170,6 +173,18 @@ class ManageOwners extends React.Component { userAddress={userAddress} createTransaction={createTransaction} /> + ) }