mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 01:39:44 +01:00
Merge pull request #4574 from MetaMask/i4409-i4410-ens-input-enhancements
[new-ui] Improve ENS input errors and update ens validation on network change
This commit is contained in:
commit
1839ab5346
@ -265,6 +265,9 @@
|
|||||||
"encryptNewDen": {
|
"encryptNewDen": {
|
||||||
"message": "Encrypt your new DEN"
|
"message": "Encrypt your new DEN"
|
||||||
},
|
},
|
||||||
|
"ensNameNotFound": {
|
||||||
|
"message": "ENS name not found"
|
||||||
|
},
|
||||||
"enterPassword": {
|
"enterPassword": {
|
||||||
"message": "Enter password"
|
"message": "Enter password"
|
||||||
},
|
},
|
||||||
|
@ -12,6 +12,7 @@ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
|
|||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const ToAutoComplete = require('./send/to-autocomplete')
|
const ToAutoComplete = require('./send/to-autocomplete')
|
||||||
const log = require('loglevel')
|
const log = require('loglevel')
|
||||||
|
const { isValidENSAddress } = require('../util')
|
||||||
|
|
||||||
EnsInput.contextTypes = {
|
EnsInput.contextTypes = {
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
@ -25,31 +26,34 @@ function EnsInput () {
|
|||||||
Component.call(this)
|
Component.call(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EnsInput.prototype.onChange = function (recipient) {
|
||||||
|
const network = this.props.network
|
||||||
|
const networkHasEnsSupport = getNetworkEnsSupport(network)
|
||||||
|
|
||||||
|
this.props.onChange({ toAddress: recipient })
|
||||||
|
|
||||||
|
if (!networkHasEnsSupport) return
|
||||||
|
|
||||||
|
if (recipient.match(ensRE) === null) {
|
||||||
|
return this.setState({
|
||||||
|
loadingEns: false,
|
||||||
|
ensResolution: null,
|
||||||
|
ensFailure: null,
|
||||||
|
toError: null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
loadingEns: true,
|
||||||
|
})
|
||||||
|
this.checkName(recipient)
|
||||||
|
}
|
||||||
|
|
||||||
EnsInput.prototype.render = function () {
|
EnsInput.prototype.render = function () {
|
||||||
const props = this.props
|
const props = this.props
|
||||||
const opts = extend(props, {
|
const opts = extend(props, {
|
||||||
list: 'addresses',
|
list: 'addresses',
|
||||||
onChange: (recipient) => {
|
onChange: this.onChange.bind(this),
|
||||||
const network = this.props.network
|
|
||||||
const networkHasEnsSupport = getNetworkEnsSupport(network)
|
|
||||||
|
|
||||||
props.onChange(recipient)
|
|
||||||
|
|
||||||
if (!networkHasEnsSupport) return
|
|
||||||
|
|
||||||
if (recipient.match(ensRE) === null) {
|
|
||||||
return this.setState({
|
|
||||||
loadingEns: false,
|
|
||||||
ensResolution: null,
|
|
||||||
ensFailure: null,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
loadingEns: true,
|
|
||||||
})
|
|
||||||
this.checkName(recipient)
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
return h('div', {
|
return h('div', {
|
||||||
style: { width: '100%', position: 'relative' },
|
style: { width: '100%', position: 'relative' },
|
||||||
@ -85,17 +89,27 @@ EnsInput.prototype.lookupEnsName = function (recipient) {
|
|||||||
nickname: recipient.trim(),
|
nickname: recipient.trim(),
|
||||||
hoverText: address + '\n' + this.context.t('clickCopy'),
|
hoverText: address + '\n' + this.context.t('clickCopy'),
|
||||||
ensFailure: false,
|
ensFailure: false,
|
||||||
|
toError: null,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((reason) => {
|
.catch((reason) => {
|
||||||
log.error(reason)
|
const setStateObj = {
|
||||||
return this.setState({
|
|
||||||
loadingEns: false,
|
loadingEns: false,
|
||||||
ensResolution: ZERO_ADDRESS,
|
ensResolution: recipient,
|
||||||
ensFailure: true,
|
ensFailure: true,
|
||||||
hoverText: reason.message,
|
toError: null,
|
||||||
})
|
}
|
||||||
|
if (isValidENSAddress(recipient) && reason.message === 'ENS name not defined.') {
|
||||||
|
setStateObj.hoverText = this.context.t('ensNameNotFound')
|
||||||
|
setStateObj.toError = 'ensNameNotFound'
|
||||||
|
setStateObj.ensFailure = false
|
||||||
|
} else {
|
||||||
|
log.error(reason)
|
||||||
|
setStateObj.hoverText = reason.message
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.setState(setStateObj)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,9 +119,14 @@ EnsInput.prototype.componentDidUpdate = function (prevProps, prevState) {
|
|||||||
// If an address is sent without a nickname, meaning not from ENS or from
|
// If an address is sent without a nickname, meaning not from ENS or from
|
||||||
// the user's own accounts, a default of a one-space string is used.
|
// the user's own accounts, a default of a one-space string is used.
|
||||||
const nickname = state.nickname || ' '
|
const nickname = state.nickname || ' '
|
||||||
|
if (prevProps.network !== this.props.network) {
|
||||||
|
const provider = global.ethereumProvider
|
||||||
|
this.ens = new ENS({ provider, network: this.props.network })
|
||||||
|
this.onChange(ensResolution)
|
||||||
|
}
|
||||||
if (prevState && ensResolution && this.props.onChange &&
|
if (prevState && ensResolution && this.props.onChange &&
|
||||||
ensResolution !== prevState.ensResolution) {
|
ensResolution !== prevState.ensResolution) {
|
||||||
this.props.onChange(ensResolution, nickname)
|
this.props.onChange({ toAddress: ensResolution, nickname, toError: state.toError })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +143,9 @@ EnsInput.prototype.ensIcon = function (recipient) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EnsInput.prototype.ensIconContents = function (recipient) {
|
EnsInput.prototype.ensIconContents = function (recipient) {
|
||||||
const { loadingEns, ensFailure, ensResolution } = this.state || { ensResolution: ZERO_ADDRESS}
|
const { loadingEns, ensFailure, ensResolution, toError } = this.state || { ensResolution: ZERO_ADDRESS }
|
||||||
|
|
||||||
|
if (toError) return
|
||||||
|
|
||||||
if (loadingEns) {
|
if (loadingEns) {
|
||||||
return h('img', {
|
return h('img', {
|
||||||
|
@ -19,9 +19,9 @@ export default class SendToRow extends Component {
|
|||||||
updateSendToError: PropTypes.func,
|
updateSendToError: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleToChange (to, nickname = '') {
|
handleToChange (to, nickname = '', toError) {
|
||||||
const { updateSendTo, updateSendToError, updateGas } = this.props
|
const { updateSendTo, updateSendToError, updateGas } = this.props
|
||||||
const toErrorObject = getToErrorObject(to)
|
const toErrorObject = getToErrorObject(to, toError)
|
||||||
updateSendTo(to, nickname)
|
updateSendTo(to, nickname)
|
||||||
updateSendToError(toErrorObject)
|
updateSendToError(toErrorObject)
|
||||||
if (toErrorObject.to === null) {
|
if (toErrorObject.to === null) {
|
||||||
@ -53,7 +53,7 @@ export default class SendToRow extends Component {
|
|||||||
inError={inError}
|
inError={inError}
|
||||||
name={'address'}
|
name={'address'}
|
||||||
network={network}
|
network={network}
|
||||||
onChange={(newTo, newNickname) => this.handleToChange(newTo, newNickname)}
|
onChange={({ toAddress, nickname, toError }) => this.handleToChange(toAddress, nickname, toError)}
|
||||||
openDropdown={() => openToDropdown()}
|
openDropdown={() => openToDropdown()}
|
||||||
placeholder={this.context.t('recipientAddress')}
|
placeholder={this.context.t('recipientAddress')}
|
||||||
to={to}
|
to={to}
|
||||||
|
@ -4,12 +4,10 @@ const {
|
|||||||
} = require('../../send.constants')
|
} = require('../../send.constants')
|
||||||
const { isValidAddress } = require('../../../../util')
|
const { isValidAddress } = require('../../../../util')
|
||||||
|
|
||||||
function getToErrorObject (to) {
|
function getToErrorObject (to, toError = null) {
|
||||||
let toError = null
|
|
||||||
|
|
||||||
if (!to) {
|
if (!to) {
|
||||||
toError = REQUIRED_ERROR
|
toError = REQUIRED_ERROR
|
||||||
} else if (!isValidAddress(to)) {
|
} else if (!isValidAddress(to) && !toError) {
|
||||||
toError = INVALID_RECIPIENT_ADDRESS_ERROR
|
toError = INVALID_RECIPIENT_ADDRESS_ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ import proxyquire from 'proxyquire'
|
|||||||
|
|
||||||
const SendToRow = proxyquire('../send-to-row.component.js', {
|
const SendToRow = proxyquire('../send-to-row.component.js', {
|
||||||
'./send-to-row.utils.js': {
|
'./send-to-row.utils.js': {
|
||||||
getToErrorObject: (to) => ({
|
getToErrorObject: (to, toError) => ({
|
||||||
to: to === false ? null : `mockToErrorObject:${to}`,
|
to: to === false ? null : `mockToErrorObject:${to}${toError}`,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}).default
|
}).default
|
||||||
@ -67,11 +67,11 @@ describe('SendToRow Component', function () {
|
|||||||
|
|
||||||
it('should call updateSendToError', () => {
|
it('should call updateSendToError', () => {
|
||||||
assert.equal(propsMethodSpies.updateSendToError.callCount, 0)
|
assert.equal(propsMethodSpies.updateSendToError.callCount, 0)
|
||||||
instance.handleToChange('mockTo2')
|
instance.handleToChange('mockTo2', '', 'mockToError')
|
||||||
assert.equal(propsMethodSpies.updateSendToError.callCount, 1)
|
assert.equal(propsMethodSpies.updateSendToError.callCount, 1)
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
propsMethodSpies.updateSendToError.getCall(0).args,
|
propsMethodSpies.updateSendToError.getCall(0).args,
|
||||||
[{ to: 'mockToErrorObject:mockTo2' }]
|
[{ to: 'mockToErrorObject:mockTo2mockToError' }]
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -138,11 +138,11 @@ describe('SendToRow Component', function () {
|
|||||||
openDropdown()
|
openDropdown()
|
||||||
assert.equal(propsMethodSpies.openToDropdown.callCount, 1)
|
assert.equal(propsMethodSpies.openToDropdown.callCount, 1)
|
||||||
assert.equal(SendToRow.prototype.handleToChange.callCount, 0)
|
assert.equal(SendToRow.prototype.handleToChange.callCount, 0)
|
||||||
onChange('mockNewTo', 'mockNewNickname')
|
onChange({ toAddress: 'mockNewTo', nickname: 'mockNewNickname', toError: 'mockToError' })
|
||||||
assert.equal(SendToRow.prototype.handleToChange.callCount, 1)
|
assert.equal(SendToRow.prototype.handleToChange.callCount, 1)
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
SendToRow.prototype.handleToChange.getCall(0).args,
|
SendToRow.prototype.handleToChange.getCall(0).args,
|
||||||
['mockNewTo', 'mockNewNickname']
|
['mockNewTo', 'mockNewNickname', 'mockToError']
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -40,6 +40,12 @@ describe('send-to-row utils', () => {
|
|||||||
to: null,
|
to: null,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should return the passed error if to is truthy but invalid if to is truthy and valid', () => {
|
||||||
|
assert.deepEqual(getToErrorObject('invalid #$ 345878', 'someExplicitError'), {
|
||||||
|
to: 'someExplicitError',
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -36,6 +36,7 @@ module.exports = {
|
|||||||
miniAddressSummary: miniAddressSummary,
|
miniAddressSummary: miniAddressSummary,
|
||||||
isAllOneCase: isAllOneCase,
|
isAllOneCase: isAllOneCase,
|
||||||
isValidAddress: isValidAddress,
|
isValidAddress: isValidAddress,
|
||||||
|
isValidENSAddress,
|
||||||
numericBalance: numericBalance,
|
numericBalance: numericBalance,
|
||||||
parseBalance: parseBalance,
|
parseBalance: parseBalance,
|
||||||
formatBalance: formatBalance,
|
formatBalance: formatBalance,
|
||||||
@ -87,6 +88,10 @@ function isValidAddress (address) {
|
|||||||
return (isAllOneCase(prefixed) && ethUtil.isValidAddress(prefixed)) || ethUtil.isValidChecksumAddress(prefixed)
|
return (isAllOneCase(prefixed) && ethUtil.isValidAddress(prefixed)) || ethUtil.isValidChecksumAddress(prefixed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidENSAddress (address) {
|
||||||
|
return address.match(/^.{7,}\.(eth|test)$/)
|
||||||
|
}
|
||||||
|
|
||||||
function isInvalidChecksumAddress (address) {
|
function isInvalidChecksumAddress (address) {
|
||||||
var prefixed = ethUtil.addHexPrefix(address)
|
var prefixed = ethUtil.addHexPrefix(address)
|
||||||
if (address === '0x0000000000000000000000000000000000000000') return false
|
if (address === '0x0000000000000000000000000000000000000000') return false
|
||||||
|
Loading…
Reference in New Issue
Block a user