diff options
-rw-r--r-- | development/states/send.json | 89 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | ui/app/components/ens-input.js | 146 | ||||
-rw-r--r-- | ui/app/conf-tx.js | 2 | ||||
-rw-r--r-- | ui/app/send.js | 17 |
5 files changed, 222 insertions, 33 deletions
diff --git a/development/states/send.json b/development/states/send.json index c054af55b..3f52185af 100644 --- a/development/states/send.json +++ b/development/states/send.json @@ -2,73 +2,108 @@ "metamask": { "isInitialized": true, "isUnlocked": true, - "currentDomain": "example.com", "rpcTarget": "https://rawtestrpc.metamask.io/", "identities": { "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { - "name": "Wallet 1", "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", - "mayBeFauceting": false + "name": "Account 1" }, "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": { - "name": "Wallet 2", "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", - "mayBeFauceting": false + "name": "Account 2" }, "0x2f8d4a878cfa04a6e60d46362f5644deab66572d": { - "name": "Wallet 3", "address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d", - "mayBeFauceting": false + "name": "Account 3" + }, + "0xd85a4b6a394794842887b8284293d69163007bbb": { + "address": "0xd85a4b6a394794842887b8284293d69163007bbb", + "name": "Account 4" } }, - "unconfTxs": {}, + "unapprovedTxs": {}, "currentFiat": "USD", - "conversionRate": 11.21283484, - "conversionDate": 1472158984, + "conversionRate": 16.88200327, + "conversionDate": 1489013762, + "noActiveNotices": true, + "frequentRpcList": [], + "network": "3", "accounts": { "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { "code": "0x", - "balance": "0x34693f54a1e25900", - "nonce": "0x100013", + "balance": "0x47c9d71831c76efe", + "nonce": "0x1b", "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" }, "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": { "code": "0x", - "nonce": "0x100000", - "balance": "0x18af912cee770000", + "balance": "0x37452b1315889f80", + "nonce": "0xa", "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb" }, "0x2f8d4a878cfa04a6e60d46362f5644deab66572d": { "code": "0x", - "nonce": "0x100000", - "balance": "0x2386f26fc10000", + "balance": "0x0", + "nonce": "0x0", "address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d" + }, + "0xd85a4b6a394794842887b8284293d69163007bbb": { + "code": "0x", + "balance": "0x0", + "nonce": "0x0", + "address": "0xd85a4b6a394794842887b8284293d69163007bbb" } }, - "transactions": [], - "network": "2", - "seedWords": null, - "unconfMsgs": {}, - "messages": [], - "shapeShiftTxList": [], + "transactions": {}, + "selectedAddressTxList": [], + "unapprovedMsgs": {}, + "unapprovedMsgCount": 0, + "unapprovedPersonalMsgs": {}, + "unapprovedPersonalMsgCount": 0, + "keyringTypes": [ + "Simple Key Pair", + "HD Key Tree" + ], + "keyrings": [ + { + "type": "HD Key Tree", + "accounts": [ + "fdea65c8e26263f6d9a1b5de9555d2931a33b825", + "c5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "2f8d4a878cfa04a6e60d46362f5644deab66572d" + ] + }, + { + "type": "Simple Key Pair", + "accounts": [ + "0xd85a4b6a394794842887b8284293d69163007bbb" + ] + } + ], + "selectedAddress": "0xd85a4b6a394794842887b8284293d69163007bbb", + "currentCurrency": "USD", "provider": { "type": "testnet" }, - "selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" + "shapeShiftTxList": [], + "lostAccounts": [] }, "appState": { "menuOpen": false, "currentView": { - "name": "sendTransaction" + "name": "sendTransaction", + "context": "0xd85a4b6a394794842887b8284293d69163007bbb" }, "accountDetail": { - "subview": "transactions" + "subview": "transactions", + "accountExport": "none", + "privateKey": "" }, - "currentDomain": "127.0.0.1:9966", "transForward": true, "isLoading": false, "warning": null, - "detailView": {} + "scrollToBottom": false, + "forgottenPassword": null }, "identities": {} } diff --git a/package.json b/package.json index 475cdb2b3..a03f8a38d 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "ethereumjs-tx": "^1.0.0", "ethereumjs-util": "ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9", "ethereumjs-wallet": "^0.6.0", + "ethjs-ens": "^1.0.1", "express": "^4.14.0", "extension-link-enabler": "^1.0.0", "extensionizer": "^1.0.0", diff --git a/ui/app/components/ens-input.js b/ui/app/components/ens-input.js new file mode 100644 index 000000000..f018cc632 --- /dev/null +++ b/ui/app/components/ens-input.js @@ -0,0 +1,146 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const extend = require('xtend') +const debounce = require('debounce') +const ENS = require('ethjs-ens') +const ensRE = /.+\.eth$/ + +const networkResolvers = { + '3': '112234455c3a32fd11230c42e7bccd4a84e02010', +} + +module.exports = EnsInput + +inherits(EnsInput, Component) +function EnsInput () { + Component.call(this) +} + +EnsInput.prototype.render = function () { + const props = this.props + const opts = extend(props, { + onChange: () => { + const network = this.props.network + let resolverAddress = networkResolvers[network] + if (!resolverAddress) return + + const recipient = document.querySelector('input[name="address"]').value + if (recipient.match(ensRE) === null) { + console.dir(recipient) + return this.setState({ + loadingEns: false, + ensResolution: null, + ensFailure: null, + }) + } + + this.setState({ + loadingEns: true, + }) + this.checkName() + }, + }) + + return h('div', { + style: { width: '100%' }, + }, [ + h('input.large-input', opts), + this.ensIcon(), + ]) +} + +EnsInput.prototype.componentDidMount = function () { + const network = this.props.network + let resolverAddress = networkResolvers[network] + + if (resolverAddress) { + const provider = web3.currentProvider + this.ens = new ENS({ provider, network }) + this.checkName = debounce(this.lookupEnsName.bind(this), 200) + } +} + +EnsInput.prototype.lookupEnsName = function () { + const recipient = document.querySelector('input[name="address"]').value + const { ensResolution } = this.state + + if (!this.ens) { + return this.setState({ + loadingEns: false, + ensFailure: true, + hoverText: 'ENS is not supported on your current network.', + }) + } + + log.info(`ENS attempting to resolve name: ${recipient}`) + this.ens.lookup(recipient.trim()) + .then((address) => { + console.log('ens called back with ' + address) + + if (address !== ensResolution) { + this.setState({ + loadingEns: false, + ensResolution: address, + hoverText: address, + }) + } + }) + .catch((reason) => { + console.log('ens threw error: ' + reason.message) + console.trace(reason) + debugger + return this.setState({ + loadingEns: false, + ensFailure: true, + hoverText: reason.message, + }) + }) +} + +EnsInput.prototype.componentDidUpdate = function (prevProps, prevState) { + const state = this.state || {} + const { ensResolution } = state + if (ensResolution && this.props.onChange && + ensResolution !== prevState.ensResolution) { + console.log('Firing on change to parent') + this.props.onChange(ensResolution) + } +} + +EnsInput.prototype.ensIcon = function (recipient) { + const { hoverText } = this.state || {} + return h('span', { + title: hoverText, + style: { + position: 'absolute', + padding: '9px', + transform: 'translatex(-40px)', + }, + }, this.ensIconContents(recipient)) +} + +EnsInput.prototype.ensIconContents = function (recipient) { + const { loadingEns, ensFailure, ensResolution } = this.state || {} + + if (loadingEns) { + return h('img', { + src: 'images/loading.svg', + style: { + width: '30px', + height: '30px', + transform: 'translateY(-6px)', + }, + }) + } + + if (ensFailure) { + return h('i.fa.fa-warning.fa-lg.warning') + } + + if (ensResolution) { + return h('i.fa.fa-check-circle.fa-lg', { + style: { color: 'green' }, + }) + } +} diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index 7e93ea29f..07985094c 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -49,7 +49,7 @@ ConfirmTxScreen.prototype.render = function () { var isNotification = isPopupOrNotification() === 'notification' log.info(`rendering a combined ${unconfTxList.length} unconf msg & txs`) - if (unconfTxList.length === 0) return h(Loading) + if (unconfTxList.length === 0) return h(Loading, { isLoading: true }) return ( diff --git a/ui/app/send.js b/ui/app/send.js index 581e3afa0..a281a5fcf 100644 --- a/ui/app/send.js +++ b/ui/app/send.js @@ -9,6 +9,7 @@ const numericBalance = require('./util').numericBalance const addressSummary = require('./util').addressSummary const isHex = require('./util').isHex const EthBalance = require('./components/eth-balance') +const EnsInput = require('./components/ens-input') const ethUtil = require('ethereumjs-util') module.exports = connect(mapStateToProps)(SendTransactionScreen) @@ -18,6 +19,7 @@ function mapStateToProps (state) { accounts: state.metamask.accounts, identities: state.metamask.identities, warning: state.appState.warning, + network: state.metamask.network, } result.error = result.warning && result.warning.split('.')[0] @@ -41,6 +43,7 @@ SendTransactionScreen.prototype.render = function () { var address = state.address var account = state.account var identity = state.identity + var network = state.network return ( @@ -145,12 +148,11 @@ SendTransactionScreen.prototype.render = function () { // 'to' field h('section.flex-row.flex-center', [ - h('input.large-input', { + h(EnsInput, { name: 'address', placeholder: 'Recipient Address', - dataset: { - persistentFormId: 'recipient-address', - }, + onChange: this.recipientDidChange.bind(this), + network, }), ]), @@ -220,8 +222,13 @@ SendTransactionScreen.prototype.back = function () { this.props.dispatch(actions.backToAccountDetail(address)) } +SendTransactionScreen.prototype.recipientDidChange = function (recipient) { + this.setState({ recipient }) +} + SendTransactionScreen.prototype.onSubmit = function () { - const recipient = document.querySelector('input[name="address"]').value + const state = this.state || {} + const recipient = state.recipient || document.querySelector('input[name="address"]').value const input = document.querySelector('input[name="amount"]').value const value = util.normalizeEthStringToWei(input) const txData = document.querySelector('input[name="txData"]').value |