From c9c8ecb6fddc891cd80c07ea952023600b34b361 Mon Sep 17 00:00:00 2001 From: jinchung Date: Sat, 23 Nov 2019 12:47:46 -0500 Subject: [PATCH 1/2] Consolidate and remove SendComponentWithData --- src/components/SendComponentWithData.js | 297 ----------------------- src/components/fields/AddressField.js | 3 +- src/hoc/index.js | 1 + src/hoc/withDataInit.js | 3 +- src/hoc/withSend.js | 49 ++++ src/redux/gas.js | 4 +- src/redux/send.js | 25 +- src/screens/SendSheet.js | 189 +++------------ src/screens/SendSheetWithData.js | 310 +++++++++++++++++++++++- 9 files changed, 397 insertions(+), 484 deletions(-) delete mode 100644 src/components/SendComponentWithData.js create mode 100644 src/hoc/withSend.js diff --git a/src/components/SendComponentWithData.js b/src/components/SendComponentWithData.js deleted file mode 100644 index b1e6e860db1..00000000000 --- a/src/components/SendComponentWithData.js +++ /dev/null @@ -1,297 +0,0 @@ -import { get } from 'lodash'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { compose } from 'recompact'; -import { estimateGasLimit } from '../handlers/web3'; -import { greaterThan } from '../helpers/utilities'; -import { checkIsValidAddress } from '../helpers/validators'; -import { withAccountData, withGas, withUniqueTokens } from '../hoc'; -import lang from '../languages'; -import { - sendClearFields, - sendMaxBalance, - sendModalInit, - sendToggleConfirmationView, - sendTransaction, - sendUpdateAssetAmount, - sendUpdateNativeAmount, - sendUpdateRecipient, - sendUpdateSelected, -} from '../redux/send'; -import { ethereumUtils } from '../utils'; - -const mapStateToProps = ({ send, settings }) => ({ - accountType: settings.accountType, - address: send.address, - assetAmount: send.assetAmount, - confirm: send.confirm, - fetching: send.fetching, - isSufficientBalance: send.isSufficientBalance, - nativeAmount: send.nativeAmount, - nativeCurrency: settings.nativeCurrency, - network: settings.network, - recipient: send.recipient, - selected: send.selected, - txHash: send.txHash, -}); - -/** - * Create SendComponent connected to redux with actions for sending assets. - * @param {Component} SendComponent React component for sending. - * @param {Object} options - * {Function} options.sendTransactionCallback Function to be run after sendTransaction redux action. - * {String} options.defaultAsset Symbol for default asset to send. - * @return {Component} SendComponent connected to redux. - */ -export const withSendComponentWithData = (SendComponent, options) => { - class SendComponentWithData extends Component { - static propTypes = { - accountType: PropTypes.string, - address: PropTypes.string, - assetAmount: PropTypes.string.isRequired, - assets: PropTypes.array.isRequired, - confirm: PropTypes.bool.isRequired, - fetching: PropTypes.bool.isRequired, - gasLimit: PropTypes.number, - gasPrices: PropTypes.object.isRequired, - gasUpdateDefaultGasLimit: PropTypes.func.isRequired, - gasUpdateTxFee: PropTypes.func.isRequired, - isSufficientBalance: PropTypes.bool.isRequired, - isSufficientGas: PropTypes.bool.isRequired, - nativeAmount: PropTypes.string.isRequired, - nativeCurrency: PropTypes.string.isRequired, - network: PropTypes.string.isRequired, - recipient: PropTypes.string.isRequired, - selected: PropTypes.object.isRequired, - selectedGasPrice: PropTypes.shape({ txFee: PropTypes.object }), - selectedGasPriceOption: PropTypes.string.isRequired, - sendClearFields: PropTypes.func.isRequired, - sendMaxBalance: PropTypes.func.isRequired, - sendModalInit: PropTypes.func.isRequired, - sendToggleConfirmationView: PropTypes.func.isRequired, - sendTransaction: PropTypes.func.isRequired, - sendUpdateAssetAmount: PropTypes.func.isRequired, - sendUpdateNativeAmount: PropTypes.func.isRequired, - sendUpdateRecipient: PropTypes.func.isRequired, - sendUpdateSelected: PropTypes.func.isRequired, - txFees: PropTypes.object.isRequired, - txHash: PropTypes.string.isRequired, - }; - - constructor(props) { - super(props); - - this.state = { - isValidAddress: false, - showQRCodeReader: false, - }; - - this.defaultAsset = options.defaultAsset; - this.gasFormat = options.gasFormat || 'long'; - this.sendTransactionCallback = - options.sendTransactionCallback || function noop() {}; - } - - componentDidMount() { - this.props.sendModalInit({ - defaultAsset: this.defaultAsset, - gasFormat: this.gasFormat, - }); - this.props.gasUpdateDefaultGasLimit(); - } - - async componentDidUpdate(prevProps) { - const { address, assetAmount, recipient, selected } = this.props; - - if (recipient !== prevProps.recipient) { - const validAddress = await checkIsValidAddress(recipient); - // eslint-disable-next-line react/no-did-update-set-state - this.setState({ isValidAddress: validAddress }); - } - - if (this.state.isValidAddress) { - if ( - selected.symbol !== prevProps.selected.symbol || - recipient !== prevProps.recipient || - assetAmount !== prevProps.assetAmount - ) { - estimateGasLimit({ - address, - amount: assetAmount, - asset: selected, - recipient, - }) - .then(gasLimit => { - this.props.gasUpdateTxFee(gasLimit); - }) - .catch(() => { - this.props.gasUpdateTxFee(); - }); - } - } - } - - onAddressInputFocus = async () => { - const { recipient } = this.props; - - const validAddress = await checkIsValidAddress(recipient); - this.setState({ isValidAddress: validAddress }); - }; - - onAddressInputBlur = async () => { - const { recipient } = this.props; - - const validAddress = await checkIsValidAddress(recipient); - this.setState({ isValidAddress: validAddress }); - }; - - onGoBack = () => this.props.sendToggleConfirmationView(false); - - onSendMaxBalance = () => this.props.sendMaxBalance(); - - onSendAnother = () => { - this.props.sendToggleConfirmationView(false); - this.props.sendClearFields(); - this.props.sendModalInit({ defaultAsset: this.defaultAsset }); - }; - - onSubmit = async event => { - if (event && typeof event.preventDefault === 'function') { - event.preventDefault(); - } - - if (!this.props.selectedGasPrice.txFee) { - return; - } - - // Balance checks - if (!this.props.confirm) { - const isAddressValid = await checkIsValidAddress(this.props.recipient); - if (!isAddressValid) { - console.log(lang.t('notification.error.invalid_address')); - return; - } - if (this.props.selected.address === 'eth') { - const { - requestedAmount, - balance, - amountWithFees, - } = ethereumUtils.transactionData( - this.props.assets, - this.props.assetAmount, - this.props.selectedGasPrice - ); - - if (greaterThan(requestedAmount, balance)) { - return; - } - if (greaterThan(amountWithFees, balance)) { - return; - } - } else if (!this.props.selected.isNft) { - const { - requestedAmount, - balance, - txFee, - } = ethereumUtils.transactionData( - this.props.assets, - this.props.assetAmount, - this.props.selectedGasPrice - ); - - const tokenBalance = get(this.props, 'selected.balance.amount'); - - if (greaterThan(requestedAmount, tokenBalance)) { - return; - } - if (greaterThan(txFee, balance)) { - return; - } - } - - this.props.sendToggleConfirmationView(true); - - return this.props.sendTransaction( - { - address: this.props.address, - amount: this.props.assetAmount, - asset: this.props.selected, - gasLimit: this.props.gasLimit, - gasPrice: this.props.selectedGasPrice, - recipient: this.props.recipient, - }, - this.sendTransactionCallback - ); - } - }; - - onClose = () => { - this.props.sendClearFields(); - }; - - // QR Code Reader Handlers - toggleQRCodeReader = () => - this.setState(prevState => ({ - showQRCodeReader: !prevState.showQRCodeReader, - })); - - onQRCodeValidate = async rawData => { - const data = rawData.match(/0x\w{40}/g) - ? rawData.match(/0x\w{40}/g)[0] - : null; - let result = false; - if (data) { - result = await checkIsValidAddress(data); - } - const onError = () => - console.log(lang.t('notification.error.invalid_address_scanned')); - return { data, onError, result }; - }; - - onQRCodeScan = data => { - this.props.sendUpdateRecipient(data); - this.setState({ showQRCodeReader: false }); - }; - - onQRCodeError = () => { - console.log(lang.t('notification.error.failed_scanning_qr_code')); - }; - - render() { - return ( - - ); - } - } - - return compose( - connect(mapStateToProps, { - sendClearFields, - sendMaxBalance, - sendModalInit, - sendToggleConfirmationView, - sendTransaction, - sendUpdateAssetAmount, - sendUpdateNativeAmount, - sendUpdateRecipient, - sendUpdateSelected, - }), - withGas, - withAccountData, - withUniqueTokens - )(SendComponentWithData); -}; diff --git a/src/components/fields/AddressField.js b/src/components/fields/AddressField.js index 7a99260c0b5..bc0346dfe56 100644 --- a/src/components/fields/AddressField.js +++ b/src/components/fields/AddressField.js @@ -49,8 +49,9 @@ export default withNavigation( this.inputRef.focus() ); } + shouldComponentUpdate(nextProps, nextState) { - const isNewAddress = isNewValueForPath(this.props, this.state, 'address'); + const isNewAddress = isNewValueForPath(nextProps, this.state, 'address'); const isNewInputValue = isNewValueForPath( this.state, nextState, diff --git a/src/hoc/index.js b/src/hoc/index.js index c7eab59d81e..7fb726b791d 100644 --- a/src/hoc/index.js +++ b/src/hoc/index.js @@ -28,6 +28,7 @@ export { default as withRequests } from './withRequests'; export { default as withRotationForDirection } from './withRotationForDirection'; export { default as withSafeAreaViewInsetValues } from './withSafeAreaViewInsetValues'; export { default as withSelectedInput } from './withSelectedInput'; +export { default as withSend } from './withSend'; export { default as withSendFeedback } from './withSendFeedback'; export { default as withStatusBarStyle } from './withStatusBarStyle'; export { default as withTransactionConfirmationScreen } from './withTransactionConfirmationScreen'; diff --git a/src/hoc/withDataInit.js b/src/hoc/withDataInit.js index a258c3732c4..ada27b7a110 100644 --- a/src/hoc/withDataInit.js +++ b/src/hoc/withDataInit.js @@ -84,6 +84,7 @@ export default Component => }, clearAccountData: ownProps => async () => { web3ListenerClearState(); + gasClearState(); const p0 = ownProps.explorerClearState(); const p1 = ownProps.dataClearState(); const p2 = ownProps.clearIsWalletEmpty(); @@ -93,7 +94,6 @@ export default Component => const p6 = ownProps.nonceClearState(); const p7 = ownProps.requestsClearState(); const p8 = ownProps.uniswapClearState(); - const p9 = ownProps.gasClearState(); return promiseUtils.PromiseAllWithFails([ p0, p1, @@ -104,7 +104,6 @@ export default Component => p6, p7, p8, - p9, ]); }, initializeAccountData: ownProps => async () => { diff --git a/src/hoc/withSend.js b/src/hoc/withSend.js new file mode 100644 index 00000000000..e0ce21f582b --- /dev/null +++ b/src/hoc/withSend.js @@ -0,0 +1,49 @@ +import { connect } from 'react-redux'; +import { + sendClearFields, + sendCreatedTransaction, + sendMaxBalance, + sendModalInit, + sendToggleConfirmationView, + sendUpdateAssetAmount, + sendUpdateNativeAmount, + sendUpdateRecipient, + sendUpdateSelected, +} from '../redux/send'; + +const mapStateToProps = ({ + send: { + address, + assetAmount, + confirm, + fetching, + isSufficientBalance, + nativeAmount, + recipient, + selected, + txHash, + }, +}) => ({ + address, + assetAmount, + confirm, + fetching, + isSufficientBalance, + nativeAmount, + recipient, + selected, + txHash, +}); + +export default Component => + connect(mapStateToProps, { + sendClearFields, + sendCreatedTransaction, + sendMaxBalance, + sendModalInit, + sendToggleConfirmationView, + sendUpdateAssetAmount, + sendUpdateNativeAmount, + sendUpdateRecipient, + sendUpdateSelected, + })(Component); diff --git a/src/redux/gas.js b/src/redux/gas.js index bab930fa30b..f87b687bf43 100644 --- a/src/redux/gas.js +++ b/src/redux/gas.js @@ -177,9 +177,7 @@ const getSelectedGasPrice = ( }; }; -export const gasClearState = () => () => { - clearInterval(getGasPricesInterval); -}; +export const gasClearState = () => clearInterval(getGasPricesInterval); // -- Reducer --------------------------------------------------------------- // const INITIAL_STATE = { diff --git a/src/redux/send.js b/src/redux/send.js index 6e534552e83..97c59ea8972 100644 --- a/src/redux/send.js +++ b/src/redux/send.js @@ -1,10 +1,11 @@ import { get, isEmpty } from 'lodash'; +import { createSignableTransaction } from '../handlers/web3'; import { convertAmountAndPriceToNativeDisplay, convertAmountFromNativeValue, formatInputDecimals, } from '../helpers/utilities'; -import { createSignableTransaction } from '../handlers/web3'; +import { sendTransaction } from '../model/wallet'; import { ethereumUtils } from '../utils'; import { dataAddNewTransaction } from './data'; @@ -29,24 +30,15 @@ const SEND_CLEAR_FIELDS = 'send/SEND_CLEAR_FIELDS'; // -- Actions --------------------------------------------------------------- // -export const sendModalInit = (options = {}) => (dispatch, getState) => { +export const sendModalInit = () => (dispatch, getState) => { const { accountAddress } = getState().settings; - const { assets } = getState().data; - const selected = - assets.filter(asset => asset.address === options.defaultAsset)[0] || {}; dispatch({ - payload: { - address: accountAddress, - selected, - }, + payload: accountAddress, type: SEND_MODAL_INIT, }); }; -export const sendTransaction = ( - transactionDetails, - signAndSendTransactionCb -) => (dispatch, getState) => +export const sendCreatedTransaction = transactionDetails => dispatch => new Promise((resolve, reject) => { dispatch({ type: SEND_TRANSACTION_REQUEST }); const { @@ -57,7 +49,6 @@ export const sendTransaction = ( gasPrice, gasLimit, } = transactionDetails; - const { accountType } = getState().settings; const txDetails = { amount, asset, @@ -69,8 +60,7 @@ export const sendTransaction = ( }; return createSignableTransaction(txDetails) .then(signableTransactionDetails => { - signAndSendTransactionCb({ - accountType, + sendTransaction({ transaction: signableTransactionDetails, }) .then(txHash => { @@ -231,8 +221,7 @@ export default (state = INITIAL_STATE, action) => { case SEND_MODAL_INIT: return { ...state, - address: action.payload.address, - selected: action.payload.selected, + address: action.payload, }; case SEND_TRANSACTION_REQUEST: return { ...state, fetching: true }; diff --git a/src/screens/SendSheet.js b/src/screens/SendSheet.js index 11305645f7d..a9337e5078f 100644 --- a/src/screens/SendSheet.js +++ b/src/screens/SendSheet.js @@ -1,10 +1,8 @@ -import analytics from '@segment/analytics-react-native'; -import { get, isEmpty, isString, toLower } from 'lodash'; +import { isEmpty } from 'lodash'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { Keyboard, KeyboardAvoidingView } from 'react-native'; import { getStatusBarHeight, isIphoneX } from 'react-native-iphone-x-helper'; -import { compose, withHandlers, withProps } from 'recompact'; import styled from 'styled-components/primitives'; import { Column } from '../components/layout'; import { @@ -15,16 +13,8 @@ import { SendHeader, SendTransactionSpeed, } from '../components/send'; -import { - withAccountData, - withAccountSettings, - withContacts, - withDataInit, - withTransitionProps, - withUniqueTokens, -} from '../hoc'; import { borders, colors } from '../styles'; -import { deviceUtils, gasUtils, isNewValueForPath } from '../utils'; +import { deviceUtils, isNewValueForPath } from '../utils'; const statusBarHeight = getStatusBarHeight(true); @@ -40,70 +30,38 @@ const SheetContainer = styled(Column)` top: ${statusBarHeight}; `; -class SendSheet extends Component { +export default class SendSheet extends Component { static propTypes = { allAssets: PropTypes.array, assetAmount: PropTypes.string, contacts: PropTypes.object, fetchData: PropTypes.func, gasPrices: PropTypes.object, - gasUpdateGasPriceOption: PropTypes.func, isSufficientBalance: PropTypes.bool, isSufficientGas: PropTypes.bool, isValidAddress: PropTypes.bool, nativeCurrencySymbol: PropTypes.string, navigation: PropTypes.object, - onSubmit: PropTypes.func, + onChangeAssetAmount: PropTypes.func, + onChangeInput: PropTypes.func, + onChangeNativeAmount: PropTypes.func, + onLongPressSend: PropTypes.func, + onPressTransactionSpeed: PropTypes.func, + onResetAssetSelection: PropTypes.func, + onSelectAsset: PropTypes.func, recipient: PropTypes.string, removeContact: PropTypes.func, selected: PropTypes.object, selectedGasPrice: PropTypes.object, sendableUniqueTokens: PropTypes.arrayOf(PropTypes.object), - sendClearFields: PropTypes.func, - sendMaxBalance: PropTypes.func, - sendUpdateAssetAmount: PropTypes.func, - sendUpdateNativeAmount: PropTypes.func, sendUpdateRecipient: PropTypes.func, - sendUpdateSelected: PropTypes.func, sortedContacts: PropTypes.array, }; - static defaultProps = { - isSufficientBalance: false, - isSufficientGas: false, - isValidAddress: false, - }; - - state = { - currentInput: '', - isAuthorizing: false, - }; - - componentDidMount = async () => { - const { navigation, sendUpdateRecipient } = this.props; - const address = get(navigation, 'state.params.address'); - - if (address) { - sendUpdateRecipient(address); - } - }; - componentDidUpdate(prevProps) { - const { - contacts, - isValidAddress, - navigation, - selected, - sendUpdateSelected, - } = this.props; - - const asset = get(navigation, 'state.params.asset'); + const { contacts, isValidAddress, navigation, selected } = this.props; if (isValidAddress && !prevProps.isValidAddress) { - if (asset) { - sendUpdateSelected(asset); - } - Keyboard.dismiss(); } @@ -136,91 +94,24 @@ class SendSheet extends Component { } } - componentWillUnmount() { - this.props.sendClearFields(); - } - - onChangeAssetAmount = assetAmount => { - if (isString(assetAmount)) { - this.props.sendUpdateAssetAmount(assetAmount); - analytics.track('Changed token input in Send flow'); - } - }; - - onChangeNativeAmount = nativeAmount => { - if (isString(nativeAmount)) { - this.props.sendUpdateNativeAmount(nativeAmount); - analytics.track('Changed native currency input in Send flow'); - } - }; - - onLongPressSend = () => { - this.setState({ isAuthorizing: true }); - - if (isIphoneX()) { - this.sendTransaction(); - } else { - this.onPressTransactionSpeed(this.sendTransaction); - } - }; - - onPressTransactionSpeed = onSuccess => { - const { gasPrices, gasUpdateGasPriceOption, txFees } = this.props; - gasUtils.showTransactionSpeedOptions( - gasPrices, - txFees, - gasUpdateGasPriceOption, - onSuccess - ); - }; - - onResetAssetSelection = () => { - analytics.track('Reset asset selection in Send flow'); - this.props.sendUpdateSelected({}); - }; - - onSelectAsset = asset => this.props.sendUpdateSelected(asset); - - sendTransaction = () => { - const { - assetAmount, - navigation, - onSubmit, - recipient, - selected, - sendClearFields, - } = this.props; - - if (Number(assetAmount) <= 0) return false; - - return onSubmit() - .then(() => { - this.setState({ isAuthorizing: false }); - analytics.track('Sent transaction', { - assetName: selected.name, - assetType: selected.isNft ? 'unique_token' : 'token', - isRecepientENS: toLower(recipient.slice(-4)) === '.eth', - }); - sendClearFields(); - navigation.navigate('ProfileScreen'); - }) - .catch(() => { - this.setState({ isAuthorizing: false }); - }); - }; - - onChangeInput = event => { - this.setState({ currentInput: event }); - this.props.sendUpdateRecipient(event); - }; - render() { const { allAssets, contacts, + currentInput, fetchData, + isAuthorizing, + isSufficientBalance, + isSufficientGas, isValidAddress, nativeCurrencySymbol, + onChangeAssetAmount, + onChangeInput, + onChangeNativeAmount, + onLongPressSend, + onPressTransactionSpeed, + onResetAssetSelection, + onSelectAsset, recipient, removeContact, selected, @@ -240,9 +131,8 @@ class SendSheet extends Component { @@ -259,7 +149,7 @@ class SendSheet extends Component { )} @@ -270,20 +160,22 @@ class SendSheet extends Component { buttonRenderer={ } - onChangeAssetAmount={this.onChangeAssetAmount} - onChangeNativeAmount={this.onChangeNativeAmount} - onResetAssetSelection={this.onResetAssetSelection} + onChangeAssetAmount={onChangeAssetAmount} + onChangeNativeAmount={onChangeNativeAmount} + onResetAssetSelection={onResetAssetSelection} selected={selected} txSpeedRenderer={ isIphoneX() && ( ) } @@ -295,18 +187,3 @@ class SendSheet extends Component { ); } } - -export default compose( - withAccountData, - withContacts, - withUniqueTokens, - withAccountSettings, - withDataInit, - withTransitionProps, - withProps(({ transitionProps: { isTransitioning } }) => ({ - isTransitioning, - })), - withHandlers({ - fetchData: ({ refreshAccountData }) => async () => refreshAccountData(), - }) -)(SendSheet); diff --git a/src/screens/SendSheetWithData.js b/src/screens/SendSheetWithData.js index 3e8d7a77d6b..676159f4113 100644 --- a/src/screens/SendSheetWithData.js +++ b/src/screens/SendSheetWithData.js @@ -1,11 +1,292 @@ -import { withSendComponentWithData } from '../components/SendComponentWithData'; -import { sendTransaction } from '../model/wallet'; +import analytics from '@segment/analytics-react-native'; +import { get, isString, toLower } from 'lodash'; +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import { isIphoneX } from 'react-native-iphone-x-helper'; +import { compose, withHandlers, withProps } from 'recompact'; +import { estimateGasLimit } from '../handlers/web3'; +import { greaterThan } from '../helpers/utilities'; +import { checkIsValidAddress } from '../helpers/validators'; +import { + withAccountData, + withAccountSettings, + withContacts, + withDataInit, + withGas, + withSend, + withTransitionProps, + withUniqueTokens, +} from '../hoc'; +import lang from '../languages'; +import { ethereumUtils, gasUtils, isNewValueForPath } from '../utils'; import SendSheet from './SendSheet'; -const SendSheetWithData = withSendComponentWithData(SendSheet, { - gasFormat: 'short', - sendTransactionCallback: sendTransaction, -}); +class SendSheetWithData extends Component { + static propTypes = { + address: PropTypes.string, + assetAmount: PropTypes.string.isRequired, + assets: PropTypes.array.isRequired, + confirm: PropTypes.bool.isRequired, + fetching: PropTypes.bool.isRequired, + gasLimit: PropTypes.number, + gasPrices: PropTypes.object.isRequired, + gasUpdateDefaultGasLimit: PropTypes.func.isRequired, + gasUpdateTxFee: PropTypes.func.isRequired, + isSufficientBalance: PropTypes.bool.isRequired, + isSufficientGas: PropTypes.bool.isRequired, + nativeAmount: PropTypes.string.isRequired, + nativeCurrency: PropTypes.string.isRequired, + network: PropTypes.string.isRequired, + recipient: PropTypes.string.isRequired, + selected: PropTypes.object.isRequired, + selectedGasPrice: PropTypes.shape({ txFee: PropTypes.object }), + selectedGasPriceOption: PropTypes.string.isRequired, + sendClearFields: PropTypes.func.isRequired, + sendCreatedTransaction: PropTypes.func.isRequired, + sendMaxBalance: PropTypes.func.isRequired, + sendModalInit: PropTypes.func.isRequired, + sendToggleConfirmationView: PropTypes.func.isRequired, + sendUpdateAssetAmount: PropTypes.func.isRequired, + sendUpdateNativeAmount: PropTypes.func.isRequired, + sendUpdateRecipient: PropTypes.func.isRequired, + sendUpdateSelected: PropTypes.func.isRequired, + txFees: PropTypes.object.isRequired, + txHash: PropTypes.string.isRequired, + }; + + constructor(props) { + super(props); + + this.state = { + contacts: [], + currentInput: '', + isAuthorizing: false, + isValidAddress: false, + }; + } + + componentDidMount() { + this.props.sendModalInit(); + this.props.gasUpdateDefaultGasLimit(); + + const { navigation, sendUpdateRecipient } = this.props; + const address = get(navigation, 'state.params.address'); + + if (address) { + sendUpdateRecipient(address); + } + } + + async componentDidUpdate(prevProps, prevState) { + const { + address, + assetAmount, + navigation, + recipient, + selected, + sendUpdateSelected, + } = this.props; + const { isValidAddress } = this.state; + + const asset = get(navigation, 'state.params.asset'); + + if (isValidAddress && !prevState.isValidAddress) { + if (asset) { + sendUpdateSelected(asset); + } + } + + const isNewRecipient = isNewValueForPath( + this.props, + prevProps, + 'recipient' + ); + if (isNewRecipient) { + const validAddress = await checkIsValidAddress(recipient); + // eslint-disable-next-line react/no-did-update-set-state + this.setState({ isValidAddress: validAddress }); + } + + if (isValidAddress) { + if ( + selected.symbol !== prevProps.selected.symbol || + recipient !== prevProps.recipient || + assetAmount !== prevProps.assetAmount + ) { + estimateGasLimit({ + address, + amount: assetAmount, + asset: selected, + recipient, + }) + .then(gasLimit => { + this.props.gasUpdateTxFee(gasLimit); + }) + .catch(() => { + this.props.gasUpdateTxFee(null); + }); + } + } + } + + componentWillUnmount() { + this.props.sendClearFields(); + } + + onChangeAssetAmount = assetAmount => { + if (isString(assetAmount)) { + this.props.sendUpdateAssetAmount(assetAmount); + analytics.track('Changed token input in Send flow'); + } + }; + + onChangeNativeAmount = nativeAmount => { + if (isString(nativeAmount)) { + this.props.sendUpdateNativeAmount(nativeAmount); + analytics.track('Changed native currency input in Send flow'); + } + }; + + onLongPressSend = () => { + this.setState({ isAuthorizing: true }); + + if (isIphoneX()) { + this.submitTransaction(); + } else { + this.onPressTransactionSpeed(this.submitTransaction); + } + }; + + onPressTransactionSpeed = onSuccess => { + const { gasPrices, gasUpdateGasPriceOption, txFees } = this.props; + gasUtils.showTransactionSpeedOptions( + gasPrices, + txFees, + gasUpdateGasPriceOption, + onSuccess + ); + }; + + onResetAssetSelection = () => { + analytics.track('Reset asset selection in Send flow'); + this.props.sendUpdateSelected({}); + }; + + onSelectAsset = asset => this.props.sendUpdateSelected(asset); + + submitTransaction = async () => { + const { + assetAmount, + navigation, + recipient, + selected, + sendClearFields, + } = this.props; + + if (Number(assetAmount) <= 0) return false; + + try { + await this.onSubmit(); + this.setState({ isAuthorizing: false }); + analytics.track('Sent transaction', { + assetName: selected.name, + assetType: selected.isNft ? 'unique_token' : 'token', + isRecepientENS: toLower(recipient.slice(-4)) === '.eth', + }); + sendClearFields(); + navigation.navigate('ProfileScreen'); + } catch { + this.setState({ isAuthorizing: false }); + } + }; + + onChangeInput = event => { + this.setState({ currentInput: event }); + this.props.sendUpdateRecipient(event); + }; + + onSubmit = async () => { + if (!this.props.selectedGasPrice.txFee) { + return; + } + + // Balance checks + if (!this.props.confirm) { + const isAddressValid = await checkIsValidAddress(this.props.recipient); + if (!isAddressValid) { + console.log(lang.t('notification.error.invalid_address')); + return; + } + if (this.props.selected.address === 'eth') { + const { + requestedAmount, + balance, + amountWithFees, + } = ethereumUtils.transactionData( + this.props.assets, + this.props.assetAmount, + this.props.selectedGasPrice + ); + + if (greaterThan(requestedAmount, balance)) { + return; + } + if (greaterThan(amountWithFees, balance)) { + return; + } + } else if (!this.props.selected.isNft) { + const { + requestedAmount, + balance, + txFee, + } = ethereumUtils.transactionData( + this.props.assets, + this.props.assetAmount, + this.props.selectedGasPrice + ); + + const tokenBalance = get(this.props, 'selected.balance.amount'); + + if (greaterThan(requestedAmount, tokenBalance)) { + return; + } + if (greaterThan(txFee, balance)) { + return; + } + } + + this.props.sendToggleConfirmationView(true); + + return this.props.sendCreatedTransaction({ + address: this.props.address, + amount: this.props.assetAmount, + asset: this.props.selected, + gasLimit: this.props.gasLimit, + gasPrice: this.props.selectedGasPrice, + recipient: this.props.recipient, + }); + } + }; + + render() { + return ( + + ); + } +} SendSheetWithData.navigationOptions = ({ navigation: { @@ -17,4 +298,19 @@ SendSheetWithData.navigationOptions = ({ }, }); -export default SendSheetWithData; +export default compose( + withAccountData, + withAccountSettings, + withContacts, + withDataInit, + withSend, + withGas, + withUniqueTokens, + withTransitionProps, + withProps(({ transitionProps: { isTransitioning } }) => ({ + isTransitioning, + })), + withHandlers({ + fetchData: ({ refreshAccountData }) => async () => refreshAccountData(), + }) +)(SendSheetWithData); From a7700ae45987cb4c5c16e9b35b62d94b2a949ae0 Mon Sep 17 00:00:00 2001 From: jinchung Date: Sat, 23 Nov 2019 12:49:36 -0500 Subject: [PATCH 2/2] Remove send redux and use state --- src/components/send/SendAssetForm.js | 7 +- src/hoc/index.js | 1 - src/hoc/withSend.js | 49 ----- src/redux/reducers.js | 2 - src/redux/send.js | 267 ------------------------- src/screens/ExchangeModal.js | 47 ----- src/screens/SendSheet.js | 5 + src/screens/SendSheetWithData.js | 288 +++++++++++++++++---------- 8 files changed, 198 insertions(+), 468 deletions(-) delete mode 100644 src/hoc/withSend.js delete mode 100644 src/redux/send.js diff --git a/src/components/send/SendAssetForm.js b/src/components/send/SendAssetForm.js index 3ef3981be69..3f6efbb5523 100644 --- a/src/components/send/SendAssetForm.js +++ b/src/components/send/SendAssetForm.js @@ -39,6 +39,7 @@ const SendAssetForm = ({ buttonRenderer, onResetAssetSelection, selected, + sendMaxBalance, txSpeedRenderer, ...props }) => { @@ -69,7 +70,11 @@ const SendAssetForm = ({ {selected.isNft ? ( ) : ( - + )} ({ - address, - assetAmount, - confirm, - fetching, - isSufficientBalance, - nativeAmount, - recipient, - selected, - txHash, -}); - -export default Component => - connect(mapStateToProps, { - sendClearFields, - sendCreatedTransaction, - sendMaxBalance, - sendModalInit, - sendToggleConfirmationView, - sendUpdateAssetAmount, - sendUpdateNativeAmount, - sendUpdateRecipient, - sendUpdateSelected, - })(Component); diff --git a/src/redux/reducers.js b/src/redux/reducers.js index 28a57b1bb20..6dc761475c6 100644 --- a/src/redux/reducers.js +++ b/src/redux/reducers.js @@ -16,7 +16,6 @@ import openStateSettings from './openStateSettings'; import requests from './requests'; import selectedInput from './selectedInput'; import selectedWithFab from './selectedWithFab'; -import send from './send'; import settings from './settings'; import uniqueTokens from './uniqueTokens'; import uniswap from './uniswap'; @@ -39,7 +38,6 @@ export default combineReducers({ requests, selectedInput, selectedWithFab, - send, settings, uniqueTokens, uniswap, diff --git a/src/redux/send.js b/src/redux/send.js deleted file mode 100644 index 97c59ea8972..00000000000 --- a/src/redux/send.js +++ /dev/null @@ -1,267 +0,0 @@ -import { get, isEmpty } from 'lodash'; -import { createSignableTransaction } from '../handlers/web3'; -import { - convertAmountAndPriceToNativeDisplay, - convertAmountFromNativeValue, - formatInputDecimals, -} from '../helpers/utilities'; -import { sendTransaction } from '../model/wallet'; -import { ethereumUtils } from '../utils'; -import { dataAddNewTransaction } from './data'; - -// -- Constants ------------------------------------------------------------- // - -const SEND_MODAL_INIT = 'send/SEND_MODAL_INIT'; - -const SEND_TRANSACTION_REQUEST = 'send/SEND_TRANSACTION_REQUEST'; -const SEND_TRANSACTION_SUCCESS = 'send/SEND_TRANSACTION_SUCCESS'; -const SEND_TRANSACTION_FAILURE = 'send/SEND_TRANSACTION_FAILURE'; - -const SEND_TOGGLE_CONFIRMATION_VIEW = 'send/SEND_TOGGLE_CONFIRMATION_VIEW'; - -const SEND_UPDATE_NATIVE_AMOUNT = 'send/SEND_UPDATE_NATIVE_AMOUNT'; - -const SEND_UPDATE_RECIPIENT = 'send/SEND_UPDATE_RECIPIENT'; -const SEND_UPDATE_ASSET_AMOUNT = 'send/SEND_UPDATE_ASSET_AMOUNT'; -const SEND_UPDATE_SELECTED = 'send/SEND_UPDATE_SELECTED'; -const SEND_UPDATE_NFT_SELECTED = 'send/SEND_UPDATE_NFT_SELECTED'; - -const SEND_CLEAR_FIELDS = 'send/SEND_CLEAR_FIELDS'; - -// -- Actions --------------------------------------------------------------- // - -export const sendModalInit = () => (dispatch, getState) => { - const { accountAddress } = getState().settings; - dispatch({ - payload: accountAddress, - type: SEND_MODAL_INIT, - }); -}; - -export const sendCreatedTransaction = transactionDetails => dispatch => - new Promise((resolve, reject) => { - dispatch({ type: SEND_TRANSACTION_REQUEST }); - const { - address, - recipient, - amount, - asset, - gasPrice, - gasLimit, - } = transactionDetails; - const txDetails = { - amount, - asset, - from: address, - gasLimit, - gasPrice: gasPrice.value.amount, - nonce: null, - to: recipient, - }; - return createSignableTransaction(txDetails) - .then(signableTransactionDetails => { - sendTransaction({ - transaction: signableTransactionDetails, - }) - .then(txHash => { - if (!isEmpty(txHash)) { - txDetails.hash = txHash; - dispatch(dataAddNewTransaction(txDetails)) - .then(() => { - dispatch({ - payload: txHash, - type: SEND_TRANSACTION_SUCCESS, - }); - resolve(txHash); - }) - .catch(error => { - reject(error); - }); - } else { - dispatch({ type: SEND_TRANSACTION_FAILURE }); - reject(new Error('No transaction hash.')); - } - }) - .catch(error => { - dispatch({ type: SEND_TRANSACTION_FAILURE }); - reject(error); - }); - }) - .catch(error => { - dispatch({ type: SEND_TRANSACTION_FAILURE }); - reject(error); - }); - }); - -export const sendToggleConfirmationView = boolean => (dispatch, getState) => { - let confirm = boolean; - if (!confirm) { - confirm = !getState().send.confirm; - } - dispatch({ - payload: confirm, - type: SEND_TOGGLE_CONFIRMATION_VIEW, - }); -}; - -export const sendUpdateRecipient = recipient => dispatch => - dispatch({ - payload: recipient, - type: SEND_UPDATE_RECIPIENT, - }); - -export const sendUpdateAssetAmount = assetAmount => (dispatch, getState) => { - const { nativeCurrency } = getState().settings; - const { selected } = getState().send; - const { selectedGasPrice } = getState().gas; - const _assetAmount = assetAmount.replace(/[^0-9.]/g, ''); - let _nativeAmount = ''; - if (_assetAmount.length) { - const priceUnit = get(selected, 'price.value', 0); - const { amount: nativeAmount } = convertAmountAndPriceToNativeDisplay( - _assetAmount, - priceUnit, - nativeCurrency - ); - _nativeAmount = formatInputDecimals(nativeAmount, _assetAmount); - } - const balanceAmount = ethereumUtils.getBalanceAmount( - selectedGasPrice, - selected - ); - dispatch({ - payload: { - assetAmount: _assetAmount, - isSufficientBalance: Number(_assetAmount) <= Number(balanceAmount), - nativeAmount: _nativeAmount, - }, - type: SEND_UPDATE_ASSET_AMOUNT, - }); -}; - -export const sendUpdateNativeAmount = nativeAmount => (dispatch, getState) => { - const { selected } = getState().send; - const { selectedGasPrice } = getState().gas; - const _nativeAmount = nativeAmount.replace(/[^0-9.]/g, ''); - let _assetAmount = ''; - if (_nativeAmount.length) { - const priceUnit = get(selected, 'price.value', 0); - const assetAmount = convertAmountFromNativeValue( - _nativeAmount, - priceUnit, - selected.decimals - ); - _assetAmount = formatInputDecimals(assetAmount, _nativeAmount); - } - - const balanceAmount = ethereumUtils.getBalanceAmount( - selectedGasPrice, - selected - ); - - dispatch({ - payload: { - assetAmount: _assetAmount, - isSufficientBalance: Number(_assetAmount) <= Number(balanceAmount), - nativeAmount: _nativeAmount, - }, - type: SEND_UPDATE_ASSET_AMOUNT, - }); -}; - -export const sendUpdateSelected = asset => (dispatch, getState) => { - if (get(asset, 'isNft')) { - dispatch({ - payload: { - selected: { - ...asset, - symbol: asset.asset_contract.name, - }, - }, - type: SEND_UPDATE_NFT_SELECTED, - }); - } else { - const state = getState(); - const assetAmount = get(state, 'send.assetAmount'); - dispatch({ - payload: asset, - type: SEND_UPDATE_SELECTED, - }); - dispatch(sendUpdateAssetAmount(assetAmount)); - } -}; - -export const sendMaxBalance = () => (dispatch, getState) => { - const { selected } = getState().send; - const { selectedGasPrice } = getState().gas; - const balanceAmount = ethereumUtils.getBalanceAmount( - selectedGasPrice, - selected - ); - dispatch(sendUpdateAssetAmount(balanceAmount)); -}; - -export const sendClearFields = () => ({ type: SEND_CLEAR_FIELDS }); - -// -- Reducer --------------------------------------------------------------- // -const INITIAL_STATE = { - address: '', - assetAmount: '', - confirm: false, - fetching: false, - isSufficientBalance: false, - nativeAmount: '', - recipient: '', - selected: {}, - txHash: '', -}; - -export default (state = INITIAL_STATE, action) => { - switch (action.type) { - case SEND_MODAL_INIT: - return { - ...state, - address: action.payload, - }; - case SEND_TRANSACTION_REQUEST: - return { ...state, fetching: true }; - case SEND_TRANSACTION_SUCCESS: - return { - ...state, - fetching: false, - txHash: action.payload, - }; - case SEND_TRANSACTION_FAILURE: - return { - ...state, - confirm: false, - fetching: false, - txHash: '', - }; - case SEND_TOGGLE_CONFIRMATION_VIEW: - return { ...state, confirm: action.payload }; - case SEND_UPDATE_RECIPIENT: - return { ...state, recipient: action.payload }; - case SEND_UPDATE_NATIVE_AMOUNT: - case SEND_UPDATE_ASSET_AMOUNT: - return { - ...state, - assetAmount: action.payload.assetAmount, - isSufficientBalance: action.payload.isSufficientBalance, - nativeAmount: action.payload.nativeAmount, - }; - case SEND_UPDATE_SELECTED: - return { ...state, selected: action.payload }; - case SEND_UPDATE_NFT_SELECTED: - return { - ...state, - assetAmount: '1', - isSufficientBalance: true, - selected: action.payload.selected, - }; - case SEND_CLEAR_FIELDS: - return { ...state, ...INITIAL_STATE }; - default: - return state; - } -}; diff --git a/src/screens/ExchangeModal.js b/src/screens/ExchangeModal.js index 7ab49aba792..90daa3eb2b9 100644 --- a/src/screens/ExchangeModal.js +++ b/src/screens/ExchangeModal.js @@ -7,7 +7,6 @@ import { tradeTokensForExactEthWithData, tradeTokensForExactTokensWithData, } from '@uniswap/sdk'; -import BigNumber from 'bignumber.js'; import { get, isNil, toLower } from 'lodash'; import PropTypes from 'prop-types'; import React, { Component, Fragment } from 'react'; @@ -35,7 +34,6 @@ import { estimateSwapGasLimit, executeSwap } from '../handlers/uniswap'; import { convertAmountFromNativeValue, convertAmountToNativeAmount, - convertAmountToNativeDisplay, convertAmountToRawAmount, convertNumberToString, convertRawAmountToDecimalFormat, @@ -113,8 +111,6 @@ class ExchangeModal extends Component { inputAmountDisplay: null, inputAsExactAmount: false, inputCurrency: ethereumUtils.getAsset(this.props.allAssets), - inputExecutionRate: null, - inputNativePrice: null, isAssetApproved: true, isAuthorizing: false, isSufficientBalance: true, @@ -123,8 +119,6 @@ class ExchangeModal extends Component { outputAmount: null, outputAmountDisplay: null, outputCurrency: null, - outputExecutionRate: null, - outputNativePrice: null, showConfirmButton: false, slippage: null, tradeDetails: null, @@ -336,7 +330,6 @@ class ExchangeModal extends Component { chainId, gasUpdateTxFee, inputReserve, - nativeCurrency, outputReserve, selectedGasPrice, } = this.props; @@ -420,38 +413,6 @@ class ExchangeModal extends Component { ); } - let inputExecutionRate = ''; - let outputExecutionRate = ''; - let inputNativePrice = ''; - let outputNativePrice = ''; - - if (inputCurrency) { - const inputPriceValue = get(inputCurrency, 'price.value', 0); - inputExecutionRate = updatePrecisionToDisplay( - get(tradeDetails, 'executionRate.rate', BigNumber(0)), - inputPriceValue - ); - - inputNativePrice = convertAmountToNativeDisplay( - inputPriceValue, - nativeCurrency - ); - } - - if (outputCurrency) { - const outputPriceValue = get(outputCurrency, 'price.value', 0); - outputExecutionRate = updatePrecisionToDisplay( - get(tradeDetails, 'executionRate.rateInverted', BigNumber(0)), - outputPriceValue, - true - ); - - outputNativePrice = convertAmountToNativeDisplay( - outputPriceValue, - nativeCurrency - ); - } - const slippage = convertNumberToString( get(tradeDetails, 'executionRateSlippage', 0) ); @@ -465,11 +426,7 @@ class ExchangeModal extends Component { parseFloat(inputBalance) >= parseFloat(inputAmount); this.setState({ - inputExecutionRate, - inputNativePrice, isSufficientBalance, - outputExecutionRate, - outputNativePrice, slippage, tradeDetails, }); @@ -811,8 +768,6 @@ class ExchangeModal extends Component { inputAmount, inputAmountDisplay, inputCurrency, - // inputExecutionRate, - // inputNativePrice, isAssetApproved, isAuthorizing, isSufficientBalance, @@ -821,8 +776,6 @@ class ExchangeModal extends Component { outputAmount, outputAmountDisplay, outputCurrency, - // outputExecutionRate, - // outputNativePrice, showConfirmButton, slippage, } = this.state; diff --git a/src/screens/SendSheet.js b/src/screens/SendSheet.js index a9337e5078f..4d9afa03cdd 100644 --- a/src/screens/SendSheet.js +++ b/src/screens/SendSheet.js @@ -35,8 +35,10 @@ export default class SendSheet extends Component { allAssets: PropTypes.array, assetAmount: PropTypes.string, contacts: PropTypes.object, + currentInput: PropTypes.string, fetchData: PropTypes.func, gasPrices: PropTypes.object, + isAuthorizing: PropTypes.bool, isSufficientBalance: PropTypes.bool, isSufficientGas: PropTypes.bool, isValidAddress: PropTypes.bool, @@ -54,6 +56,7 @@ export default class SendSheet extends Component { selected: PropTypes.object, selectedGasPrice: PropTypes.object, sendableUniqueTokens: PropTypes.arrayOf(PropTypes.object), + sendMaxBalance: PropTypes.func, sendUpdateRecipient: PropTypes.func, sortedContacts: PropTypes.array, }; @@ -116,6 +119,7 @@ export default class SendSheet extends Component { removeContact, selected, selectedGasPrice, + sendMaxBalance, sendableUniqueTokens, sendUpdateRecipient, sortedContacts, @@ -170,6 +174,7 @@ export default class SendSheet extends Component { onChangeNativeAmount={onChangeNativeAmount} onResetAssetSelection={onResetAssetSelection} selected={selected} + sendMaxBalance={sendMaxBalance} txSpeedRenderer={ isIphoneX() && ( { - if (isString(assetAmount)) { - this.props.sendUpdateAssetAmount(assetAmount); - analytics.track('Changed token input in Send flow'); + sendUpdateAssetAmount = assetAmount => { + const { nativeCurrency, selectedGasPrice } = this.props; + const { selected } = this.state; + const _assetAmount = assetAmount.replace(/[^0-9.]/g, ''); + let _nativeAmount = ''; + if (_assetAmount.length) { + const priceUnit = get(selected, 'price.value', 0); + const { amount: nativeAmount } = convertAmountAndPriceToNativeDisplay( + _assetAmount, + priceUnit, + nativeCurrency + ); + _nativeAmount = formatInputDecimals(nativeAmount, _assetAmount); } + const balanceAmount = ethereumUtils.getBalanceAmount( + selectedGasPrice, + selected + ); + this.setState({ + assetAmount: _assetAmount, + isSufficientBalance: Number(_assetAmount) <= Number(balanceAmount), + nativeAmount: _nativeAmount, + }); }; onChangeNativeAmount = nativeAmount => { - if (isString(nativeAmount)) { - this.props.sendUpdateNativeAmount(nativeAmount); - analytics.track('Changed native currency input in Send flow'); + if (!isString(nativeAmount)) return; + const { selected } = this.state; + const { selectedGasPrice } = this.props; + const _nativeAmount = nativeAmount.replace(/[^0-9.]/g, ''); + let _assetAmount = ''; + if (_nativeAmount.length) { + const priceUnit = get(selected, 'price.value', 0); + const assetAmount = convertAmountFromNativeValue( + _nativeAmount, + priceUnit, + selected.decimals + ); + _assetAmount = formatInputDecimals(assetAmount, _nativeAmount); + } + + const balanceAmount = ethereumUtils.getBalanceAmount( + selectedGasPrice, + selected + ); + + this.setState({ + assetAmount: _assetAmount, + isSufficientBalance: Number(_assetAmount) <= Number(balanceAmount), + nativeAmount: _nativeAmount, + }); + analytics.track('Changed native currency input in Send flow'); + }; + + sendMaxBalance = () => { + const balanceAmount = ethereumUtils.getBalanceAmount( + this.props.selectedGasPrice, + this.state.selected + ); + this.sendUpdateAssetAmount(balanceAmount); + }; + + onChangeAssetAmount = assetAmount => { + if (isString(assetAmount)) { + this.sendUpdateAssetAmount(assetAmount); + analytics.track('Changed token input in Send flow'); } }; @@ -169,19 +206,12 @@ class SendSheetWithData extends Component { onResetAssetSelection = () => { analytics.track('Reset asset selection in Send flow'); - this.props.sendUpdateSelected({}); + this.sendUpdateSelected({}); }; - onSelectAsset = asset => this.props.sendUpdateSelected(asset); - submitTransaction = async () => { - const { - assetAmount, - navigation, - recipient, - selected, - sendClearFields, - } = this.props; + const { navigation } = this.props; + const { assetAmount, recipient, selected } = this.state; if (Number(assetAmount) <= 0) return false; @@ -193,39 +223,63 @@ class SendSheetWithData extends Component { assetType: selected.isNft ? 'unique_token' : 'token', isRecepientENS: toLower(recipient.slice(-4)) === '.eth', }); - sendClearFields(); navigation.navigate('ProfileScreen'); } catch { this.setState({ isAuthorizing: false }); } }; - onChangeInput = event => { - this.setState({ currentInput: event }); - this.props.sendUpdateRecipient(event); + onChangeInput = event => + this.setState({ currentInput: event, recipient: event }); + + sendUpdateRecipient = recipient => this.setState({ recipient }); + + sendUpdateSelected = selected => { + if (get(selected, 'isNft')) { + this.setState({ + assetAmount: '1', + isSufficientBalance: true, + selected: { + ...selected, + symbol: get(selected, 'asset_contract.name'), + }, + }); + } else { + const assetAmount = this.state.assetAmount; + this.setState({ selected }); + this.sendUpdateAssetAmount(assetAmount); + } }; onSubmit = async () => { - if (!this.props.selectedGasPrice.txFee) { + const { + accountAddress, + assets, + dataAddNewTransaction, + gasLimit, + selectedGasPrice, + } = this.props; + const { assetAmount, confirm, recipient, selected } = this.state; + if (!selectedGasPrice.txFee) { return; } // Balance checks - if (!this.props.confirm) { - const isAddressValid = await checkIsValidAddress(this.props.recipient); + if (!confirm) { + const isAddressValid = await checkIsValidAddress(recipient); if (!isAddressValid) { console.log(lang.t('notification.error.invalid_address')); return; } - if (this.props.selected.address === 'eth') { + if (selected.address === 'eth') { const { requestedAmount, balance, amountWithFees, } = ethereumUtils.transactionData( - this.props.assets, - this.props.assetAmount, - this.props.selectedGasPrice + assets, + assetAmount, + selectedGasPrice ); if (greaterThan(requestedAmount, balance)) { @@ -234,18 +288,18 @@ class SendSheetWithData extends Component { if (greaterThan(amountWithFees, balance)) { return; } - } else if (!this.props.selected.isNft) { + } else if (!selected.isNft) { const { requestedAmount, balance, txFee, } = ethereumUtils.transactionData( - this.props.assets, - this.props.assetAmount, - this.props.selectedGasPrice + assets, + assetAmount, + selectedGasPrice ); - const tokenBalance = get(this.props, 'selected.balance.amount'); + const tokenBalance = get(selected, 'balance.amount'); if (greaterThan(requestedAmount, tokenBalance)) { return; @@ -255,33 +309,65 @@ class SendSheetWithData extends Component { } } - this.props.sendToggleConfirmationView(true); - - return this.props.sendCreatedTransaction({ - address: this.props.address, - amount: this.props.assetAmount, - asset: this.props.selected, - gasLimit: this.props.gasLimit, - gasPrice: this.props.selectedGasPrice, - recipient: this.props.recipient, - }); + this.setState({ confirm: true }); + + const txDetails = { + amount: assetAmount, + asset: selected, + from: accountAddress, + gasLimit, + gasPrice: get(selectedGasPrice, 'value.amount'), + nonce: null, + to: recipient, + }; + try { + const signableTransaction = await createSignableTransaction(txDetails); + const txHash = sendTransaction({ + transaction: signableTransaction, + }); + if (!isEmpty(txHash)) { + txDetails.hash = txHash; + await dataAddNewTransaction(txDetails); + } + // eslint-disable-next-line no-empty + } catch (error) {} } }; render() { + const { + assetAmount, + contacts, + currentInput, + isAuthorizing, + isSufficientBalance, + isSufficientGas, + isValidAddress, + nativeAmount, + recipient, + selected, + } = this.state; return ( ); @@ -303,9 +389,9 @@ export default compose( withAccountSettings, withContacts, withDataInit, - withSend, withGas, withUniqueTokens, + withTransactionConfirmationScreen, withTransitionProps, withProps(({ transitionProps: { isTransitioning } }) => ({ isTransitioning,