Resolve ENS name onChange, add ENS support to load safe route

This commit is contained in:
mmv 2019-08-15 18:42:51 +04:00
parent 6f92662e03
commit 2acd0e17bf
8 changed files with 64 additions and 60 deletions

View File

@ -1,13 +1,12 @@
// @flow
import * as React from 'react'
import { Field } from 'react-final-form'
import { OnChange } from 'react-final-form-listeners'
import TextField from '~/components/forms/TextField'
import {
composeValidators,
required,
mustBeEthereumAddress,
ifElseValidator,
ensResolverHasAddress,
} from '~/components/forms/validator'
import { getAddressFromENS } from '~/logic/wallets/getWeb3'
@ -22,9 +21,7 @@ type Props = {
inputAdornment?: React.Element,
}
const isValidEnsName = name => /^([\w-]+\.)+(eth|test|xyz|luxe)$/.test(name)
const { useState, useEffect } = React
const isValidEnsName = (name) => /^([\w-]+\.)+(eth|test|xyz|luxe)$/.test(name)
// an idea for second field was taken from here
// https://github.com/final-form/react-final-form-listeners/blob/master/src/OnBlur.js
@ -46,7 +43,7 @@ const AddressInput = ({
type="text"
validate={composeValidators(
required,
ifElseValidator(isValidEnsName, ensResolverHasAddress, mustBeEthereumAddress),
mustBeEthereumAddress,
...validators,
)}
inputAdornment={inputAdornment}
@ -55,7 +52,21 @@ const AddressInput = ({
className={className}
testId={testId}
/>
<Field
<OnChange name={name}>
{async (value) => {
if (isValidEnsName(value)) {
try {
const resolverAddr = await getAddressFromENS(value)
fieldMutator(resolverAddr)
} catch (err) {
console.error('Failed to resolve address for ENS name: ', err)
}
}
}}
</OnChange>
{/* onBlur - didn't work because of the complex validation
(if you submit before it gets the address, breaks everything) */}
{/* <Field
name={name}
subscription={{ active: true, value: true }}
render={({ meta, input }) => {
@ -82,7 +93,7 @@ const AddressInput = ({
return null
}}
/>
/> */}
</>
)

View File

@ -1,6 +1,6 @@
// @flow
import { type FieldValidator } from 'final-form'
import { getWeb3, getAddressFromENS } from '~/logic/wallets/getWeb3'
import { getWeb3 } from '~/logic/wallets/getWeb3'
export const simpleMemoize = (fn: Function) => {
let lastArg
@ -61,7 +61,7 @@ export const ok = () => undefined
export const mustBeEthereumAddress = simpleMemoize((address: Field) => {
const isAddress: boolean = getWeb3().utils.isAddress(address)
return isAddress ? undefined : 'Address should be a valid Ethereum address or ENS domain'
return isAddress ? undefined : 'Address should be a valid Ethereum address or ENS name'
})
export const minMaxLength = (minLen: string | number, maxLen: string | number) => (value: string) => (value.length >= +minLen && value.length <= +maxLen ? undefined : `Should be ${minLen} to ${maxLen} symbols`)
@ -90,24 +90,4 @@ export const differentFrom = (diffValue: string) => (value: string) => {
return undefined
}
export const ensResolverHasAddress = async (value: string) => {
let error
try {
await getAddressFromENS(value)
} catch {
error = 'Couldn\'t resolve the address'
}
return error
}
export const noErrorsOn = (name: string, errors: Object) => errors[name] === undefined
export const ifElseValidator = (ifFunc: Function, thenFunc: Function, elseFunc: Function) => (value: string) => {
if (ifFunc(value)) {
return thenFunc(value)
}
return elseFunc(value)
}

View File

@ -5,6 +5,7 @@ import SafeProxy from '@gnosis.pm/safe-contracts/build/contracts/Proxy.json'
import InputAdornment from '@material-ui/core/InputAdornment'
import CheckCircle from '@material-ui/icons/CheckCircle'
import Field from '~/components/forms/Field'
import AddressInput from '~/components/forms/AddressInput'
import {
composeValidators, required, noErrorsOn, mustBeEthereumAddress,
} from '~/components/forms/validator'
@ -19,6 +20,7 @@ import { getSafeMasterContract } from '~/logic/contracts/safeContracts'
type Props = {
classes: Object,
errors: Object,
form: Object,
}
const styles = () => ({
@ -80,8 +82,8 @@ export const safeFieldsValidation = async (values: Object) => {
return errors
}
const Details = ({ classes, errors }: Props) => (
<React.Fragment>
const Details = ({ classes, errors, form }: Props) => (
<>
<Block margin="sm">
<Paragraph noMargin size="md" color="primary">
Adding an existing Safe only requires the Safe address. Optionally you can give it a name. In case your
@ -99,9 +101,12 @@ const Details = ({ classes, errors }: Props) => (
/>
</Block>
<Block margin="lg" className={classes.root}>
<Field
<AddressInput
name={FIELD_LOAD_ADDRESS}
component={TextField}
fieldMutator={(val) => {
form.mutators.setValue(FIELD_LOAD_ADDRESS, val)
}}
inputAdornment={
noErrorsOn(FIELD_LOAD_ADDRESS, errors) && {
endAdornment: (
@ -117,17 +122,17 @@ const Details = ({ classes, errors }: Props) => (
text="Safe Address"
/>
</Block>
</React.Fragment>
</>
)
const DetailsForm = withStyles(styles)(Details)
const DetailsPage = () => (controls: React.Node, { errors }: Object) => (
<React.Fragment>
const DetailsPage = () => (controls: React.Node, { errors, form }: Object) => (
<>
<OpenPaper controls={controls} container={605}>
<DetailsForm errors={errors} />
<DetailsForm errors={errors} form={form} />
</OpenPaper>
</React.Fragment>
</>
)
export default DetailsPage

View File

@ -29,6 +29,12 @@ const back = () => {
history.goBack()
}
const formMutators = {
setValue: ([field, value], state, { changeValue }) => {
changeValue(state, field, () => value)
},
}
const Layout = ({
provider, onLoadSafeSubmit, network, userAddress,
}: Props) => {
@ -36,7 +42,7 @@ const Layout = ({
const initialValues = {}
return (
<React.Fragment>
<>
{provider ? (
<Block>
<Row align="center">
@ -45,7 +51,13 @@ const Layout = ({
</IconButton>
<Heading tag="h2">Load existing Safe</Heading>
</Row>
<Stepper onSubmit={onLoadSafeSubmit} steps={steps} initialValues={initialValues} testId="load-safe-form">
<Stepper
onSubmit={onLoadSafeSubmit}
steps={steps}
initialValues={initialValues}
mutators={formMutators}
testId="load-safe-form"
>
<StepperPage validate={safeFieldsValidation}>{DetailsForm}</StepperPage>
<StepperPage network={network}>{OwnerList}</StepperPage>
<StepperPage network={network} userAddress={userAddress}>
@ -56,7 +68,7 @@ const Layout = ({
) : (
<div>No account detected</div>
)}
</React.Fragment>
</>
)
}

View File

@ -118,7 +118,7 @@ const OwnerListComponent = (props: Props) => {
}, [])
return (
<React.Fragment>
<>
<Block className={classes.title}>
<Paragraph noMargin size="md" color="primary">
{`This Safe has ${owners.length} owners. Optional: Provide a name for each owner.`}
@ -159,18 +159,18 @@ const OwnerListComponent = (props: Props) => {
</Row>
))}
</Block>
</React.Fragment>
</>
)
}
const OwnerListPage = withStyles(styles)(OwnerListComponent)
const OwnerList = ({ updateInitialProps }: Object, network: string) => (controls: React$Node, { values }: Object) => (
<React.Fragment>
<>
<OpenPaper controls={controls} padding={false}>
<OwnerListPage network={network} updateInitialProps={updateInitialProps} values={values} />
</OpenPaper>
</React.Fragment>
</>
)
export default OwnerList

View File

@ -51,7 +51,7 @@ const Layout = ({
const initialValues = initialValuesFrom(userAccount)
return (
<React.Fragment>
<>
{provider ? (
<Block>
<Row align="center">
@ -75,7 +75,7 @@ const Layout = ({
) : (
<div>No web3 provider detected</div>
)}
</React.Fragment>
</>
)
}

View File

@ -106,7 +106,7 @@ const SafeOwners = (props: Props) => {
}
return (
<React.Fragment>
<>
<Block className={classes.title}>
<Paragraph noMargin size="md" color="primary">
Specify the owners of the Safe.
@ -212,14 +212,14 @@ owner(s)
</Row>
</Block>
{qrModalOpen && <ScanQRModal isOpen={qrModalOpen} onScan={handleScan} onClose={closeQrModal} />}
</React.Fragment>
</>
)
}
const SafeOwnersForm = withStyles(styles)(SafeOwners)
const SafeOwnersPage = ({ updateInitialProps }: Object) => (controls: React.Node, { values, errors, form }: Object) => (
<React.Fragment>
<>
<OpenPaper controls={controls} padding={false}>
<SafeOwnersForm
otherAccounts={getAccountsFrom(values)}
@ -229,7 +229,7 @@ const SafeOwnersPage = ({ updateInitialProps }: Object) => (controls: React.Node
values={values}
/>
</OpenPaper>
</React.Fragment>
</>
)
export default SafeOwnersPage

View File

@ -19,11 +19,7 @@ import Field from '~/components/forms/Field'
import TextField from '~/components/forms/TextField'
import { type Token } from '~/logic/tokens/store/model/token'
import {
composeValidators,
required,
mustBeFloat,
maxValue,
greaterThan,
composeValidators, required, mustBeFloat, maxValue, greaterThan,
} from '~/components/forms/validator'
import TokenSelectField from '~/routes/safe/components/Balances/SendModal/screens/SendFunds/TokenSelectField'
import SafeInfo from '~/routes/safe/components/Balances/SendModal/SafeInfo'
@ -74,7 +70,7 @@ const SendFunds = ({
}
return (
<React.Fragment>
<>
<Row align="center" grow className={classes.heading}>
<Paragraph weight="bolder" className={classes.manage} noMargin>
Send Funds
@ -102,7 +98,7 @@ const SendFunds = ({
const { token } = formState.values
return (
<React.Fragment>
<>
<Row margin="md">
<Col xs={12}>
<AddressInput
@ -174,12 +170,12 @@ const SendFunds = ({
Review
</Button>
</Row>
</React.Fragment>
</>
)
}}
</GnoForm>
</Block>
</React.Fragment>
</>
)
}