Add "Retry" option for failed transactions (#7296)

* Begin mocking out retry ui

* Remove "Failed"

* I guess this works?

* Update corresponding test

* wip

* Ok, this appears to be working now

* cleanup

* Move this back to 3

* I don't think I need this

* Rename showRetry to showSpeedUp

* Address PR feedback

* Remove notes

* Rename shouldShowRetry -> shouldShowSpeedUp

* oops
feature/default_network_editable
ricky 5 years ago committed by Dan J Miller
parent c8878f46d4
commit af888d0ab2
  1. 3
      app/_locales/en/messages.json
  2. 3
      ui/app/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js
  3. 13
      ui/app/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js
  4. 1
      ui/app/components/app/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-container.test.js
  5. 2
      ui/app/components/app/transaction-list-item-details/tests/transaction-list-item-details.component.test.js
  6. 15
      ui/app/components/app/transaction-list-item-details/transaction-list-item-details.component.js
  7. 7
      ui/app/components/app/transaction-list-item/transaction-list-item.component.js
  8. 4
      ui/app/components/app/transaction-list/transaction-list.component.js
  9. 6
      ui/app/pages/routes/index.js
  10. 23
      ui/app/store/actions.js

@ -1442,6 +1442,9 @@
"viewOnEtherscan": { "viewOnEtherscan": {
"message": "View on Etherscan" "message": "View on Etherscan"
}, },
"retryTransaction": {
"message": "Retry Transaction"
},
"visitWebSite": { "visitWebSite": {
"message": "Visit our web site" "message": "Visit our web site"
}, },

@ -42,6 +42,7 @@ export default class GasModalPageContainer extends Component {
]), ]),
customPriceIsSafe: PropTypes.bool, customPriceIsSafe: PropTypes.bool,
isSpeedUp: PropTypes.bool, isSpeedUp: PropTypes.bool,
isRetry: PropTypes.bool,
disableSave: PropTypes.bool, disableSave: PropTypes.bool,
isEthereumNetwork: PropTypes.bool, isEthereumNetwork: PropTypes.bool,
} }
@ -80,6 +81,7 @@ export default class GasModalPageContainer extends Component {
gasEstimatesLoading, gasEstimatesLoading,
customPriceIsSafe, customPriceIsSafe,
isSpeedUp, isSpeedUp,
isRetry,
infoRowProps: { infoRowProps: {
transactionFee, transactionFee,
}, },
@ -99,6 +101,7 @@ export default class GasModalPageContainer extends Component {
gasEstimatesLoading={gasEstimatesLoading} gasEstimatesLoading={gasEstimatesLoading}
customPriceIsSafe={customPriceIsSafe} customPriceIsSafe={customPriceIsSafe}
isSpeedUp={isSpeedUp} isSpeedUp={isSpeedUp}
isRetry={isRetry}
isEthereumNetwork={isEthereumNetwork} isEthereumNetwork={isEthereumNetwork}
/> />
) )

@ -6,6 +6,7 @@ import {
setGasLimit, setGasLimit,
setGasPrice, setGasPrice,
createSpeedUpTransaction, createSpeedUpTransaction,
createRetryTransaction,
hideSidebar, hideSidebar,
updateSendAmount, updateSendAmount,
setGasTotal, setGasTotal,
@ -153,6 +154,7 @@ const mapStateToProps = (state, ownProps) => {
}, },
transaction: txData || transaction, transaction: txData || transaction,
isSpeedUp: transaction.status === 'submitted', isSpeedUp: transaction.status === 'submitted',
isRetry: transaction.status === 'failed',
txId: transaction.id, txId: transaction.id,
insufficientBalance, insufficientBalance,
gasEstimatesLoading, gasEstimatesLoading,
@ -187,6 +189,9 @@ const mapDispatchToProps = dispatch => {
createSpeedUpTransaction: (txId, gasPrice) => { createSpeedUpTransaction: (txId, gasPrice) => {
return dispatch(createSpeedUpTransaction(txId, gasPrice)) return dispatch(createSpeedUpTransaction(txId, gasPrice))
}, },
createRetryTransaction: (txId, gasPrice) => {
return dispatch(createRetryTransaction(txId, gasPrice))
},
hideGasButtonGroup: () => dispatch(hideGasButtonGroup()), hideGasButtonGroup: () => dispatch(hideGasButtonGroup()),
setCustomTimeEstimate: (timeEstimateInSeconds) => dispatch(setCustomTimeEstimate(timeEstimateInSeconds)), setCustomTimeEstimate: (timeEstimateInSeconds) => dispatch(setCustomTimeEstimate(timeEstimateInSeconds)),
hideSidebar: () => dispatch(hideSidebar()), hideSidebar: () => dispatch(hideSidebar()),
@ -206,6 +211,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
isConfirm, isConfirm,
txId, txId,
isSpeedUp, isSpeedUp,
isRetry,
insufficientBalance, insufficientBalance,
maxModeOn, maxModeOn,
customGasPrice, customGasPrice,
@ -221,6 +227,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
setGasData: dispatchSetGasData, setGasData: dispatchSetGasData,
updateConfirmTxGasAndCalculate: dispatchUpdateConfirmTxGasAndCalculate, updateConfirmTxGasAndCalculate: dispatchUpdateConfirmTxGasAndCalculate,
createSpeedUpTransaction: dispatchCreateSpeedUpTransaction, createSpeedUpTransaction: dispatchCreateSpeedUpTransaction,
createRetryTransaction: dispatchCreateRetryTransaction,
hideSidebar: dispatchHideSidebar, hideSidebar: dispatchHideSidebar,
cancelAndClose: dispatchCancelAndClose, cancelAndClose: dispatchCancelAndClose,
hideModal: dispatchHideModal, hideModal: dispatchHideModal,
@ -248,6 +255,10 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
dispatchCreateSpeedUpTransaction(txId, gasPrice) dispatchCreateSpeedUpTransaction(txId, gasPrice)
dispatchHideSidebar() dispatchHideSidebar()
dispatchCancelAndClose() dispatchCancelAndClose()
} else if (isRetry) {
dispatchCreateRetryTransaction(txId, gasPrice)
dispatchHideSidebar()
dispatchCancelAndClose()
} else { } else {
dispatchSetGasData(gasLimit, gasPrice) dispatchSetGasData(gasLimit, gasPrice)
dispatchHideGasButtonGroup() dispatchHideGasButtonGroup()
@ -268,7 +279,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
}, },
cancelAndClose: () => { cancelAndClose: () => {
dispatchCancelAndClose() dispatchCancelAndClose()
if (isSpeedUp) { if (isSpeedUp || isRetry) {
dispatchHideSidebar() dispatchHideSidebar()
} }
}, },

