8 } from 'features/shared/components'
9 import {chainClient} from 'utility/environment'
10 import {reduxForm} from 'redux-form'
11 import React from 'react'
12 import styles from './New.scss'
13 import TxContainer from './NewTransactionsContainer/TxContainer'
14 import { btmID } from 'utility/environment'
15 import actions from 'actions'
16 import ConfirmModal from './ConfirmModal/ConfirmModal'
17 import { balance , getAssetDecimal, normalTxActionBuilder} from '../../transactions'
18 import {withNamespaces} from 'react-i18next'
20 class NormalTxForm extends React.Component {
23 this.connection = chainClient().connection
29 this.submitWithValidation = this.submitWithValidation.bind(this)
30 this.disableSubmit = this.disableSubmit.bind(this)
31 this.addReceiverItem = this.addReceiverItem.bind(this)
35 return !(this.state.estimateGas)
38 submitWithValidation(data) {
39 return new Promise((resolve, reject) => {
40 this.props.submitForm(Object.assign({}, data, {state: this.state, form: 'normalTx'}))
42 this.props.closeModal()
43 this.props.destroyForm()
46 if(err.message !== 'PasswordWrong'){
47 this.props.closeModal()
54 confirmedTransaction(e, assetDecimal){
58 cancel={this.props.closeModal}
59 onSubmit={this.submitWithValidation}
60 gas={this.state.estimateGas}
61 btmAmountUnit={this.props.btmAmountUnit}
62 assetDecimal={assetDecimal}
63 asset={this.props.asset}
69 const counter = this.state.counter
70 this.props.fields.receivers.addField({
79 removeReceiverItem(index) {
80 const receiver = this.props.fields.receivers
81 const promise = new Promise(function(resolve, reject) {
83 receiver.removeField(index)
90 promise.then(() => this.estimateNormalTransactionGas())
93 estimateNormalTransactionGas() {
94 const transaction = this.props.values
95 const accountAlias = transaction.accountAlias
96 const accountId = transaction.accountId
97 const assetAlias = transaction.assetAlias
98 const assetId = transaction.assetId
99 const receivers = transaction.receivers
100 const addresses = receivers.map(x => x.address)
101 const amounts = receivers.map(x => Number(x.amount))
103 const {t, i18n} = this.props
105 const noAccount = !accountAlias && !accountId
106 const noAsset = !assetAlias && !assetId
108 if ( addresses.includes('') || amounts.includes(0)|| noAccount || noAsset) {
109 this.setState({estimateGas: null})
113 const actions = normalTxActionBuilder(transaction, Math.pow(10, 7), 'amount' )
115 const body = {actions, ttl: 1}
116 this.connection.request('/build-transaction', body).then(resp => {
117 return this.connection.request('/estimate-transaction-gas', {
118 transactionTemplate: resp.data
120 this.setState({estimateGas: Math.ceil(resp.data.totalNeu/100000)*100000})
125 this.setState({estimateGas: null, address: null})
126 const errorMsg = err.code && i18n.exists(`btmError.${err.code}`) && t(`btmError.${err.code}`) || err.msg
127 this.props.showError(new Error(errorMsg))
133 fields: {accountId, accountAlias, assetId, assetAlias, receivers, gasLevel},
137 const t = this.props.t;
138 [accountAlias, accountId, assetAlias, assetId].forEach(key => {
139 key.onBlur = this.estimateNormalTransactionGas.bind(this)
141 (receivers.map(receiver => receiver.amount)).forEach(amount =>{
142 amount.onBlur = this.estimateNormalTransactionGas.bind(this)
145 let submitLabel = t('transaction.new.submit')
147 const assetDecimal = getAssetDecimal(this.props.fields, this.props.asset) || 0
149 const showAvailableBalance = (accountAlias.value || accountId.value) &&
150 (assetAlias.value || assetId.value)
152 const availableBalance = balance(this.props.values, assetDecimal, this.props.balances, this.props.btmAmountUnit)
154 const showBtmAmountUnit = (assetAlias.value === 'BTM' || assetId.value === btmID)
159 onSubmit={e => this.confirmedTransaction(e, assetDecimal)}
160 submitting={submitting}
161 submitLabel= {submitLabel}
162 disabled={this.disableSubmit()}
163 className={styles.container}
165 <div className={styles.borderBottom}>
166 <label className={styles.title}>{t('transaction.normal.from')}</label>
167 <div className={`${styles.mainBox} `}>
169 key='account-selector-field'
170 keyIndex='normaltx-account'
171 title={t('form.account')}
172 aliasField={Autocomplete.AccountAlias}
180 key='asset-selector-field'
181 keyIndex='normaltx-asset'
182 title={ t('form.asset')}
183 aliasField={Autocomplete.AssetAlias}
189 {showAvailableBalance && availableBalance &&
190 <small className={styles.balanceHint}>{t('transaction.normal.availableBalance')} {availableBalance}</small>}
194 <label className={styles.title}>{t('transaction.normal.to')}</label>
196 <div className={styles.mainBox}>
197 {receivers.map((receiver, index) =>
199 className={this.props.tutorialVisible? styles.tutorialItem: styles.subjectField}
200 key={receiver.id.value}>
201 <TextField title={t('form.address')} fieldProps={{
204 receiver.address.onBlur(e)
205 this.estimateNormalTransactionGas()
210 isBTM={showBtmAmountUnit}
211 title={t('form.amount')}
212 fieldProps={receiver.amount}
213 decimal={assetDecimal}
217 className={`btn btn-danger btn-xs ${styles.deleteButton}`}
220 onClick={() => this.removeReceiverItem(index)}
222 {t('commonWords.remove')}
228 className='btn btn-default'
229 onClick={this.addReceiverItem}
231 {t('commonWords.addField')}
235 <label className={styles.title}>{t('transaction.normal.selectFee')}</label>
236 <div className={styles.txFeeBox}>
238 gas={this.state.estimateGas}
239 fieldProps={gasLevel}
240 btmAmountUnit={this.props.btmAmountUnit}
242 <span className={styles.feeDescription}> {t('transaction.normal.feeDescription')}</span>
250 const validate = (values, props) => {
251 const errors = {gas: {}}
255 if (values.amount && !/^\d+(\.\d+)?$/i.test(values.amount)) {
256 errors.amount = ( t('errorMessage.amountError') )
261 const asyncValidate = (values, dispatch, props) => {
265 values.receivers.forEach((receiver, idx) => {
266 const address = values.receivers[idx].address
267 if ( !address || address.length === 0)
268 promises.push(Promise.resolve())
271 chainClient().accounts.validateAddresses(address)
274 if (!resp.data.valid) {
275 errors[idx] = {address: props.t('errorMessage.addressError')}
283 return Promise.all(promises).then(() => {
284 if (errors.length > 0) throw {
291 const mapDispatchToProps = (dispatch) => ({
292 showError: err => dispatch({type: 'ERROR', payload: err}),
293 closeModal: () => dispatch(actions.app.hideModal),
294 showModal: (body) => dispatch(actions.app.showModal(
296 actions.app.hideModal,
303 ...BaseNew.mapDispatchToProps('transaction')(dispatch)
306 export default withNamespaces('translations') (BaseNew.connect(
307 BaseNew.mapStateToProps('transaction'),
310 form: 'NormalTransactionForm',
317 'receivers[].amount',
318 'receivers[].address',
322 asyncBlurFields: ['receivers[].address'],