diff --git a/src/components/Table/TableHead.tsx b/src/components/Table/TableHead.tsx index 6b948ddf..6d341fa6 100644 --- a/src/components/Table/TableHead.tsx +++ b/src/components/Table/TableHead.tsx @@ -4,7 +4,11 @@ import TableRow from '@material-ui/core/TableRow' import TableSortLabel from '@material-ui/core/TableSortLabel' import * as React from 'react' -export const cellWidth = (width) => { +interface CellWidth { + maxWidth: string +} + +export const cellWidth = (width: string | number): CellWidth | undefined => { if (!width) { return undefined } diff --git a/src/logic/wallets/getWeb3.ts b/src/logic/wallets/getWeb3.ts index 24549816..6747e51c 100644 --- a/src/logic/wallets/getWeb3.ts +++ b/src/logic/wallets/getWeb3.ts @@ -32,17 +32,11 @@ export const WALLET_PROVIDER = { } export const ETHEREUM_NETWORK_IDS = { - // $FlowFixMe 1: ETHEREUM_NETWORK.MAINNET, - // $FlowFixMe 2: ETHEREUM_NETWORK.MORDEN, - // $FlowFixMe 3: ETHEREUM_NETWORK.ROPSTEN, - // $FlowFixMe 4: ETHEREUM_NETWORK.RINKEBY, - // $FlowFixMe 5: ETHEREUM_NETWORK.GOERLI, - // $FlowFixMe 42: ETHEREUM_NETWORK.KOVAN, } diff --git a/src/logic/wallets/store/reducer/provider.ts b/src/logic/wallets/store/reducer/provider.ts index b1386419..0bba816e 100644 --- a/src/logic/wallets/store/reducer/provider.ts +++ b/src/logic/wallets/store/reducer/provider.ts @@ -2,14 +2,15 @@ import { handleActions } from 'redux-actions' import { ADD_PROVIDER } from 'src/logic/wallets/store/actions/addProvider' import { REMOVE_PROVIDER } from 'src/logic/wallets/store/actions/removeProvider' -import { makeProvider } from 'src/logic/wallets/store/model/provider' +import { makeProvider, ProviderRecord, ProviderProps } from 'src/logic/wallets/store/model/provider' export const PROVIDER_REDUCER_ID = 'providers' export default handleActions( { - [ADD_PROVIDER]: (state, { payload }) => makeProvider(payload), - [REMOVE_PROVIDER]: () => makeProvider(), + [ADD_PROVIDER]: (state: { providers: ProviderProps }, { payload }: { payload: ProviderProps }): ProviderRecord => + makeProvider(payload), + [REMOVE_PROVIDER]: (): ProviderRecord => makeProvider(), }, makeProvider(), ) diff --git a/src/logic/wallets/store/selectors/index.ts b/src/logic/wallets/store/selectors/index.ts index 310fada3..ff19254d 100644 --- a/src/logic/wallets/store/selectors/index.ts +++ b/src/logic/wallets/store/selectors/index.ts @@ -2,28 +2,30 @@ import { createSelector } from 'reselect' import { ETHEREUM_NETWORK, ETHEREUM_NETWORK_IDS } from 'src/logic/wallets/getWeb3' import { PROVIDER_REDUCER_ID } from 'src/logic/wallets/store/reducer/provider' +import { GnosisState } from 'src/store' +import { ProviderRecord } from '../model/provider' -export const providerSelector = (state) => state[PROVIDER_REDUCER_ID] +export const providerSelector = (state: GnosisState): ProviderRecord => state[PROVIDER_REDUCER_ID] -export const userAccountSelector = createSelector(providerSelector, (provider) => { +export const userAccountSelector = createSelector(providerSelector, (provider: ProviderRecord): string => { const account = provider.get('account') - return account || '' }) -export const providerNameSelector = createSelector(providerSelector, (provider) => { +export const providerNameSelector = createSelector(providerSelector, (provider: ProviderRecord): string | undefined => { const name = provider.get('name') - return name ? name.toLowerCase() : undefined }) -export const networkSelector = createSelector(providerSelector, (provider) => { +export const networkSelector = createSelector(providerSelector, (provider: ProviderRecord): string => { const networkId = provider.get('network') - const network = ETHEREUM_NETWORK_IDS[networkId] || ETHEREUM_NETWORK.UNKNOWN - - return network + return ETHEREUM_NETWORK_IDS[networkId] || ETHEREUM_NETWORK.UNKNOWN }) -export const loadedSelector = createSelector(providerSelector, (provider) => provider.get('loaded')) +export const loadedSelector = createSelector(providerSelector, (provider: ProviderRecord): boolean => + provider.get('loaded'), +) -export const availableSelector = createSelector(providerSelector, (provider) => provider.get('available')) +export const availableSelector = createSelector(providerSelector, (provider: ProviderRecord): boolean => + provider.get('available'), +) diff --git a/src/routes/safe/components/Settings/Advanced/dataFetcher.ts b/src/routes/safe/components/Settings/Advanced/dataFetcher.ts index 01117650..bbf8cf3c 100644 --- a/src/routes/safe/components/Settings/Advanced/dataFetcher.ts +++ b/src/routes/safe/components/Settings/Advanced/dataFetcher.ts @@ -10,30 +10,31 @@ export const getModuleData = (modules: Set): List<{ [MODULES_TABLE_ADDRE } interface TableColumn { - id: string - order: boolean - disablePadding: boolean - label: string + align?: 'inherit' | 'left' | 'center' | 'right' | 'justify' custom: boolean - align?: string + disablePadding: boolean + id: string + label: string + order: boolean + width?: number } export const generateColumns = (): List => { const addressColumn: TableColumn = { - id: MODULES_TABLE_ADDRESS_ID, - order: false, - disablePadding: false, - label: 'Address', - custom: false, align: 'left', + custom: false, + disablePadding: false, + id: MODULES_TABLE_ADDRESS_ID, + label: 'Address', + order: false, } const actionsColumn: TableColumn = { - id: MODULES_TABLE_ACTIONS_ID, - order: false, - disablePadding: false, - label: '', custom: true, + disablePadding: false, + id: MODULES_TABLE_ACTIONS_ID, + label: '', + order: false, } return List([addressColumn, actionsColumn]) diff --git a/src/routes/safe/components/Settings/Advanced/index.tsx b/src/routes/safe/components/Settings/Advanced/index.tsx index 13473375..f80b8a86 100644 --- a/src/routes/safe/components/Settings/Advanced/index.tsx +++ b/src/routes/safe/components/Settings/Advanced/index.tsx @@ -1,101 +1,86 @@ -import { Set } from 'immutable' +import { GenericModal, ModalFooterConfirmation } from '@gnosis.pm/safe-react-components' import { makeStyles } from '@material-ui/core/styles' -// import { useSnackbar } from 'notistack' -import React, { useState } from 'react' -import { /*useDispatch, */ useSelector } from 'react-redux' +import TableContainer from '@material-ui/core/TableContainer' import cn from 'classnames' +import { Set } from 'immutable' +import { useSnackbar } from 'notistack' +import React from 'react' +import { useDispatch, useSelector } from 'react-redux' -// import ChangeThreshold from './ChangeThreshold' +import { generateColumns, MODULES_TABLE_ADDRESS_ID, getModuleData } from './dataFetcher' import { styles } from './style' +import BinIcon from '../assets/icons/bin.svg' -import Modal from 'src/components/Modal' +import DividerLine from 'src/components/DividerLine' +import Identicon from 'src/components/Identicon' import Block from 'src/components/layout/Block' import Bold from 'src/components/layout/Bold' -// import Button from 'src/components/layout/Button' import Heading from 'src/components/layout/Heading' +import Img from 'src/components/layout/Img' import Paragraph from 'src/components/layout/Paragraph' -// import Row from 'src/components/layout/Row' -// 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 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 { - // safeOwnersSelector, - // safeParamAddressFromStateSelector, - // safeThresholdSelector, + safeParamAddressFromStateSelector, safeNonceSelector, safeModulesSelector, } from 'src/routes/safe/store/selectors' -import DividerLine from 'src/components/DividerLine' -import TableContainer from '@material-ui/core/TableContainer' -import Table from '../../../../../components/Table' -import TableRow from '@material-ui/core/TableRow' -import TableCell from '@material-ui/core/TableCell' -import { cellWidth } from '../../../../../components/Table/TableHead' -import OwnerAddressTableCell from '../ManageOwners/OwnerAddressTableCell' -import Row from '../../../../../components/layout/Row' -import Img from '../../../../../components/layout/Img' -// import RenameOwnerIcon from '../ManageOwners/assets/icons/rename-owner.svg' -// import ReplaceOwnerIcon from '../ManageOwners/assets/icons/replace-owner.svg' -import RemoveOwnerIcon from '../assets/icons/bin.svg' -import { generateColumns, MODULES_TABLE_ADDRESS_ID, getModuleData } from './dataFetcher' -import { grantedSelector } from '../../../container/selector' -// import RemoveOwnerModal from '../ManageOwners/RemoveOwnerModal' -// import { getOwnersWithNameFromAddressBook } from '../../../../../logic/addressBook/utils' -// import { getOwnerData } from '../ManageOwners/dataFetcher' export const REMOVE_MODULE_BTN_TEST_ID = 'remove-module-btn' export const MODULES_ROW_TEST_ID = 'owners-row' const useStyles = makeStyles(styles) -const useToggle = (initialOn = false) => { - const [on, setOn] = useState(initialOn) - const toggle = () => setOn(!on) - - return { on, toggle } -} - const Advanced: React.FC = () => { const classes = useStyles() + const columns = generateColumns() const autoColumns = columns.filter(({ custom }) => !custom) - // const { enqueueSnackbar, closeSnackbar } = useSnackbar() - // const dispatch = useDispatch() - - const { on, toggle } = useToggle() - + const safeAddress = useSelector(safeParamAddressFromStateSelector) const nonce = useSelector(safeNonceSelector) const granted = useSelector(grantedSelector) const modules = useSelector(safeModulesSelector) - console.log(modules) const moduleData = getModuleData(Set(modules)) - // const ownersAdbk = getOwnersWithNameFromAddressBook(addressBook, owners) - // const ownerData = getOwnerData(ownersAdbk) - // const safeAddress = useSelector(safeParamAddressFromStateSelector) - // const owners = useSelector(safeOwnersSelector) + const [viewRemoveModuleModal, setViewRemoveModuleModal] = React.useState(false) + const hideRemoveModuleModal = () => setViewRemoveModuleModal(false) - // const onChangeThreshold = async (newThreshold) => { - // const safeInstance = await getGnosisSafeInstanceAt(safeAddress) - // const txData = safeInstance.contract.methods.changeThreshold(newThreshold).encodeABI() - // - // dispatch( - // createTransaction({ - // safeAddress, - // to: safeAddress, - // valueInWei: 0, - // txData, - // notifiedTransaction: TX_NOTIFICATION_TYPES.SETTINGS_CHANGE_TX, - // enqueueSnackbar, - // closeSnackbar, - // } as any), - // ) - // } + const [selectedModule, setSelectedModule] = React.useState(null) + const triggerRemoveSelectedModule = (moduleAddress: string): void => { + setSelectedModule(moduleAddress) + setViewRemoveModuleModal(true) + } + + const { enqueueSnackbar, closeSnackbar } = useSnackbar() + const dispatch = useDispatch() + + const removeSelectedModule = async (): Promise => { + const safeInstance = await getGnosisSafeInstanceAt(safeAddress) + const txData = safeInstance.contract.methods.disableModule(selectedModule).encodeABI() + + dispatch( + createTransaction({ + safeAddress, + to: safeAddress, + valueInWei: '0', + txData, + notifiedTransaction: TX_NOTIFICATION_TYPES.SETTINGS_CHANGE_TX, + enqueueSnackbar, + closeSnackbar, + }), + ) + } return ( <> + {/* Nonce */} Safe Nonce @@ -107,6 +92,8 @@ const Advanced: React.FC = () => { + + {/* Modules */} Safe Modules @@ -145,23 +132,31 @@ const Advanced: React.FC = () => { key={index} tabIndex={-1} > - {autoColumns.map((column: any) => ( - - {column.id === MODULES_TABLE_ADDRESS_ID ? ( - - ) : ( - row[column.id] - )} - - ))} + {autoColumns.map((column) => { + const columnId = column.id + const rowElement = row[columnId] + + return ( + + {columnId === MODULES_TABLE_ADDRESS_ID ? ( + + + {rowElement} + + ) : ( + rowElement + )} + + ) + })} {granted && ( Remove module )} @@ -174,21 +169,21 @@ const Advanced: React.FC = () => { )} - {/**/} - - {/**/} - + {viewRemoveModuleModal && ( + This is the body} + footer={ + + } + /> + )} ) } diff --git a/src/routes/safe/store/actions/createTransaction.ts b/src/routes/safe/store/actions/createTransaction.ts index a6cb1b57..d5121aaa 100644 --- a/src/routes/safe/store/actions/createTransaction.ts +++ b/src/routes/safe/store/actions/createTransaction.ts @@ -1,5 +1,6 @@ import { push } from 'connected-react-router' import { List, Map } from 'immutable' +import { WithSnackbarProps } from 'notistack' import { batch } from 'react-redux' import semverSatisfies from 'semver/functions/satisfies' @@ -35,6 +36,8 @@ import { makeConfirmation } from '../models/confirmation' import fetchTransactions from './transactions/fetchTransactions' import { safeTransactionsSelector } from 'src/routes/safe/store/selectors' import { TransactionStatus, TxArgs } from 'src/routes/safe/store/models/types/transaction' +import { Dispatch } from 'redux' +import { GnosisState } from 'src/store' export const removeTxFromStore = (tx, safeAddress, dispatch, state) => { if (tx.isCancellationTx) { @@ -79,6 +82,18 @@ export const storeTx = async (tx, safeAddress, dispatch, state) => { } } +interface CreateTransaction extends WithSnackbarProps { + navigateToTransactionsTab?: boolean + notifiedTransaction: string + operation?: number + origin?: string + safeAddress: string + to: string + txData: string + txNonce?: number | string + valueInWei: string +} + const createTransaction = ({ safeAddress, to, @@ -91,7 +106,7 @@ const createTransaction = ({ operation = CALL, navigateToTransactionsTab = true, origin = null, -}) => async (dispatch, getState) => { +}: CreateTransaction) => async (dispatch: Dispatch, getState: () => GnosisState): Promise => { const state = getState() if (navigateToTransactionsTab) { diff --git a/src/routes/safe/store/selectors/index.ts b/src/routes/safe/store/selectors/index.ts index 5a90552d..e97ed981 100644 --- a/src/routes/safe/store/selectors/index.ts +++ b/src/routes/safe/store/selectors/index.ts @@ -181,7 +181,7 @@ const baseSafe = makeSafe() export const safeFieldSelector = (field: keyof SafeRecordProps) => ( safe: SafeRecord, -): SafeRecord[keyof SafeRecordProps] => safe.get(field, baseSafe.get(field)) +): SafeRecord[keyof SafeRecordProps] | null => (safe ? safe.get(field, baseSafe.get(field)) : null) export const safeNameSelector = createSelector(safeSelector, safeFieldSelector('name')) diff --git a/src/store/index.ts b/src/store/index.ts index 8574ecc4..633c267a 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,6 +1,6 @@ import { connectRouter, routerMiddleware } from 'connected-react-router' import { createHashHistory } from 'history' -import { applyMiddleware, combineReducers, compose, createStore } from 'redux' +import { applyMiddleware, combineReducers, compose, createStore, CombinedState } from 'redux' import thunk from 'redux-thunk' import addressBookMiddleware from 'src/logic/addressBook/store/middleware/addressBookMiddleware' @@ -29,6 +29,8 @@ import incomingTransactions, { } from 'src/routes/safe/store/reducer/incomingTransactions' import safe, { SAFE_REDUCER_ID } from 'src/routes/safe/store/reducer/safe' import transactions, { TRANSACTIONS_REDUCER_ID } from 'src/routes/safe/store/reducer/transactions' +import { ProviderRecord } from '../logic/wallets/store/model/provider' +import { SafeRecordProps } from '../routes/safe/store/models/safe' export const history = createHashHistory({ hashType: 'slash' }) @@ -63,6 +65,22 @@ const reducers = combineReducers({ [CURRENT_SESSION_REDUCER_ID]: currentSession, }) +export type GnosisState = CombinedState<{ + [PROVIDER_REDUCER_ID]: ProviderRecord + [SAFE_REDUCER_ID]: SafeRecordProps + // [NFT_ASSETS_REDUCER_ID]: nftAssetReducer, + // [NFT_TOKENS_REDUCER_ID]: nftTokensReducer, + // [TOKEN_REDUCER_ID]: tokens, + // [TRANSACTIONS_REDUCER_ID]: transactions, + // [CANCELLATION_TRANSACTIONS_REDUCER_ID]: cancellationTransactions, + // [INCOMING_TRANSACTIONS_REDUCER_ID]: incomingTransactions, + // [NOTIFICATIONS_REDUCER_ID]: notifications, + // [CURRENCY_VALUES_KEY]: currencyValues, + // [COOKIES_REDUCER_ID]: cookies, + // [ADDRESS_BOOK_REDUCER_ID]: addressBook, + // [CURRENT_SESSION_REDUCER_ID]: currentSession, +}> + export const store: any = createStore(reducers, finalCreateStore) export const aNewStore = (localState?: any) => createStore(reducers, localState, finalCreateStore)