@ -157,6 +157,7 @@ describe('gas-modal-page-container container', () => {
}, },
insufficientBalance: true, insufficientBalance: true,
isSpeedUp: false, isSpeedUp: false,
isRetry: false,
txId: 34, txId: 34,
isEthereumNetwork: true, isEthereumNetwork: true,
isMainnet: true, isMainnet: true,

@ -70,7 +70,7 @@ describe('TransactionListItemDetails Component', () => {
const wrapper = shallow( const wrapper = shallow(
<TransactionListItemDetails <TransactionListItemDetails
transactionGroup={transactionGroup} transactionGroup={transactionGroup}
showRetry={true} showSpeedUp={true}
/>, />,
{ context: { t: (str1, str2) => str2 ? str1 + str2 : str1 } } { context: { t: (str1, str2) => str2 ? str1 + str2 : str1 } }
) )

@ -21,6 +21,7 @@ export default class TransactionListItemDetails extends PureComponent {
onCancel: PropTypes.func, onCancel: PropTypes.func,
onRetry: PropTypes.func, onRetry: PropTypes.func,
showCancel: PropTypes.bool, showCancel: PropTypes.bool,
showSpeedUp: PropTypes.bool,
showRetry: PropTypes.bool, showRetry: PropTypes.bool,
isEarliestNonce: PropTypes.bool, isEarliestNonce: PropTypes.bool,
cancelDisabled: PropTypes.bool, cancelDisabled: PropTypes.bool,
@ -123,6 +124,7 @@ export default class TransactionListItemDetails extends PureComponent {
const { justCopied } = this.state const { justCopied } = this.state
const { const {
transactionGroup, transactionGroup,
showSpeedUp,
showRetry, showRetry,
onCancel, onCancel,
onRetry, onRetry,
@ -138,7 +140,7 @@ export default class TransactionListItemDetails extends PureComponent {
<div>{ t('details') }</div> <div>{ t('details') }</div>
<div className="transaction-list-item-details__header-buttons"> <div className="transaction-list-item-details__header-buttons">
{ {
showRetry && ( showSpeedUp && (
<Button <Button
type="raised" type="raised"
onClick={this.handleRetry} onClick={this.handleRetry}
@ -172,6 +174,17 @@ export default class TransactionListItemDetails extends PureComponent {
<img src="/images/arrow-popout.svg" /> <img src="/images/arrow-popout.svg" />
</Button> </Button>
</Tooltip> </Tooltip>
{
showRetry && <Tooltip title={blockExplorerUrl ? t('viewOnCustomBlockExplorer', [blockExplorerUrl]) : t('retryTransaction')}>
<Button
type="raised"
onClick={this.handleRetry}
className="transaction-list-item-details__header-button"
>
<i className="fa fa-refresh"></i>
</Button>
</Tooltip>
}
</div> </div>
</div> </div>
<div className="transaction-list-item-details__body"> <div className="transaction-list-item-details__body">

@ -24,7 +24,7 @@ export default class TransactionListItem extends PureComponent {
showCancelModal: PropTypes.func, showCancelModal: PropTypes.func,
showCancel: PropTypes.bool, showCancel: PropTypes.bool,
hasEnoughCancelGas: PropTypes.bool, hasEnoughCancelGas: PropTypes.bool,
showRetry: PropTypes.bool, showSpeedUp: PropTypes.bool,
isEarliestNonce: PropTypes.bool, isEarliestNonce: PropTypes.bool,
showFiat: PropTypes.bool, showFiat: PropTypes.bool,
token: PropTypes.object, token: PropTypes.object,
@ -177,7 +177,7 @@ export default class TransactionListItem extends PureComponent {
primaryTransaction, primaryTransaction,
showCancel, showCancel,
hasEnoughCancelGas, hasEnoughCancelGas,
showRetry, showSpeedUp,
tokenData, tokenData,
transactionGroup, transactionGroup,
rpcPrefs, rpcPrefs,
@ -233,7 +233,8 @@ export default class TransactionListItem extends PureComponent {
<TransactionListItemDetails <TransactionListItemDetails
transactionGroup={transactionGroup} transactionGroup={transactionGroup}
onRetry={this.handleRetry} onRetry={this.handleRetry}
showRetry={showRetry} showSpeedUp={showSpeedUp}
showRetry={getStatusKey(primaryTransaction) === 'failed'}
isEarliestNonce={isEarliestNonce} isEarliestNonce={isEarliestNonce}
onCancel={this.handleCancel} onCancel={this.handleCancel}
showCancel={showCancel} showCancel={showCancel}

@ -37,7 +37,7 @@ export default class TransactionList extends PureComponent {
} }
} }
shouldShowRetry = (transactionGroup, isEarliestNonce) => { shouldShowSpeedUp = (transactionGroup, isEarliestNonce) => {
const { transactions = [], hasRetried } = transactionGroup const { transactions = [], hasRetried } = transactionGroup
const [earliestTransaction = {}] = transactions const [earliestTransaction = {}] = transactions
const { submittedTime } = earliestTransaction const { submittedTime } = earliestTransaction
@ -100,7 +100,7 @@ export default class TransactionList extends PureComponent {
<TransactionListItem <TransactionListItem
transactionGroup={transactionGroup} transactionGroup={transactionGroup}
key={`${transactionGroup.nonce}:${index}`} key={`${transactionGroup.nonce}:${index}`}
showRetry={isPendingTx && this.shouldShowRetry(transactionGroup, index === 0)} showSpeedUp={isPendingTx && this.shouldShowSpeedUp(transactionGroup, index === 0)}
showCancel={isPendingTx && this.shouldShowCancel(transactionGroup)} showCancel={isPendingTx && this.shouldShowCancel(transactionGroup)}
isEarliestNonce={isPendingTx && index === 0} isEarliestNonce={isPendingTx && index === 0}
token={selectedToken} token={selectedToken}

@ -206,6 +206,10 @@ class Routes extends Component {
} }
: null : null
const sidebarShouldClose = sidebarTransaction &&
!sidebarTransaction.status === 'failed' &&
!submittedPendingTransactions.find(({ id }) => id === sidebarTransaction.id)
return ( return (
<div <div
className={classnames('app', { 'mouse-user-styles': isMouseUser})} className={classnames('app', { 'mouse-user-styles': isMouseUser})}
@ -232,7 +236,7 @@ class Routes extends Component {
} }
<Sidebar <Sidebar
sidebarOpen={sidebarIsOpen} sidebarOpen={sidebarIsOpen}
sidebarShouldClose={sidebarTransaction && !submittedPendingTransactions.find(({ id }) => id === sidebarTransaction.id)} sidebarShouldClose={sidebarShouldClose}
hideSidebar={this.props.hideSidebar} hideSidebar={this.props.hideSidebar}
transitionName={sidebarTransitionName} transitionName={sidebarTransitionName}
type={sidebarType} type={sidebarType}

@ -350,6 +350,7 @@ var actions = {
createCancelTransaction, createCancelTransaction,
createSpeedUpTransaction, createSpeedUpTransaction,
createRetryTransaction,
approveProviderRequestByOrigin, approveProviderRequestByOrigin,
rejectProviderRequestByOrigin, rejectProviderRequestByOrigin,
@ -1860,6 +1861,28 @@ function createSpeedUpTransaction (txId, customGasPrice) {
} }
} }
function createRetryTransaction (txId, customGasPrice) {
log.debug('background.createRetryTransaction')
let newTx
return dispatch => {
return new Promise((resolve, reject) => {
background.createSpeedUpTransaction(txId, customGasPrice, (err, newState) => {
if (err) {
dispatch(actions.displayWarning(err.message))
return reject(err)
}
const { selectedAddressTxList } = newState
newTx = selectedAddressTxList[selectedAddressTxList.length - 1]
resolve(newState)
})
})
.then(newState => dispatch(actions.updateMetamaskState(newState)))
.then(() => newTx)
}
}
// //
// config // config
// //

Loading…
Cancel
Save