mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Add retry button to TransactionListItem
This commit is contained in:
parent
fa8313f903
commit
5ddd9b55be
@ -16,6 +16,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__token-balance {
|
||||
margin-left: 12px;
|
||||
font-size: 1.5rem;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
margin-bottom: 12px;
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__primary-balance {
|
||||
font-size: 1.5rem;
|
||||
|
||||
|
@ -30,7 +30,7 @@ export default class TokenViewBalance extends PureComponent {
|
||||
<TokenBalance
|
||||
token={selectedToken}
|
||||
withSymbol
|
||||
className="token-view-balance__primary-balance"
|
||||
className="token-view-balance__token-balance"
|
||||
/>
|
||||
) : (
|
||||
<div className="token-view-balance__balance">
|
||||
|
@ -1 +1 @@
|
||||
export { default } from './transaction-action.container'
|
||||
export { default } from './transaction-action.component'
|
||||
|
@ -1,4 +0,0 @@
|
||||
import withMethodData from '../../higher-order-components/with-method-data'
|
||||
import TransactionAction from './transaction-action.component'
|
||||
|
||||
export default withMethodData(TransactionAction)
|
@ -2,21 +2,36 @@
|
||||
box-sizing: border-box;
|
||||
min-height: 74px;
|
||||
padding: 8px 20px;
|
||||
display: grid;
|
||||
grid-template-columns: 45px 1fr 1fr 1fr;
|
||||
grid-template-areas:
|
||||
"identicon action status primary-amount"
|
||||
"identicon nonce status secondary-amount";
|
||||
border-bottom: 1px solid $geyser;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
padding: 8px 20px 12px;
|
||||
grid-template-columns: 45px 5fr 3fr;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba($alto, .2);
|
||||
}
|
||||
|
||||
&__grid {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 45px 1fr 1fr 1fr;
|
||||
grid-template-areas:
|
||||
"nonce nonce nonce"
|
||||
"identicon action primary-amount"
|
||||
"identicon status secondary-amount";
|
||||
"identicon action status primary-amount"
|
||||
"identicon nonce status secondary-amount";
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
grid-template-columns: 45px 5fr 3fr;
|
||||
grid-template-areas:
|
||||
"nonce nonce nonce"
|
||||
"identicon action primary-amount"
|
||||
"identicon status secondary-amount";
|
||||
}
|
||||
}
|
||||
|
||||
&__identicon {
|
||||
@ -87,8 +102,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__retry {
|
||||
background: #d1edff;
|
||||
border-radius: 12px;
|
||||
font-size: .75rem;
|
||||
padding: 4px 12px;
|
||||
cursor: pointer;
|
||||
margin-top: 8px;
|
||||
|
||||
&:hover {
|
||||
background: rgba($alto, .2);
|
||||
@media screen and (max-width: $break-small) {
|
||||
font-size: .5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import TransactionAction from '../transaction-action'
|
||||
import { formatDate } from '../../util'
|
||||
import prefixForNetwork from '../../../lib/etherscan-prefix-for-network'
|
||||
import { CONFIRM_TRANSACTION_ROUTE } from '../../routes'
|
||||
import { UNAPPROVED_STATUS } from '../../constants/transactions'
|
||||
import { UNAPPROVED_STATUS, TOKEN_METHOD_TRANSFER } from '../../constants/transactions'
|
||||
import { hexToDecimal } from '../../helpers/conversions.util'
|
||||
|
||||
export default class TransactionListItem extends PureComponent {
|
||||
@ -15,6 +15,10 @@ export default class TransactionListItem extends PureComponent {
|
||||
transaction: PropTypes.object,
|
||||
ethTransactionAmount: PropTypes.string,
|
||||
fiatDisplayValue: PropTypes.string,
|
||||
methodData: PropTypes.object,
|
||||
showRetry: PropTypes.bool,
|
||||
retryTransaction: PropTypes.func,
|
||||
setSelectedToken: PropTypes.func,
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
@ -30,44 +34,92 @@ export default class TransactionListItem extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
handleRetryClick = event => {
|
||||
event.stopPropagation()
|
||||
|
||||
const {
|
||||
transaction: { txParams: { to } = {} },
|
||||
methodData: { name } = {},
|
||||
setSelectedToken,
|
||||
} = this.props
|
||||
|
||||
if (name === TOKEN_METHOD_TRANSFER) {
|
||||
setSelectedToken(to)
|
||||
}
|
||||
|
||||
this.resubmit()
|
||||
}
|
||||
|
||||
resubmit () {
|
||||
const { transaction: { id }, retryTransaction, history } = this.props
|
||||
retryTransaction(id)
|
||||
.then(id => history.push(`${CONFIRM_TRANSACTION_ROUTE}/${id}`))
|
||||
}
|
||||
|
||||
render () {
|
||||
const { transaction, ethTransactionAmount, fiatDisplayValue } = this.props
|
||||
const {
|
||||
transaction,
|
||||
ethTransactionAmount,
|
||||
fiatDisplayValue,
|
||||
methodData,
|
||||
showRetry,
|
||||
} = this.props
|
||||
const { txParams = {} } = transaction
|
||||
const nonce = hexToDecimal(txParams.nonce)
|
||||
|
||||
const nonceAndDateText = `#${nonce} - ${formatDate(transaction.time)}`
|
||||
const fiatDisplayText = `-${fiatDisplayValue}`
|
||||
const ethDisplayText = `-${ethTransactionAmount} ETH`
|
||||
|
||||
return (
|
||||
<div
|
||||
className="transaction-list-item"
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
<Identicon
|
||||
className="transaction-list-item__identicon"
|
||||
address={txParams.to}
|
||||
diameter={34}
|
||||
/>
|
||||
<TransactionAction
|
||||
transaction={transaction}
|
||||
className="transaction-list-item__action"
|
||||
/>
|
||||
<div className="transaction-list-item__nonce">
|
||||
{ `#${nonce} - ${formatDate(transaction.time)}` }
|
||||
</div>
|
||||
<TransactionStatus
|
||||
className="transaction-list-item__status"
|
||||
status={transaction.status}
|
||||
/>
|
||||
<div
|
||||
className="transaction-list-item__amount transaction-list-item__amount--primary"
|
||||
title={`-${fiatDisplayValue}`}
|
||||
>
|
||||
{ `-${fiatDisplayValue}` }
|
||||
</div>
|
||||
<div
|
||||
className="transaction-list-item__amount transaction-list-item__amount--secondary"
|
||||
title={`-${ethTransactionAmount} ETH`}
|
||||
>
|
||||
{ `-${ethTransactionAmount} ETH` }
|
||||
<div className="transaction-list-item__grid">
|
||||
<Identicon
|
||||
className="transaction-list-item__identicon"
|
||||
address={txParams.to}
|
||||
diameter={34}
|
||||
/>
|
||||
<TransactionAction
|
||||
transaction={transaction}
|
||||
methodData={methodData}
|
||||
className="transaction-list-item__action"
|
||||
/>
|
||||
<div
|
||||
className="transaction-list-item__nonce"
|
||||
title={nonceAndDateText}
|
||||
>
|
||||
{ nonceAndDateText }
|
||||
</div>
|
||||
<TransactionStatus
|
||||
className="transaction-list-item__status"
|
||||
status={transaction.status}
|
||||
/>
|
||||
<div
|
||||
className="transaction-list-item__amount transaction-list-item__amount--primary"
|
||||
title={fiatDisplayText}
|
||||
>
|
||||
{ fiatDisplayText }
|
||||
</div>
|
||||
<div
|
||||
className="transaction-list-item__amount transaction-list-item__amount--secondary"
|
||||
title={ethDisplayText}
|
||||
>
|
||||
{ ethDisplayText }
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
showRetry && !methodData.isFetching && (
|
||||
<div
|
||||
className="transaction-list-item__retry"
|
||||
onClick={this.handleRetryClick}
|
||||
>
|
||||
<span>Taking too long? Increase the gas price on your transaction</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { connect } from 'react-redux'
|
||||
import { withRouter } from 'react-router-dom'
|
||||
import { compose } from 'recompose'
|
||||
import withMethodData from '../../higher-order-components/with-method-data'
|
||||
import TransactionListItem from './transaction-list-item.component'
|
||||
import { setSelectedToken, retryTransaction } from '../../actions'
|
||||
import { getEthFromWeiHex, getValueFromWeiHex } from '../../helpers/conversions.util'
|
||||
import { formatCurrency } from '../../helpers/confirm-transaction/util'
|
||||
|
||||
@ -22,7 +24,15 @@ const mapStateToProps = (state, ownProps) => {
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
setSelectedToken: tokenAddress => dispatch(setSelectedToken(tokenAddress)),
|
||||
retryTransaction: transactionId => dispatch(retryTransaction(transactionId)),
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withRouter,
|
||||
connect(mapStateToProps),
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
withMethodData,
|
||||
)(TransactionListItem)
|
||||
|
@ -10,16 +10,24 @@ export default class TransactionList extends PureComponent {
|
||||
static defaultProps = {
|
||||
pendingTransactions: [],
|
||||
completedTransactions: [],
|
||||
transactionToRetry: {},
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
pendingTransactions: PropTypes.array,
|
||||
completedTransactions: PropTypes.array,
|
||||
transactionToRetry: PropTypes.object,
|
||||
}
|
||||
|
||||
shouldShowRetry = transaction => {
|
||||
const { transactionToRetry } = this.props
|
||||
const { id, submittedTime } = transaction
|
||||
return id === transactionToRetry.id && Date.now() - submittedTime > 30000
|
||||
}
|
||||
|
||||
renderTransactions () {
|
||||
const { t } = this.context
|
||||
const { pendingTransactions, completedTransactions } = this.props
|
||||
const { pendingTransactions = [], completedTransactions = [] } = this.props
|
||||
|
||||
return (
|
||||
<div className="transaction-list__transactions">
|
||||
@ -34,6 +42,7 @@ export default class TransactionList extends PureComponent {
|
||||
<TransactionListItem
|
||||
transaction={transaction}
|
||||
key={transaction.id}
|
||||
showRetry={this.shouldShowRetry(transaction)}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
@ -6,11 +6,15 @@ import {
|
||||
pendingTransactionsSelector,
|
||||
completedTransactionsSelector,
|
||||
} from '../../selectors/transactions'
|
||||
import { getLatestSubmittedTxWithEarliestNonce } from '../../helpers/transactions.util'
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const pendingTransactions = pendingTransactionsSelector(state)
|
||||
|
||||
return {
|
||||
pendingTransactions: pendingTransactionsSelector(state),
|
||||
completedTransactions: completedTransactionsSelector(state),
|
||||
pendingTransactions,
|
||||
transactionToRetry: getLatestSubmittedTxWithEarliestNonce(pendingTransactions),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,8 @@ import ethUtil from 'ethereumjs-util'
|
||||
import MethodRegistry from 'eth-method-registry'
|
||||
const registry = new MethodRegistry({ provider: global.ethereumProvider })
|
||||
|
||||
import { hexToDecimal } from './conversions.util'
|
||||
|
||||
import {
|
||||
TOKEN_METHOD_TRANSFER,
|
||||
TOKEN_METHOD_APPROVE,
|
||||
@ -55,3 +57,22 @@ export async function getMethodData (data = {}) {
|
||||
params: parsedResult.args,
|
||||
}
|
||||
}
|
||||
|
||||
export function getLatestSubmittedTxWithEarliestNonce (transactions = []) {
|
||||
if (!transactions.length) {
|
||||
return {}
|
||||
}
|
||||
|
||||
return transactions.reduce((acc, current) => {
|
||||
const accNonce = hexToDecimal(acc.nonce)
|
||||
const currentNonce = hexToDecimal(current.nonce)
|
||||
|
||||
if (currentNonce < accNonce) {
|
||||
return current
|
||||
} else if (currentNonce === accNonce) {
|
||||
return current.submittedTime > acc.submittedTime ? current : acc
|
||||
} else {
|
||||
return acc
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ const MIN_GAS_PRICE_BN = MIN_GAS_PRICE_GWEI_BN.mul(GWEI_FACTOR)
|
||||
|
||||
// formatData :: ( date: <Unix Timestamp> ) -> String
|
||||
function formatDate (date) {
|
||||
return vreme.format(new Date(date), 'March 16 2014, at 14:30')
|
||||
return vreme.format(new Date(date), '3/16/2014 at 14:30')
|
||||
}
|
||||
|
||||
var valueTable = {
|
||||
|
Loading…
Reference in New Issue
Block a user