diff options
Diffstat (limited to 'ui')
-rw-r--r-- | ui/app/components/account-panel.js | 2 | ||||
-rw-r--r-- | ui/app/components/eth-balance.js | 3 | ||||
-rw-r--r-- | ui/app/components/mini-account-panel.js | 84 | ||||
-rw-r--r-- | ui/app/components/pending-tx-details.js | 173 | ||||
-rw-r--r-- | ui/app/components/pending-tx.js | 10 | ||||
-rw-r--r-- | ui/app/css/lib.css | 6 | ||||
-rw-r--r-- | ui/app/util.js | 17 | ||||
-rw-r--r-- | ui/lib/contract-namer.js | 17 |
8 files changed, 271 insertions, 41 deletions
diff --git a/ui/app/components/account-panel.js b/ui/app/components/account-panel.js index d50522fa2..abaaf8163 100644 --- a/ui/app/components/account-panel.js +++ b/ui/app/components/account-panel.js @@ -22,7 +22,7 @@ AccountPanel.prototype.render = function () { var panelState = { key: `accountPanel${identity.address}`, identiconKey: identity.address, - identiconLabel: identity.name, + identiconLabel: identity.name || '', attributes: [ { key: 'ADDRESS', diff --git a/ui/app/components/eth-balance.js b/ui/app/components/eth-balance.js index c7240ea21..113e698ad 100644 --- a/ui/app/components/eth-balance.js +++ b/ui/app/components/eth-balance.js @@ -13,7 +13,8 @@ function EthBalanceComponent () { EthBalanceComponent.prototype.render = function () { var state = this.props var style = state.style - var value = formatBalance(state.value) + + const value = formatBalance(state.value) return ( diff --git a/ui/app/components/mini-account-panel.js b/ui/app/components/mini-account-panel.js new file mode 100644 index 000000000..1ec7b4d41 --- /dev/null +++ b/ui/app/components/mini-account-panel.js @@ -0,0 +1,84 @@ +const inherits = require('util').inherits +const Component = require('react').Component +const h = require('react-hyperscript') +const Identicon = require('./identicon') + +module.exports = AccountPanel + + +inherits(AccountPanel, Component) +function AccountPanel () { + Component.call(this) +} + +AccountPanel.prototype.render = function () { + var props = this.props + var picOrder = props.picOrder || 'left' + const { attrs, imageSeed } = props + + return ( + + h('.identity-panel.flex-row.flex-left', { + style: { + cursor: props.onClick ? 'pointer' : undefined, + }, + onClick: props.onClick, + }, [ + + this.genIcon(imageSeed, picOrder), + + h('div.flex-column.flex-justify-center', { + style: { + lineHeight: '15px', + order: 2, + display: 'flex', + alignItems: picOrder === 'left' ? 'flex-begin' : 'flex-end', + }, + }, [ + + attrs.map((attr) => { + return h('span.font-small', { + key: `mini-${attr}`, + style: { + fontFamily: 'Montserrat Light, Montserrat, sans-serif', + }, + }, attr) + }), + ]), + ]) + ) +} + +AccountPanel.prototype.genIcon = function (seed, picOrder) { + const props = this.props + + // When there is no seed value, this is a contract creation. + // We then show the contract icon. + if (!seed) { + return h('.identicon-wrapper.flex-column.select-none', { + style: { + order: picOrder === 'left' ? 1 : 3, + }, + }, [ + h('i.fa.fa-file-text-o.fa-lg', { + style: { + fontSize: '42px', + transform: 'translate(0px, -16px)', + }, + }), + ]) + } + + // If there was a seed, we return an identicon for that address. + return h('.identicon-wrapper.flex-column.select-none', { + style: { + order: picOrder === 'left' ? 1 : 3, + }, + }, [ + h(Identicon, { + address: seed, + imageify: props.imageifyIdenticons, + }), + ]) +} + diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index 2ba613f20..8ecca856a 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -2,10 +2,16 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits -const AccountPanel = require('./account-panel') +const MiniAccountPanel = require('./mini-account-panel') const addressSummary = require('../util').addressSummary -const readableDate = require('../util').readableDate const formatBalance = require('../util').formatBalance +const nameForAddress = require('../../lib/contract-namer') +const ethUtil = require('ethereumjs-util') +const BN = ethUtil.BN + +const baseGasFee = new BN('21000', 10) +const gasCost = new BN('4a817c800', 16) +const baseFeeHex = baseGasFee.mul(gasCost).toString(16) module.exports = PendingTxDetails @@ -14,52 +20,155 @@ function PendingTxDetails () { Component.call(this) } -PendingTxDetails.prototype.render = function () { - var state = this.props - return this.renderGeneric(h, state) -} +const PTXP = PendingTxDetails.prototype -PendingTxDetails.prototype.renderGeneric = function (h, state) { - var txData = state.txData +PTXP.render = function () { + var props = this.props + var txData = props.txData var txParams = txData.txParams || {} - var address = txParams.from || state.selectedAddress - var identity = state.identities[address] || { address: address } - var account = state.accounts[address] || { address: address } + var address = txParams.from || props.selectedAddress + var identity = props.identities[address] || { address: address } + var balance = props.accounts[address].balance - return ( + var gasCost = ethUtil.stripHexPrefix(txParams.gas || baseFeeHex) + var txValue = ethUtil.stripHexPrefix(txParams.value || '0x0') + var maxCost = ((new BN(txValue, 16)).add(new BN(gasCost, 16))).toString(16) + var dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0 + console.dir(identity) + console.dir({props}) + return ( h('div', [ - // account that will sign - h(AccountPanel, { - showFullAddress: true, - identity: identity, - account: account, - imageifyIdenticons: state.imageifyIdenticons, - }), + h('.flex-row.flex-center', { + style: { + maxWidth: '100%', + }, + }, [ + + h(MiniAccountPanel, { + attrs: [ + identity.name, + addressSummary(address, 6, 4, false), + formatBalance(balance).formatted, + ], + imageSeed: address, + imageifyIdenticons: props.imageifyIdenticons, + picOrder: 'right', + }), - // tx data - h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [ + h('img', { + src: 'images/forward-carrat.svg', + style: { + padding: '5px 6px 0px 10px', + height: '48px', + }, + }), - h('.flex-row.flex-space-between', [ - h('label.font-small', 'TO ADDRESS'), - h('span.font-small', addressSummary(txParams.to)), + this.miniAccountPanelForRecipient(), + ]), + + h('style', ` + .table-box { + margin: 7px 6px 0px 6px; + } + .table-box .row { + margin: 0px; + background: rgb(236,236,236); + display: flex; + justify-content: space-between; + font-family: Montserrat Light, sans-serif; + font-size: 13px; + padding: 5px 15px; + } + `), + + h('.table-box', [ + + h('.row', [ + h('.cell.label', 'Amount'), + h('.cell.value', formatBalance(txParams.value).formatted), ]), - h('.flex-row.flex-space-between', [ - h('label.font-small', 'DATE'), - h('span.font-small', readableDate(txData.time)), + h('.cell.row', [ + h('.cell.label', 'Max Transaction Fee'), + h('.cell.value', formatBalance(gasCost).formatted), ]), - h('.flex-row.flex-space-between', [ - h('label.font-small', 'AMOUNT'), - h('span.font-small', formatBalance(txParams.value)), + h('.cell.row', { + style: { + fontFamily: 'Montserrat Regular', + background: 'rgb(216,216,216)', + }, + }, [ + h('.cell.label', 'Max Total'), + h('.cell.value', formatBalance(maxCost).formatted), ]), - ]), + + h('.cell.row', { + style: { + background: '#f7f7f7', + paddingBottom: '0px', + }, + }, [ + h('.cell.label'), + h('.cell.value', `Data included: ${dataLength} bytes`), + ]), + ]), // End of Table + + this.warnIfNeeded(), ]) - ) +} + +PTXP.miniAccountPanelForRecipient = function () { + var props = this.props + var txData = props.txData + var txParams = txData.txParams || {} + var isContractDeploy = !('to' in txParams) + + // If it's not a contract deploy, send to the account + if (!isContractDeploy) { + return h(MiniAccountPanel, { + attrs: [ + nameForAddress(txParams.to), + addressSummary(txParams.to, 6, 4, false), + ], + imageSeed: txParams.to, + imageifyIdenticons: props.imageifyIdenticons, + picOrder: 'left', + }) + } else { + return h(MiniAccountPanel, { + attrs: [ + 'New Contract', + ], + imageifyIdenticons: props.imageifyIdenticons, + picOrder: 'left', + }) + } +} + +// Should analyze if there is a DELEGATECALL opcode +// in the recipient contract, and show a warning if so. +PTXP.warnIfNeeded = function () { + const containsDelegateCall = !!this.props.txData.containsDelegateCall + + if (!containsDelegateCall) { + return null + } + return h('span.error', { + style: { + fontFamily: 'Montserrat Light', + fontSize: '13px', + display: 'flex', + justifyContent: 'center', + }, + }, [ + h('i.fa.fa-lg.fa-info-circle', { style: { margin: '5px' } }), + h('span', ' Your identity may be used in other contracts!'), + ]) } diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js index 5d1fd4c16..77dba87ee 100644 --- a/ui/app/components/pending-tx.js +++ b/ui/app/components/pending-tx.js @@ -33,7 +33,11 @@ PendingTx.prototype.render = function () { h(PendingTxDetails, state), // send + cancel - h('.flex-row.flex-space-around', [ + h('.flex-row.flex-space-around', { + style: { + marginTop: '14px', + }, + }, [ h('button', { onClick: state.cancelTransaction, }, 'Reject'), @@ -41,9 +45,7 @@ PendingTx.prototype.render = function () { onClick: state.sendTransaction, }, 'Approve'), ]), - ]) - ) - } + diff --git a/ui/app/css/lib.css b/ui/app/css/lib.css index a7da11e77..22b26d4f1 100644 --- a/ui/app/css/lib.css +++ b/ui/app/css/lib.css @@ -220,3 +220,9 @@ hr.horizontal-line { .invisible { visibility: hidden; } + +.one-line-concat { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/ui/app/util.js b/ui/app/util.js index db12a1282..89853d746 100644 --- a/ui/app/util.js +++ b/ui/app/util.js @@ -21,6 +21,7 @@ for (var currency in valueTable) { module.exports = { valuesFor: valuesFor, addressSummary: addressSummary, + miniAddressSummary: miniAddressSummary, isAllOneCase: isAllOneCase, isValidAddress: isValidAddress, numericBalance: numericBalance, @@ -43,10 +44,19 @@ function valuesFor (obj) { .map(function (key) { return obj[key] }) } -function addressSummary (address) { +function addressSummary (address, firstSegLength = 10, lastSegLength = 4, includeHex = true) { + if (!address) return '' + let checked = ethUtil.toChecksumAddress(address) + if (!includeHex) { + checked = ethUtil.stripHexPrefix(checked) + } + return checked ? checked.slice(0, firstSegLength) + '...' + checked.slice(checked.length - lastSegLength) : '...' +} + +function miniAddressSummary (address) { if (!address) return '' var checked = ethUtil.toChecksumAddress(address) - return checked ? checked.slice(0, 2 + 8) + '...' + checked.slice(-4) : '...' + return checked ? checked.slice(0, 4) + '...' + checked.slice(-4) : '...' } function isValidAddress (address) { @@ -94,7 +104,8 @@ function parseBalance (balance) { return [beforeDecimal, afterDecimal] } -// Takes wei hex, returns "None" or "${formattedAmount} ETH" +// Takes wei hex, returns an object with three properties. +// Its "formatted" property is what we generally use to render values. function formatBalance (balance, decimalsToKeep) { var parsed = parseBalance(balance) var beforeDecimal = parsed[0] diff --git a/ui/lib/contract-namer.js b/ui/lib/contract-namer.js new file mode 100644 index 000000000..eae066ad5 --- /dev/null +++ b/ui/lib/contract-namer.js @@ -0,0 +1,17 @@ +/* CONTRACT NAMER + * + * Takes an address, + * Returns a nicname if we have one stored, + * otherwise returns null. + */ + +const nicknames = {} + +module.exports = function(address) { + + if (address in nicknames) { + return nicknames[address] + } + + return null +} |