diff options
author | Alexander Tseung <alextsg@gmail.com> | 2018-06-23 14:52:45 +0800 |
---|---|---|
committer | Alexander Tseung <alextsg@gmail.com> | 2018-07-07 07:27:08 +0800 |
commit | ea9d51e427b8e607e612a01629bebf153e516ad9 (patch) | |
tree | 6363cd72b7517442c718901cc8b8ed023d134081 /ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js | |
parent | b4aaf30d6fe829f18dea68a5e6cc321b9fb00d4e (diff) | |
download | tangerine-wallet-browser-ea9d51e427b8e607e612a01629bebf153e516ad9.tar.gz tangerine-wallet-browser-ea9d51e427b8e607e612a01629bebf153e516ad9.tar.zst tangerine-wallet-browser-ea9d51e427b8e607e612a01629bebf153e516ad9.zip |
Refactor and redesign confirm transaction views
Diffstat (limited to 'ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js')
-rw-r--r-- | ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js new file mode 100644 index 000000000..3b60a5d5d --- /dev/null +++ b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -0,0 +1,382 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import ConfirmPageContainer, { ConfirmDetailRow } from '../../confirm-page-container' +import { formatCurrency, getHexGasTotal } from '../../../helpers/confirm-transaction/util' +import { isBalanceSufficient } from '../../send_/send.utils' +import { DEFAULT_ROUTE } from '../../../routes' +import { conversionGreaterThan } from '../../../conversion-util' +import { MIN_GAS_LIMIT_DEC } from '../../send_/send.constants' + +export default class ConfirmTransactionBase extends Component { + static contextTypes = { + t: PropTypes.func, + } + + static propTypes = { + match: PropTypes.object, + history: PropTypes.object, + // Redux props + txData: PropTypes.object, + tokenData: PropTypes.object, + tokenProps: PropTypes.object, + isTxReprice: PropTypes.bool, + nonce: PropTypes.string, + fromName: PropTypes.string, + fromAddress: PropTypes.string, + toName: PropTypes.string, + toAddress: PropTypes.string, + transactionStatus: PropTypes.string, + ethTransactionAmount: PropTypes.string, + ethTransactionFee: PropTypes.string, + ethTransactionTotal: PropTypes.string, + fiatTransactionAmount: PropTypes.string, + fiatTransactionFee: PropTypes.string, + fiatTransactionTotal: PropTypes.string, + hexGasTotal: PropTypes.string, + balance: PropTypes.string, + currentCurrency: PropTypes.string, + conversionRate: PropTypes.number, + setTransactionToConfirm: PropTypes.func, + clearConfirmTransaction: PropTypes.func, + cancelTransaction: PropTypes.func, + clearSend: PropTypes.func, + sendTransaction: PropTypes.func, + editTransaction: PropTypes.func, + showCustomizeGasModal: PropTypes.func, + updateGasAndCalculate: PropTypes.func, + showTransactionConfirmedModal: PropTypes.func, + // Component props + action: PropTypes.string, + hideDetails: PropTypes.bool, + hideData: PropTypes.bool, + detailsComponent: PropTypes.node, + dataComponent: PropTypes.node, + summaryComponent: PropTypes.node, + contentComponent: PropTypes.node, + title: PropTypes.string, + subtitle: PropTypes.string, + hideSubtitle: PropTypes.bool, + valid: PropTypes.bool, + errorMessage: PropTypes.string, + warning: PropTypes.string, + identiconAddress: PropTypes.string, + onEdit: PropTypes.func, + onEditGas: PropTypes.func, + onCancel: PropTypes.func, + onSubmit: PropTypes.func, + } + + componentDidMount () { + const { match: { params: { id } = {} }, setTransactionToConfirm } = this.props + setTransactionToConfirm(id) + } + + componentDidUpdate (prevProps) { + const { + transactionStatus, + showTransactionConfirmedModal, + history, + clearConfirmTransaction, + match: { params: { id } = {} }, + setTransactionToConfirm, + txData, + } = this.props + + if (transactionStatus === 'dropped') { + showTransactionConfirmedModal({ + onHide: () => { + clearConfirmTransaction() + history.push(DEFAULT_ROUTE) + }, + }) + + return + } + + if (id && id !== txData.id + '') { + setTransactionToConfirm(id) + } + } + + getError () { + const INSUFFICIENT_FUNDS_ERROR = this.context.t('insufficientFunds') + const TRANSACTION_ERROR = this.context.t('transactionError') + const { + balance, + conversionRate, + hexGasTotal, + txData: { + simulationFails, + txParams: { + value: amount, + } = {}, + } = {}, + } = this.props + + const insufficientBalance = balance && !isBalanceSufficient({ + amount, + gasTotal: hexGasTotal || '0x0', + balance, + conversionRate, + }) + + if (insufficientBalance) { + return { + valid: false, + errorMessage: INSUFFICIENT_FUNDS_ERROR, + } + } + + if (simulationFails) { + return { + valid: false, + errorMessage: TRANSACTION_ERROR, + } + } + + return { + valid: true, + } + } + + validateEditGas ({ gasLimit, gasPrice }) { + const { t } = this.context + const { + balance, + conversionRate, + txData: { + txParams: { + value: amount, + } = {}, + } = {}, + } = this.props + + const INSUFFICIENT_FUNDS_ERROR = t('insufficientFunds') + const GAS_LIMIT_TOO_LOW_ERROR = t('gasLimitTooLow') + + const gasTotal = getHexGasTotal({ gasLimit, gasPrice }) + const hasSufficientBalance = isBalanceSufficient({ + amount, + gasTotal, + balance, + conversionRate, + }) + + if (!hasSufficientBalance) { + return { + valid: false, + errorMessage: INSUFFICIENT_FUNDS_ERROR, + } + } + + const gasLimitTooLow = gasLimit && conversionGreaterThan( + { + value: MIN_GAS_LIMIT_DEC, + fromNumericBase: 'dec', + conversionRate, + }, + { + value: gasLimit, + fromNumericBase: 'hex', + }, + ) + + if (gasLimitTooLow) { + return { + valid: false, + errorMessage: GAS_LIMIT_TOO_LOW_ERROR, + } + } + + return { + valid: true, + } + } + + handleEditGas () { + const { onEditGas, showCustomizeGasModal, txData, updateGasAndCalculate } = this.props + + if (onEditGas) { + onEditGas() + } else { + showCustomizeGasModal({ + txData, + onSubmit: txData => updateGasAndCalculate(txData), + validate: ({ gasLimit, gasPrice }) => this.validateEditGas({ gasLimit, gasPrice }), + }) + } + } + + renderDetails () { + const { + detailsComponent, + fiatTransactionFee, + ethTransactionFee, + currentCurrency, + fiatTransactionTotal, + ethTransactionTotal, + hideDetails, + } = this.props + + if (hideDetails) { + return null + } + + return ( + detailsComponent || ( + <div className="confirm-page-container-content__details"> + <div className="confirm-page-container-content__gas-fee"> + <ConfirmDetailRow + label="Gas Fee" + fiatFee={formatCurrency(fiatTransactionFee, currentCurrency)} + ethFee={ethTransactionFee} + headerText="Edit" + headerTextClassName="confirm-detail-row__header-text--edit" + onHeaderClick={() => this.handleEditGas()} + /> + </div> + <div> + <ConfirmDetailRow + label="Total" + fiatFee={formatCurrency(fiatTransactionTotal, currentCurrency)} + ethFee={ethTransactionTotal} + headerText="Amount + Gas Fee" + headerTextClassName="confirm-detail-row__header-text--total" + fiatFeeColor="#2f9ae0" + /> + </div> + </div> + ) + ) + } + + renderData () { + const { t } = this.context + const { + txData: { + txParams: { + data, + } = {}, + } = {}, + tokenData: { + name, + params, + } = {}, + hideData, + dataComponent, + } = this.props + + if (hideData) { + return null + } + + return dataComponent || ( + <div className="confirm-page-container-content__data"> + <div className="confirm-page-container-content__data-box-label"> + {`${t('functionType')}:`} + <span className="confirm-page-container-content__function-type"> + { name } + </span> + </div> + <div className="confirm-page-container-content__data-box"> + { JSON.stringify(params, null, 2) } + </div> + <div className="confirm-page-container-content__data-box-label"> + {`${t('hexData')}:`} + </div> + <div className="confirm-page-container-content__data-box"> + { data } + </div> + </div> + ) + } + + handleEdit () { + const { txData, tokenData, tokenProps, onEdit } = this.props + onEdit({ txData, tokenData, tokenProps }) + } + + handleCancel () { + const { onCancel, txData, cancelTransaction, history, clearConfirmTransaction } = this.props + + if (onCancel) { + onCancel(txData) + } else { + cancelTransaction(txData) + .then(() => { + clearConfirmTransaction() + history.push(DEFAULT_ROUTE) + }) + } + } + + handleSubmit () { + const { sendTransaction, clearConfirmTransaction, txData, history, onSubmit } = this.props + + if (onSubmit) { + onSubmit(txData) + } else { + sendTransaction(txData) + .then(() => { + clearConfirmTransaction() + history.push(DEFAULT_ROUTE) + }) + } + } + + render () { + const { + isTxReprice, + fromName, + fromAddress, + toName, + toAddress, + tokenData, + ethTransactionAmount, + fiatTransactionAmount, + valid: propsValid, + errorMessage: propsErrorMessage, + currentCurrency, + action, + title, + subtitle, + hideSubtitle, + identiconAddress, + summaryComponent, + contentComponent, + onEdit, + nonce, + warning, + } = this.props + + const { name } = tokenData + const fiatConvertedAmount = formatCurrency(fiatTransactionAmount, currentCurrency) + const { valid, errorMessage } = this.getError() + + return ( + <ConfirmPageContainer + fromName={fromName} + fromAddress={fromAddress} + toName={toName} + toAddress={toAddress} + showEdit={onEdit && !isTxReprice} + action={action || name} + title={title || `${fiatConvertedAmount} ${currentCurrency.toUpperCase()}`} + subtitle={subtitle || `\u2666 ${ethTransactionAmount}`} + hideSubtitle={hideSubtitle} + summaryComponent={summaryComponent} + detailsComponent={this.renderDetails()} + dataComponent={this.renderData()} + contentComponent={contentComponent} + nonce={nonce} + identiconAddress={identiconAddress} + errorMessage={propsErrorMessage || errorMessage} + warning={warning} + valid={propsValid || valid} + onEdit={() => this.handleEdit()} + onCancel={() => this.handleCancel()} + onSubmit={() => this.handleSubmit()} + /> + ) + } +} |