import { BaseNew, TextField, Autocomplete, ObjectSelectorField, AmountField, GasField } from 'features/shared/components' import {chainClient} from 'utility/environment' import {reduxForm} from 'redux-form' import React from 'react' import styles from './New.scss' import TxContainer from './NewTransactionsContainer/TxContainer' import { btmID } from 'utility/environment' import actions from 'actions' import ConfirmModal from './ConfirmModal/ConfirmModal' import { balance , getAssetDecimal, normalTxActionBuilder, normalChainTxActionBuilder} from '../../transactions' import {withNamespaces} from 'react-i18next' class NormalTxForm extends React.Component { constructor(props) { super(props) this.connection = chainClient().connection this.state = { estimateGas:null, chainGas: 0, counter: 1 } this.submitWithValidation = this.submitWithValidation.bind(this) this.disableSubmit = this.disableSubmit.bind(this) this.addReceiverItem = this.addReceiverItem.bind(this) } disableSubmit() { return !(this.state.estimateGas) } submitWithValidation(data) { return new Promise((resolve, reject) => { this.props.submitForm(Object.assign({}, data, {state: this.state, form: 'normalTx'})) .then(() => { this.props.closeModal() this.props.destroyForm() }) .catch((err) => { if(err.message !== 'PasswordWrong'){ this.props.closeModal() } reject({_error: err}) }) }) } confirmedTransaction(e, assetDecimal){ e.preventDefault() this.props.showModal( ) } addReceiverItem() { const counter = this.state.counter this.props.fields.receivers.addField({ id: counter }) this.setState({ counter: counter+1, estimateGas: null, chainGas:0 }) } removeReceiverItem(index) { const receiver = this.props.fields.receivers const promise = new Promise(function(resolve, reject) { try { receiver.removeField(index) } catch (err) { reject(err) } resolve() }) promise.then(() => this.estimateNormalTransactionGas()) } estimateNormalTransactionGas(type) { const transaction = this.props.values const accountAlias = transaction.accountAlias const accountId = transaction.accountId const assetAlias = transaction.assetAlias const assetId = transaction.assetId const receivers = transaction.receivers const addresses = receivers.map(x => x.address) const amounts = receivers.map(x => Number(x.amount)) const isChainTx = type==='check'? !transaction.isChainTx: transaction.isChainTx const {t, i18n} = this.props const noAccount = !accountAlias && !accountId const noAsset = !assetAlias && !assetId if ( addresses.includes('') || amounts.includes(NaN)|| amounts.includes(0)|| noAccount || noAsset) { this.setState({estimateGas: null, chainGas:0}) return } const isBTM = (assetAlias==='BTM') || (assetId === btmID) if(isBTM && isChainTx){ const actions = normalChainTxActionBuilder(transaction, 'amount' ) const body = {actions, ttl: 1} this.connection.request('/build-chain-transactions', body).then(resp => { return this.connection.request('/estimate-chain-transaction-gas', { transactionTemplates: resp.data }).then(resp => { this.setState({ estimateGas: Math.ceil(resp.data.totalNeu/100000)*100000, chainGas: Math.ceil(resp.data.chainTxNeu/100000)*100000 }) }).catch(err =>{ throw err }) }).catch(err=>{ this.setState({estimateGas: null, chainGas:0, address: null}) const errorMsg = err.code && i18n.exists(`btmError.${err.code}`) && t(`btmError.${err.code}`) || err.msg this.props.showError(new Error(errorMsg)) }) }else{ const actions = normalTxActionBuilder(transaction, Math.pow(10, 7), 'amount' ) const body = {actions, ttl: 1} this.connection.request('/build-transaction', body).then(resp => { return this.connection.request('/estimate-transaction-gas', { transactionTemplate: resp.data }).then(resp => { this.setState({ estimateGas: Math.ceil(resp.data.totalNeu/100000)*100000, chainGas:0 }) }).catch(err =>{ throw err }) }).catch(err=>{ this.setState({estimateGas: null,chainGas:0, address: null}) const errorMsg = err.code && i18n.exists(`btmError.${err.code}`) && t(`btmError.${err.code}`) || err.msg this.props.showError(new Error(errorMsg)) }) } } render() { const { fields: {accountId, accountAlias, assetId, assetAlias, receivers, isChainTx ,gasLevel}, error, submitting } = this.props const t = this.props.t; [accountAlias, accountId, assetAlias, assetId].forEach(key => { key.onBlur = this.estimateNormalTransactionGas.bind(this) }); (receivers.map(receiver => receiver.amount)).forEach(amount =>{ amount.onBlur = this.estimateNormalTransactionGas.bind(this) }) let submitLabel = t('transaction.new.submit') const assetDecimal = getAssetDecimal(this.props.fields, this.props.asset) || 0 const showAvailableBalance = (accountAlias.value || accountId.value) && (assetAlias.value || assetId.value) const availableBalance = balance(this.props.values, assetDecimal, this.props.balances, this.props.btmAmountUnit) const showBtmAmountUnit = (assetAlias.value === 'BTM' || assetId.value === btmID) return this.confirmedTransaction(e, assetDecimal)} submitting={submitting} submitLabel={submitLabel} disabled={this.disableSubmit()} className={styles.container} >
{showAvailableBalance && availableBalance && {t('transaction.normal.availableBalance')} {availableBalance}}
{receivers.map((receiver, index) =>
{ receiver.address.onBlur(e) this.estimateNormalTransactionGas() }, }}/>
)}
{showBtmAmountUnit && [,
{t('transaction.normal.chainTx')}
{t('transaction.normal.chainTxNote')}
]}
{t('transaction.normal.feeDescription')}
} } const validate = (values, props) => { const errors = {gas: {}} const t = props.t // Numerical if (values.amount && !/^\d+(\.\d+)?$/i.test(values.amount)) { errors.amount = ( t('errorMessage.amountError') ) } return errors } const asyncValidate = (values, dispatch, props) => { const errors = [] const promises = [] values.receivers.forEach((receiver, idx) => { const address = values.receivers[idx].address if ( !address || address.length === 0) promises.push(Promise.resolve()) else{ promises.push( chainClient().accounts.validateAddresses(address) .then( (resp) => { if (!resp.data.valid) { errors[idx] = {address: props.t('errorMessage.addressError')} } return {} } )) } }) return Promise.all(promises).then(() => { if (errors.length > 0) throw { receivers: errors } return {} }) } const mapDispatchToProps = (dispatch) => ({ showError: err => dispatch({type: 'ERROR', payload: err}), closeModal: () => dispatch(actions.app.hideModal), showModal: (body) => dispatch(actions.app.showModal( body, actions.app.hideModal, null, { dialog: true, noCloseBtn: true } )), ...BaseNew.mapDispatchToProps('transaction')(dispatch) }) export default withNamespaces('translations') (BaseNew.connect( BaseNew.mapStateToProps('transaction'), mapDispatchToProps, reduxForm({ form: 'NormalTransactionForm', fields: [ 'accountAlias', 'accountId', 'assetAlias', 'assetId', 'receivers[].id', 'receivers[].amount', 'receivers[].address', 'gasLevel', 'isChainTx' ], asyncValidate, asyncBlurFields: ['receivers[].address'], validate, touchOnChange: true, initialValues: { gasLevel: '1', isChainTx:false, receivers:[{ id: 0, amount:'', address:'' }] }, })(NormalTxForm) ))