From 8b5f2a95df45c24061c13c51ca874e933e743381 Mon Sep 17 00:00:00 2001 From: Chi Kei Chan Date: Tue, 12 Sep 2017 23:02:51 -0700 Subject: [PATCH] Improve styling in Confirmation Screen; Show decoded send token data --- app/scripts/popup-core.js | 1 - package.json | 2 + ui/app/actions.js | 1 - ui/app/components/pending-tx.js | 339 +++++++++++------------ ui/app/css/itcss/components/confirm.scss | 104 ++++--- 5 files changed, 234 insertions(+), 213 deletions(-) diff --git a/app/scripts/popup-core.js b/app/scripts/popup-core.js index 94ba141bc..2e4334bb1 100644 --- a/app/scripts/popup-core.js +++ b/app/scripts/popup-core.js @@ -49,7 +49,6 @@ function setupControllerConnection (connectionStream, cb) { }) connectionStream.pipe(accountManagerDnode).pipe(connectionStream) accountManagerDnode.once('remote', function (accountManager) { - console.log({ accountManager }) // setup push events accountManager.on = eventEmitter.on.bind(eventEmitter) cb(null, accountManager) diff --git a/package.json b/package.json index 82f464986..87a324837 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ ] }, "dependencies": { + "abi-decoder": "^1.0.8", "async": "^2.5.0", "await-semaphore": "^0.1.1", "babel-runtime": "^6.23.0", @@ -92,6 +93,7 @@ "gulp-eslint": "^4.0.0", "gulp-sass": "^3.1.0", "hat": "0.0.3", + "human-standard-token-abi": "^1.0.2", "idb-global": "^2.1.0", "identicon.js": "^2.3.1", "iframe": "^1.0.0", diff --git a/ui/app/actions.js b/ui/app/actions.js index 3ee11ddb5..c04808125 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -194,7 +194,6 @@ module.exports = actions var background = null function _setBackgroundConnection (backgroundConnection) { background = backgroundConnection - console.log({ background }) } function goHome () { diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js index ab425abf5..5b2aa253f 100644 --- a/ui/app/components/pending-tx.js +++ b/ui/app/components/pending-tx.js @@ -1,12 +1,14 @@ const Component = require('react').Component const { connect } = require('react-redux') const h = require('react-hyperscript') +const abi = require('human-standard-token-abi') +const abiDecoder = require('abi-decoder') +abiDecoder.addABI(abi) const inherits = require('util').inherits const actions = require('../actions') const clone = require('clone') const FiatValue = require('./fiat-value') const Identicon = require('./identicon') -const { setCurrentCurrency } = require('../actions') const ethUtil = require('ethereumjs-util') const BN = ethUtil.BN @@ -33,16 +35,19 @@ function mapStateToProps (state) { conversionRate, identities, } = state.metamask - + const accounts = state.metamask.accounts + const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0] return { conversionRate, identities, + selectedAddress, } } function mapDispatchToProps (dispatch) { return { - setCurrentCurrencyToUSD: () => dispatch(setCurrentCurrency('USD')) + setCurrentCurrencyToUSD: () => dispatch(actions.setCurrentCurrency('USD')), + backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)), } } @@ -54,258 +59,242 @@ function PendingTx () { txData: null, submitting: false, } + this.onSubmit = this.onSubmit.bind(this) } PendingTx.prototype.componentWillMount = function () { this.props.setCurrentCurrencyToUSD() } -PendingTx.prototype.render = function () { - const props = this.props - const { blockGasLimit, conversionRate, identities } = props - +PendingTx.prototype.getTotal = function () { + const { conversionRate } = this.props const txMeta = this.gatherTxMeta() const txParams = txMeta.txParams || {} + const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data) + const { params = [] } = decodedData || {} + const { name, value } = params[1] || {} + const amountBn = name === '_value' + ? new BN(value) + : hexToBn(txParams.value) - // Account Details - const address = txParams.from || props.selectedAddress - const account = props.accounts[address] - const balance = account ? account.balance : '0x0' + const USD = conversionUtil(amountBn, { + fromFormat: 'BN', + toCurrency: 'USD', + conversionRate, + }) + const ETH = conversionUtil(amountBn, { + fromFormat: 'BN', + toCurrency: 'ETH', + conversionRate, + }) - // recipient check - const isValidAddress = !txParams.to || util.isValidAddress(txParams.to) + return { + USD, + ETH, + } +} + +PendingTx.prototype.getGasFee = function () { + const { conversionRate } = this.props + const txMeta = this.gatherTxMeta() + const txParams = txMeta.txParams || {} // Gas const gas = txParams.gas const gasBn = hexToBn(gas) - // Gas Price const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_BN.toString(16) const gasPriceBn = hexToBn(gasPrice) const txFeeBn = gasBn.mul(gasPriceBn) - const amountBn = hexToBn(txParams.value) - - // TODO: insufficient balance should be handled on send screen - // const maxCost = txFeeBn.add(amountBn) - // const balanceBn = hexToBn(balance) - // const insufficientBalance = balanceBn.lt(maxCost) - const fromName = identities[txParams.from].name - const to = identities[txParams.to] - const toName = to ? to.name : ' ' - - const endOfFromAddress = txParams.from.slice(txParams.from.length - 4) - const endOfToAddress = txParams.to.slice(txParams.to.length - 4) - - const gasFeeInUSD = conversionUtil(txFeeBn, { + const USD = conversionUtil(txFeeBn, { fromFormat: 'BN', fromCurrency: 'GWEI', toCurrency: 'USD', conversionRate, }) - const gasFeeInETH = conversionUtil(txFeeBn, { + const ETH = conversionUtil(txFeeBn, { fromFormat: 'BN', fromCurrency: 'GWEI', toCurrency: 'ETH', conversionRate, }) - const totalInUSD = conversionUtil(amountBn, { - fromFormat: 'BN', - toCurrency: 'USD', - conversionRate, - }) - const totalInETH = conversionUtil(amountBn, { - fromFormat: 'BN', - toCurrency: 'ETH', - conversionRate, - }) + return { + USD, + ETH, + } +} + +PendingTx.prototype.getData = function () { + const { identities } = this.props + const txMeta = this.gatherTxMeta() + const txParams = txMeta.txParams || {} + const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data) + const { name, params = [] } = decodedData || {} + const { type, value } = params[0] || {} + const { USD: gasFeeInUSD, ETH: gasFeeInETH } = this.getGasFee() + const { USD: totalInUSD, ETH: totalInETH } = this.getTotal() + + if (name === 'transfer' && type === 'address') { + return { + from: { + address: txParams.from, + name: identities[txParams.from].name, + }, + to: { + address: value, + name: identities[value] ? identities[value].name : 'New Recipient', + }, + memo: txParams.memo || '', + gasFeeInUSD, + gasFeeInETH, + totalInUSD, + totalInETH, + } + } else { + return { + from: { + address: txParams.from, + name: identities[txParams.from].name, + }, + to: { + address: txParams.to, + name: identities[txParams.to] ? identities[txParams.to].name : 'New Recipient', + }, + memo: txParams.memo || '', + gasFeeInUSD, + gasFeeInETH, + totalInUSD, + totalInETH, + } + } +} + +PendingTx.prototype.render = function () { + const { backToAccountDetail, selectedAddress } = this.props + const txMeta = this.gatherTxMeta() + const txParams = txMeta.txParams || {} + + // recipient check + // const isValidAddress = !txParams.to || util.isValidAddress(txParams.to) + + const { + from: { + address: fromAddress, + name: fromName, + }, + to: { + address: toAddress, + name: toName, + }, + memo, + gasFeeInUSD, + gasFeeInETH, + totalInUSD, + totalInETH, + } = this.getData() this.inputs = [] return ( h('div.flex-column.flex-grow.confirm-screen-container', { - style: { - // overflow: 'scroll', - minWidth: '355px', // TODO: maxWidth TBD, use home.html - }, + style: { minWidth: '355px' }, }, [ - // Main Send token Card - h('div.confirm-screen-wrapper.flex-column.flex-grow', {}, [ - - h('h3.flex-center.confirm-screen-header', {}, [ - - h('button.confirm-screen-back-button', {}, 'BACK'), - - h('div.confirm-screen-title', {}, 'Confirm Transaction'), - + h('div.confirm-screen-wrapper.flex-column.flex-grow', [ + h('h3.flex-center.confirm-screen-header', [ + h('button.confirm-screen-back-button', { + onClick: () => backToAccountDetail(selectedAddress), + }, 'BACK'), + h('div.confirm-screen-title', 'Confirm Transaction'), ]), - - h('div.flex-row.flex-center.confirm-screen-identicons', {}, [ - - h('div.confirm-screen-account-wrapper', {}, [ + h('div.flex-row.flex-center.confirm-screen-identicons', [ + h('div.confirm-screen-account-wrapper', [ h( Identicon, { - address: txParams.from, - diameter: 64, - style: {}, + address: fromAddress, + diameter: 100, }, ), - h('span.confirm-screen-account-name', {}, fromName), - h('span.confirm-screen-account-number', {}, endOfFromAddress), - + h('span.confirm-screen-account-name', fromName), + h('span.confirm-screen-account-number', fromAddress.slice(fromAddress.length - 4)), ]), - h('i.fa.fa-arrow-right.fa-lg'), - - h('div.confirm-screen-account-wrapper', {}, [ + h('div.confirm-screen-account-wrapper', [ h( Identicon, { address: txParams.to, - diameter: 64, - style: {}, + diameter: 100, }, ), - h('span.confirm-screen-account-name', {}, toName), - h('span.confirm-screen-account-number', {}, endOfToAddress), - ]) - + h('span.confirm-screen-account-name', toName), + h('span.confirm-screen-account-number', toAddress.slice(toAddress.length - 4)), + ]), ]), h('h3.flex-center.confirm-screen-sending-to-message', { style: { textAlign: 'center', fontSize: '16px', - } + }, }, [ - `You're sending to Recipient ...${endOfToAddress}` + `You're sending to Recipient ...${toAddress.slice(toAddress.length - 4)}`, ]), - h('h3.flex-center.confirm-screen-send-amount', {}, [`$${totalInUSD}`]), - - h('h3.flex-center.confirm-screen-send-amount-currency', {}, [ - 'USD', + h('h3.flex-center.confirm-screen-send-amount', [`$${totalInUSD}`]), + h('h3.flex-center.confirm-screen-send-amount-currency', [ 'USD' ]), + h('div.flex-center.confirm-memo-wrapper', [ + h('h3.confirm-screen-send-memo', [ memo ]), ]), - h('div.flex-center.confirm-memo-wrapper', {}, h( - 'h3.confirm-screen-send-memo', {}, txParams.memo || 'Fake memo' - )), - - // TODO: put this error message in the right place - // props.error && h('span.error.flex-center', props.error), - - h('section.flex-row.flex-center.confirm-screen-row', { - }, [ - h('div', { - style: { - width: '50%', - }, - }, [ - h('span.confirm-screen-label', {}, [ - 'From', + h('div.confirm-screen-rows', [ + h('section.flex-row.flex-center.confirm-screen-row', [ + h('span.confirm-screen-label.confirm-screen-section-column', [ 'From' ]), + h('div.confirm-screen-section-column', [ + h('div.confirm-screen-row-info', fromName), + h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`), ]), ]), - h('div', { - style: { - width: '50%', - }, - }, [ - h('div.confirm-screen-row-info', {}, fromName), - - h('div.confirm-screen-row-detail', {}, `...${endOfFromAddress}`), - ]), - ]), - - - h('section.flex-row.flex-center.confirm-screen-row', { - }, [ - h('div', { - style: { - width: '50%', - }, - }, [ - h('span.confirm-screen-label', {}, [ - 'To', + h('section.flex-row.flex-center.confirm-screen-row', [ + h('span.confirm-screen-label.confirm-screen-section-column', [ 'To' ]), + h('div.confirm-screen-section-column', [ + h('div.confirm-screen-row-info', toName), + h('div.confirm-screen-row-detail', `...${toAddress.slice(toAddress.length - 4)}`), ]), ]), - h('div', { - style: { - width: '50%', - }, - }, [ - h('div.confirm-screen-row-info', {}, toName), + h('section.flex-row.flex-center.confirm-screen-row', [ + h('span.confirm-screen-label.confirm-screen-section-column', [ 'Gas Fee' ]), + h('div.confirm-screen-section-column', [ + h('div.confirm-screen-row-info', `$${gasFeeInUSD} USD`), - h('div.confirm-screen-row-detail', {}, `...${endOfToAddress}`), - ]), - ]), - - - h('section.flex-row.flex-center.confirm-screen-row', { - }, [ - h('div', { - style: { - width: '50%', - }, - }, [ - h('span.confirm-screen-label', {}, [ - 'Gas Fee', + h('div.confirm-screen-row-detail', `${gasFeeInETH} ETH`), ]), ]), - h('div', { - style: { - width: '50%', - }, - }, [ - h('div.confirm-screen-row-info', {}, `$${gasFeeInUSD} USD`), - h('div.confirm-screen-row-detail', {}, `${gasFeeInETH} ETH`), - ]), - ]), - - - h('section.flex-row.flex-center.confirm-screen-total-box ', {}, [ - h('div', { - style: { - width: '50%', - }, - }, [ - h('span.confirm-screen-label', {}, [ - 'Total ', + h('section.flex-row.flex-center.confirm-screen-total-box ', [ + h('div.confirm-screen-section-column', [ + h('span.confirm-screen-label', [ 'Total ' ]), + h('div.confirm-screen-total-box__subtitle', [ 'Amount + Gas' ]), ]), - h('div', { - style: { - textAlign: 'left', - fontSize: '8px', - }, - }, [ - 'Amount + Gas', + h('div.confirm-screen-section-column', [ + h('div.confirm-screen-row-info', `$${totalInUSD} USD`), + h('div.confirm-screen-row-detail', `${totalInETH} ETH`), ]), - - ]), - - h('div', { - style: { - width: '50%', - }, - }, [ - h('div.confirm-screen-row-info', {}, `$${totalInUSD} USD`), - - h('div.confirm-screen-row-detail', {}, `${totalInETH} ETH`), - ]), + ]), ]), - ]), // end of container + ]), h('form#pending-tx-form.flex-column.flex-center', { - onSubmit: this.onSubmit.bind(this), + onSubmit: this.onSubmit, }, [ // Reset Button // h('button', { @@ -325,7 +314,7 @@ PendingTx.prototype.render = function () { // Cancel Button h('button.cancel.btn-light.confirm-screen-cancel-button', {}, 'CANCEL'), ]), - ]) // end of minwidth wrapper + ]) ) } diff --git a/ui/app/css/itcss/components/confirm.scss b/ui/app/css/itcss/components/confirm.scss index 865915c30..12322462e 100644 --- a/ui/app/css/itcss/components/confirm.scss +++ b/ui/app/css/itcss/components/confirm.scss @@ -4,6 +4,7 @@ @media screen and (max-width: 575px) { margin-top: 35px; + width: 100%; } @media screen and (min-width: 576px) { @@ -14,30 +15,21 @@ .confirm-screen-wrapper { display: flex; flex-direction: column; - min-width: 320px; - min-height: 753px; + align-items: center; z-index: 100; top: 5%; font-family: 'DIN NEXT'; - margin-left: 3.5%; - margin-right: 3.5%; background: $white; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .08); - padding-top: 20px; - padding-bottom: 31px; + // padding: 20px 24px 32px; + color: $scorpion; + width: 100%; @media screen and (min-width: $break-large) { width: 498px; } } -.confirm-screen-wrapper > h3, -.confirm-screen-wrapper > div, -.confirm-screen-wrapper > section { - margin-left: 23px; - margin-right: 23px; -} - .confirm-screen-wrapper > .confirm-screen-total-box { margin-left: 10px; margin-right: 10px; @@ -48,6 +40,7 @@ } .confirm-screen-wrapper > .confirm-screen-header { + @media screen and (max-width: $break-small) { margin-left: 8px; } @@ -56,23 +49,30 @@ .confirm-screen-header { font-size: 26px; position: relative; + display: flex; + flex-flow: row nowrap; + align-items: center; + width: 100%; + padding: 20px 24px 0; @media screen and (max-width: $break-small) { - font-size: 22px; + font-size: 22px; } } .confirm-screen-title { + line-height: 27px; + @media screen and (max-width: $break-small) { - margin-left: 22px; - margin-right: 8px; + margin-left: 22px; + margin-right: 8px; } } .confirm-screen-back-button { background: $white; border: 1px solid $dusty-gray; - left: 0; + left: 24px; position: absolute; text-align: center; color: $black; @@ -82,7 +82,7 @@ width: 54px; @media screen and (max-width: $break-small) { - margin-right: 12px; + margin-right: 12px; } } @@ -91,7 +91,12 @@ flex-direction: column; } -.confirm-screen-account-name, .confirm-screen-row-info { +.confirm-screen-account-name { + margin-top: 12px; +} + +.confirm-screen-account-name, +.confirm-screen-row-info { font-size: 16px; line-height: 24px; color: $scorpion; @@ -106,11 +111,11 @@ } .confirm-screen-identicons { - margin-top: 48px; + margin-top: 24px; i { align-self: start; - margin: 20px 14px 0 14px; + margin: 42px 14px 0; } } @@ -118,13 +123,14 @@ text-align: center; font-size: 16px; margin-top: 30px; + font-family: 'DIN NEXT Light'; } .confirm-screen-send-amount { font-size: 64px; color: $scorpion; margin-top: 12px; - line-height: 60px; + line-height: 60px; text-align: center; font-family: 'DIN NEXT Light'; } @@ -136,8 +142,9 @@ } .confirm-memo-wrapper { + min-height: 24px; width: 100%; - border-bottom: 1px solid $gallery; + border-bottom: 1px solid $alto; } .confirm-screen-send-memo { @@ -147,11 +154,12 @@ text-align: center; margin-top: 21px; margin-bottom: 18px; + font-family: 'DIN NEXT Light'; } .confirm-screen-label { - font-size: 18px; - line-height: 25px; + font-size: 18px; + line-height: 40px; color: $scorpion; text-align: left; } @@ -163,32 +171,60 @@ section .confirm-screen-account-number, text-align: left; } +.confirm-screen-rows { + display: flex; + flex-flow: column nowrap; + width: 100%; + padding: 0 24px 32px; +} + +.confirm-screen-section-column { + flex: .5; +} + .confirm-screen-row { - margin-top: 15px; - margin-bottom: 11.5px; + display: flex; + flex-flow: row nowrap; + border-bottom: 1px solid $alto; + width: calc(100% - 24px); + align-items: center; + padding: 12px 0; + margin: 0 12px; } .confirm-screen-row-detail { font-size: 12px; line-height: 16px; color: $dusty-gray; + font-family: 'DIN NEXT Light'; } .confirm-screen-total-box { background-color: $wild-sand; border-radius: 8px; - margin-left: 10px; - margin-right: 10px; - padding: 22px 14px 22px; - margin-bottom: 10px; + padding: 22px 14px; margin-top: 13px; + + .confirm-screen-label { + line-height: 18px; + } + + .confirm-screen-row-detail { + color: $scorpion; + } + + &__subtitle { + font-size: 14px; + line-height: 20px; + font-family: 'DIN NEXT Light'; + } } .confirm-screen-confirm-button { height: 62px; width: 216.88px; border-radius: 2px; - background-color: #02C9B1; + background-color: #02c9b1; font-size: 16px; color: $white; text-align: center; @@ -218,7 +254,3 @@ section .confirm-screen-account-number, #pending-tx-form { flex: 1 0 auto; } - -.confirm-screen-row + .confirm-screen-row { - border-top: 1px solid $gallery; -}