diff options
author | Kevin Serrano <kevgagser@gmail.com> | 2016-07-08 07:33:06 +0800 |
---|---|---|
committer | Kevin Serrano <kevgagser@gmail.com> | 2016-07-08 07:33:06 +0800 |
commit | 86b7cc6637edefd2f628238a2abff540c82e1a78 (patch) | |
tree | a6126d8b83ec7b66cd2a138c670863beedbefba1 /ui | |
parent | 9a9c1f2cd48235be47e65b42c84bd9bf55ecbec4 (diff) | |
parent | 44a45f38be5251eacd2c43ebbc55acf34f65084c (diff) | |
download | tangerine-wallet-browser-86b7cc6637edefd2f628238a2abff540c82e1a78.tar tangerine-wallet-browser-86b7cc6637edefd2f628238a2abff540c82e1a78.tar.gz tangerine-wallet-browser-86b7cc6637edefd2f628238a2abff540c82e1a78.tar.bz2 tangerine-wallet-browser-86b7cc6637edefd2f628238a2abff540c82e1a78.tar.lz tangerine-wallet-browser-86b7cc6637edefd2f628238a2abff540c82e1a78.tar.xz tangerine-wallet-browser-86b7cc6637edefd2f628238a2abff540c82e1a78.tar.zst tangerine-wallet-browser-86b7cc6637edefd2f628238a2abff540c82e1a78.zip |
Resolve merge conflict.
Diffstat (limited to 'ui')
-rw-r--r-- | ui/app/components/account-panel.js | 2 | ||||
-rw-r--r-- | ui/app/components/eth-balance.js | 31 | ||||
-rw-r--r-- | ui/app/components/mini-account-panel.js | 74 | ||||
-rw-r--r-- | ui/app/components/pending-tx-details.js | 223 | ||||
-rw-r--r-- | ui/app/components/pending-tx.js | 38 | ||||
-rw-r--r-- | ui/app/conf-tx.js | 4 | ||||
-rw-r--r-- | ui/app/css/index.css | 4 | ||||
-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 | 31 |
10 files changed, 361 insertions, 69 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 e31b3ed97..301674083 100644 --- a/ui/app/components/eth-balance.js +++ b/ui/app/components/eth-balance.js @@ -12,9 +12,11 @@ function EthBalanceComponent () { } EthBalanceComponent.prototype.render = function () { - var state = this.props - var style = state.style - var value = formatBalance(state.value) + var props = this.props + var style = props.style + + const value = formatBalance(props.value) + return ( h('.ether-balance', { @@ -30,30 +32,37 @@ EthBalanceComponent.prototype.render = function () { ) } EthBalanceComponent.prototype.renderBalance = function (value) { + const props = this.props if (value === 'None') return value var balanceObj = generateBalanceObject(value) - var balance = balanceObj.balance var label = balanceObj.label + var tagName = props.inline ? 'span' : 'div' + var topTag = props.inline ? 'div' : '.flex-column' return ( + h(Tooltip, { position: 'bottom', title: value.split(' ')[0], }, [ - h('.flex-column', { + h(topTag, { style: { alignItems: 'flex-end', - lineHeight: '13px', - fontFamily: 'Montserrat Light', + lineHeight: props.fontSize || '13px', + fontFamily: 'Montserrat Regular', textRendering: 'geometricPrecision', }, }, [ - h('div', balance), - h('div', { + h(tagName, { + style: { + fontSize: props.fontSize || '12px', + }, + }, balance + ' '), + h(tagName, { style: { - color: ' #AEAEAE', - fontSize: '12px', + color: props.labelColor || '#AEAEAE', + fontSize: props.fontSize || '12px', }, }, label), ]), diff --git a/ui/app/components/mini-account-panel.js b/ui/app/components/mini-account-panel.js new file mode 100644 index 000000000..c09cf5b7a --- /dev/null +++ b/ui/app/components/mini-account-panel.js @@ -0,0 +1,74 @@ +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 { 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', + }, + }, this.props.children), + ]) + ) +} + +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..b2c16e772 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -2,10 +2,17 @@ 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 EtherBalance = require('./eth-balance') 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 +21,204 @@ 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 + 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, { + imageSeed: address, + imageifyIdenticons: props.imageifyIdenticons, + picOrder: 'right', + }, [ + h('span.font-small', { + style: { + fontFamily: 'Montserrat Bold, Montserrat, sans-serif', + }, + }, identity.name), + h('span.font-small', { + style: { + fontFamily: 'Montserrat Light, Montserrat, sans-serif', + }, + }, addressSummary(address, 6, 4, false)), + + h('span.font-small', { + style: { + fontFamily: 'Montserrat Light, Montserrat, sans-serif', + }, + }, h(EtherBalance, { + value: balance, + inline: true, + })), + + ]), + + h('img', { + src: 'images/forward-carrat.svg', + style: { + padding: '5px 6px 0px 10px', + height: '37px', + }, + }), + + this.miniAccountPanelForRecipient(), + ]), - // tx data - h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [ + h('style', ` + .table-box { + margin: 7px 0px 0px 0px; + width: 100%; + } + .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 25px; + } + .table-box .row .value { + font-family: Montserrat Regular; + } + `), - h('.flex-row.flex-space-between', [ - h('label.font-small', 'TO ADDRESS'), - h('span.font-small', addressSummary(txParams.to)), + h('.table-box', [ + + h('.row', [ + h('.cell.label', 'Amount'), + h('.cell.value', formatBalance(txParams.value)), ]), - 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)), ]), - 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: 'white', + padding: '10px 25px', + }, + }, [ + h('.cell.label', 'Max Total'), + h('.cell.value', { + style: { + display: 'flex', + alignItems: 'center', + }, + }, [ + h(EtherBalance, { + value: maxCost, + inline: true, + labelColor: 'black', + fontSize: '16px', + }), + ]), ]), - ]), + + h('.cell.row', { + style: { + background: '#f7f7f7', + paddingBottom: '0px', + }, + }, [ + h('.cell.label'), + h('.cell.value', { + style: { + fontFamily: 'Montserrat Light', + fontSize: '11px', + }, + }, `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, { + imageSeed: txParams.to, + imageifyIdenticons: props.imageifyIdenticons, + picOrder: 'left', + }, [ + h('span.font-small', { + style: { + fontFamily: 'Montserrat Bold, Montserrat, sans-serif', + }, + }, nameForAddress(txParams.to, props.identities)), + h('span.font-small', { + style: { + fontFamily: 'Montserrat Light, Montserrat, sans-serif', + }, + }, addressSummary(txParams.to, 6, 4, false)), + ]) + + } else { + return h(MiniAccountPanel, { + imageifyIdenticons: props.imageifyIdenticons, + picOrder: 'left', + }, [ + + h('span.font-small', { + style: { + fontFamily: 'Montserrat Bold, Montserrat, sans-serif', + }, + }, 'New Contract'), + + ]) + } +} + +// 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..1feedbbbc 100644 --- a/ui/app/components/pending-tx.js +++ b/ui/app/components/pending-tx.js @@ -21,29 +21,35 @@ PendingTx.prototype.render = function () { key: txData.id, }, [ - // header - h('h3', { - style: { - fontWeight: 'bold', - textAlign: 'center', - }, - }, 'Submit Transaction'), - // tx info h(PendingTxDetails, state), + h('style', ` + .conf-buttons button { + margin-left: 10px; + text-transform: uppercase; + } + `), + // send + cancel - h('.flex-row.flex-space-around', [ - h('button', { + h('.flex-row.flex-space-around.conf-buttons', { + style: { + display: 'flex', + justifyContent: 'flex-end', + margin: '14px 25px', + }, + }, [ + h('button.confirm', { + onClick: state.sendTransaction, + style: { background: 'rgb(251,117,1)' }, + }, 'Accept'), + + h('button.cancel', { onClick: state.cancelTransaction, + style: { background: 'rgb(254,35,17)' }, }, 'Reject'), - h('button', { - onClick: state.sendTransaction, - }, 'Approve'), ]), - ]) - ) - } + diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index 8455826b8..db876dd9b 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -39,14 +39,14 @@ ConfirmTxScreen.prototype.render = function () { return ( - h('.unconftx-section.flex-column.flex-grow', [ + h('.flex-column.flex-grow', [ // subtitle and nav h('.section-title.flex-row.flex-center', [ h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', { onClick: this.goHome.bind(this), }), - h('h2.page-subtitle', 'Confirmation'), + h('h2.page-subtitle', 'Confirm Transaction'), ]), h('h3', { diff --git a/ui/app/css/index.css b/ui/app/css/index.css index de694f873..3f52b6ed4 100644 --- a/ui/app/css/index.css +++ b/ui/app/css/index.css @@ -411,10 +411,6 @@ input.large-input { } /* tx confirm */ -.unconftx-section { - margin: 0 20px; -} - .unconftx-section input[type=password] { height: 22px; padding: 2px; 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 befb2aad8..b86bc6035 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, @@ -44,10 +45,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) { @@ -95,7 +105,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..62c7933e8 --- /dev/null +++ b/ui/lib/contract-namer.js @@ -0,0 +1,31 @@ +/* CONTRACT NAMER + * + * Takes an address, + * Returns a nicname if we have one stored, + * otherwise returns null. + */ + +// Nickname keys must be stored in lower case. +const nicknames = {} + +module.exports = function(addr, identities = {}) { + + const address = addr.toLowerCase() + const ids = hashFromIdentities(identities) + + console.dir({ addr, ids }) + return addrFromHash(address, ids) || addrFromHash(address, nicknames) +} + +function hashFromIdentities(identities) { + const result = {} + for (let key in identities) { + result[key] = identities[key].name + } + return result +} + +function addrFromHash(addr, hash) { + const address = addr.toLowerCase() + return hash[address] || null +} |