Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

Commit

Permalink
Bug: #482 - Address book Custom transactions Recipient validation (#577)
Browse files Browse the repository at this point in the history
* (fix) text input bottom border

* Fix #482 input

* Merge branch 'development' of https://github.com/gnosis/safe-react into fix/#482-address-book

# Conflicts:
#	src/components/forms/TextField/index.jsx
#	src/routes/safe/components/Balances/SendModal/screens/AddressBookInput/index.jsx
#	src/routes/safe/components/Balances/SendModal/screens/SendFunds/TokenSelectField/index.jsx
#	src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.jsx
#	yarn.lock

* Fix custom tx addresses filtering

* Merge branch 'development' of https://github.com/gnosis/safe-react into fix/#482-address-book

# Conflicts:
#	yarn.lock

* Remove console logs
Fixed prettier issues

* Remove unnecessary template string

* Fix `tokenAddress` string conversion

* Use `secondaryBackground` value

Co-authored-by: Gabriel Rodríguez Alsina <[email protected]>
Co-authored-by: Fernando <[email protected]>
  • Loading branch information
3 people authored Feb 26, 2020
1 parent a10359f commit cf1ae74
Show file tree
Hide file tree
Showing 8 changed files with 376 additions and 157 deletions.
46 changes: 29 additions & 17 deletions src/components/forms/TextField/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,39 +21,51 @@ const styles = () => ({
class TextField extends React.PureComponent<TextFieldProps> {
render() {
const {
classes,
input: { name, onChange, value, ...restInput },
meta,
text,
inputAdornment,
classes,
testId,
rows,
meta,
multiline,
render,
rows,
testId,
text,
...rest
} = this.props
const helperText = value ? text : undefined
const showError = (meta.touched || !meta.pristine) && !meta.valid
const underline = meta.active || (meta.visited && !meta.valid)
const isInactiveAndPristineOrUntouched = !meta.active && (meta.pristine || !meta.touched)
const isInvalidAndUntouched = typeof meta.error === 'undefined' ? true : !meta.touched

const inputRoot = helperText ? classes.root : undefined
const inputProps = { ...restInput, autoComplete: 'off', 'data-testid': testId }
const inputRootProps = { ...inputAdornment, disableUnderline: !underline, className: inputRoot }
const disableUnderline = isInactiveAndPristineOrUntouched && isInvalidAndUntouched

const inputRoot = helperText ? classes.root : ''
const statusClasses = meta.valid ? 'isValid' : meta.error && (meta.dirty || meta.touched) ? 'isInvalid' : ''
const inputProps = {
...restInput,
autoComplete: 'off',
'data-testid': testId,
}
const inputRootProps = {
...inputAdornment,
className: `${inputRoot} ${statusClasses}`,
disableUnderline: disableUnderline,
}

return (
<MuiTextField
style={overflowStyle}
{...rest}
name={name}
helperText={showError ? meta.error : helperText || ' '} // blank in order to force to have helper text
error={meta.error && (meta.touched || !meta.pristine)}
InputProps={inputRootProps}
error={meta.error && (meta.touched || !meta.pristine)}
helperText={showError ? meta.error : helperText || ' '} // blank in order to force to have helper text
// eslint-disable-next-line
inputProps={inputProps}
multiline={multiline}
name={name}
onChange={onChange}
value={value}
// data-testid={testId}
rows={rows}
multiline={multiline}
style={overflowStyle}
value={value}
{...rest}
/>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react'
import { withStyles } from '@material-ui/core/styles'
import { useSelector } from 'react-redux'
import Autocomplete from '@material-ui/lab/Autocomplete'
import TextField from '@material-ui/core/TextField'
import MuiTextField from '@material-ui/core/TextField'
import makeStyles from '@material-ui/core/styles/makeStyles'
import { List } from 'immutable'
import { styles } from './style'
Expand Down Expand Up @@ -38,6 +38,15 @@ const textFieldInputStyle = makeStyles(() => ({
},
}))

const filterAddressBookWithContractAddresses = async addressBook => {
const abFlags = await Promise.all(
addressBook.map(async ({ address }) => {
return (await mustBeEthereumContractAddress(address)) === undefined
}),
)
return addressBook.filter((adbkEntry, index) => abFlags[index])
}

const isValidEnsName = name => /^([\w-]+\.)+(eth|test|xyz|luxe)$/.test(name)

const AddressBookInput = ({
Expand Down Expand Up @@ -78,8 +87,10 @@ const AddressBookInput = ({
isValidText = await mustBeEthereumContractAddress(resolvedAddress)
}

// Filters the entries based on the input of the user
const filteredADBK = addressBook.filter(adbkEntry => {
// First removes the entries that are not contracts if the operation is custom tx
const adbkToFilter = isCustomTx ? await filterAddressBookWithContractAddresses(addressBook) : addressBook
// Then Filters the entries based on the input of the user
const filteredADBK = adbkToFilter.filter(adbkEntry => {
const { name, address } = adbkEntry
return (
name.toLowerCase().includes(addressValue.toLowerCase()) ||
Expand All @@ -100,10 +111,8 @@ const AddressBookInput = ({
setADBKList(addressBook)
return
}
const abFlags = await Promise.all(
addressBook.map(async ({ address }) => mustBeEthereumContractAddress(address) === undefined),
)
const filteredADBK = addressBook.filter((adbkEntry, index) => abFlags[index])

const filteredADBK = await filterAddressBookWithContractAddresses(addressBook)
setADBKList(filteredADBK)
}
filterAdbkContractAddresses()
Expand All @@ -112,6 +121,14 @@ const AddressBookInput = ({
const labelStyling = textFieldLabelStyle()
const txInputStyling = textFieldInputStyle()

let statusClasses = ''
if (!isValidForm) {
statusClasses = 'isInvalid'
}
if (isValidForm && inputTouched) {
statusClasses = 'isValid'
}

return (
<>
<Autocomplete
Expand Down Expand Up @@ -164,7 +181,7 @@ const AddressBookInput = ({
)
}}
renderInput={params => (
<TextField
<MuiTextField
{...params}
label={!isValidForm ? validationText : 'Recipient'}
error={!isValidForm}
Expand All @@ -182,6 +199,7 @@ const AddressBookInput = ({
classes: {
...txInputStyling,
},
className: statusClasses,
}}
InputLabelProps={{
shrink: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ const SendCustomTx = ({ classes, onClose, safeAddress, safeName, ethBalance, onS
<TextareaField
name="data"
type="text"
rows={3}
placeholder="Data (hex encoded)*"
text="Data (hex encoded)*"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,27 @@ import { formatAmount } from '~/logic/tokens/utils/formatAmount'
import { selectedTokenStyles, selectStyles } from './style'

type SelectFieldProps = {
tokens: List<Token>,
classes: Object,
initialValue: string,
isValid: boolean,
tokens: List<Token>,
}

type SelectedTokenProps = {
tokenAddress?: string,
classes: Object,
tokenAddress?: string,
tokens: List<Token>,
}

const SelectedToken = ({ tokenAddress, tokens, classes }: SelectedTokenProps) => {
const SelectedToken = ({ classes, tokenAddress, tokens }: SelectedTokenProps) => {
const token = tokens.find(({ address }) => address === tokenAddress)

return (
<MenuItem className={classes.container}>
{token ? (
<>
<ListItemIcon className={classes.tokenImage}>
<Img src={token.logoUri} height={28} alt={token.name} onError={setImageToPlaceholder} />
<Img alt={token.name} height={28} onError={setImageToPlaceholder} src={token.logoUri} />
</ListItemIcon>
<ListItemText
className={classes.tokenData}
Expand All @@ -44,30 +45,30 @@ const SelectedToken = ({ tokenAddress, tokens, classes }: SelectedTokenProps) =>
/>
</>
) : (
<Paragraph color="disabled" size="md" weight="light" style={{ opacity: 0.5 }}>
<Paragraph color="disabled" size="md" style={{ opacity: 0.5 }} weight="light">
Select an asset*
</Paragraph>
)}
</MenuItem>
)
}

const SelectedTokenStyled = withStyles(selectedTokenStyles)(SelectedToken)

const TokenSelectField = ({ tokens, classes, initialValue }: SelectFieldProps) => (
const TokenSelectField = ({ classes, initialValue, isValid, tokens }: SelectFieldProps) => (
<Field
name="token"
component={SelectField}
className={isValid ? 'isValid' : 'isInvalid'}
classes={{ selectMenu: classes.selectMenu }}
validate={required}
renderValue={tokenAddress => <SelectedTokenStyled tokenAddress={tokenAddress} tokens={tokens} />}
initialValue={initialValue}
component={SelectField}
displayEmpty
initialValue={initialValue}
name="token"
renderValue={tokenAddress => <SelectedTokenStyled tokenAddress={tokenAddress} tokens={tokens} />}
validate={required}
>
{tokens.map(token => (
<MenuItem key={token.address} value={token.address}>
<ListItemIcon>
<Img src={token.logoUri} height={28} alt={token.name} onError={setImageToPlaceholder} />
<Img alt={token.name} height={28} onError={setImageToPlaceholder} src={token.logoUri} />
</ListItemIcon>
<ListItemText primary={token.name} secondary={`${formatAmount(token.balance)} ${token.symbol}`} />
</MenuItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,18 +169,18 @@ const SendFunds = ({
<Block justify="left">
<Block>
<Paragraph
weight="bolder"
className={classes.selectAddress}
noMargin
onClick={() => setSelectedEntry(null)}
weight="bolder"
>
{selectedEntry.name}
</Paragraph>
<Paragraph
weight="bolder"
className={classes.selectAddress}
noMargin
onClick={() => setSelectedEntry(null)}
weight="bolder"
>
{selectedEntry.address}
</Paragraph>
Expand All @@ -196,20 +196,20 @@ const SendFunds = ({
<Row margin="md">
<Col xs={11}>
<AddressBookInput
pristine={pristine}
fieldMutator={mutators.setRecipient}
pristine={pristine}
recipientAddress={recipientAddress}
setSelectedEntry={setSelectedEntry}
setIsValidAddress={setIsValidAddress}
setSelectedEntry={setSelectedEntry}
/>
</Col>
<Col xs={1} center="xs" middle="xs" className={classes}>
<Img
src={QRIcon}
alt="Scan QR"
className={classes.qrCodeBtn}
role="button"
height={20}
alt="Scan QR"
role="button"
src={QRIcon}
onClick={() => {
openQrModal()
}}
Expand All @@ -220,7 +220,11 @@ const SendFunds = ({
)}
<Row margin="sm">
<Col>
<TokenSelectField tokens={tokens} initialValue={selectedToken} />
<TokenSelectField
initialValue={selectedToken}
isValid={tokenAddress && String(tokenAddress).toUpperCase() !== 'ETHER'}
tokens={tokens}
/>
</Col>
</Row>
<Row margin="xs">
Expand All @@ -236,8 +240,8 @@ const SendFunds = ({
<Row margin="md">
<Col>
<Field
name="amount"
component={TextField}
name="amount"
type="text"
validate={composeValidators(
required,
Expand All @@ -247,7 +251,6 @@ const SendFunds = ({
)}
placeholder="Amount*"
text="Amount*"
className={classes.addressInput}
inputAdornment={
selectedTokenRecord && {
endAdornment: <InputAdornment position="end">{selectedTokenRecord.symbol}</InputAdornment>,
Expand All @@ -268,13 +271,13 @@ const SendFunds = ({
Cancel
</Button>
<Button
type="submit"
variant="contained"
minWidth={140}
className={classes.submitButton}
color="primary"
data-testid="review-tx-btn"
className={classes.submitButton}
disabled={shouldDisableSubmitButton}
minWidth={140}
type="submit"
variant="contained"
>
Review
</Button>
Expand Down
1 change: 0 additions & 1 deletion src/routes/safe/store/actions/fetchTransactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,6 @@ export default (safeAddress: string) => async (dispatch: ReduxDispatch<GlobalSta

const { outgoing, cancel }: SafeTransactionsType = await loadSafeTransactions(safeAddress)
const incomingTransactions: Map<string, List<IncomingTransaction>> = await loadSafeIncomingTransactions(safeAddress)

dispatch(addCancellationTransactions(cancel))
dispatch(addTransactions(outgoing))
dispatch(addIncomingTransactions(incomingTransactions))
Expand Down
Loading

0 comments on commit cf1ae74

Please sign in to comment.