refactor: Advanced settings component
This commit is contained in:
parent
ad9ee25b36
commit
cc103c00d7
|
@ -0,0 +1,125 @@
|
||||||
|
import { Button, Text } from '@gnosis.pm/safe-react-components'
|
||||||
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
|
import TableContainer from '@material-ui/core/TableContainer'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import cn from 'classnames'
|
||||||
|
import React from 'react'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
|
import { generateColumns, ModuleAddressColumn, MODULES_TABLE_ADDRESS_ID } from './dataFetcher'
|
||||||
|
import RemoveModuleModal from './RemoveModuleModal'
|
||||||
|
import { styles } from './style'
|
||||||
|
|
||||||
|
import { grantedSelector } from 'src/routes/safe/container/selector'
|
||||||
|
import { ModulePair } from 'src/routes/safe/store/models/safe'
|
||||||
|
import Table from 'src/components/Table'
|
||||||
|
import { TableCell, TableRow } from 'src/components/layout/Table'
|
||||||
|
import Block from 'src/components/layout/Block'
|
||||||
|
import Identicon from 'src/components/Identicon'
|
||||||
|
import Row from 'src/components/layout/Row'
|
||||||
|
|
||||||
|
const REMOVE_MODULE_BTN_TEST_ID = 'remove-module-btn'
|
||||||
|
const MODULES_ROW_TEST_ID = 'owners-row'
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
|
const AddressText = styled(Text)`
|
||||||
|
margin-left: 12px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const TableActionButton = styled(Button)`
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
interface ModulesTableProps {
|
||||||
|
moduleData: ModuleAddressColumn | null
|
||||||
|
}
|
||||||
|
|
||||||
|
const ModulesTable = ({ moduleData }: ModulesTableProps): React.ReactElement => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const columns = generateColumns()
|
||||||
|
const autoColumns = columns.filter(({ custom }) => !custom)
|
||||||
|
|
||||||
|
const granted = useSelector(grantedSelector)
|
||||||
|
|
||||||
|
const [viewRemoveModuleModal, setViewRemoveModuleModal] = React.useState(false)
|
||||||
|
const hideRemoveModuleModal = () => setViewRemoveModuleModal(false)
|
||||||
|
|
||||||
|
const [selectedModule, setSelectedModule] = React.useState(null)
|
||||||
|
const triggerRemoveSelectedModule = (module: ModulePair): void => {
|
||||||
|
setSelectedModule(module)
|
||||||
|
setViewRemoveModuleModal(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TableContainer>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
data={moduleData}
|
||||||
|
defaultFixed
|
||||||
|
defaultOrderBy={MODULES_TABLE_ADDRESS_ID}
|
||||||
|
disablePagination
|
||||||
|
label="Modules"
|
||||||
|
noBorder
|
||||||
|
size={moduleData.length}
|
||||||
|
>
|
||||||
|
{(sortedData) =>
|
||||||
|
sortedData.map((row, index) => (
|
||||||
|
<TableRow
|
||||||
|
className={cn(classes.hide, index >= 3 && index === sortedData.size - 1 && classes.noBorderBottom)}
|
||||||
|
data-testid={MODULES_ROW_TEST_ID}
|
||||||
|
key={index}
|
||||||
|
tabIndex={-1}
|
||||||
|
>
|
||||||
|
{autoColumns.map((column, index) => {
|
||||||
|
const columnId = column.id
|
||||||
|
const rowElement = row[columnId]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment key={`${columnId}-${index}`}>
|
||||||
|
<TableCell align={column.align} component="td" key={columnId}>
|
||||||
|
{columnId === MODULES_TABLE_ADDRESS_ID ? (
|
||||||
|
<Block justify="left">
|
||||||
|
<Identicon address={rowElement[0]} diameter={32} />
|
||||||
|
<AddressText size="lg">{rowElement[0]}</AddressText>
|
||||||
|
</Block>
|
||||||
|
) : (
|
||||||
|
rowElement
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell component="td">
|
||||||
|
<Row align="end" className={classes.actions}>
|
||||||
|
{granted && (
|
||||||
|
<TableActionButton
|
||||||
|
size="md"
|
||||||
|
iconType="delete"
|
||||||
|
color="error"
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => triggerRemoveSelectedModule(rowElement)}
|
||||||
|
data-testid={REMOVE_MODULE_BTN_TEST_ID}
|
||||||
|
>
|
||||||
|
{null}
|
||||||
|
</TableActionButton>
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
|
</TableCell>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
{viewRemoveModuleModal && <RemoveModuleModal onClose={hideRemoveModuleModal} selectedModule={selectedModule} />}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModulesTable
|
|
@ -0,0 +1,144 @@
|
||||||
|
import { Button } 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'
|
||||||
|
import OpenInNew from '@material-ui/icons/OpenInNew'
|
||||||
|
import cn from 'classnames'
|
||||||
|
import { useSnackbar } from 'notistack'
|
||||||
|
import React from 'react'
|
||||||
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
import { styles } from './style'
|
||||||
|
|
||||||
|
import { ModulePair } from 'src/routes/safe/store/models/safe'
|
||||||
|
import { safeParamAddressFromStateSelector } from 'src/routes/safe/store/selectors'
|
||||||
|
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
|
||||||
|
import createTransaction from 'src/routes/safe/store/actions/createTransaction'
|
||||||
|
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
||||||
|
import Modal from 'src/components/Modal'
|
||||||
|
import Row from 'src/components/layout/Row'
|
||||||
|
import Paragraph from 'src/components/layout/Paragraph'
|
||||||
|
import Hairline from 'src/components/layout/Hairline'
|
||||||
|
import Block from 'src/components/layout/Block'
|
||||||
|
import Col from 'src/components/layout/Col'
|
||||||
|
import Identicon from 'src/components/Identicon'
|
||||||
|
import Link from 'src/components/layout/Link'
|
||||||
|
import { getEtherScanLink } from 'src/logic/wallets/getWeb3'
|
||||||
|
import { md, secondary } from 'src/theme/variables'
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
|
const FooterWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
`
|
||||||
|
|
||||||
|
const openIconStyle = {
|
||||||
|
height: md,
|
||||||
|
color: secondary,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RemoveModuleModal {
|
||||||
|
onClose: () => void
|
||||||
|
selectedModule: ModulePair
|
||||||
|
}
|
||||||
|
|
||||||
|
const RemoveModuleModal = ({ onClose, selectedModule }: RemoveModuleModal): React.ReactElement => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const safeAddress = useSelector(safeParamAddressFromStateSelector)
|
||||||
|
|
||||||
|
const { enqueueSnackbar, closeSnackbar } = useSnackbar()
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
|
||||||
|
const removeSelectedModule = async (): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
|
||||||
|
const [module, prevModule] = selectedModule
|
||||||
|
const txData = safeInstance.contract.methods.disableModule(prevModule, module).encodeABI()
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
createTransaction({
|
||||||
|
safeAddress,
|
||||||
|
to: safeAddress,
|
||||||
|
valueInWei: '0',
|
||||||
|
txData,
|
||||||
|
notifiedTransaction: TX_NOTIFICATION_TYPES.SETTINGS_CHANGE_TX,
|
||||||
|
enqueueSnackbar,
|
||||||
|
closeSnackbar,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`failed to remove the module ${selectedModule}`, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
description="Remove the selected Module"
|
||||||
|
handleClose={onClose}
|
||||||
|
paperClassName={classes.modal}
|
||||||
|
title="Remove Module"
|
||||||
|
open
|
||||||
|
>
|
||||||
|
<Row align="center" className={classes.modalHeading} grow>
|
||||||
|
<Paragraph className={classes.modalManage} noMargin weight="bolder">
|
||||||
|
Remove Module
|
||||||
|
</Paragraph>
|
||||||
|
<IconButton disableRipple onClick={onClose}>
|
||||||
|
<Close className={classes.modalClose} />
|
||||||
|
</IconButton>
|
||||||
|
</Row>
|
||||||
|
<Hairline />
|
||||||
|
<Block className={classes.modalContainer}>
|
||||||
|
<Row className={classes.modalOwner}>
|
||||||
|
<Col align="center" xs={1}>
|
||||||
|
<Identicon address={selectedModule[0]} diameter={32} />
|
||||||
|
</Col>
|
||||||
|
<Col xs={11}>
|
||||||
|
<Block className={cn(classes.modalName, classes.modalUserName)}>
|
||||||
|
<Paragraph noMargin size="lg" weight="bolder">
|
||||||
|
{selectedModule[0]}
|
||||||
|
</Paragraph>
|
||||||
|
<Block className={classes.modalUser} justify="center">
|
||||||
|
<Paragraph color="disabled" noMargin size="md">
|
||||||
|
{selectedModule[0]}
|
||||||
|
</Paragraph>
|
||||||
|
<Link
|
||||||
|
className={classes.modalOpen}
|
||||||
|
target="_blank"
|
||||||
|
to={getEtherScanLink('address', selectedModule[0])}
|
||||||
|
>
|
||||||
|
<OpenInNew style={openIconStyle} />
|
||||||
|
</Link>
|
||||||
|
</Block>
|
||||||
|
</Block>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Hairline />
|
||||||
|
<Row className={classes.modalDescription}>
|
||||||
|
<Paragraph noMargin>
|
||||||
|
After removing this module, any feature or app that uses this module might no longer work. If this Safe
|
||||||
|
requires more then one signature, the module removal will have to be confirmed by other owners as well.
|
||||||
|
</Paragraph>
|
||||||
|
</Row>
|
||||||
|
</Block>
|
||||||
|
<Hairline />
|
||||||
|
<Row align="center" className={classes.modalButtonRow}>
|
||||||
|
<FooterWrapper>
|
||||||
|
<Button size="md" color="secondary" onClick={onClose}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button color="error" size="md" variant="contained" onClick={removeSelectedModule}>
|
||||||
|
Remove
|
||||||
|
</Button>
|
||||||
|
</FooterWrapper>
|
||||||
|
</Row>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RemoveModuleModal
|
|
@ -5,9 +5,9 @@ import { ModulePair } from 'src/routes/safe/store/models/safe'
|
||||||
export const MODULES_TABLE_ADDRESS_ID = 'address'
|
export const MODULES_TABLE_ADDRESS_ID = 'address'
|
||||||
export const MODULES_TABLE_ACTIONS_ID = 'actions'
|
export const MODULES_TABLE_ACTIONS_ID = 'actions'
|
||||||
|
|
||||||
export const getModuleData = (
|
export type ModuleAddressColumn = { [MODULES_TABLE_ADDRESS_ID]: ModulePair }[]
|
||||||
modulesList: List<ModulePair> | null,
|
|
||||||
): List<{ [MODULES_TABLE_ADDRESS_ID]: ModulePair }> | undefined => {
|
export const getModuleData = (modulesList: ModulePair[] | null): ModuleAddressColumn | undefined => {
|
||||||
return modulesList?.map((modules) => ({
|
return modulesList?.map((modules) => ({
|
||||||
[MODULES_TABLE_ADDRESS_ID]: modules,
|
[MODULES_TABLE_ADDRESS_ID]: modules,
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -1,51 +1,18 @@
|
||||||
import { Button, Loader, Text, Title, theme } from '@gnosis.pm/safe-react-components'
|
import { Loader, Text, theme, Title } from '@gnosis.pm/safe-react-components'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import TableContainer from '@material-ui/core/TableContainer'
|
|
||||||
import cn from 'classnames'
|
|
||||||
import { useSnackbar } from 'notistack'
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
import { generateColumns, MODULES_TABLE_ADDRESS_ID, getModuleData } from './dataFetcher'
|
|
||||||
import { styles } from './style'
|
|
||||||
|
|
||||||
import Identicon from 'src/components/Identicon'
|
|
||||||
import Block from 'src/components/layout/Block'
|
|
||||||
import Row from 'src/components/layout/Row'
|
|
||||||
import { TableCell, TableRow } from 'src/components/layout/Table'
|
|
||||||
import Table from 'src/components/Table'
|
|
||||||
|
|
||||||
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
|
|
||||||
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
|
||||||
import { grantedSelector } from 'src/routes/safe/container/selector'
|
|
||||||
import createTransaction from 'src/routes/safe/store/actions/createTransaction'
|
|
||||||
import {
|
|
||||||
safeParamAddressFromStateSelector,
|
|
||||||
safeNonceSelector,
|
|
||||||
safeModulesSelector,
|
|
||||||
} from 'src/routes/safe/store/selectors'
|
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import Modal from 'src/components/Modal'
|
|
||||||
import Paragraph from 'src/components/layout/Paragraph'
|
|
||||||
import IconButton from '@material-ui/core/IconButton'
|
|
||||||
import Close from '@material-ui/icons/Close'
|
|
||||||
import Hairline from 'src/components/layout/Hairline'
|
|
||||||
import Col from 'src/components/layout/Col'
|
|
||||||
import Link from 'src/components/layout/Link'
|
|
||||||
import OpenInNew from '@material-ui/icons/OpenInNew'
|
|
||||||
import { getEtherScanLink } from 'src/logic/wallets/getWeb3'
|
|
||||||
import { md, secondary } from 'src/theme/variables'
|
|
||||||
import { ModulePair } from 'src/routes/safe/store/models/safe'
|
|
||||||
|
|
||||||
export const REMOVE_MODULE_BTN_TEST_ID = 'remove-module-btn'
|
import { getModuleData } from './dataFetcher'
|
||||||
export const MODULES_ROW_TEST_ID = 'owners-row'
|
import { styles } from './style'
|
||||||
|
import ModulesTable from './ModulesTable'
|
||||||
|
|
||||||
|
import Block from 'src/components/layout/Block'
|
||||||
|
import { safeModulesSelector, safeNonceSelector } from 'src/routes/safe/store/selectors'
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const AddressText = styled(Text)`
|
|
||||||
margin-left: 12px;
|
|
||||||
`
|
|
||||||
|
|
||||||
const InfoText = styled(Text)`
|
const InfoText = styled(Text)`
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
`
|
`
|
||||||
|
@ -54,69 +21,28 @@ const Bold = styled.strong`
|
||||||
color: ${theme.colors.text};
|
color: ${theme.colors.text};
|
||||||
`
|
`
|
||||||
|
|
||||||
const TableActionButton = styled(Button)`
|
const NoModuleLegend = (): React.ReactElement => (
|
||||||
background-color: transparent;
|
<InfoText color="secondaryLight" size="xl">
|
||||||
|
No modules enabled
|
||||||
|
</InfoText>
|
||||||
|
)
|
||||||
|
|
||||||
&:hover {
|
const LoadingModules = (): React.ReactElement => {
|
||||||
background-color: transparent;
|
const classes = useStyles()
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const FooterWrapper = styled.div`
|
return (
|
||||||
display: flex;
|
<Block className={classes.container}>
|
||||||
justify-content: space-around;
|
<Loader size="md" />
|
||||||
`
|
</Block>
|
||||||
|
)
|
||||||
const openIconStyle = {
|
|
||||||
height: md,
|
|
||||||
color: secondary,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Advanced = (): React.ReactElement => {
|
const Advanced = (): React.ReactElement => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
const columns = generateColumns()
|
|
||||||
const autoColumns = columns.filter(({ custom }) => !custom)
|
|
||||||
|
|
||||||
const safeAddress = useSelector(safeParamAddressFromStateSelector)
|
|
||||||
const nonce = useSelector(safeNonceSelector)
|
const nonce = useSelector(safeNonceSelector)
|
||||||
const granted = useSelector(grantedSelector)
|
|
||||||
const modules = useSelector(safeModulesSelector)
|
const modules = useSelector(safeModulesSelector)
|
||||||
const moduleData = getModuleData(modules) ?? modules
|
const moduleData = getModuleData(modules) ?? null
|
||||||
|
|
||||||
const [viewRemoveModuleModal, setViewRemoveModuleModal] = React.useState(false)
|
|
||||||
const hideRemoveModuleModal = () => setViewRemoveModuleModal(false)
|
|
||||||
|
|
||||||
const [selectedModule, setSelectedModule] = React.useState(null)
|
|
||||||
const triggerRemoveSelectedModule = (module: ModulePair): void => {
|
|
||||||
setSelectedModule(module)
|
|
||||||
setViewRemoveModuleModal(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const { enqueueSnackbar, closeSnackbar } = useSnackbar()
|
|
||||||
const dispatch = useDispatch()
|
|
||||||
|
|
||||||
const removeSelectedModule = async (): Promise<void> => {
|
|
||||||
try {
|
|
||||||
const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
|
|
||||||
const [module, prevModule] = selectedModule
|
|
||||||
const txData = safeInstance.contract.methods.disableModule(prevModule, module).encodeABI()
|
|
||||||
|
|
||||||
dispatch(
|
|
||||||
createTransaction({
|
|
||||||
safeAddress,
|
|
||||||
to: safeAddress,
|
|
||||||
valueInWei: '0',
|
|
||||||
txData,
|
|
||||||
notifiedTransaction: TX_NOTIFICATION_TYPES.SETTINGS_CHANGE_TX,
|
|
||||||
enqueueSnackbar,
|
|
||||||
closeSnackbar,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`failed to remove the module ${selectedModule}`, e.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -151,139 +77,15 @@ const Advanced = (): React.ReactElement => {
|
||||||
</a>
|
</a>
|
||||||
.
|
.
|
||||||
</InfoText>
|
</InfoText>
|
||||||
{moduleData === null ? (
|
|
||||||
<InfoText color="secondaryLight" size="xl">
|
|
||||||
No modules enabled
|
|
||||||
</InfoText>
|
|
||||||
) : moduleData.size === 0 ? (
|
|
||||||
<Block className={classes.container}>
|
|
||||||
<Loader size="md" />
|
|
||||||
</Block>
|
|
||||||
) : (
|
|
||||||
<TableContainer>
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
data={moduleData}
|
|
||||||
defaultFixed
|
|
||||||
defaultOrderBy={MODULES_TABLE_ADDRESS_ID}
|
|
||||||
disablePagination
|
|
||||||
label="Modules"
|
|
||||||
noBorder
|
|
||||||
size={moduleData.size}
|
|
||||||
>
|
|
||||||
{(sortedData) =>
|
|
||||||
sortedData.map((row, index) => (
|
|
||||||
<TableRow
|
|
||||||
className={cn(classes.hide, index >= 3 && index === sortedData.size - 1 && classes.noBorderBottom)}
|
|
||||||
data-testid={MODULES_ROW_TEST_ID}
|
|
||||||
key={index}
|
|
||||||
tabIndex={-1}
|
|
||||||
>
|
|
||||||
{autoColumns.map((column) => {
|
|
||||||
const columnId = column.id
|
|
||||||
const rowElement = row[columnId]
|
|
||||||
|
|
||||||
return (
|
{moduleData === null ? (
|
||||||
<>
|
<NoModuleLegend />
|
||||||
<TableCell align={column.align} component="td" key={columnId}>
|
) : moduleData?.length === 0 ? (
|
||||||
{columnId === MODULES_TABLE_ADDRESS_ID ? (
|
<LoadingModules />
|
||||||
<Block justify="left">
|
) : (
|
||||||
<Identicon address={rowElement[0]} diameter={32} />
|
<ModulesTable moduleData={moduleData} />
|
||||||
<AddressText size="lg">{rowElement[0]}</AddressText>
|
|
||||||
</Block>
|
|
||||||
) : (
|
|
||||||
rowElement
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell component="td">
|
|
||||||
<Row align="end" className={classes.actions}>
|
|
||||||
{granted && (
|
|
||||||
<TableActionButton
|
|
||||||
size="md"
|
|
||||||
iconType="delete"
|
|
||||||
color="error"
|
|
||||||
variant="outlined"
|
|
||||||
onClick={() => triggerRemoveSelectedModule(rowElement)}
|
|
||||||
data-testid={REMOVE_MODULE_BTN_TEST_ID}
|
|
||||||
>
|
|
||||||
{null}
|
|
||||||
</TableActionButton>
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
</TableCell>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</TableRow>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
)}
|
)}
|
||||||
</Block>
|
</Block>
|
||||||
{viewRemoveModuleModal && (
|
|
||||||
<Modal
|
|
||||||
description="Remove the selected Module"
|
|
||||||
handleClose={hideRemoveModuleModal}
|
|
||||||
open={viewRemoveModuleModal}
|
|
||||||
paperClassName={classes.modal}
|
|
||||||
title="Remove Module"
|
|
||||||
>
|
|
||||||
<Row align="center" className={classes.modalHeading} grow>
|
|
||||||
<Paragraph className={classes.modalManage} noMargin weight="bolder">
|
|
||||||
Remove Module
|
|
||||||
</Paragraph>
|
|
||||||
<IconButton disableRipple onClick={hideRemoveModuleModal}>
|
|
||||||
<Close className={classes.modalClose} />
|
|
||||||
</IconButton>
|
|
||||||
</Row>
|
|
||||||
<Hairline />
|
|
||||||
<Block className={classes.modalContainer}>
|
|
||||||
<Row className={classes.modalOwner}>
|
|
||||||
<Col align="center" xs={1}>
|
|
||||||
<Identicon address={selectedModule[0]} diameter={32} />
|
|
||||||
</Col>
|
|
||||||
<Col xs={11}>
|
|
||||||
<Block className={cn(classes.modalName, classes.modalUserName)}>
|
|
||||||
<Paragraph noMargin size="lg" weight="bolder">
|
|
||||||
{selectedModule[0]}
|
|
||||||
</Paragraph>
|
|
||||||
<Block className={classes.modalUser} justify="center">
|
|
||||||
<Paragraph color="disabled" noMargin size="md">
|
|
||||||
{selectedModule[0]}
|
|
||||||
</Paragraph>
|
|
||||||
<Link
|
|
||||||
className={classes.modalOpen}
|
|
||||||
target="_blank"
|
|
||||||
to={getEtherScanLink('address', selectedModule[0])}
|
|
||||||
>
|
|
||||||
<OpenInNew style={openIconStyle} />
|
|
||||||
</Link>
|
|
||||||
</Block>
|
|
||||||
</Block>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Hairline />
|
|
||||||
<Row className={classes.modalDescription}>
|
|
||||||
<Paragraph noMargin>
|
|
||||||
After removing this module, any feature or app that uses this module might no longer work. If this Safe
|
|
||||||
requires more then one signature, the module removal will have to be confirmed by other owners as well.
|
|
||||||
</Paragraph>
|
|
||||||
</Row>
|
|
||||||
</Block>
|
|
||||||
<Hairline />
|
|
||||||
<Row align="center" className={classes.modalButtonRow}>
|
|
||||||
<FooterWrapper>
|
|
||||||
<Button size="md" color="secondary" onClick={hideRemoveModuleModal}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button color="error" size="md" variant="contained" onClick={removeSelectedModule}>
|
|
||||||
Remove
|
|
||||||
</Button>
|
|
||||||
</FooterWrapper>
|
|
||||||
</Row>
|
|
||||||
</Modal>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ export type SafeRecordProps = {
|
||||||
threshold: number
|
threshold: number
|
||||||
ethBalance: string
|
ethBalance: string
|
||||||
owners: List<{ name: string; address: string }>
|
owners: List<{ name: string; address: string }>
|
||||||
modules: List<ModulePair> | null
|
modules: ModulePair[] | null
|
||||||
activeTokens: Set<string>
|
activeTokens: Set<string>
|
||||||
activeAssets: Set<string>
|
activeAssets: Set<string>
|
||||||
blacklistedTokens: Set<string>
|
blacklistedTokens: Set<string>
|
||||||
|
@ -33,7 +33,7 @@ const makeSafe = Record<SafeRecordProps>({
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
ethBalance: '0',
|
ethBalance: '0',
|
||||||
owners: List([]),
|
owners: List([]),
|
||||||
modules: List(),
|
modules: [],
|
||||||
activeTokens: Set(),
|
activeTokens: Set(),
|
||||||
activeAssets: Set(),
|
activeAssets: Set(),
|
||||||
blacklistedTokens: Set(),
|
blacklistedTokens: Set(),
|
||||||
|
|
Loading…
Reference in New Issue