1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

second commit

This commit is contained in:
Dan 2018-04-11 11:51:54 -02:30
parent f4d8da9277
commit 59c887301a
18 changed files with 462 additions and 51 deletions

View File

@ -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,
}

View File

@ -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)),
}
}

View File

@ -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
}

View File

@ -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,
}

View File

@ -7,42 +7,43 @@ import {
getGasTotal,
getSelectedBalance,
getTokenBalance,
getSendFromBalance,
} from '../../send.selectors.js'
import {
getMaxModeOn,
getSendAmountError,
sendAmountIsInError,
} from './send-amount-row.selectors.js'
import { getAmountErrorObject } from './send-to-row.utils.js'
import { getAmountErrorObject } from './send-amount-row.utils.js'
import {
updateSendErrors,
updateSendTo,
updateSendAmount,
setMaxModeTo,
} from '../../../actions'
import {
openToDropdown,
closeToDropdown,
} from '../../../ducks/send'
import SendToRow from './send-to-row.component'
import SendAmountRow from './send-amount-row.component'
export default connect(mapStateToProps, mapDispatchToProps)(SendToRow)
function mapStateToProps (state) {
updateSendTo
return {
to: getSendTo(state),
toAccounts: getSendToAccounts(state),
toDropdownOpen: getToDropdownOpen(state),
inError: sendToIsInError(state),
network: getCurrentNetwork(state),
selectedToken: getSelectedToken(state),
primaryCurrency: getPrimaryCurrency(state),
convertedCurrency: getConvertedCurrency(state),
amountConversionRate: getAmountConversionRate(state),
inError: sendAmountIsInError(state),
amount: getSendAmount(state),
maxModeOn: getMaxModeOn(state),
gasTotal: getGasTotal(state),
tokenBalance: getTokenBalance(state),
balance: getSendFromBalance(state),
}
}
function mapDispatchToProps (dispatch) {
return {
updateSendToError: (to) => {
dispatch(updateSendErrors(getToErrorObject(to)))
},
updateSendTo: (to, nickname) => dispatch(updateSendTo(to, nickname)),
openToDropdown: () => dispatch(()),
closeToDropdown: () => dispatch(()),
}
return {
updateSendAmountError: (amountDataObject) => {
dispatch(updateSendErrors(getAmountErrorObject(amountDataObject)))
},
updateSendAmount: newAmount => dispatch(updateSendAmount(newAmount)),
setMaxModeTo: bool => dispatch(setMaxModeTo(bool)),
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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>
);
}
}

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import SendRowWrapper from '../../../send/from-dropdown'
import FromDropdown from ''
import SendRowWrapper from '../send-row-wrapper/send-row-wrapper.component'
import FromDropdown from '../../../send/from-dropdown'
export default class SendFromRow extends Component {

View File

@ -1,6 +1,6 @@
import {
getSendFrom,
conversionRateSelector,
getConversionRate,
getSelectedTokenContract,
getCurrentAccountWithSendEtherInfo,
accountsWithSendEtherInfoSelector,
@ -23,7 +23,7 @@ function mapStateToProps (state) {
return {
from: getSendFrom(state) || getCurrentAccountWithSendEtherInfo(state),
fromAccounts: accountsWithSendEtherInfoSelector(state),
conversionRate: conversionRateSelector(state),
conversionRate: getConversionRate(state),
fromDropdownOpen: getFromDropdownOpen(state),
tokenContract: getSelectedTokenContract(state),
}

View File

@ -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,
}

View File

@ -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' })),
}
}

View File

@ -0,0 +1,9 @@
const selectors = {
sendGasIsInError,
}
module.exports = selectors
function sendGasIsInError (state) {
return state.send.errors.gasLoading
}

View File

@ -19,14 +19,23 @@ export default class SendRowWrapper extends Component {
children,
} = this.props
let formField = children[0]
let customLabelContent = null
if (children.length === 2) {
formField = children[1]
customLabelContent = children[0]
}
return (
<div className="send-v2__form-row">
<div className="send-v2__form-label">
{label}
(showError && <SendRowErrorMessage errorType={errorType}/>)
{customLabelContent}
</div>
<div className="send-v2__form-field">
{children}
{formField}
</div>
</div>
);

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import SendRowWrapper from '../../../send/from-dropdown'
import SendRowWrapper from '../send-row-wrapper/send-row-wrapper.component'
import ToDropdown from '../../../ens-input'
export default class SendToRow extends Component {
@ -37,7 +37,11 @@ export default class SendToRow extends Component {
} = this.props
return (
<SendRowWrapper label={`${this.context.t('to')}:`}>
<SendRowWrapper
label={`${this.context.t('to')}:`}
showError={inError}
errorType={'to'}
>
<EnsInput
name={'address'}
placeholder={this.context.t('recipient Address')}

View File

@ -10,5 +10,5 @@ function getToDropdownOpen (state) {
}
function sendToIsInError (state) {
return Boolean(state.metamask.send.to)
return Boolean(state.metamask.send.errors.to)
}

View File

@ -5,31 +5,33 @@ import {
} from './conversion-util'
const selectors = {
accountsWithSendEtherInfoSelector,
autoAddToBetaUI,
getConversionRate,
getAddressBook,
getConversionRate,
getCurrentAccountWithSendEtherInfo,
getCurrentCurrency,
getCurrentNetwork,
getCurrentViewContext,
getForceGasMin,
getGasLimit,
getGasPrice,
getSelectedAccount,
getSelectedAddress,
getSelectedIdentity,
getSelectedAccount,
getSelectedToken,
getSelectedTokenExchangeRate,
getTokenExchangeRate,
conversionRateSelector,
transactionsSelector,
accountsWithSendEtherInfoSelector,
getCurrentAccountWithSendEtherInfo,
getGasPrice,
getGasLimit,
getForceGasMin,
getAddressBook,
getSendFrom,
getCurrentCurrency,
getSendAmount,
getSelectedTokenToFiatRate,
getSelectedTokenContract,
autoAddToBetaUI,
getSendMaxModeState,
getCurrentViewContext,
getSelectedTokenExchangeRate,
getSelectedTokenToFiatRate,
getSendAmount,
getSendErrors,
getSendFrom,
getSendFromBalance,
getSendMaxModeState,
getSendTo,
getCurrentNetwork,
getTokenExchangeRate,
transactionsSelector,
}
module.exports = selectors
@ -82,7 +84,7 @@ function getTokenExchangeRate (state, tokenSymbol) {
return tokenExchangeRate
}
function conversionRateSelector (state) {
function getConversionRate (state) {
return state.metamask.conversionRate
}
@ -142,6 +144,11 @@ function getSendFrom (state) {
return state.metamask.send.from
}
function getSendFromBalance (state) {
const from = state.metamask.send.from || {}
return from.balance
}
function getSendAmount (state) {
return state.metamask.send.amount
}
@ -156,7 +163,7 @@ function getCurrentCurrency (state) {
function getSelectedTokenToFiatRate (state) {
const selectedTokenExchangeRate = getSelectedTokenExchangeRate(state)
const conversionRate = conversionRateSelector(state)
const conversionRate = getConversionRate(state)
const tokenToFiatRate = multiplyCurrencies(
conversionRate,