mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
first commit
This commit is contained in:
parent
77486a2365
commit
284dd85a99
@ -167,6 +167,8 @@ var actions = {
|
||||
UPDATE_MAX_MODE: 'UPDATE_MAX_MODE',
|
||||
UPDATE_SEND: 'UPDATE_SEND',
|
||||
CLEAR_SEND: 'CLEAR_SEND',
|
||||
OPEN_FROM_DROPDOWN: 'OPEN_FROM_DROPDOWN',
|
||||
CLOSE_FROM_DROPDOWN: 'CLOSE_FROM_DROPDOWN',
|
||||
updateGasLimit,
|
||||
updateGasPrice,
|
||||
updateGasTotal,
|
||||
|
@ -0,0 +1,18 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default class PageContainerContent extends Component {
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className="page-container__content">
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default class PageContainerFooter extends Component {
|
||||
|
||||
static propTypes = {
|
||||
onCancel: PropTypes.func,
|
||||
onSubmit: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { onCancel, onSubmit, disabled } = this.props
|
||||
|
||||
return (
|
||||
<div className="page-container__footer">
|
||||
|
||||
<button
|
||||
className="btn-secondary--lg page-container__footer-button"
|
||||
onClick={() => onCancel()}
|
||||
>
|
||||
{this.context.t('cancel')}
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="btn-primary--lg page-container__footer-button"
|
||||
disabled={disabled}
|
||||
onClick={(e) => onSubmit(e)}
|
||||
>
|
||||
{this.context.t('next')}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PageContainerFooter.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default class PageContainerHeader extends Component {
|
||||
|
||||
static propTypes = {
|
||||
title: PropTypes.string,
|
||||
subtitle: PropTypes.string,
|
||||
onClose: PropTypes.func,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { title, subtitle, onClose } = this.props
|
||||
|
||||
return (
|
||||
<div className="page-container__header">
|
||||
|
||||
<div className="page-container__title">
|
||||
{title}
|
||||
</div>
|
||||
|
||||
<div className="page-container__subtitle">
|
||||
{subtitle}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="page-container__header-close"
|
||||
onClick={() => onClose()}
|
||||
/>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
18
ui/app/components/page-container/page-container.component.js
Normal file
18
ui/app/components/page-container/page-container.component.js
Normal file
@ -0,0 +1,18 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default class PageContainer extends Component {
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className="page-container">
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
0
ui/app/components/send_/README.md
Normal file
0
ui/app/components/send_/README.md
Normal file
@ -0,0 +1,48 @@
|
||||
import {
|
||||
getSelectedToken,
|
||||
getPrimaryCurrency,
|
||||
getAmountConversionRate,
|
||||
getConvertedCurrency,
|
||||
getSendAmount,
|
||||
getGasTotal,
|
||||
getSelectedBalance,
|
||||
getTokenBalance,
|
||||
} from '../../send.selectors.js'
|
||||
import {
|
||||
getMaxModeOn,
|
||||
getSendAmountError,
|
||||
} from './send-amount-row.selectors.js'
|
||||
import { getAmountErrorObject } from './send-to-row.utils.js'
|
||||
import {
|
||||
updateSendErrors,
|
||||
updateSendTo,
|
||||
} from '../../../actions'
|
||||
import {
|
||||
openToDropdown,
|
||||
closeToDropdown,
|
||||
} from '../../../ducks/send'
|
||||
import SendToRow from './send-to-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),
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
updateSendToError: (to) => {
|
||||
dispatch(updateSendErrors(getToErrorObject(to)))
|
||||
},
|
||||
updateSendTo: (to, nickname) => dispatch(updateSendTo(to, nickname)),
|
||||
openToDropdown: () => dispatch(()),
|
||||
closeToDropdown: () => dispatch(()),
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import SendRowWrapper from '../../../send/from-dropdown'
|
||||
import FromDropdown from ''
|
||||
|
||||
export default class SendFromRow 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 {
|
||||
from,
|
||||
fromAccounts,
|
||||
conversionRate,
|
||||
fromDropdownOpen,
|
||||
tokenContract,
|
||||
openFromDropdown,
|
||||
closeFromDropdown,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<SendRowWrapper label={`${this.context.t('from')}:`}>
|
||||
<FromDropdown
|
||||
dropdownOpen={fromDropdownOpen}
|
||||
accounts={fromAccounts}
|
||||
selectedAccount={from}
|
||||
onSelect={newFrom => this.handleFromChange(newFrom)}
|
||||
openDropdown={() => openFromDropdown()}
|
||||
closeDropdown={() => closeFromDropdown()}
|
||||
conversionRate={conversionRate}
|
||||
/>
|
||||
</SendRowWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SendFromRow.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
import {
|
||||
getSendFrom,
|
||||
conversionRateSelector,
|
||||
getSelectedTokenContract,
|
||||
getCurrentAccountWithSendEtherInfo,
|
||||
accountsWithSendEtherInfoSelector,
|
||||
} from '../../send.selectors.js'
|
||||
import { getFromDropdownOpen } from './send-from-row.selectors.js'
|
||||
import { calcTokenUpdateAmount } from './send-from-row.utils.js'
|
||||
import {
|
||||
updateSendTokenBalance,
|
||||
updateSendFrom,
|
||||
} from '../../../actions'
|
||||
import {
|
||||
openFromDropdown,
|
||||
closeFromDropdown,
|
||||
} from '../../../ducks/send'
|
||||
import SendFromRow from './send-from-row.component'
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SendFromRow)
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
from: getSendFrom(state) || getCurrentAccountWithSendEtherInfo(state),
|
||||
fromAccounts: accountsWithSendEtherInfoSelector(state),
|
||||
conversionRate: conversionRateSelector(state),
|
||||
fromDropdownOpen: getFromDropdownOpen(state),
|
||||
tokenContract: getSelectedTokenContract(state),
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
updateSendTokenBalance: (usersToken, selectedToken) => {
|
||||
if (!usersToken) return
|
||||
|
||||
const tokenBalance = calcTokenUpdateAmount(selectedToken, selectedToken)
|
||||
dispatch(updateSendTokenBalance(tokenBalance))
|
||||
},
|
||||
updateSendFrom: newFrom => dispatch(updateSendFrom(newFrom)),
|
||||
openFromDropdown: () => dispatch(()),
|
||||
closeFromDropdown: () => dispatch(()),
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
const selectors = {
|
||||
getFromDropdownOpen,
|
||||
}
|
||||
|
||||
module.exports = selectors
|
||||
|
||||
function getFromDropdownOpen (state) {
|
||||
return state.send.fromDropdownOpen
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
const {
|
||||
calcTokenAmount,
|
||||
} = require('../../token-util')
|
||||
|
||||
function calcTokenUpdateAmount (usersToken, selectedToken) {
|
||||
const { decimals } = selectedToken || {}
|
||||
return calcTokenAmount(usersToken.balance.toString(), decimals)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
calcTokenUpdateAmount
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
export default class SendRowErrorMessage extends Component {
|
||||
|
||||
static propTypes = {
|
||||
errors: PropTypes.object,
|
||||
errorType: PropTypes.string,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { errors, errorType } = this.props
|
||||
const errorMessage = errors[errorType]
|
||||
|
||||
return (
|
||||
errorMessage
|
||||
? <div className='send-v2__error'>{errorMessage}</div>
|
||||
: null
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SendRowErrorMessage.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import { getSendErrors } from '../../../send.selectors'
|
||||
import SendRowErrorMessage from './send-row-error-message.component'
|
||||
|
||||
export default connect(mapStateToProps)(SendRowErrorMessage)
|
||||
|
||||
function mapStateToProps (state, ownProps) {
|
||||
return {
|
||||
errors: getSendErrors(state),
|
||||
errorType: ownProps.errorType,
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import SendRowErrorMessage from './send-row-error-message/send-row-error-message.container'
|
||||
|
||||
export default class SendRowWrapper extends Component {
|
||||
|
||||
static propTypes = {
|
||||
label: PropTypes.string,
|
||||
showError: PropTypes.bool,
|
||||
children: PropTypes.node,
|
||||
errorType: PropTypes.string,
|
||||
};
|
||||
|
||||
render () {
|
||||
const {
|
||||
label,
|
||||
errorType = '',
|
||||
showError = false,
|
||||
children,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<div className="send-v2__form-row">
|
||||
<div className="send-v2__form-label">
|
||||
{label}
|
||||
(showError && <SendRowErrorMessage errorType={errorType}/>)
|
||||
</div>
|
||||
<div className="send-v2__form-field">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SendRowWrapper.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import SendRowWrapper from '../../../send/from-dropdown'
|
||||
import ToDropdown from '../../../ens-input'
|
||||
|
||||
export default class SendToRow extends Component {
|
||||
|
||||
static propTypes = {
|
||||
to: PropTypes.string,
|
||||
toAccounts: PropTypes.array,
|
||||
toDropdownOpen: PropTypes.bool,
|
||||
inError: PropTypes.bool,
|
||||
updateSendTo: PropTypes.func,
|
||||
updateSendToError: PropTypes.func,
|
||||
openToDropdown: PropTypes.func,
|
||||
closeToDropdown: PropTypes.func,
|
||||
network: PropTypes.number,
|
||||
};
|
||||
|
||||
handleToChange (to, nickname = '') {
|
||||
const { updateSendTo, updateSendToError } = this.props
|
||||
updateSendTo(to, nickname)
|
||||
updateSendErrors(to)
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
from,
|
||||
fromAccounts,
|
||||
conversionRate,
|
||||
fromDropdownOpen,
|
||||
tokenContract,
|
||||
openToDropdown,
|
||||
closeToDropdown,
|
||||
network,
|
||||
inError,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<SendRowWrapper label={`${this.context.t('to')}:`}>
|
||||
<EnsInput
|
||||
name={'address'}
|
||||
placeholder={this.context.t('recipient Address')}
|
||||
network={network},
|
||||
to={to},
|
||||
accounts={toAccounts}
|
||||
dropdownOpen={toDropdownOpen}
|
||||
openDropdown={() => openToDropdown()}
|
||||
closeDropdown={() => closeToDropdown()}
|
||||
onChange={this.handleToChange}
|
||||
inError={inError}
|
||||
/>
|
||||
</SendRowWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SendToRow.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
import {
|
||||
getSendTo,
|
||||
getToAccounts,
|
||||
getCurrentNetwork,
|
||||
} from '../../send.selectors.js'
|
||||
import {
|
||||
getToDropdownOpen,
|
||||
sendToIsInError,
|
||||
} from './send-to-row.selectors.js'
|
||||
import { getToErrorObject } from './send-to-row.utils.js'
|
||||
import {
|
||||
updateSendErrors,
|
||||
updateSendTo,
|
||||
} from '../../../actions'
|
||||
import {
|
||||
openToDropdown,
|
||||
closeToDropdown,
|
||||
} from '../../../ducks/send'
|
||||
import SendToRow from './send-to-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),
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
updateSendToError: (to) => {
|
||||
dispatch(updateSendErrors(getToErrorObject(to)))
|
||||
},
|
||||
updateSendTo: (to, nickname) => dispatch(updateSendTo(to, nickname)),
|
||||
openToDropdown: () => dispatch(()),
|
||||
closeToDropdown: () => dispatch(()),
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
const selectors = {
|
||||
getToDropdownOpen,
|
||||
sendToIsInError,
|
||||
}
|
||||
|
||||
module.exports = selectors
|
||||
|
||||
function getToDropdownOpen (state) {
|
||||
return state.send.toDropdownOpen
|
||||
}
|
||||
|
||||
function sendToIsInError (state) {
|
||||
return Boolean(state.metamask.send.to)
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
const { isValidAddress } = require('../../../../util')
|
||||
|
||||
function getToErrorObject (to) {
|
||||
let toError = null
|
||||
|
||||
if (!to) {
|
||||
toError = 'required'
|
||||
} else if (!isValidAddress(to)) {
|
||||
toError = 'invalidAddressRecipient'
|
||||
}
|
||||
|
||||
return { to: toError }
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getToErrorObject
|
||||
}
|
0
ui/app/components/send_/send-footer/README.md
Normal file
0
ui/app/components/send_/send-footer/README.md
Normal file
0
ui/app/components/send_/send-header/README.md
Normal file
0
ui/app/components/send_/send-header/README.md
Normal file
32
ui/app/components/send_/send-header/send-header.component.js
Normal file
32
ui/app/components/send_/send-header/send-header.component.js
Normal file
@ -0,0 +1,32 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import PageContainerHeader from '../../page-container/page-container-header.component'
|
||||
|
||||
export default class SendHeader extends Component {
|
||||
|
||||
static propTypes = {
|
||||
isToken: PropTypes.bool,
|
||||
clearSend: PropTypes.func,
|
||||
goHome: PropTypes.func,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { isToken, clearSend, goHome } = this.props
|
||||
|
||||
return (
|
||||
<PageContainerHeader
|
||||
title={isToken ? this.context.t('sendTokens') : this.context.t('sendETH')}
|
||||
subtitle={this.context.t('onlySendToEtherAddress')}
|
||||
onClose={() => {
|
||||
clearSend()
|
||||
goHome()
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SendHeader.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
19
ui/app/components/send_/send-header/send-header.container.js
Normal file
19
ui/app/components/send_/send-header/send-header.container.js
Normal file
@ -0,0 +1,19 @@
|
||||
import { connect } from 'react-redux'
|
||||
import { goHome, clearSend } from '../../../actions'
|
||||
import SendHeader from './send-header.component'
|
||||
import { getSelectedToken } from '../../../selectors'
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SendHeader)
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
isToken: Boolean(getSelectedToken(state))
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
goHome: () => dispatch(goHome()),
|
||||
clearSend: () => dispatch(clearSend()),
|
||||
}
|
||||
}
|
0
ui/app/components/send_/send.component.js
Normal file
0
ui/app/components/send_/send.component.js
Normal file
0
ui/app/components/send_/send.container.js
Normal file
0
ui/app/components/send_/send.container.js
Normal file
0
ui/app/components/send_/send.scss
Normal file
0
ui/app/components/send_/send.scss
Normal file
217
ui/app/components/send_/send.selectors.js
Normal file
217
ui/app/components/send_/send.selectors.js
Normal file
@ -0,0 +1,217 @@
|
||||
import { valuesFor } from '../../util'
|
||||
import abi from 'human-standard-token-abi'
|
||||
import {
|
||||
multiplyCurrencies,
|
||||
} from './conversion-util'
|
||||
|
||||
const selectors = {
|
||||
getSelectedAddress,
|
||||
getSelectedIdentity,
|
||||
getSelectedAccount,
|
||||
getSelectedToken,
|
||||
getSelectedTokenExchangeRate,
|
||||
getTokenExchangeRate,
|
||||
conversionRateSelector,
|
||||
transactionsSelector,
|
||||
accountsWithSendEtherInfoSelector,
|
||||
getCurrentAccountWithSendEtherInfo,
|
||||
getGasPrice,
|
||||
getGasLimit,
|
||||
getForceGasMin,
|
||||
getAddressBook,
|
||||
getSendFrom,
|
||||
getCurrentCurrency,
|
||||
getSendAmount,
|
||||
getSelectedTokenToFiatRate,
|
||||
getSelectedTokenContract,
|
||||
autoAddToBetaUI,
|
||||
getSendMaxModeState,
|
||||
getCurrentViewContext,
|
||||
getSendErrors,
|
||||
getSendTo,
|
||||
getCurrentNetwork,
|
||||
}
|
||||
|
||||
module.exports = selectors
|
||||
|
||||
function getSelectedAddress (state) {
|
||||
const selectedAddress = state.metamask.selectedAddress || Object.keys(state.metamask.accounts)[0]
|
||||
|
||||
return selectedAddress
|
||||
}
|
||||
|
||||
function getSelectedIdentity (state) {
|
||||
const selectedAddress = getSelectedAddress(state)
|
||||
const identities = state.metamask.identities
|
||||
|
||||
return identities[selectedAddress]
|
||||
}
|
||||
|
||||
function getSelectedAccount (state) {
|
||||
const accounts = state.metamask.accounts
|
||||
const selectedAddress = getSelectedAddress(state)
|
||||
|
||||
return accounts[selectedAddress]
|
||||
}
|
||||
|
||||
function getSelectedToken (state) {
|
||||
const tokens = state.metamask.tokens || []
|
||||
const selectedTokenAddress = state.metamask.selectedTokenAddress
|
||||
const selectedToken = tokens.filter(({ address }) => address === selectedTokenAddress)[0]
|
||||
const sendToken = state.metamask.send.token
|
||||
|
||||
return selectedToken || sendToken || null
|
||||
}
|
||||
|
||||
function getSelectedTokenExchangeRate (state) {
|
||||
const tokenExchangeRates = state.metamask.tokenExchangeRates
|
||||
const selectedToken = getSelectedToken(state) || {}
|
||||
const { symbol = '' } = selectedToken
|
||||
|
||||
const pair = `${symbol.toLowerCase()}_eth`
|
||||
const { rate: tokenExchangeRate = 0 } = tokenExchangeRates[pair] || {}
|
||||
|
||||
return tokenExchangeRate
|
||||
}
|
||||
|
||||
function getTokenExchangeRate (state, tokenSymbol) {
|
||||
const pair = `${tokenSymbol.toLowerCase()}_eth`
|
||||
const tokenExchangeRates = state.metamask.tokenExchangeRates
|
||||
const { rate: tokenExchangeRate = 0 } = tokenExchangeRates[pair] || {}
|
||||
|
||||
return tokenExchangeRate
|
||||
}
|
||||
|
||||
function conversionRateSelector (state) {
|
||||
return state.metamask.conversionRate
|
||||
}
|
||||
|
||||
function getAddressBook (state) {
|
||||
return state.metamask.addressBook
|
||||
}
|
||||
|
||||
function accountsWithSendEtherInfoSelector (state) {
|
||||
const {
|
||||
accounts,
|
||||
identities,
|
||||
} = state.metamask
|
||||
|
||||
const accountsWithSendEtherInfo = Object.entries(accounts).map(([key, account]) => {
|
||||
return Object.assign({}, account, identities[key])
|
||||
})
|
||||
|
||||
return accountsWithSendEtherInfo
|
||||
}
|
||||
|
||||
function getCurrentAccountWithSendEtherInfo (state) {
|
||||
const currentAddress = getSelectedAddress(state)
|
||||
const accounts = accountsWithSendEtherInfoSelector(state)
|
||||
|
||||
return accounts.find(({ address }) => address === currentAddress)
|
||||
}
|
||||
|
||||
function transactionsSelector (state) {
|
||||
const { network, selectedTokenAddress } = state.metamask
|
||||
const unapprovedMsgs = valuesFor(state.metamask.unapprovedMsgs)
|
||||
const shapeShiftTxList = (network === '1') ? state.metamask.shapeShiftTxList : undefined
|
||||
const transactions = state.metamask.selectedAddressTxList || []
|
||||
const txsToRender = !shapeShiftTxList ? transactions.concat(unapprovedMsgs) : transactions.concat(unapprovedMsgs, shapeShiftTxList)
|
||||
|
||||
// console.log({txsToRender, selectedTokenAddress})
|
||||
return selectedTokenAddress
|
||||
? txsToRender
|
||||
.filter(({ txParams }) => txParams && txParams.to === selectedTokenAddress)
|
||||
.sort((a, b) => b.time - a.time)
|
||||
: txsToRender
|
||||
.sort((a, b) => b.time - a.time)
|
||||
}
|
||||
|
||||
function getGasPrice (state) {
|
||||
return state.metamask.send.gasPrice
|
||||
}
|
||||
|
||||
function getGasLimit (state) {
|
||||
return state.metamask.send.gasLimit
|
||||
}
|
||||
|
||||
function getForceGasMin (state) {
|
||||
return state.metamask.send.forceGasMin
|
||||
}
|
||||
|
||||
function getSendFrom (state) {
|
||||
return state.metamask.send.from
|
||||
}
|
||||
|
||||
function getSendAmount (state) {
|
||||
return state.metamask.send.amount
|
||||
}
|
||||
|
||||
function getSendMaxModeState (state) {
|
||||
return state.metamask.send.maxModeOn
|
||||
}
|
||||
|
||||
function getCurrentCurrency (state) {
|
||||
return state.metamask.currentCurrency
|
||||
}
|
||||
|
||||
function getSelectedTokenToFiatRate (state) {
|
||||
const selectedTokenExchangeRate = getSelectedTokenExchangeRate(state)
|
||||
const conversionRate = conversionRateSelector(state)
|
||||
|
||||
const tokenToFiatRate = multiplyCurrencies(
|
||||
conversionRate,
|
||||
selectedTokenExchangeRate,
|
||||
{ toNumericBase: 'dec' }
|
||||
)
|
||||
|
||||
return tokenToFiatRate
|
||||
}
|
||||
|
||||
function getSelectedTokenContract (state) {
|
||||
const selectedToken = getSelectedToken(state)
|
||||
return selectedToken
|
||||
? global.eth.contract(abi).at(selectedToken.address)
|
||||
: null
|
||||
}
|
||||
|
||||
function autoAddToBetaUI (state) {
|
||||
const autoAddTransactionThreshold = 12
|
||||
const autoAddAccountsThreshold = 2
|
||||
const autoAddTokensThreshold = 1
|
||||
|
||||
const numberOfTransactions = state.metamask.selectedAddressTxList.length
|
||||
const numberOfAccounts = Object.keys(state.metamask.accounts).length
|
||||
const numberOfTokensAdded = state.metamask.tokens.length
|
||||
|
||||
const userPassesThreshold = (numberOfTransactions > autoAddTransactionThreshold) &&
|
||||
(numberOfAccounts > autoAddAccountsThreshold) &&
|
||||
(numberOfTokensAdded > autoAddTokensThreshold)
|
||||
const userIsNotInBeta = !state.metamask.featureFlags.betaUI
|
||||
|
||||
return userIsNotInBeta && userPassesThreshold
|
||||
}
|
||||
|
||||
function getCurrentViewContext (state) {
|
||||
const { currentView = {} } = state.appState
|
||||
return currentView.context
|
||||
}
|
||||
|
||||
function getSendErrors (state) {
|
||||
return state.metamask.send.errors
|
||||
}
|
||||
|
||||
function getSendTo (state) {
|
||||
return state.metamask.send.to
|
||||
}
|
||||
|
||||
function getSendToAccounts (state) {
|
||||
const fromAccounts = accountsWithSendEtherInfoSelector(state)
|
||||
const addressBookAccounts = getAddressBook(state)
|
||||
const allAccounts = [...fromAccounts, ...addressBookAccounts]
|
||||
// TODO: figure out exactly what the below returns and put a descriptive variable name on it
|
||||
return Object.entries(allAccounts).map(([key, account]) => account)
|
||||
}
|
||||
|
||||
function getCurrentNetwork (state) {
|
||||
return state.metamask.network
|
||||
}
|
0
ui/app/components/send_/send.utils.js
Normal file
0
ui/app/components/send_/send.utils.js
Normal file
0
ui/app/components/send_/tests/send-utils.test.js
Normal file
0
ui/app/components/send_/tests/send-utils.test.js
Normal file
54
ui/app/ducks/send.js
Normal file
54
ui/app/ducks/send.js
Normal file
@ -0,0 +1,54 @@
|
||||
import extend from 'xtend'
|
||||
|
||||
// Actions
|
||||
const OPEN_FROM_DROPDOWN = 'metamask/send/OPEN_FROM_DROPDOWN';
|
||||
const CLOSE_FROM_DROPDOWN = 'metamask/send/CLOSE_FROM_DROPDOWN';
|
||||
const OPEN_TO_DROPDOWN = 'metamask/send/OPEN_TO_DROPDOWN';
|
||||
const CLOSE_TO_DROPDOWN = 'metamask/send/CLOSE_TO_DROPDOWN';
|
||||
|
||||
// TODO: determine if this approach to initState is consistent with conventional ducks pattern
|
||||
const initState = {
|
||||
fromDropdownOpen: false,
|
||||
toDropdownOpen: false,
|
||||
}
|
||||
|
||||
// Reducer
|
||||
export default function reducer(state = initState, action = {}) {
|
||||
switch (action.type) {
|
||||
case OPEN_FROM_DROPDOWN:
|
||||
return extend(sendState, {
|
||||
fromDropdownOpen: true,
|
||||
})
|
||||
case CLOSE_FROM_DROPDOWN:
|
||||
return extend(sendState, {
|
||||
fromDropdownOpen: false,
|
||||
})
|
||||
case OPEN_TO_DROPDOWN:
|
||||
return extend(sendState, {
|
||||
toDropdownOpen: true,
|
||||
})
|
||||
case CLOSE_TO_DROPDOWN:
|
||||
return extend(sendState, {
|
||||
toDropdownOpen: false,
|
||||
})
|
||||
default:
|
||||
return sendState
|
||||
}
|
||||
}
|
||||
|
||||
// Action Creators
|
||||
export function openFromDropdown() {
|
||||
return { type: OPEN_FROM_DROPDOWN };
|
||||
}
|
||||
|
||||
export function closeFromDropdown() {
|
||||
return { type: CLOSE_FROM_DROPDOWN };
|
||||
}
|
||||
|
||||
export function openToDropdown() {
|
||||
return { type: OPEN_TO_DROPDOWN };
|
||||
}
|
||||
|
||||
export function closeToDropdown() {
|
||||
return { type: CLOSE_TO_DROPDOWN };
|
||||
}
|
@ -8,6 +8,7 @@ const reduceIdentities = require('./reducers/identities')
|
||||
const reduceMetamask = require('./reducers/metamask')
|
||||
const reduceApp = require('./reducers/app')
|
||||
const reduceLocale = require('./reducers/locale')
|
||||
const reduceSend = require('./ducks/send')
|
||||
|
||||
window.METAMASK_CACHED_LOG_STATE = null
|
||||
|
||||
@ -45,6 +46,12 @@ function rootReducer (state, action) {
|
||||
|
||||
state.localeMessages = reduceLocale(state, action)
|
||||
|
||||
//
|
||||
// Send
|
||||
//
|
||||
|
||||
state.send = reduceSend(state, action)
|
||||
|
||||
window.METAMASK_CACHED_LOG_STATE = state
|
||||
return state
|
||||
}
|
||||
|
@ -31,6 +31,11 @@ const {
|
||||
} = require('./components/send/send-utils')
|
||||
const { isValidAddress } = require('./util')
|
||||
|
||||
import PageContainer from './components/page-container/page-container.component'
|
||||
import SendHeader from './components/send_/send-header/send-header.container'
|
||||
import PageContainerContent from './components/page-container/page-container-content.component'
|
||||
import PageContainerFooter from './components/page-container/page-container-footer.component'
|
||||
|
||||
SendTransactionScreen.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
@ -181,25 +186,6 @@ SendTransactionScreen.prototype.componentDidUpdate = function (prevProps) {
|
||||
}
|
||||
}
|
||||
|
||||
SendTransactionScreen.prototype.renderHeader = function () {
|
||||
const { selectedToken, clearSend, goHome } = this.props
|
||||
|
||||
return h('div.page-container__header', [
|
||||
|
||||
h('div.page-container__title', selectedToken ? this.context.t('sendTokens') : this.context.t('sendETH')),
|
||||
|
||||
h('div.page-container__subtitle', this.context.t('onlySendToEtherAddress')),
|
||||
|
||||
h('div.page-container__header-close', {
|
||||
onClick: () => {
|
||||
clearSend()
|
||||
goHome()
|
||||
},
|
||||
}),
|
||||
|
||||
])
|
||||
}
|
||||
|
||||
SendTransactionScreen.prototype.renderErrorMessage = function (errorType) {
|
||||
const { errors } = this.props
|
||||
const errorMessage = errors[errorType]
|
||||
@ -477,7 +463,7 @@ SendTransactionScreen.prototype.renderMemoRow = function () {
|
||||
}
|
||||
|
||||
SendTransactionScreen.prototype.renderForm = function () {
|
||||
return h('.page-container__content', {}, [
|
||||
return h(PageContainerContent, [
|
||||
h('.send-v2__form', [
|
||||
this.renderFromRow(),
|
||||
|
||||
@ -486,9 +472,6 @@ SendTransactionScreen.prototype.renderForm = function () {
|
||||
this.renderAmountRow(),
|
||||
|
||||
this.renderGasRow(),
|
||||
|
||||
// this.renderMemoRow(),
|
||||
|
||||
]),
|
||||
])
|
||||
}
|
||||
@ -506,26 +489,22 @@ SendTransactionScreen.prototype.renderFooter = function () {
|
||||
const missingTokenBalance = selectedToken && !tokenBalance
|
||||
const noErrors = !amountError && toError === null
|
||||
|
||||
return h('div.page-container__footer', [
|
||||
h('button.btn-secondary--lg.page-container__footer-button', {
|
||||
onClick: () => {
|
||||
clearSend()
|
||||
goHome()
|
||||
},
|
||||
}, this.context.t('cancel')),
|
||||
h('button.btn-primary--lg.page-container__footer-button', {
|
||||
disabled: !noErrors || !gasTotal || missingTokenBalance,
|
||||
onClick: event => this.onSubmit(event),
|
||||
}, this.context.t('next')),
|
||||
])
|
||||
return h(PageContainerFooter, {
|
||||
onCancel: () => {
|
||||
clearSend()
|
||||
goHome()
|
||||
},
|
||||
onSubmit: e => this.onSubmit(e),
|
||||
disabled: !noErrors || !gasTotal || missingTokenBalance,
|
||||
})
|
||||
}
|
||||
|
||||
SendTransactionScreen.prototype.render = function () {
|
||||
return (
|
||||
|
||||
h('div.page-container', [
|
||||
h(PageContainer, [
|
||||
|
||||
this.renderHeader(),
|
||||
h(SendHeader),
|
||||
|
||||
this.renderForm(),
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user