Merge branch 'master' into i216

This commit is contained in:
Dan Finlay 2016-05-26 15:21:27 -07:00
commit 921ced1630
10 changed files with 174 additions and 62 deletions

View File

@ -3,6 +3,8 @@
## Current Master ## Current Master
- Redesigned init, vault create, vault restore and seed confirmation screens. - Redesigned init, vault create, vault restore and seed confirmation screens.
- Added pending transactions to transaction list on account screen.
- Clicking a pending transaction takes you back to the transaction approval screen.
## 2.1.0 2016-05-26 ## 2.1.0 2016-05-26

View File

@ -1,7 +1,7 @@
{ {
"name": "__MSG_appName__", "name": "__MSG_appName__",
"short_name": "Metamask", "short_name": "Metamask",
"version": "2.0.1", "version": "2.1.0",
"manifest_version": 2, "manifest_version": 2,
"description": "__MSG_appDescription__", "description": "__MSG_appDescription__",
"icons": { "icons": {

View File

@ -201,6 +201,9 @@ AccountDetailScreen.prototype.transactionList = function() {
network, network,
unconfTxs, unconfTxs,
unconfMsgs, unconfMsgs,
viewPendingTx:(txId) => {
this.props.dispatch(actions.viewPendingTx(txId))
}
}) })
} }

View File

