parent
f4d8da9277
commit
59c887301a
@ -0,0 +1,54 @@ |
||||
import React, { Component } from 'react' |
||||
import PropTypes from 'prop-types' |
||||
|
||||
export default class AmountMaxButton extends Component { |
||||
|
||||
static propTypes = { |
||||
tokenBalance: PropTypes.string, |
||||
gasTotal: PropTypes.string, |
||||
balance: PropTypes.string, |
||||
selectedToken: PropTypes.object, |
||||
setAmountToMax: PropTypes.func, |
||||
setMaxModeTo: PropTypes.func, |
||||
maxModeOn: PropTypes.bool, |
||||
}; |
||||
|
||||
setAmountToMax = function () { |
||||
const { |
||||
balance, |
||||
tokenBalance, |
||||
selectedToken, |
||||
gasTotal, |
||||
setAmountToMax, |
||||
} = this.props |
||||
|
||||
setAmountToMax({ |
||||
tokenBalance, |
||||
selectedToken, |
||||
gasTotal, |
||||
setAmountToMax, |
||||
}) |
||||
} |
||||
|
||||
render () { |
||||
const { setMaxModeTo } = this.props |
||||
|
||||
return ( |
||||
<div |
||||
className='send-v2__amount-max' |
||||
onClick={(event) => { |
||||
event.preventDefault() |
||||
setMaxModeTo(true) |
||||
this.setAmountToMax() |
||||
}} |
||||
> |
||||
{!maxModeOn ? this.context.t('max') : '' ])} |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
} |
||||
|
||||
AmountMaxButton.contextTypes = { |
||||
t: PropTypes.func, |
||||
} |
@ -0,0 +1,36 @@ |
||||
import { |
||||
getSelectedToken, |
||||
getGasTotal, |
||||
getTokenBalance, |
||||
getSendFromBalance, |
||||
} from '../../../send.selectors.js' |
||||
import { getMaxModeOn } from '../send-amount-row.selectors.js' |
||||
import { calcMaxAmount } from './amount-max-button.utils.js' |
||||
import { |
||||
updateSendAmount, |
||||
setMaxModeTo, |
||||
} from '../../../actions' |
||||
import AmountMaxButton from './amount-max-button.component' |
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SendToRow) |
||||
|
||||
function mapStateToProps (state) { |
||||
|
||||
return { |
||||
selectedToken: getSelectedToken(state), |
||||
maxModeOn: getMaxModeOn(state), |
||||
gasTotal: getGasTotal(state), |
||||
tokenBalance: getTokenBalance(state), |
||||
balance: getSendFromBalance(state), |
||||
} |
||||
} |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
setAmountToMax: maxAmountDataObject => { |
||||
updateSendErrors({ amount: null }) |
||||
updateSendAmount(calcMaxAmount(maxAmountDataObject)) |
||||
} |
||||
setMaxModeTo: bool => dispatch(setMaxModeTo(bool)), |
||||
} |
||||
} |
@ -0,0 +1,22 @@ |
||||
const { |
||||
multiplyCurrencies, |
||||
subtractCurrencies, |
||||
} = require('../../../../conversion-util') |
||||
const ethUtil = require('ethereumjs-util') |
||||
|
||||
function calcMaxAmount ({ balance, gasTotal, selectedToken, tokenBalance }) { |
||||
const { decimals } = selectedToken || {} |
||||
const multiplier = Math.pow(10, Number(decimals || 0)) |
||||
|
||||
return selectedToken |
||||
? multiplyCurrencies(tokenBalance, multiplier, {toNumericBase: 'hex'}) |
||||
: subtractCurrencies( |
||||
ethUtil.addHexPrefix(balance), |
||||
ethUtil.addHexPrefix(gasTotal), |
||||
{ toNumericBase: 'hex' } |
||||
) |
||||
} |
||||
|
||||
module.exports = { |
||||
calcMaxAmount |
||||
} |
@ -0,0 +1,91 @@ |
||||
import React, { Component } from 'react' |
||||
import PropTypes from 'prop-types' |
||||
import SendRowWrapper from '../send-row-wrapper/send-row-wrapper.component' |
||||
import AmountMaxButton from '../amount-max-button/amount-max-button.component' |
||||
import CurrencyDisplay from '../../../send/currency-display' |
||||
|
||||
export default class SendAmountRow extends Component { |
||||
|
||||
static propTypes = { |
||||
amountConversionRate: PropTypes.string, |
||||
conversionRate: PropTypes.string, |
||||
from: PropTypes.object, |
||||
gasTotal: PropTypes.string, |
||||
primaryCurrency: PropTypes.string, |
||||
selectedToken: PropTypes.object, |
||||
tokenBalance: PropTypes.string, |
||||
updateSendAmountError: PropTypes.func, |
||||
updateSendAmount: PropTypes.func, |
||||
setMaxModeTo: PropTypes.func |
||||
} |
||||
|
||||
validateAmount (amount) { |
||||
const { |
||||
amountConversionRate, |
||||
conversionRate, |
||||
from: { balance }, |
||||
gasTotal, |
||||
primaryCurrency, |
||||
selectedToken, |
||||
tokenBalance, |
||||
updateSendAmountError, |
||||
} = this.props |
||||
|
||||
updateSendAmountError({ |
||||
amount, |
||||
amountConversionRate, |
||||
balance, |
||||
conversionRate, |
||||
gasTotal, |
||||
primaryCurrency, |
||||
selectedToken, |
||||
tokenBalance, |
||||
}) |
||||
} |
||||
|
||||
handleAmountChange (amount) { |
||||
const { updateSendAmount, setMaxModeTo } = this.props |
||||
|
||||
setMaxModeTo(false) |
||||
this.validateAmount(amount) |
||||
updateSendAmount(amount) |
||||
} |
||||
|
||||
render () { |
||||
const { |
||||
amount, |
||||
amountConversionRate, |
||||
convertedCurrency, |
||||
inError, |
||||
gasTotal, |
||||
maxModeOn, |
||||
primaryCurrency = 'ETH', |
||||
selectedToken, |
||||
} = this.props |
||||
|
||||
return ( |
||||
<SendRowWrapper |
||||
label={`${this.context.t('amount')}:`} |
||||
showError={inError} |
||||
errorType={'amount'} |
||||
> |
||||
!inError && gasTotal && <AmountMaxButton /> |
||||
<CurrencyDisplay |
||||
inError={inError}, |
||||
primaryCurrency={primaryCurrency}, |
||||
convertedCurrency={convertedCurrency}, |
||||
selectedToken={selectedToken}, |
||||
value={amount || '0x0'}, |
||||
conversionRate={amountConversionRate}, |
||||
handleChange={this.handleAmountChange}, |
||||
> |
||||
</SendRowWrapper> |
||||
); |
||||
} |
||||
|
||||
} |
||||
|
||||
SendAmountRow.contextTypes = { |
||||
t: PropTypes.func, |
||||
} |
||||
|
@ -0,0 +1,14 @@ |
||||
const selectors = { |
||||
getMaxModeOn, |
||||
sendAmountIsInError, |
||||
} |
||||
|
||||
module.exports = selectors |
||||
|
||||
function getMaxModeOn (state) { |
||||
return state.metamask.send.maxModeOn |
||||
} |
||||
|
||||
function sendAmountIsInError (state) { |
||||
return Boolean(state.metamask.send.errors.amount) |
||||
} |
@ -0,0 +1,55 @@ |
||||
const { isValidAddress } = require('../../../../util') |
||||
|
||||
function getAmountErrorObject ({ |
||||
amount, |
||||
balance, |
||||
amountConversionRate, |
||||
conversionRate, |
||||
primaryCurrency, |
||||
selectedToken, |
||||
gasTotal, |
||||
tokenBalance, |
||||
}) { |
||||
let insufficientFunds = false |
||||
if (gasTotal && conversionRate) { |
||||
insufficientFunds = !isBalanceSufficient({ |
||||
amount: selectedToken ? '0x0' : amount, |
||||
gasTotal, |
||||
balance, |
||||
primaryCurrency, |
||||
amountConversionRate, |
||||
conversionRate, |
||||
}) |
||||
} |
||||
|
||||
let inSufficientTokens = false |
||||
if (selectedToken && tokenBalance !== null) { |
||||
const { decimals } = selectedToken |
||||
inSufficientTokens = !isTokenBalanceSufficient({ |
||||
tokenBalance, |
||||
amount, |
||||
decimals, |
||||
}) |
||||
} |
||||
|
||||
const amountLessThanZero = conversionGreaterThan( |
||||
{ value: 0, fromNumericBase: 'dec' }, |
||||
{ value: amount, fromNumericBase: 'hex' }, |
||||
) |
||||
|
||||
let amountError = null |
||||
|
||||
if (insufficientFunds) { |
||||
amountError = this.context.t('insufficientFunds') |
||||
} else if (insufficientTokens) { |
||||
amountError = this.context.t('insufficientTokens') |
||||
} else if (amountLessThanZero) { |
||||
amountError = this.context.t('negativeETH') |
||||
} |
||||
|
||||
return { amount: amountError } |
||||
} |
||||
|
||||
module.exports = { |
||||
getAmountErrorObject |
||||
} |
@ -0,0 +1,23 @@ |
||||
import React, { Component } from 'react' |
||||
import PageContainerContent from '../../page-container/page-container-header.component' |
||||
import SendFromRow from './send-from-row/send-from-row.component' |
||||
import SendToRow from './send-to-row/send-to-row.component' |
||||
import SendAmountRow from './send-amount-row/send-amount-row.component' |
||||
import SendGasRow from './send-gas-row/send-gas-row.component' |
||||
|
||||
export default class SendContent extends Component { |
||||
|
||||
render () { |
||||
return ( |
||||
<PageContainerContent> |
||||
<div className='.send-v2__form'> |
||||
<SendFromRow /> |
||||
<SendToRow /> |
||||
<SendAmountRow /> |
||||
<SendGasRow /> |
||||
</div> |
||||
</PageContainerContent> |
||||
); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,60 @@ |
||||
import React, { Component } from 'react' |
||||
import PropTypes from 'prop-types' |
||||
import SendRowWrapper from '../send-row-wrapper/send-row-wrapper.component' |
||||
import GasFeeDisplay from '../../../send/gas-fee-display-v2' |
||||
|
||||
export default class SendGasRow extends Component { |
||||
|
||||
static propTypes = { |
||||
closeFromDropdown: PropTypes.func, |
||||
conversionRate: PropTypes.string, |
||||
from: PropTypes.string, |
||||
fromAccounts: PropTypes.array, |
||||
fromDropdownOpen: PropTypes.bool, |
||||
openFromDropdown: PropTypes.func, |
||||
tokenContract: PropTypes.object, |
||||
updateSendFrom: PropTypes.func, |
||||
updateSendTokenBalance: PropTypes.func, |
||||
}; |
||||
|
||||
async handleFromChange (newFrom) { |
||||
const { |
||||
updateSendFrom, |
||||
tokenContract, |
||||
updateSendTokenBalance, |
||||
} = this.props |
||||
|
||||
if (tokenContract) { |
||||
const usersToken = await tokenContract.balanceOf(newFrom.address) |
||||
updateSendTokenBalance(usersToken) |
||||
} |
||||
updateSendFrom(newFrom) |
||||
} |
||||
|
||||
render () { |
||||
const { |
||||
conversionRate, |
||||
convertedCurrency, |
||||
showCustomizeGasModal, |
||||
gasTotal, |
||||
gasLoadingError, |
||||
} = this.props |
||||
|
||||
return ( |
||||
<SendRowWrapper label={`${this.context.t('gasFee')}:`}> |
||||
<GasFeeDisplay |
||||
gasTotal={gasTotal}, |
||||
conversionRate={conversionRate}, |
||||
convertedCurrency={convertedCurrency}, |
||||
onClick={() => showCustomizeGasModal()}, |
||||
gasLoadingError={gasLoadingError}, |
||||
/> |
||||
</SendRowWrapper> |
||||
); |
||||
} |
||||
|
||||
} |
||||
|
||||
SendGasRow.contextTypes = { |
||||
t: PropTypes.func, |
||||
} |
@ -0,0 +1,26 @@ |
||||
import { |
||||
getConversionRate, |
||||
getConvertedCurrency, |
||||
getGasTotal, |
||||
} from '../../send.selectors.js' |
||||
import { getGasLoadingError } from './send-gas-row.selectors.js' |
||||
import { calcTokenUpdateAmount } from './send-gas-row.utils.js' |
||||
import { showModal } from '../../../actions' |
||||
import SendGasRow from './send-from-row.component' |
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SendGasRow) |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
conversionRate: getConversionRate(state), |
||||
convertedCurrency: getConvertedCurrency(state), |
||||
gasTotal: getGasTotal(state), |
||||
gasLoadingError: getGasLoadingError(state), |
||||
} |
||||
} |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
showCustomizeGasModal: () => dispatch(showModal({ name: 'CUSTOMIZE_GAS' })), |
||||
} |
||||
} |
@ -0,0 +1,9 @@ |
||||
const selectors = { |
||||
sendGasIsInError, |
||||
} |
||||
|
||||
module.exports = selectors |
||||
|
||||
function sendGasIsInError (state) { |
||||
return state.send.errors.gasLoading |
||||
} |
Loading…
Reference in new issue