mirror of
https://github.com/status-im/safe-react.git
synced 2025-01-12 02:54:09 +00:00
Merge branch 'development' into release/v2.15.0
This commit is contained in:
commit
1d3d61d812
@ -130,10 +130,13 @@ const SafeHeader = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Network */}
|
||||
<StyledTextLabel size="sm" networkInfo={networkInfo}>
|
||||
{networkInfo.label}
|
||||
</StyledTextLabel>
|
||||
|
||||
<Container>
|
||||
{/* Identicon */}
|
||||
<IdenticonContainer>
|
||||
<FlexSpacer />
|
||||
<Identicon address={address} size="lg" />
|
||||
@ -142,6 +145,7 @@ const SafeHeader = ({
|
||||
</UnStyledButton>
|
||||
</IdenticonContainer>
|
||||
|
||||
{/* SafeInfo */}
|
||||
<Text size="xl">{safeName}</Text>
|
||||
<StyledEthHashInfo hash={address} shortenHash={4} textSize="sm" />
|
||||
<IconContainer>
|
||||
|
@ -16,7 +16,7 @@ const HelpContainer = styled.div`
|
||||
const HelpCenterLink = styled.a`
|
||||
height: 30px;
|
||||
width: 166px;
|
||||
padding: 6px 0 0 16px;
|
||||
padding: 8px 0 8px 16px;
|
||||
margin: 14px 0px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
|
@ -6,53 +6,62 @@ import Header from './Header'
|
||||
import Footer from './Footer'
|
||||
import Sidebar from './Sidebar'
|
||||
|
||||
const Grid = styled.div`
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
const Container = styled.div`
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
background-color: ${({ theme }) => theme.colors.background};
|
||||
display: grid;
|
||||
grid-template-columns: 200px 1fr;
|
||||
grid-template-rows: 54px 1fr;
|
||||
grid-template-areas:
|
||||
'topbar topbar'
|
||||
'sidebar body';
|
||||
`
|
||||
|
||||
const GridTopbarWrapper = styled.nav`
|
||||
const HeaderWrapper = styled.nav`
|
||||
height: 54px;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
|
||||
background-color: white;
|
||||
box-shadow: 0 2px 4px 0 rgba(212, 212, 211, 0.59);
|
||||
border-bottom: 2px solid ${({ theme }) => theme.colors.separator};
|
||||
z-index: 999;
|
||||
grid-area: topbar;
|
||||
box-shadow: 0 0 4px 0 rgba(212, 212, 211, 0.59);
|
||||
`
|
||||
|
||||
const GridSidebarWrapper = styled.aside`
|
||||
width: 200px;
|
||||
padding: 62px 8px 0 8px;
|
||||
const BodyWrapper = styled.div`
|
||||
height: calc(100% - 54px);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
`
|
||||
|
||||
const SidebarWrapper = styled.aside`
|
||||
height: 100%;
|
||||
width: 200px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 1;
|
||||
|
||||
padding: 8px;
|
||||
background-color: ${({ theme }) => theme.colors.white};
|
||||
border-right: 2px solid ${({ theme }) => theme.colors.separator};
|
||||
`
|
||||
|
||||
const ContentWrapper = styled.section`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
grid-area: sidebar;
|
||||
`
|
||||
overflow-x: auto;
|
||||
|
||||
const GridBodyWrapper = styled.section`
|
||||
margin: 0 16px 0 16px;
|
||||
grid-area: body;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-content: stretch;
|
||||
`
|
||||
padding: 0 16px;
|
||||
|
||||
export const BodyWrapper = styled.div`
|
||||
flex: 1 100%;
|
||||
`
|
||||
> :nth-child(1) {
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
export const FooterWrapper = styled.footer`
|
||||
margin: 0 16px;
|
||||
> :nth-child(2) {
|
||||
width: 100%;
|
||||
height: 59px;
|
||||
}
|
||||
`
|
||||
|
||||
type Props = {
|
||||
@ -77,29 +86,29 @@ const Layout: React.FC<Props> = ({
|
||||
children,
|
||||
sidebarItems,
|
||||
}): React.ReactElement => (
|
||||
<Grid>
|
||||
<GridTopbarWrapper>
|
||||
<Container>
|
||||
<HeaderWrapper>
|
||||
<Header />
|
||||
</GridTopbarWrapper>
|
||||
<GridSidebarWrapper>
|
||||
<Sidebar
|
||||
items={sidebarItems}
|
||||
safeAddress={safeAddress}
|
||||
safeName={safeName}
|
||||
balance={balance}
|
||||
granted={granted}
|
||||
onToggleSafeList={onToggleSafeList}
|
||||
onReceiveClick={onReceiveClick}
|
||||
onNewTransactionClick={onNewTransactionClick}
|
||||
/>
|
||||
</GridSidebarWrapper>
|
||||
<GridBodyWrapper>
|
||||
<BodyWrapper>{children}</BodyWrapper>
|
||||
<FooterWrapper>
|
||||
</HeaderWrapper>
|
||||
<BodyWrapper>
|
||||
<SidebarWrapper>
|
||||
<Sidebar
|
||||
items={sidebarItems}
|
||||
safeAddress={safeAddress}
|
||||
safeName={safeName}
|
||||
balance={balance}
|
||||
granted={granted}
|
||||
onToggleSafeList={onToggleSafeList}
|
||||
onReceiveClick={onReceiveClick}
|
||||
onNewTransactionClick={onNewTransactionClick}
|
||||
/>
|
||||
</SidebarWrapper>
|
||||
<ContentWrapper>
|
||||
<div>{children}</div>
|
||||
<Footer />
|
||||
</FooterWrapper>
|
||||
</GridBodyWrapper>
|
||||
</Grid>
|
||||
</ContentWrapper>
|
||||
</BodyWrapper>
|
||||
</Container>
|
||||
)
|
||||
|
||||
export default Layout
|
||||
|
@ -169,13 +169,19 @@ describe('Forms > Validators', () => {
|
||||
it('Returns undefined for an address not contained in the passed array', async () => {
|
||||
const addresses = ['0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe']
|
||||
|
||||
expect(uniqueAddress(addresses)('0xe7e3272a84cf3fe180345b9f7234ba705eB5E2CA')).toBeUndefined()
|
||||
expect(uniqueAddress(addresses)()).toBeUndefined()
|
||||
})
|
||||
|
||||
it('Returns an error message for an address already contained in the array', async () => {
|
||||
const addresses = ['0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe']
|
||||
it('Returns an error message for an array with duplicated values', async () => {
|
||||
const addresses = ['0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe', '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe']
|
||||
|
||||
expect(uniqueAddress(addresses)(addresses[0])).toEqual(ADDRESS_REPEATED_ERROR)
|
||||
expect(uniqueAddress(addresses)()).toEqual(ADDRESS_REPEATED_ERROR)
|
||||
})
|
||||
|
||||
it('Returns an error message for an array with duplicated checksum and not checksum values', async () => {
|
||||
const addresses = ['0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe', '0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae']
|
||||
|
||||
expect(uniqueAddress(addresses)()).toEqual(ADDRESS_REPEATED_ERROR)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -1,13 +1,11 @@
|
||||
import { List } from 'immutable'
|
||||
import { getWeb3 } from 'src/logic/wallets/getWeb3'
|
||||
import memoize from 'lodash.memoize'
|
||||
import { isFeatureEnabled } from 'src/config'
|
||||
import { FEATURES } from 'src/config/networks/network.d'
|
||||
|
||||
import { sameAddress } from 'src/logic/wallets/ethAddresses'
|
||||
import { getWeb3 } from 'src/logic/wallets/getWeb3'
|
||||
import { List } from 'immutable'
|
||||
|
||||
type ValidatorReturnType = string | undefined
|
||||
type GenericValidatorType = (...args: unknown[]) => ValidatorReturnType
|
||||
export type GenericValidatorType = (...args: unknown[]) => ValidatorReturnType
|
||||
type AsyncValidator = (...args: unknown[]) => Promise<ValidatorReturnType>
|
||||
export type Validator = GenericValidatorType | AsyncValidator
|
||||
|
||||
@ -89,13 +87,18 @@ export const minMaxLength = (minLen: number, maxLen: number) => (value: string):
|
||||
|
||||
export const ADDRESS_REPEATED_ERROR = 'Address already introduced'
|
||||
|
||||
export const uniqueAddress = (addresses: string[] | List<string>): GenericValidatorType =>
|
||||
memoize(
|
||||
(value: string): ValidatorReturnType => {
|
||||
const addressAlreadyExists = addresses.some((address) => sameAddress(value, address))
|
||||
return addressAlreadyExists ? ADDRESS_REPEATED_ERROR : undefined
|
||||
},
|
||||
)
|
||||
export const uniqueAddress = (addresses: string[] | List<string>): GenericValidatorType => (): ValidatorReturnType => {
|
||||
// @ts-expect-error both list and array have signatures for map but TS thinks they're not compatible
|
||||
const lowercaseAddresses = addresses.map((address) => address.toLowerCase())
|
||||
const uniqueAddresses = new Set(lowercaseAddresses)
|
||||
const lengthPropName = 'size' in addresses ? 'size' : 'length'
|
||||
|
||||
if (uniqueAddresses.size !== addresses?.[lengthPropName]) {
|
||||
return ADDRESS_REPEATED_ERROR
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
export const composeValidators = (...validators: Validator[]) => (value: unknown): ValidatorReturnType =>
|
||||
validators.reduce(
|
||||
|
@ -8,6 +8,10 @@ import { AddressBookState } from 'src/logic/addressBook/model/addressBook'
|
||||
|
||||
export const addressBookSelector = (state: AppReduxState): AddressBookState => state[ADDRESS_BOOK_REDUCER_ID]
|
||||
|
||||
export const addressBookAddressesListSelector = createSelector(addressBookSelector, (addressBook): string[] => {
|
||||
return addressBook.map((entry) => entry.address)
|
||||
})
|
||||
|
||||
export const getNameFromAddressBookSelector = createSelector(
|
||||
addressBookSelector,
|
||||
(_, address) => address,
|
||||
|
@ -2,7 +2,6 @@ import { List } from 'immutable'
|
||||
import {
|
||||
checkIfEntryWasDeletedFromAddressBook,
|
||||
getAddressBookFromStorage,
|
||||
getAddressesListFromAddressBook,
|
||||
getNameFromAddressBook,
|
||||
getOwnersWithNameFromAddressBook,
|
||||
isValidAddressBookName,
|
||||
@ -28,24 +27,6 @@ const getMockOldAddressBookEntry = ({ address = '', name = '', isOwner = false }
|
||||
}
|
||||
}
|
||||
|
||||
describe('getAddressesListFromAdbk', () => {
|
||||
const entry1 = getMockAddressBookEntry('123456', 'test1')
|
||||
const entry2 = getMockAddressBookEntry('78910', 'test2')
|
||||
const entry3 = getMockAddressBookEntry('4781321', 'test3')
|
||||
|
||||
it('It should returns the list of addresses within the addressBook given a safeAddressBook', () => {
|
||||
// given
|
||||
const safeAddressBook = [entry1, entry2, entry3]
|
||||
const expectedResult = [entry1.address, entry2.address, entry3.address]
|
||||
|
||||
// when
|
||||
const result = getAddressesListFromAddressBook(safeAddressBook)
|
||||
|
||||
// then
|
||||
expect(result).toStrictEqual(expectedResult)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getNameFromSafeAddressBook', () => {
|
||||
const entry1 = getMockAddressBookEntry('123456', 'test1')
|
||||
const entry2 = getMockAddressBookEntry('78910', 'test2')
|
||||
|
@ -56,9 +56,6 @@ export const saveAddressBook = async (addressBook: AddressBookState): Promise<vo
|
||||
}
|
||||
}
|
||||
|
||||
export const getAddressesListFromAddressBook = (addressBook: AddressBookState): string[] =>
|
||||
addressBook.map((entry) => entry.address)
|
||||
|
||||
type GetNameFromAddressBookOptions = {
|
||||
filterOnlyValidName: boolean
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { getNetworkId } from 'src/config'
|
||||
import { getNetworkId, getNetworkInfo } from 'src/config'
|
||||
import { ETHEREUM_NETWORK } from 'src/config/networks/network.d'
|
||||
import { nftAssetsListAddressesSelector } from 'src/logic/collectibles/store/selectors'
|
||||
import { TxServiceModel } from 'src/logic/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions'
|
||||
@ -18,6 +18,14 @@ export const CK_ADDRESS = {
|
||||
[ETHEREUM_NETWORK.RINKEBY]: '0x16baf0de678e52367adc69fd067e5edd1d33e3bf',
|
||||
}
|
||||
|
||||
// Note: xDAI ENS is missing, once we have it we need to add it here
|
||||
const ENS_CONTRACT_ADDRESS = {
|
||||
[ETHEREUM_NETWORK.MAINNET]: '0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85',
|
||||
[ETHEREUM_NETWORK.RINKEBY]: '0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85',
|
||||
[ETHEREUM_NETWORK.ENERGY_WEB_CHAIN]: '0x0A6d64413c07E10E890220BBE1c49170080C6Ca0',
|
||||
[ETHEREUM_NETWORK.VOLTA]: '0xd7CeF70Ba7efc2035256d828d5287e2D285CD1ac',
|
||||
}
|
||||
|
||||
// safeTransferFrom(address,address,uint256)
|
||||
export const SAFE_TRANSFER_FROM_WITHOUT_DATA_HASH = '42842e0e'
|
||||
|
||||
@ -50,12 +58,11 @@ export const getERC721Symbol = async (contractAddress: string): Promise<string>
|
||||
try {
|
||||
const ERC721token = await getERC721TokenContract()
|
||||
const tokenInstance = await ERC721token.at(contractAddress)
|
||||
tokenSymbol = tokenInstance.symbol()
|
||||
tokenSymbol = await tokenInstance.symbol()
|
||||
} catch (err) {
|
||||
// If the contract address is an ENS token contract, we know that the ERC721 standard is not proper implemented
|
||||
// The method symbol() is missing
|
||||
const ENS_TOKEN_CONTRACT = '0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85'
|
||||
if (sameAddress(contractAddress, ENS_TOKEN_CONTRACT)) {
|
||||
if (isENSContract(contractAddress)) {
|
||||
return 'ENS'
|
||||
}
|
||||
console.error(`Failed to retrieve token symbol for ERC721 token ${contractAddress}`)
|
||||
@ -64,6 +71,11 @@ export const getERC721Symbol = async (contractAddress: string): Promise<string>
|
||||
return tokenSymbol
|
||||
}
|
||||
|
||||
export const isENSContract = (contractAddress: string): boolean => {
|
||||
const { id } = getNetworkInfo()
|
||||
return sameAddress(contractAddress, ENS_CONTRACT_ADDRESS[id])
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if the provided contract is a valid ERC721
|
||||
* @param {string} contractAddress
|
||||
|
@ -10,6 +10,7 @@ import { web3ReadOnly } from 'src/logic/wallets/getWeb3'
|
||||
import { makeIncomingTransaction } from 'src/logic/safe/store/models/incomingTransaction'
|
||||
import fetchTransactions from 'src/logic/safe/store/actions/transactions/fetchTransactions/fetchTransactions'
|
||||
import { TransactionTypes } from 'src/logic/safe/store/models/types/transaction'
|
||||
import { isENSContract } from 'src/logic/collectibles/utils'
|
||||
|
||||
export type IncomingTxServiceModel = {
|
||||
blockNumber: number
|
||||
@ -76,12 +77,18 @@ const batchIncomingTxsTokenDataRequest = (txs: IncomingTxServiceModel[]) => {
|
||||
batch.execute()
|
||||
|
||||
return Promise.all(whenTxsValues).then((txsValues) =>
|
||||
txsValues.map(([tx, symbol, decimals, ethTx, ethTxReceipt]) => [
|
||||
tx,
|
||||
symbol ? symbol : nativeCoin.symbol,
|
||||
decimals ? decimals : nativeCoin.decimals,
|
||||
new bn(ethTx?.gasPrice ?? 0).times(ethTxReceipt?.gasUsed ?? 0),
|
||||
]),
|
||||
txsValues.map(([tx, symbolFetched, decimals, ethTx, ethTxReceipt]) => {
|
||||
let symbol = symbolFetched
|
||||
if (!symbolFetched) {
|
||||
symbol = isENSContract(tx.tokenAddress) ? 'ENS' : nativeCoin.symbol
|
||||
}
|
||||
return [
|
||||
tx,
|
||||
symbol,
|
||||
decimals ? decimals : nativeCoin.decimals,
|
||||
new bn(ethTx?.gasPrice ?? 0).times(ethTxReceipt?.gasUsed ?? 0),
|
||||
]
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -208,6 +208,17 @@ export const safeModulesSelector = createSelector(safeSelector, safeFieldSelecto
|
||||
|
||||
export const safeFeaturesEnabledSelector = createSelector(safeSelector, safeFieldSelector('featuresEnabled'))
|
||||
|
||||
export const safeOwnersAddressesListSelector = createSelector(
|
||||
safeOwnersSelector,
|
||||
(owners): List<string> => {
|
||||
if (!owners) {
|
||||
return List([])
|
||||
}
|
||||
|
||||
return owners?.map(({ address }) => address)
|
||||
},
|
||||
)
|
||||
|
||||
export const getActiveTokensAddressesForAllSafes = createSelector(safesListSelector, (safes) => {
|
||||
const addresses = Set().withMutations((set) => {
|
||||
safes.forEach((safe) => {
|
||||
|
@ -9,7 +9,7 @@ import Row from 'src/components/layout/Row'
|
||||
import { initContracts } from 'src/logic/contracts/safeContracts'
|
||||
import Review from 'src/routes/open/components/ReviewInformation'
|
||||
import SafeNameField from 'src/routes/open/components/SafeNameForm'
|
||||
import SafeOwnersFields from 'src/routes/open/components/SafeOwnersConfirmationsForm'
|
||||
import { SafeOwnersPage } from 'src/routes/open/components/SafeOwnersConfirmationsForm'
|
||||
import {
|
||||
FIELD_CONFIRMATIONS,
|
||||
FIELD_SAFE_NAME,
|
||||
@ -129,7 +129,7 @@ const Layout = (props: LayoutProps): React.ReactElement => {
|
||||
testId="create-safe-form"
|
||||
>
|
||||
<StepperPage component={SafeNameField} />
|
||||
<StepperPage component={SafeOwnersFields} />
|
||||
<StepperPage component={SafeOwnersPage} />
|
||||
<StepperPage network={network} userAccount={userAccount} component={Review} />
|
||||
</Stepper>
|
||||
</Block>
|
||||
|
@ -236,21 +236,13 @@ const SafeOwnersForm = (props): React.ReactElement => {
|
||||
)
|
||||
}
|
||||
|
||||
const SafeOwnersPage = ({ updateInitialProps }) =>
|
||||
export const SafeOwnersPage = () =>
|
||||
function OpenSafeOwnersPage(controls, { errors, form, values }) {
|
||||
return (
|
||||
<>
|
||||
<OpenPaper controls={controls} padding={false}>
|
||||
<SafeOwnersForm
|
||||
errors={errors}
|
||||
form={form}
|
||||
otherAccounts={getAccountsFrom(values)}
|
||||
updateInitialProps={updateInitialProps}
|
||||
values={values}
|
||||
/>
|
||||
<SafeOwnersForm errors={errors} form={form} otherAccounts={getAccountsFrom(values)} values={values} />
|
||||
</OpenPaper>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SafeOwnersPage
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { uniqueAddress } from 'src/components/forms/validator'
|
||||
import { GenericValidatorType, uniqueAddress } from 'src/components/forms/validator'
|
||||
|
||||
export const getAddressValidator = (addresses, position) => {
|
||||
export const getAddressValidator = (addresses: string[], position: number): GenericValidatorType => {
|
||||
// thanks Rich Harris
|
||||
// https://twitter.com/Rich_Harris/status/1125850391155965952
|
||||
const copy = addresses.slice()
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { List } from 'immutable'
|
||||
|
||||
import { makeOwner } from 'src/logic/safe/store/models/owner'
|
||||
import { SafeOwner } from '../../../logic/safe/store/models/safe'
|
||||
import { SafeOwner } from 'src/logic/safe/store/models/safe'
|
||||
|
||||
export const getAccountsFrom = (values) => {
|
||||
const accounts = Object.keys(values)
|
||||
|
@ -19,8 +19,7 @@ import Col from 'src/components/layout/Col'
|
||||
import Hairline from 'src/components/layout/Hairline'
|
||||
import Paragraph from 'src/components/layout/Paragraph'
|
||||
import Row from 'src/components/layout/Row'
|
||||
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
|
||||
import { getAddressesListFromAddressBook } from 'src/logic/addressBook/utils'
|
||||
import { addressBookAddressesListSelector } from 'src/logic/addressBook/store/selectors'
|
||||
|
||||
export const CREATE_ENTRY_INPUT_NAME_ID = 'create-entry-input-name'
|
||||
export const CREATE_ENTRY_INPUT_ADDRESS_ID = 'create-entry-input-address'
|
||||
@ -42,8 +41,7 @@ const CreateEditEntryModalComponent = ({
|
||||
}
|
||||
}
|
||||
|
||||
const addressBook = useSelector(addressBookSelector)
|
||||
const addressBookAddressesList = getAddressesListFromAddressBook(addressBook)
|
||||
const addressBookAddressesList = useSelector(addressBookAddressesListSelector)
|
||||
const entryDoesntExist = uniqueAddress(addressBookAddressesList)
|
||||
|
||||
const formMutators = {
|
||||
|
@ -9,7 +9,7 @@ import { CustomTx } from './screens/ContractInteraction/ReviewCustomTx'
|
||||
import { ContractInteractionTx } from './screens/ContractInteraction'
|
||||
import { CustomTxProps } from './screens/ContractInteraction/SendCustomTx'
|
||||
import { ReviewTxProp } from './screens/ReviewTx'
|
||||
import { NFTToken } from 'src/logic/collectibles/sources/collectibles'
|
||||
import { NFTToken } from 'src/logic/collectibles/sources/collectibles.d'
|
||||
import { SendCollectibleTxInfo } from './screens/SendCollectible'
|
||||
|
||||
const ChooseTxType = React.lazy(() => import('./screens/ChooseTxType'))
|
||||
|
@ -13,7 +13,7 @@ import Img from 'src/components/layout/Img'
|
||||
import Paragraph from 'src/components/layout/Paragraph'
|
||||
import { setImageToPlaceholder } from 'src/routes/safe/components/Balances/utils'
|
||||
import { textShortener } from 'src/utils/strings'
|
||||
import { NFTToken } from 'src/logic/collectibles/sources/collectibles'
|
||||
import { NFTToken } from 'src/logic/collectibles/sources/collectibles.d'
|
||||
|
||||
const useSelectedCollectibleStyles = makeStyles(selectedTokenStyles)
|
||||
|
||||
|
@ -14,7 +14,7 @@ import Paragraph from 'src/components/layout/Paragraph'
|
||||
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
||||
import { setImageToPlaceholder } from 'src/routes/safe/components/Balances/utils'
|
||||
import { textShortener } from 'src/utils/strings'
|
||||
import { NFTAssets } from 'src/logic/collectibles/sources/collectibles'
|
||||
import { NFTAssets } from 'src/logic/collectibles/sources/collectibles.d'
|
||||
|
||||
const useSelectedTokenStyles = makeStyles(selectedTokenStyles)
|
||||
|
||||
|
@ -21,7 +21,7 @@ import { getNameFromAddressBook } from 'src/logic/addressBook/utils'
|
||||
import { nftTokensSelector, safeActiveSelectorMap } from 'src/logic/collectibles/store/selectors'
|
||||
import SafeInfo from 'src/routes/safe/components/Balances/SendModal/SafeInfo'
|
||||
import { AddressBookInput } from 'src/routes/safe/components/Balances/SendModal/screens/AddressBookInput'
|
||||
import { NFTToken } from 'src/logic/collectibles/sources/collectibles'
|
||||
import { NFTToken } from 'src/logic/collectibles/sources/collectibles.d'
|
||||
import { getExplorerInfo } from 'src/config'
|
||||
import { sameAddress } from 'src/logic/wallets/ethAddresses'
|
||||
import { sm } from 'src/theme/variables'
|
||||
|
@ -2,7 +2,7 @@ import { createStyles, makeStyles } from '@material-ui/core/styles'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
|
||||
import OwnerForm from './screens/OwnerForm'
|
||||
import { OwnerForm } from './screens/OwnerForm'
|
||||
import ReviewAddOwner from './screens/Review'
|
||||
import ThresholdForm from './screens/ThresholdForm'
|
||||
|
||||
@ -16,7 +16,7 @@ import createTransaction from 'src/logic/safe/store/actions/createTransaction'
|
||||
import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
|
||||
import { checksumAddress } from 'src/utils/checksumAddress'
|
||||
import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
import { Dispatch } from 'src/logic/safe/store/actions/types'
|
||||
import { Dispatch } from 'src/logic/safe/store/actions/types.d'
|
||||
|
||||
const styles = createStyles({
|
||||
biggerModalWindow: {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import Close from '@material-ui/icons/Close'
|
||||
import React from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
@ -18,7 +18,7 @@ import Col from 'src/components/layout/Col'
|
||||
import Hairline from 'src/components/layout/Hairline'
|
||||
import Paragraph from 'src/components/layout/Paragraph'
|
||||
import Row from 'src/components/layout/Row'
|
||||
import { safeOwnersSelector } from 'src/logic/safe/store/selectors'
|
||||
import { safeOwnersAddressesListSelector } from 'src/logic/safe/store/selectors'
|
||||
|
||||
export const ADD_OWNER_NAME_INPUT_TEST_ID = 'add-owner-name-input'
|
||||
export const ADD_OWNER_ADDRESS_INPUT_TEST_ID = 'add-owner-address-testid'
|
||||
@ -30,12 +30,20 @@ const formMutators = {
|
||||
},
|
||||
}
|
||||
|
||||
const OwnerForm = ({ classes, onClose, onSubmit }) => {
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
type OwnerFormProps = {
|
||||
onClose: () => void
|
||||
onSubmit: (values) => void
|
||||
}
|
||||
|
||||
export const OwnerForm = ({ onClose, onSubmit }: OwnerFormProps): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
const handleSubmit = (values) => {
|
||||
onSubmit(values)
|
||||
}
|
||||
const owners = useSelector(safeOwnersSelector)
|
||||
const ownerDoesntExist = uniqueAddress(owners?.map((o) => o.address) || [])
|
||||
const owners = useSelector(safeOwnersAddressesListSelector)
|
||||
const ownerDoesntExist = uniqueAddress(owners)
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -72,7 +80,6 @@ const OwnerForm = ({ classes, onClose, onSubmit }) => {
|
||||
<Row margin="md">
|
||||
<Col xs={8}>
|
||||
<Field
|
||||
className={classes.addressInput}
|
||||
component={TextField}
|
||||
name="ownerName"
|
||||
placeholder="Owner name*"
|
||||
@ -86,7 +93,6 @@ const OwnerForm = ({ classes, onClose, onSubmit }) => {
|
||||
<Row margin="md">
|
||||
<Col xs={8}>
|
||||
<AddressInput
|
||||
className={classes.addressInput}
|
||||
fieldMutator={mutators.setOwnerAddress}
|
||||
name="ownerAddress"
|
||||
placeholder="Owner address*"
|
||||
@ -102,11 +108,10 @@ const OwnerForm = ({ classes, onClose, onSubmit }) => {
|
||||
</Block>
|
||||
<Hairline />
|
||||
<Row align="center" className={classes.buttonRow}>
|
||||
<Button className={classes.button} minWidth={140} onClick={onClose}>
|
||||
<Button minWidth={140} onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
className={classes.button}
|
||||
color="primary"
|
||||
minWidth={140}
|
||||
testId={ADD_OWNER_NEXT_BTN_TEST_ID}
|
||||
@ -123,5 +128,3 @@ const OwnerForm = ({ classes, onClose, onSubmit }) => {
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default withStyles(styles as any)(OwnerForm)
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { lg, md, secondaryText, sm } from 'src/theme/variables'
|
||||
import { createStyles } from '@material-ui/core'
|
||||
|
||||
export const styles = () => ({
|
||||
export const styles = createStyles({
|
||||
heading: {
|
||||
padding: `${sm} ${lg}`,
|
||||
justifyContent: 'flex-start',
|
||||
|
@ -13,7 +13,7 @@ import createTransaction from 'src/logic/safe/store/actions/createTransaction'
|
||||
import removeSafeOwner from 'src/logic/safe/store/actions/removeSafeOwner'
|
||||
|
||||
import { safeParamAddressFromStateSelector, safeThresholdSelector } from 'src/logic/safe/store/selectors'
|
||||
import { Dispatch } from 'src/logic/safe/store/actions/types'
|
||||
import { Dispatch } from 'src/logic/safe/store/actions/types.d'
|
||||
|
||||
const styles = createStyles({
|
||||
biggerModalWindow: {
|
||||
|
@ -15,7 +15,7 @@ import { safeParamAddressFromStateSelector, safeThresholdSelector } from 'src/lo
|
||||
import { checksumAddress } from 'src/utils/checksumAddress'
|
||||
import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
import { sameAddress } from 'src/logic/wallets/ethAddresses'
|
||||
import { Dispatch } from 'src/logic/safe/store/actions/types'
|
||||
import { Dispatch } from 'src/logic/safe/store/actions/types.d'
|
||||
|
||||
const styles = createStyles({
|
||||
biggerModalWindow: {
|
||||
|
@ -19,7 +19,7 @@ import Hairline from 'src/components/layout/Hairline'
|
||||
import Paragraph from 'src/components/layout/Paragraph'
|
||||
import Row from 'src/components/layout/Row'
|
||||
import { ScanQRWrapper } from 'src/components/ScanQRModal/ScanQRWrapper'
|
||||
import { safeOwnersSelector } from 'src/logic/safe/store/selectors'
|
||||
import { safeOwnersAddressesListSelector } from 'src/logic/safe/store/selectors'
|
||||
|
||||
import { styles } from './style'
|
||||
import { getExplorerInfo } from 'src/config'
|
||||
@ -39,8 +39,8 @@ const OwnerForm = ({ classes, onClose, onSubmit, ownerAddress, ownerName }) => {
|
||||
const handleSubmit = (values) => {
|
||||
onSubmit(values)
|
||||
}
|
||||
const owners = useSelector(safeOwnersSelector)
|
||||
const ownerDoesntExist = uniqueAddress(owners?.map((o) => o.address) || [])
|
||||
const owners = useSelector(safeOwnersAddressesListSelector)
|
||||
const ownerDoesntExist = uniqueAddress(owners)
|
||||
|
||||
return (
|
||||
<>
|
||||
|
Loading…
x
Reference in New Issue
Block a user