@ -76,6 +76,8 @@ var actions = {
txError: txError, txError: txError,
nextTx: nextTx, nextTx: nextTx,
previousTx: previousTx, previousTx: previousTx,
viewPendingTx: viewPendingTx,
VIEW_PENDING_TX: 'VIEW_PENDING_TX',
// app messages // app messages
showAccountDetail: showAccountDetail, showAccountDetail: showAccountDetail,
BACK_TO_ACCOUNT_DETAIL: 'BACK_TO_ACCOUNT_DETAIL', BACK_TO_ACCOUNT_DETAIL: 'BACK_TO_ACCOUNT_DETAIL',
@ -387,6 +389,13 @@ function nextTx() {
} }
} }
function viewPendingTx(txId) {
return {
type: actions.VIEW_PENDING_TX,
value: txId,
}
}
function previousTx() { function previousTx() {
return { return {
type: actions.PREVIOUS_TX, type: actions.PREVIOUS_TX,

View File

@ -0,0 +1,46 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const Identicon = require('./identicon')
module.exports = TransactionIcon
inherits(TransactionIcon, Component)
function TransactionIcon() {
Component.call(this)
}
TransactionIcon.prototype.render = function() {
const { transaction, txParams, isTx, isMsg } = this.props
if (transaction.status === 'rejected') {
return h('i.fa.fa-exclamation-triangle.fa-lg.error', {
style: {
width: '24px',
}
})
}
if (isMsg) {
return h('i.fa.fa-certificate.fa-lg', {
style: {
width: '24px',
}
})
}
if (txParams.to) {
return h(Identicon, {
diameter: 24,
address: txParams.to || transaction.hash,
})
} else {
return h('i.fa.fa-file-text-o.fa-lg', {
style: {
width: '24px',
}
})
}
}

View File

@ -2,13 +2,14 @@ const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const Identicon = require('./identicon')
const EtherBalance = require('./eth-balance') const EtherBalance = require('./eth-balance')
const addressSummary = require('../util').addressSummary const addressSummary = require('../util').addressSummary
const explorerLink = require('../../lib/explorer-link') const explorerLink = require('../../lib/explorer-link')
const formatBalance = require('../util').formatBalance const formatBalance = require('../util').formatBalance
const vreme = new (require('vreme')) const vreme = new (require('vreme'))
const TransactionIcon = require('./transaction-list-item-icon')
module.exports = TransactionListItem module.exports = TransactionListItem
@ -18,20 +19,36 @@ function TransactionListItem() {
} }
TransactionListItem.prototype.render = function() { TransactionListItem.prototype.render = function() {
const { transaction, i } = this.props const { transaction, i, network } = this.props
var date = formatDate(transaction.time) var date = formatDate(transaction.time)
let isLinkable = false
const numericNet = parseInt(network)
isLinkable = numericNet === 1 || numericNet === 2
var isMsg = ('msgParams' in transaction) var isMsg = ('msgParams' in transaction)
var isTx = ('txParams' in transaction) var isTx = ('txParams' in transaction)
var isPending = transaction.status === 'unconfirmed'
var txParams = transaction.txParams let txParams
if (isTx) {
txParams = transaction.txParams
} else if (isMsg) {
txParams = transaction.msgParams
}
const isClickable = ('hash' in transaction && isLinkable) || isPending
return ( return (
h(`.transaction-list-item.flex-row.flex-space-between${transaction.hash ? '.pointer' : ''}`, { h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
key: `tx-${transaction.id + i}`, key: `tx-${transaction.id + i}`,
onClick: (event) => { onClick: (event) => {
if (!transaction.hash) return if (isPending) {
this.props.showTx(transaction.id)
}
if (!transaction.hash || !isLinkable) return
var url = explorerLink(transaction.hash, parseInt(network)) var url = explorerLink(transaction.hash, parseInt(network))
chrome.tabs.create({ url }) chrome.tabs.create({ url })
}, },
@ -42,53 +59,59 @@ TransactionListItem.prototype.render = function() {
// large identicon // large identicon
h('.identicon-wrapper.flex-column.flex-center.select-none', [ h('.identicon-wrapper.flex-column.flex-center.select-none', [
identicon(txParams, transaction), transaction.status === 'unconfirmed' ? h('.red-dot', ' ') :
h(TransactionIcon, { txParams, transaction, isTx, isMsg }),
]), ]),
h('.flex-column', [ h('.flex-column', [
domainField(txParams),
h('div', date), h('div', date),
recipientField(txParams, transaction, isTx, isMsg),
recipientField(txParams, transaction),
]), ]),
h(EtherBalance, { isTx ? h(EtherBalance, {
value: txParams.value, value: txParams.value,
}), }) : h('.flex-column'),
]) ])
) )
} }
function domainField(txParams) {
return h('div', {
style: {
fontSize: 'small',
color: '#ABA9AA',
},
},[
txParams.origin,
])
}
function recipientField(txParams, transaction) { function recipientField(txParams, transaction, isTx, isMsg) {
if (txParams.to) { let message
return h('div', {
style: {
fontSize: 'small',
color: '#ABA9AA',
},
}, [
addressSummary(txParams.to),
failIfFailed(transaction),
])
if (isMsg) {
message = 'Signature Requested'
} else if (txParams.to) {
message = addressSummary(txParams.to)
} else { } else {
message = 'Contract Published'
return h('div', {
style: {
fontSize: 'small',
color: '#ABA9AA',
},
},[
'Contract Published',
failIfFailed(transaction),
])
} }
return h('div', {
style: {
fontSize: 'small',
color: '#ABA9AA',
},
},[
message,
failIfFailed(transaction),
])
} }
TransactionListItem.prototype.renderMessage = function() { TransactionListItem.prototype.renderMessage = function() {
const { transaction, i } = this.props const { transaction, i, network } = this.props
return h('div', 'wowie, thats a message') return h('div', 'wowie, thats a message')
} }
@ -96,31 +119,11 @@ function formatDate(date){
return vreme.format(new Date(date), 'March 16 2014 14:30') return vreme.format(new Date(date), 'March 16 2014 14:30')
} }
function identicon(txParams, transaction) {
if (transaction.status === 'rejected') {
return h('i.fa.fa-exclamation-triangle.fa-lg.error', {
style: {
width: '24px',
}
})
}
if (txParams.to) {
return h(Identicon, {
diameter: 24,
address: txParams.to || transaction.hash,
})
} else {
return h('i.fa.fa-file-text-o.fa-lg', {
style: {
width: '24px',
}
})
}
}
function failIfFailed(transaction) { function failIfFailed(transaction) {
if (transaction.status === 'rejected') { if (transaction.status === 'rejected') {
return h('span.error', ' (Rejected)')
}
if (transaction.status === 'failed') {
return h('span.error', ' (Failed)') return h('span.error', ' (Failed)')
} }
} }

View File

@ -14,7 +14,8 @@ function TransactionList() {
TransactionList.prototype.render = function() { TransactionList.prototype.render = function() {
const { txsToRender, network, unconfTxs, unconfMsgs } = this.props const { txsToRender, network, unconfTxs, unconfMsgs } = this.props
const transactions = txsToRender const transactions = txsToRender.concat(unconfMsgs)
.sort((a, b) => b.time - a.time)
return ( return (
@ -51,7 +52,10 @@ TransactionList.prototype.render = function() {
transactions.length ? transactions.length ?
transactions.map((transaction, i) => { transactions.map((transaction, i) => {
return h(TransactionListItem, { return h(TransactionListItem, {
transaction, i transaction, i, network,
showTx:(txId) => {
this.props.viewPendingTx(txId)
},
}) })
}) })
: :

View File

@ -291,6 +291,8 @@ input.large-input {
.identity-panel .identicon-wrapper { .identity-panel .identicon-wrapper {
margin: 4px; margin: 4px;
margin-top: 8px; margin-top: 8px;
display: flex;
align-items: center;
} }
.identity-panel .identicon-wrapper span { .identity-panel .identicon-wrapper span {

View File

@ -167,6 +167,20 @@ hr.horizontal-line {
background: white; background: white;
} }
.red-dot {
position: inherit;
background: red;
color: white;
border-radius: 10px;
height: 12px;
min-width: 12px;
margin-left: 6px;
display: flex;
align-items: center;
justify-content: center;
padding: 4px;
}
.pending-dot { .pending-dot {
background: red; background: red;
left: 57px; left: 57px;
@ -180,3 +194,8 @@ hr.horizontal-line {
justify-content: center; justify-content: center;
padding: 4px; padding: 4px;
} }
.ether-balance {
display: flex;
align-items: center;
}

View File

@ -12,10 +12,10 @@ function reduceApp(state, action) {
const pendingTxs = hasPendingTxs(state) const pendingTxs = hasPendingTxs(state)
let name = 'accounts' let name = 'accounts'
if (selectedAccount) { if (selectedAccount) {
defaultView = 'accountDetail' name = 'accountDetail'
} }
if (pendingTxs) { if (pendingTxs) {
defaultView = 'confTx' name = 'confTx'
} }
var defaultView = { var defaultView = {
@ -270,6 +270,17 @@ function reduceApp(state, action) {
} }
}) })
case actions.VIEW_PENDING_TX:
const context = indexForPending(state, action.value)
return extend(appState, {
transForward: true,
currentView: {
name: 'confTx',
context,
warning: null,
}
})
case actions.PREVIOUS_TX: case actions.PREVIOUS_TX:
return extend(appState, { return extend(appState, {
transForward: false, transForward: false,
@ -366,3 +377,16 @@ function hasPendingTxs (state) {
var unconfTxList = txHelper(unconfTxs, unconfMsgs) var unconfTxList = txHelper(unconfTxs, unconfMsgs)
return unconfTxList.length > 0 return unconfTxList.length > 0
} }
function indexForPending(state, txId) {
var unconfTxs = state.metamask.unconfTxs
var unconfMsgs = state.metamask.unconfMsgs
var unconfTxList = txHelper(unconfTxs, unconfMsgs)
let idx
unconfTxList.forEach((tx, i) => {
if (tx.id === txId) {
idx = i
}
})
return idx
}