Merge pull request #4814 from whymarrh/send-data

Add hex data input to send screen
This commit is contained in:
Whymarrh Whitby 2018-07-18 15:43:21 -02:30 committed by GitHub
commit 76ac6bbb30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 150 additions and 21 deletions

View File

@ -164,6 +164,7 @@ var actions = {
UPDATE_GAS_PRICE: 'UPDATE_GAS_PRICE', UPDATE_GAS_PRICE: 'UPDATE_GAS_PRICE',
UPDATE_GAS_TOTAL: 'UPDATE_GAS_TOTAL', UPDATE_GAS_TOTAL: 'UPDATE_GAS_TOTAL',
UPDATE_SEND_FROM: 'UPDATE_SEND_FROM', UPDATE_SEND_FROM: 'UPDATE_SEND_FROM',
UPDATE_SEND_HEX_DATA: 'UPDATE_SEND_HEX_DATA',
UPDATE_SEND_TOKEN_BALANCE: 'UPDATE_SEND_TOKEN_BALANCE', UPDATE_SEND_TOKEN_BALANCE: 'UPDATE_SEND_TOKEN_BALANCE',
UPDATE_SEND_TO: 'UPDATE_SEND_TO', UPDATE_SEND_TO: 'UPDATE_SEND_TO',
UPDATE_SEND_AMOUNT: 'UPDATE_SEND_AMOUNT', UPDATE_SEND_AMOUNT: 'UPDATE_SEND_AMOUNT',
@ -183,6 +184,7 @@ var actions = {
setSendTokenBalance, setSendTokenBalance,
updateSendTokenBalance, updateSendTokenBalance,
updateSendFrom, updateSendFrom,
updateSendHexData,
updateSendTo, updateSendTo,
updateSendAmount, updateSendAmount,
updateSendMemo, updateSendMemo,
@ -838,6 +840,13 @@ function updateSendFrom (from) {
} }
} }
function updateSendHexData (value) {
return {
type: actions.UPDATE_SEND_HEX_DATA,
value,
}
}
function updateSendTo (to, nickname = '') { function updateSendTo (to, nickname = '') {
return { return {
type: actions.UPDATE_SEND_TO, type: actions.UPDATE_SEND_TO,

View File

@ -4,6 +4,7 @@ import PageContainerContent from '../../page-container/page-container-content.co
import SendAmountRow from './send-amount-row/' import SendAmountRow from './send-amount-row/'
import SendFromRow from './send-from-row/' import SendFromRow from './send-from-row/'
import SendGasRow from './send-gas-row/' import SendGasRow from './send-gas-row/'
import SendHexDataRow from './send-hex-data-row'
import SendToRow from './send-to-row/' import SendToRow from './send-to-row/'
export default class SendContent extends Component { export default class SendContent extends Component {
@ -20,6 +21,7 @@ export default class SendContent extends Component {
<SendToRow updateGas={(updateData) => this.props.updateGas(updateData)} /> <SendToRow updateGas={(updateData) => this.props.updateGas(updateData)} />
<SendAmountRow updateGas={(updateData) => this.props.updateGas(updateData)} /> <SendAmountRow updateGas={(updateData) => this.props.updateGas(updateData)} />
<SendGasRow /> <SendGasRow />
<SendHexDataRow />
</div> </div>
</PageContainerContent> </PageContainerContent>
) )

View File

@ -0,0 +1 @@
export { default } from './send-hex-data-row.container'

View File

@ -0,0 +1,40 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import SendRowWrapper from '../send-row-wrapper'
export default class SendHexDataRow extends Component {
static propTypes = {
data: PropTypes.string,
inError: PropTypes.bool,
updateSendHexData: PropTypes.func.isRequired,
};
static contextTypes = {
t: PropTypes.func,
};
onInput = (event) => {
const {updateSendHexData} = this.props
event.target.value = event.target.value.replace(/\n/g, '')
updateSendHexData(event.target.value || null)
}
render () {
const {inError} = this.props
const {t} = this.context
return (
<SendRowWrapper
label={`${t('hexData')}:`}
showError={inError}
errorType={'amount'}
>
<textarea
onInput={this.onInput}
placeholder="Optional"
className="send-v2__hex-data__input"
/>
</SendRowWrapper>
)
}
}

View File

@ -0,0 +1,21 @@
import { connect } from 'react-redux'
import {
updateSendHexData,
} from '../../../../actions'
import SendHexDataRow from './send-hex-data-row.component'
export default connect(mapStateToProps, mapDispatchToProps)(SendHexDataRow)
function mapStateToProps (state) {
return {
data: state.metamask.send.data,
}
}
function mapDispatchToProps (dispatch) {
return {
updateSendHexData (data) {
return dispatch(updateSendHexData(data))
},
}
}

View File

@ -8,6 +8,7 @@ export default class SendFooter extends Component {
static propTypes = { static propTypes = {
addToAddressBookIfNew: PropTypes.func, addToAddressBookIfNew: PropTypes.func,
amount: PropTypes.string, amount: PropTypes.string,
data: PropTypes.string,
clearSend: PropTypes.func, clearSend: PropTypes.func,
disabled: PropTypes.bool, disabled: PropTypes.bool,
editingTransactionId: PropTypes.string, editingTransactionId: PropTypes.string,
@ -41,6 +42,7 @@ export default class SendFooter extends Component {
const { const {
addToAddressBookIfNew, addToAddressBookIfNew,
amount, amount,
data,
editingTransactionId, editingTransactionId,
from: {address: from}, from: {address: from},
gasLimit: gas, gasLimit: gas,
@ -68,6 +70,7 @@ export default class SendFooter extends Component {
const promise = editingTransactionId const promise = editingTransactionId
? update({ ? update({
amount, amount,
data,
editingTransactionId, editingTransactionId,
from, from,
gas, gas,
@ -76,7 +79,7 @@ export default class SendFooter extends Component {
to, to,
unapprovedTxs, unapprovedTxs,
}) })
: sign({ selectedToken, to, amount, from, gas, gasPrice }) : sign({ data, selectedToken, to, amount, from, gas, gasPrice })
Promise.resolve(promise) Promise.resolve(promise)
.then(() => history.push(CONFIRM_TRANSACTION_ROUTE)) .then(() => history.push(CONFIRM_TRANSACTION_ROUTE))

View File

@ -18,6 +18,7 @@ import {
getSendFromObject, getSendFromObject,
getSendTo, getSendTo,
getSendToAccounts, getSendToAccounts,
getSendHexData,
getTokenBalance, getTokenBalance,
getUnapprovedTxs, getUnapprovedTxs,
} from '../send.selectors' } from '../send.selectors'
@ -35,6 +36,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(SendFooter)
function mapStateToProps (state) { function mapStateToProps (state) {
return { return {
amount: getSendAmount(state), amount: getSendAmount(state),
data: getSendHexData(state),
editingTransactionId: getSendEditingTransactionId(state), editingTransactionId: getSendEditingTransactionId(state),
from: getSendFromObject(state), from: getSendFromObject(state),
gasLimit: getGasLimit(state), gasLimit: getGasLimit(state),
@ -52,9 +54,10 @@ function mapStateToProps (state) {
function mapDispatchToProps (dispatch) { function mapDispatchToProps (dispatch) {
return { return {
clearSend: () => dispatch(clearSend()), clearSend: () => dispatch(clearSend()),
sign: ({ selectedToken, to, amount, from, gas, gasPrice }) => { sign: ({ selectedToken, to, amount, from, gas, gasPrice, data }) => {
const txParams = constructTxParams({ const txParams = constructTxParams({
amount, amount,
data,
from, from,
gas, gas,
gasPrice, gasPrice,
@ -68,6 +71,7 @@ function mapDispatchToProps (dispatch) {
}, },
update: ({ update: ({
amount, amount,
data,
editingTransactionId, editingTransactionId,
from, from,
gas, gas,
@ -78,6 +82,7 @@ function mapDispatchToProps (dispatch) {
}) => { }) => {
const editingTx = constructUpdatedTx({ const editingTx = constructUpdatedTx({
amount, amount,
data,
editingTransactionId, editingTransactionId,
from, from,
gas, gas,

View File

@ -8,8 +8,9 @@ function addHexPrefixToObjectValues (obj) {
}, {}) }, {})
} }
function constructTxParams ({ selectedToken, to, amount, from, gas, gasPrice }) { function constructTxParams ({ selectedToken, data, to, amount, from, gas, gasPrice }) {
const txParams = { const txParams = {
data,
from, from,
value: '0', value: '0',
gas, gas,
@ -21,13 +22,12 @@ function constructTxParams ({ selectedToken, to, amount, from, gas, gasPrice })
txParams.to = to txParams.to = to
} }
const hexPrefixedTxParams = addHexPrefixToObjectValues(txParams) return addHexPrefixToObjectValues(txParams)
return hexPrefixedTxParams
} }
function constructUpdatedTx ({ function constructUpdatedTx ({
amount, amount,
data,
editingTransactionId, editingTransactionId,
from, from,
gas, gas,
@ -36,9 +36,21 @@ function constructUpdatedTx ({
to, to,
unapprovedTxs, unapprovedTxs,
}) { }) {
const unapprovedTx = unapprovedTxs[editingTransactionId]
const txParamsData = unapprovedTx.txParams.data ? unapprovedTx.txParams.data : data
const editingTx = { const editingTx = {
...unapprovedTxs[editingTransactionId], ...unapprovedTx,
txParams: addHexPrefixToObjectValues({ from, gas, gasPrice }), txParams: Object.assign(
unapprovedTx.txParams,
addHexPrefixToObjectValues({
data: txParamsData,
to,
from,
gas,
gasPrice,
value: amount,
})
),
} }
if (selectedToken) { if (selectedToken) {
@ -52,18 +64,10 @@ function constructUpdatedTx ({
to: selectedToken.address, to: selectedToken.address,
data, data,
})) }))
} else { }
const { data } = unapprovedTxs[editingTransactionId].txParams
Object.assign(editingTx.txParams, addHexPrefixToObjectValues({ if (typeof editingTx.txParams.data === 'undefined') {
value: amount, delete editingTx.txParams.data
to,
data,
}))
if (typeof editingTx.txParams.data === 'undefined') {
delete editingTx.txParams.data
}
} }
return editingTx return editingTx

View File

@ -129,6 +129,7 @@ describe('SendFooter Component', function () {
assert.deepEqual( assert.deepEqual(
propsMethodSpies.update.getCall(0).args[0], propsMethodSpies.update.getCall(0).args[0],
{ {
data: undefined,
amount: 'mockAmount', amount: 'mockAmount',
editingTransactionId: 'mockEditingTransactionId', editingTransactionId: 'mockEditingTransactionId',
from: 'mockAddress', from: 'mockAddress',
@ -152,6 +153,7 @@ describe('SendFooter Component', function () {
assert.deepEqual( assert.deepEqual(
propsMethodSpies.sign.getCall(0).args[0], propsMethodSpies.sign.getCall(0).args[0],
{ {
data: undefined,
amount: 'mockAmount', amount: 'mockAmount',
from: 'mockAddress', from: 'mockAddress',
gas: 'mockGasLimit', gas: 'mockGasLimit',

View File

@ -38,6 +38,7 @@ proxyquire('../send-footer.container.js', {
getSendTo: (s) => `mockTo:${s}`, getSendTo: (s) => `mockTo:${s}`,
getSendToAccounts: (s) => `mockToAccounts:${s}`, getSendToAccounts: (s) => `mockToAccounts:${s}`,
getTokenBalance: (s) => `mockTokenBalance:${s}`, getTokenBalance: (s) => `mockTokenBalance:${s}`,
getSendHexData: (s) => `mockHexData:${s}`,
getUnapprovedTxs: (s) => `mockUnapprovedTxs:${s}`, getUnapprovedTxs: (s) => `mockUnapprovedTxs:${s}`,
}, },
'./send-footer.selectors': { isSendFormInError: (s) => `mockInError:${s}` }, './send-footer.selectors': { isSendFormInError: (s) => `mockInError:${s}` },
@ -51,6 +52,7 @@ describe('send-footer container', () => {
it('should map the correct properties to props', () => { it('should map the correct properties to props', () => {
assert.deepEqual(mapStateToProps('mockState'), { assert.deepEqual(mapStateToProps('mockState'), {
amount: 'mockAmount:mockState', amount: 'mockAmount:mockState',
data: 'mockHexData:mockState',
selectedToken: 'mockSelectedToken:mockState', selectedToken: 'mockSelectedToken:mockState',
editingTransactionId: 'mockEditingTransactionId:mockState', editingTransactionId: 'mockEditingTransactionId:mockState',
from: 'mockFromObject:mockState', from: 'mockFromObject:mockState',
@ -100,6 +102,7 @@ describe('send-footer container', () => {
assert.deepEqual( assert.deepEqual(
utilsStubs.constructTxParams.getCall(0).args[0], utilsStubs.constructTxParams.getCall(0).args[0],
{ {
data: undefined,
selectedToken: { selectedToken: {
address: '0xabc', address: '0xabc',
}, },
@ -129,6 +132,7 @@ describe('send-footer container', () => {
assert.deepEqual( assert.deepEqual(
utilsStubs.constructTxParams.getCall(0).args[0], utilsStubs.constructTxParams.getCall(0).args[0],
{ {
data: undefined,
selectedToken: undefined, selectedToken: undefined,
to: 'mockTo', to: 'mockTo',
amount: 'mockAmount', amount: 'mockAmount',
@ -160,6 +164,7 @@ describe('send-footer container', () => {
assert.deepEqual( assert.deepEqual(
utilsStubs.constructUpdatedTx.getCall(0).args[0], utilsStubs.constructUpdatedTx.getCall(0).args[0],
{ {
data: undefined,
to: 'mockTo', to: 'mockTo',
amount: 'mockAmount', amount: 'mockAmount',
from: 'mockFrom', from: 'mockFrom',

View File

@ -65,6 +65,28 @@ describe('send-footer utils', () => {
}) })
describe('constructTxParams()', () => { describe('constructTxParams()', () => {
it('should return a new txParams object with data if there data is given', () => {
assert.deepEqual(
constructTxParams({
data: 'someData',
selectedToken: false,
to: 'mockTo',
amount: 'mockAmount',
from: 'mockFrom',
gas: 'mockGas',
gasPrice: 'mockGasPrice',
}),
{
data: '0xsomeData',
to: '0xmockTo',
value: '0xmockAmount',
from: '0xmockFrom',
gas: '0xmockGas',
gasPrice: '0xmockGasPrice',
}
)
})
it('should return a new txParams object with value and to properties if there is no selectedToken', () => { it('should return a new txParams object with value and to properties if there is no selectedToken', () => {
assert.deepEqual( assert.deepEqual(
constructTxParams({ constructTxParams({
@ -76,6 +98,7 @@ describe('send-footer utils', () => {
gasPrice: 'mockGasPrice', gasPrice: 'mockGasPrice',
}), }),
{ {
data: undefined,
to: '0xmockTo', to: '0xmockTo',
value: '0xmockAmount', value: '0xmockAmount',
from: '0xmockFrom', from: '0xmockFrom',
@ -96,6 +119,7 @@ describe('send-footer utils', () => {
gasPrice: 'mockGasPrice', gasPrice: 'mockGasPrice',
}), }),
{ {
data: undefined,
value: '0x0', value: '0x0',
from: '0xmockFrom', from: '0xmockFrom',
gas: '0xmockGas', gas: '0xmockGas',

View File

@ -33,6 +33,7 @@ const selectors = {
getSelectedTokenExchangeRate, getSelectedTokenExchangeRate,
getSelectedTokenToFiatRate, getSelectedTokenToFiatRate,
getSendAmount, getSendAmount,
getSendHexData,
getSendEditingTransactionId, getSendEditingTransactionId,
getSendErrors, getSendErrors,
getSendFrom, getSendFrom,
@ -210,6 +211,10 @@ function getSendAmount (state) {
return state.metamask.send.amount return state.metamask.send.amount
} }
function getSendHexData (state) {
return state.metamask.send.data
}
function getSendEditingTransactionId (state) { function getSendEditingTransactionId (state) {
return state.metamask.send.editingTransactionId return state.metamask.send.editingTransactionId
} }

View File

@ -628,7 +628,7 @@
} }
} }
&__to-autocomplete, &__memo-text-area { &__to-autocomplete, &__memo-text-area, &__hex-data {
&__input { &__input {
height: 54px; height: 54px;
width: 100%; width: 100%;
@ -899,4 +899,4 @@
.sliders-icon { .sliders-icon {
color: $curious-blue; color: $curious-blue;
} }

View File

@ -222,6 +222,14 @@ function reduceMetamask (state, action) {
}, },
}) })
case actions.UPDATE_SEND_HEX_DATA:
return extend(metamaskState, {
send: {
...metamaskState.send,
data: action.value,
},
})
case actions.UPDATE_SEND_FROM: case actions.UPDATE_SEND_FROM:
return extend(metamaskState, { return extend(metamaskState, {
send: { send: {