1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Merge pull request #997 from MetaMask/integrateTxManagerUI

Integrate tx manager ui
This commit is contained in:
kumavis 2017-01-13 11:39:32 -08:00 committed by GitHub
commit c1253016fc
11 changed files with 99 additions and 66 deletions

View File

@ -2,6 +2,7 @@
## Current Master ## Current Master
- Create visible difference in transaction history between a approved but not yet included in a block transaction and a transaction who has been confirmed.
- Fix memory leak in RPC Cache - Fix memory leak in RPC Cache
- Override RPC commands eth_syncing and web3_clientVersion - Override RPC commands eth_syncing and web3_clientVersion
- Remove certain non-essential permissions from certain builds. - Remove certain non-essential permissions from certain builds.

View File

@ -27,7 +27,6 @@ function triggerUi () {
if (!popupIsOpen) notification.show() if (!popupIsOpen) notification.show()
} }
// On first install, open a window to MetaMask website to how-it-works. // On first install, open a window to MetaMask website to how-it-works.
extension.runtime.onInstalled.addListener(function (details) { extension.runtime.onInstalled.addListener(function (details) {
if ((details.reason === 'install') && (!METAMASK_DEBUG)) { if ((details.reason === 'install') && (!METAMASK_DEBUG)) {
extension.tabs.create({url: 'https://metamask.io/#how-it-works'}) extension.tabs.create({url: 'https://metamask.io/#how-it-works'})

View File

@ -95,7 +95,6 @@ module.exports = class KeyringController extends EventEmitter {
isInitialized: (!!wallet || !!vault), isInitialized: (!!wallet || !!vault),
isUnlocked: Boolean(this.password), isUnlocked: Boolean(this.password),
isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(), isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(),
transactions: this.configManager.getTxList(),
unconfMsgs: messageManager.unconfirmedMsgs(), unconfMsgs: messageManager.unconfirmedMsgs(),
messages: messageManager.getMsgList(), messages: messageManager.getMsgList(),
selectedAccount: address, selectedAccount: address,

View File

@ -65,6 +65,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.ethStore.on('update', this.sendUpdate.bind(this)) this.ethStore.on('update', this.sendUpdate.bind(this))
this.keyringController.on('update', this.sendUpdate.bind(this)) this.keyringController.on('update', this.sendUpdate.bind(this))
this.txManager.on('update', this.sendUpdate.bind(this))
} }
getState () { getState () {

View File

@ -83,6 +83,7 @@ module.exports = class TransactionManager extends EventEmitter {
var index = txList.findIndex(txData => txData.id === txId) var index = txList.findIndex(txData => txData.id === txId)
txList[index] = txMeta txList[index] = txMeta
this._saveTxList(txList) this._saveTxList(txList)
this.emit('update')
} }
get unapprovedTxCount () { get unapprovedTxCount () {
@ -113,10 +114,26 @@ module.exports = class TransactionManager extends EventEmitter {
txDidComplete (txMeta, onTxDoneCb, cb, err) { txDidComplete (txMeta, onTxDoneCb, cb, err) {
if (err) return cb(err) if (err) return cb(err)
var {maxCost, txFee} = this.getMaxTxCostAndFee(txMeta)
txMeta.maxCost = maxCost
txMeta.txFee = txFee
this.addTx(txMeta, onTxDoneCb) this.addTx(txMeta, onTxDoneCb)
cb(null, txMeta) cb(null, txMeta)
} }
getMaxTxCostAndFee (txMeta) {
var txParams = txMeta.txParams
var gasMultiplier = txMeta.gasMultiplier
var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txMeta.estimatedGas), 16)
var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16)
gasPrice = gasPrice.mul(new BN(gasMultiplier * 100), 10).div(new BN(100, 10))
var txFee = gasCost.mul(gasPrice)
var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16)
var maxCost = txValue.add(txFee)
return {maxCost, txFee}
}
getUnapprovedTxList () { getUnapprovedTxList () {
var txList = this.getTxList() var txList = this.getTxList()
return txList.filter((txMeta) => txMeta.status === 'unapproved') return txList.filter((txMeta) => txMeta.status === 'unapproved')
@ -167,7 +184,6 @@ module.exports = class TransactionManager extends EventEmitter {
this.updateTx(metaTx) this.updateTx(metaTx)
var rawTx = ethUtil.bufferToHex(tx.serialize()) var rawTx = ethUtil.bufferToHex(tx.serialize())
return Promise.resolve(rawTx) return Promise.resolve(rawTx)
} }
/* /*
@ -240,19 +256,31 @@ module.exports = class TransactionManager extends EventEmitter {
// checks if a signed tx is in a block and // checks if a signed tx is in a block and
// if included sets the tx status as 'confirmed' // if included sets the tx status as 'confirmed'
checkForTxInBlock () { checkForTxInBlock () {
var signedTxList = this.getFilteredTxList({status: 'signed', err: undefined}) var signedTxList = this.getFilteredTxList({status: 'signed'})
if (!signedTxList.length) return if (!signedTxList.length) return
signedTxList.forEach((tx) => { signedTxList.forEach((txMeta) => {
var txHash = tx.hash var txHash = txMeta.hash
var txId = tx.id var txId = txMeta.id
if (!txHash) return if (!txHash) {
this.txProviderUtils.query.getTransactionByHash(txHash, (err, txMeta) => { txMeta.err = {
if (err || !txMeta) { errCode: 'No hash was provided',
tx.err = err || 'Tx could possibly have not been submitted' message: 'We had an error while submitting this transaction, please try again.',
this.updateTx(tx)
return txMeta ? console.error(err) : console.debug(`txMeta is ${txMeta} for:`, tx)
} }
if (txMeta.blockNumber) { this.updateTx(txMeta)
return this._setTxStatus(txId, 'failed')
}
this.txProviderUtils.query.getTransactionByHash(txHash, (err, txParams) => {
if (err || !txParams) {
if (!txParams) return
txMeta.err = {
isWarning: true,
errorCode: err,
message: 'There was a problem loading this transaction.',
}
this.updateTx(txMeta)
return console.error(err)
}
if (txParams.blockNumber) {
this.setTxStatusConfirmed(txId) this.setTxStatusConfirmed(txId)
} }
}) })

View File

@ -26,11 +26,10 @@ function mapStateToProps (state) {
accounts: state.metamask.accounts, accounts: state.metamask.accounts,
address: state.metamask.selectedAccount, address: state.metamask.selectedAccount,
accountDetail: state.appState.accountDetail, accountDetail: state.appState.accountDetail,
transactions: state.metamask.transactions,
network: state.metamask.network, network: state.metamask.network,
unconfTxs: valuesFor(state.metamask.unconfTxs),
unconfMsgs: valuesFor(state.metamask.unconfMsgs), unconfMsgs: valuesFor(state.metamask.unconfMsgs),
shapeShiftTxList: state.metamask.shapeShiftTxList, shapeShiftTxList: state.metamask.shapeShiftTxList,
transactions: state.metamask.selectedAccountTxList || [],
} }
} }
@ -248,20 +247,10 @@ AccountDetailScreen.prototype.subview = function () {
} }
AccountDetailScreen.prototype.transactionList = function () { AccountDetailScreen.prototype.transactionList = function () {
const { transactions, unconfTxs, unconfMsgs, address, network, shapeShiftTxList } = this.props const {transactions, unconfMsgs, address, network, shapeShiftTxList } = this.props
var txsToRender = transactions.concat(unconfTxs)
// only transactions that are from the current address
.filter(tx => tx.txParams.from === address)
// only transactions that are on the current network
.filter(tx => tx.txParams.metamaskNetworkId === network)
// sort by recency
.sort((a, b) => b.time - a.time)
return h(TransactionList, { return h(TransactionList, {
txsToRender, transactions: transactions.sort((a, b) => b.time - a.time),
network, network,
unconfTxs,
unconfMsgs, unconfMsgs,
address, address,
shapeShiftTxList, shapeShiftTxList,

View File

@ -7,8 +7,6 @@ const EthBalance = require('./eth-balance')
const util = require('../util') const util = require('../util')
const addressSummary = util.addressSummary const addressSummary = util.addressSummary
const nameForAddress = require('../../lib/contract-namer') const nameForAddress = require('../../lib/contract-namer')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
module.exports = PendingTxDetails module.exports = PendingTxDetails
@ -29,15 +27,9 @@ PTXP.render = function () {
var account = props.accounts[address] var account = props.accounts[address]
var balance = account ? account.balance : '0x0' var balance = account ? account.balance : '0x0'
var gasMultiplier = txData.gasMultiplier var txFee = txData.txFee || ''
var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txData.estimatedGas), 16) var maxCost = txData.maxCost || ''
var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16)
gasPrice = gasPrice.mul(new BN(gasMultiplier * 100), 10).div(new BN(100, 10))
var txFee = gasCost.mul(gasPrice)
var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16)
var maxCost = txValue.add(txFee)
var dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0 var dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0
var imageify = props.imageifyIdenticons === undefined ? true : props.imageifyIdenticons var imageify = props.imageifyIdenticons === undefined ? true : props.imageifyIdenticons
return ( return (

View File

@ -13,13 +13,40 @@ function TransactionIcon () {
TransactionIcon.prototype.render = function () { TransactionIcon.prototype.render = function () {
const { transaction, txParams, isMsg } = this.props const { transaction, txParams, isMsg } = this.props
switch (transaction.status) {
case 'unapproved':
return h('.unapproved-tx', {
style: {
width: '24px',
height: '24px',
background: '#4dffff',
border: 'solid',
borderColor: '#AEAEAE',
borderWidth: '0.5px',
borderRadius: '13px',
},
})
if (transaction.status === 'rejected') { case 'rejected':
return h('i.fa.fa-exclamation-triangle.fa-lg.warning', { return h('i.fa.fa-exclamation-triangle.fa-lg.warning', {
style: { style: {
width: '24px', width: '24px',
}, },
}) })
case 'failed':
return h('i.fa.fa-exclamation-triangle.fa-lg.error', {
style: {
width: '24px',
},
})
case 'signed':
return h('i.fa.fa-ellipsis-h', {
style: {
fontSize: '27px',
},
})
} }
if (isMsg) { if (isMsg) {

View File

@ -8,6 +8,7 @@ const explorerLink = require('../../lib/explorer-link')
const CopyButton = require('./copyButton') const CopyButton = require('./copyButton')
const vreme = new (require('vreme')) const vreme = new (require('vreme'))
const extension = require('../../../app/scripts/lib/extension') const extension = require('../../../app/scripts/lib/extension')
const Tooltip = require('./tooltip')
const TransactionIcon = require('./transaction-list-item-icon') const TransactionIcon = require('./transaction-list-item-icon')
const ShiftListItem = require('./shift-list-item') const ShiftListItem = require('./shift-list-item')
@ -59,11 +60,7 @@ TransactionListItem.prototype.render = function () {
}, [ }, [
h('.identicon-wrapper.flex-column.flex-center.select-none', [ h('.identicon-wrapper.flex-column.flex-center.select-none', [
transaction.status === 'unapproved' ? h('i.fa.fa-ellipsis-h', { h('.pop-hover', {
style: {
fontSize: '27px',
},
}) : h('.pop-hover', {
onClick: (event) => { onClick: (event) => {
event.stopPropagation() event.stopPropagation()
if (!isTx || isPending) return if (!isTx || isPending) return
@ -139,7 +136,14 @@ function failIfFailed (transaction) {
if (transaction.status === 'rejected') { if (transaction.status === 'rejected') {
return h('span.error', ' (Rejected)') return h('span.error', ' (Rejected)')
} }
if (transaction.status === 'failed') { if (transaction.err) {
return h('span.error', ' (Failed)')
return h(Tooltip, {
title: transaction.err.message,
position: 'bottom',
}, [
h('span.error', ' (Failed)'),
])
} }
} }

View File

@ -13,12 +13,13 @@ function TransactionList () {
} }
TransactionList.prototype.render = function () { TransactionList.prototype.render = function () {
const { txsToRender, network, unconfMsgs } = this.props const { transactions, network, unconfMsgs } = this.props
var shapeShiftTxList var shapeShiftTxList
if (network === '1') { if (network === '1') {
shapeShiftTxList = this.props.shapeShiftTxList shapeShiftTxList = this.props.shapeShiftTxList
} }
const transactions = !shapeShiftTxList ? txsToRender.concat(unconfMsgs) : txsToRender.concat(unconfMsgs, shapeShiftTxList) const txsToRender = !shapeShiftTxList ? transactions.concat(unconfMsgs) : transactions.concat(unconfMsgs, shapeShiftTxList)
.sort((a, b) => b.time - a.time) .sort((a, b) => b.time - a.time)
return ( return (
@ -55,8 +56,8 @@ TransactionList.prototype.render = function () {
}, },
}, [ }, [
transactions.length txsToRender.length
? transactions.map((transaction, i) => { ? txsToRender.map((transaction, i) => {
let key let key
switch (transaction.key) { switch (transaction.key) {
case 'shapeshift': case 'shapeshift':

View File

@ -43,10 +43,9 @@ ConfirmTxScreen.prototype.render = function () {
var unconfMsgs = state.unconfMsgs var unconfMsgs = state.unconfMsgs
var unconfTxList = txHelper(unconfTxs, unconfMsgs, network) var unconfTxList = txHelper(unconfTxs, unconfMsgs, network)
var index = state.index !== undefined ? state.index : 0 var index = state.index !== undefined ? state.index : 0
var txData = unconfTxList[index] || unconfTxList[0] || {} var txData = unconfTxList[index] || {txParams: {}}
var txParams = txData.txParams || {} var txParams = txData.txParams || {}
var isNotification = isPopupOrNotification() === 'notification' var isNotification = isPopupOrNotification() === 'notification'
return ( return (
h('.flex-column.flex-grow', [ h('.flex-column.flex-grow', [
@ -125,17 +124,10 @@ function currentTxView (opts) {
} }
ConfirmTxScreen.prototype.checkBalanceAgainstTx = function (txData) { ConfirmTxScreen.prototype.checkBalanceAgainstTx = function (txData) {
var state = this.props var state = this.props
var address = txData.txParams.from || state.selectedAccount
var txParams = txData.txParams || {}
var address = txParams.from || state.selectedAccount
var account = state.accounts[address] var account = state.accounts[address]
var balance = account ? account.balance : '0x0' var balance = account ? account.balance : '0x0'
var maxCost = new BN(txData.maxCost)
var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txData.estimatedGas), 16)
var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16)
var txFee = gasCost.mul(gasPrice)
var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16)
var maxCost = txValue.add(txFee)
var balanceBn = new BN(ethUtil.stripHexPrefix(balance), 16) var balanceBn = new BN(ethUtil.stripHexPrefix(balance), 16)
return maxCost.gt(balanceBn) return maxCost.gt(balanceBn)