import React, { Component } from 'react' import PropTypes from 'prop-types' import { getAmountErrorObject, getGasFeeErrorObject, getToAddressForGasUpdate, doesAmountErrorRequireUpdate, } from './send.utils' import { debounce } from 'lodash' import { getToWarningObject, getToErrorObject } from './send-content/add-recipient/add-recipient' import SendHeader from './send-header' import AddRecipient from './send-content/add-recipient' import SendContent from './send-content' import SendFooter from './send-footer' import EnsInput from './send-content/add-recipient/ens-input' export default class SendTransactionScreen extends Component { static propTypes = { addressBook: PropTypes.arrayOf(PropTypes.object), amount: PropTypes.string, amountConversionRate: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, ]), blockGasLimit: PropTypes.string, conversionRate: PropTypes.number, editingTransactionId: PropTypes.string, fetchBasicGasEstimates: PropTypes.func.isRequired, from: PropTypes.object, gasLimit: PropTypes.string, gasPrice: PropTypes.string, gasTotal: PropTypes.string, hasHexData: PropTypes.bool, history: PropTypes.object, network: PropTypes.string, primaryCurrency: PropTypes.string, recentBlocks: PropTypes.array, resetSendState: PropTypes.func.isRequired, selectedAddress: PropTypes.string, selectedToken: PropTypes.object, showHexData: PropTypes.bool, to: PropTypes.string, toNickname: PropTypes.string, tokens: PropTypes.array, tokenBalance: PropTypes.string, tokenContract: PropTypes.object, updateAndSetGasLimit: PropTypes.func.isRequired, updateSendEnsResolution: PropTypes.func.isRequired, updateSendEnsResolutionError: PropTypes.func.isRequired, updateSendErrors: PropTypes.func.isRequired, updateSendTo: PropTypes.func.isRequired, updateSendTokenBalance: PropTypes.func.isRequired, updateToNicknameIfNecessary: PropTypes.func.isRequired, scanQrCode: PropTypes.func.isRequired, qrCodeDetected: PropTypes.func.isRequired, qrCodeData: PropTypes.object, } static contextTypes = { t: PropTypes.func, metricsEvent: PropTypes.func, } state = { query: '', toError: null, toWarning: null, } constructor (props) { super(props) this.dValidate = debounce(this.validate, 1000) } componentDidUpdate (prevProps) { const { amount, amountConversionRate, conversionRate, from: { address, balance }, gasTotal, network, primaryCurrency, selectedToken, tokenBalance, updateSendErrors, updateSendTo, updateSendTokenBalance, tokenContract, to, toNickname, addressBook, updateToNicknameIfNecessary, qrCodeData, qrCodeDetected, } = this.props let updateGas = false const { from: { balance: prevBalance }, gasTotal: prevGasTotal, tokenBalance: prevTokenBalance, network: prevNetwork, selectedToken: prevSelectedToken, to: prevTo, } = prevProps const uninitialized = [prevBalance, prevGasTotal].every((n) => n === null) const amountErrorRequiresUpdate = doesAmountErrorRequireUpdate({ balance, gasTotal, prevBalance, prevGasTotal, prevTokenBalance, selectedToken, tokenBalance, }) if (amountErrorRequiresUpdate) { const amountErrorObject = getAmountErrorObject({ amount, amountConversionRate, balance, conversionRate, gasTotal, primaryCurrency, selectedToken, tokenBalance, }) const gasFeeErrorObject = selectedToken ? getGasFeeErrorObject({ amountConversionRate, balance, conversionRate, gasTotal, primaryCurrency, selectedToken, }) : { gasFee: null } updateSendErrors(Object.assign(amountErrorObject, gasFeeErrorObject)) } if (!uninitialized) { if (network !== prevNetwork && network !== 'loading') { updateSendTokenBalance({ selectedToken, tokenContract, address, }) updateToNicknameIfNecessary(to, toNickname, addressBook) updateGas = true } } const prevTokenAddress = prevSelectedToken && prevSelectedToken.address const selectedTokenAddress = selectedToken && selectedToken.address if (selectedTokenAddress && prevTokenAddress !== selectedTokenAddress) { this.updateSendToken() updateGas = true } let scannedAddress if (qrCodeData) { if (qrCodeData.type === 'address') { scannedAddress = qrCodeData.values.address.toLowerCase() const currentAddress = prevTo && prevTo.toLowerCase() if (currentAddress !== scannedAddress) { updateSendTo(scannedAddress) updateGas = true // Clean up QR code data after handling qrCodeDetected(null) } } } if (updateGas) { if (scannedAddress) { this.updateGas({ to: scannedAddress }) } else { this.updateGas() } } } componentDidMount () { this.props.fetchBasicGasEstimates() .then(() => { this.updateGas() }) } UNSAFE_componentWillMount () { this.updateSendToken() // Show QR Scanner modal if ?scan=true if (window.location.search === '?scan=true') { this.props.scanQrCode() // Clear the queryString param after showing the modal const cleanUrl = location.href.split('?')[0] history.pushState({}, null, `${cleanUrl}`) window.location.hash = '#send' } } componentWillUnmount () { this.props.resetSendState() } onRecipientInputChange = (query) => { if (query) { this.dValidate(query) } else { this.validate(query) } this.setState({ query, }) } validate (query) { const { hasHexData, tokens, selectedToken, network, } = this.props if (!query) { return this.setState({ toError: '', toWarning: '' }) } const toErrorObject = getToErrorObject(query, null, hasHexData, tokens, selectedToken, network) const toWarningObject = getToWarningObject(query, null, tokens, selectedToken) this.setState({ toError: toErrorObject.to, toWarning: toWarningObject.to, }) } updateSendToken () { const { from: { address }, selectedToken, tokenContract, updateSendTokenBalance, } = this.props updateSendTokenBalance({ selectedToken, tokenContract, address, }) } updateGas ({ to: updatedToAddress, amount: value, data } = {}) { const { amount, blockGasLimit, editingTransactionId, gasLimit, gasPrice, recentBlocks, selectedAddress, selectedToken = {}, to: currentToAddress, updateAndSetGasLimit, } = this.props updateAndSetGasLimit({ blockGasLimit, editingTransactionId, gasLimit, gasPrice, recentBlocks, selectedAddress, selectedToken, to: getToAddressForGasUpdate(updatedToAddress, currentToAddress), value: value || amount, data, }) } render () { const { history, to } = this.props let content if (to) { content = this.renderSendContent() } else { content = this.renderAddRecipient() } return (
{ this.renderInput() } { content }
) } renderInput () { return ( { this.context.metricsEvent({ eventOpts: { category: 'Transactions', action: 'Edit Screen', name: 'Used QR scanner', }, }) this.props.scanQrCode() }} onChange={this.onRecipientInputChange} onValidAddressTyped={(address) => this.props.updateSendTo(address, '')} onPaste={(text) => { this.props.updateSendTo(text) && this.updateGas() }} onReset={() => this.props.updateSendTo('', '')} updateEnsResolution={this.props.updateSendEnsResolution} updateEnsResolutionError={this.props.updateSendEnsResolutionError} /> ) } renderAddRecipient () { const { toError, toWarning } = this.state return ( this.updateGas({ to, amount, data })} query={this.state.query} toError={toError} toWarning={toWarning} /> ) } renderSendContent () { const { history, showHexData } = this.props return [ this.updateGas({ to, amount, data })} showHexData={showHexData} />, , ] } }