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_MAX_MODE: 'UPDATE_MAX_MODE',
|
||||||
UPDATE_SEND: 'UPDATE_SEND',
|
UPDATE_SEND: 'UPDATE_SEND',
|
||||||
CLEAR_SEND: 'CLEAR_SEND',
|
CLEAR_SEND: 'CLEAR_SEND',
|
||||||
|
OPEN_FROM_DROPDOWN: 'OPEN_FROM_DROPDOWN',
|
||||||
|
CLOSE_FROM_DROPDOWN: 'CLOSE_FROM_DROPDOWN',
|
||||||
updateGasLimit,
|
updateGasLimit,
|
||||||
updateGasPrice,
|
updateGasPrice,
|
||||||
updateGasTotal,
|
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 reduceMetamask = require('./reducers/metamask')
|
||||||
const reduceApp = require('./reducers/app')
|
const reduceApp = require('./reducers/app')
|
||||||
const reduceLocale = require('./reducers/locale')
|
const reduceLocale = require('./reducers/locale')
|
||||||
|
const reduceSend = require('./ducks/send')
|
||||||
|
|
||||||
window.METAMASK_CACHED_LOG_STATE = null
|
window.METAMASK_CACHED_LOG_STATE = null
|
||||||
|
|
||||||
@ -45,6 +46,12 @@ function rootReducer (state, action) {
|
|||||||
|
|
||||||
state.localeMessages = reduceLocale(state, action)
|
state.localeMessages = reduceLocale(state, action)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Send
|
||||||
|
//
|
||||||
|
|
||||||
|
state.send = reduceSend(state, action)
|
||||||
|
|
||||||
window.METAMASK_CACHED_LOG_STATE = state
|
window.METAMASK_CACHED_LOG_STATE = state
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,11 @@ const {
|
|||||||
} = require('./components/send/send-utils')
|
} = require('./components/send/send-utils')
|
||||||
const { isValidAddress } = require('./util')
|
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 = {
|
SendTransactionScreen.contextTypes = {
|
||||||
t: PropTypes.func,
|
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) {
|
SendTransactionScreen.prototype.renderErrorMessage = function (errorType) {
|
||||||
const { errors } = this.props
|
const { errors } = this.props
|
||||||
const errorMessage = errors[errorType]
|
const errorMessage = errors[errorType]
|
||||||
@ -477,7 +463,7 @@ SendTransactionScreen.prototype.renderMemoRow = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.renderForm = function () {
|
SendTransactionScreen.prototype.renderForm = function () {
|
||||||
return h('.page-container__content', {}, [
|
return h(PageContainerContent, [
|
||||||
h('.send-v2__form', [
|
h('.send-v2__form', [
|
||||||
this.renderFromRow(),
|
this.renderFromRow(),
|
||||||
|
|
||||||
@ -486,9 +472,6 @@ SendTransactionScreen.prototype.renderForm = function () {
|
|||||||
this.renderAmountRow(),
|
this.renderAmountRow(),
|
||||||
|
|
||||||
this.renderGasRow(),
|
this.renderGasRow(),
|
||||||
|
|
||||||
// this.renderMemoRow(),
|
|
||||||
|
|
||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@ -506,26 +489,22 @@ SendTransactionScreen.prototype.renderFooter = function () {
|
|||||||
const missingTokenBalance = selectedToken && !tokenBalance
|
const missingTokenBalance = selectedToken && !tokenBalance
|
||||||
const noErrors = !amountError && toError === null
|
const noErrors = !amountError && toError === null
|
||||||
|
|
||||||
return h('div.page-container__footer', [
|
return h(PageContainerFooter, {
|
||||||
h('button.btn-secondary--lg.page-container__footer-button', {
|
onCancel: () => {
|
||||||
onClick: () => {
|
clearSend()
|
||||||
clearSend()
|
goHome()
|
||||||
goHome()
|
},
|
||||||
},
|
onSubmit: e => this.onSubmit(e),
|
||||||
}, this.context.t('cancel')),
|
disabled: !noErrors || !gasTotal || missingTokenBalance,
|
||||||
h('button.btn-primary--lg.page-container__footer-button', {
|
})
|
||||||
disabled: !noErrors || !gasTotal || missingTokenBalance,
|
|
||||||
onClick: event => this.onSubmit(event),
|
|
||||||
}, this.context.t('next')),
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.render = function () {
|
SendTransactionScreen.prototype.render = function () {
|
||||||
return (
|
return (
|
||||||
|
|
||||||
h('div.page-container', [
|
h(PageContainer, [
|
||||||
|
|
||||||
this.renderHeader(),
|
h(SendHeader),
|
||||||
|
|
||||||
this.renderForm(),
|
this.renderForm(),
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user