11 } from 'features/shared/components'
12 import {chainClient} from 'utility/environment'
13 import {reduxForm} from 'redux-form'
14 import React from 'react'
15 import styles from './New.scss'
16 import disableAutocomplete from 'utility/disableAutocomplete'
17 import { btmID } from 'utility/environment'
18 import actions from 'actions'
19 import ConfirmModal from './ConfirmModal/ConfirmModal'
20 import { balance , getAssetDecimal, normalTxActionBuilder} from '../../transactions'
21 import {withNamespaces} from 'react-i18next'
23 class NormalTxForm extends React.Component {
26 this.connection = chainClient().connection
32 this.submitWithValidation = this.submitWithValidation.bind(this)
33 this.disableSubmit = this.disableSubmit.bind(this)
34 this.addReceiverItem = this.addReceiverItem.bind(this)
38 return !(this.state.estimateGas)
41 submitWithValidation(data) {
42 return new Promise((resolve, reject) => {
43 this.props.submitForm(Object.assign({}, data, {state: this.state, form: 'normalTx'}))
45 this.props.closeModal()
46 this.props.destroyForm()
49 if(err.message !== 'PasswordWrong'){
50 this.props.closeModal()
57 confirmedTransaction(e, assetDecimal){
61 cancel={this.props.closeModal}
62 onSubmit={this.submitWithValidation}
63 gas={this.state.estimateGas}
64 btmAmountUnit={this.props.btmAmountUnit}
65 assetDecimal={assetDecimal}
66 asset={this.props.asset}
72 const counter = this.state.counter
73 this.props.fields.receivers.addField({
82 removeReceiverItem(index) {
83 const receiver = this.props.fields.receivers
84 const promise = new Promise(function(resolve, reject) {
86 receiver.removeField(index)
93 promise.then(() => this.estimateNormalTransactionGas())
96 estimateNormalTransactionGas() {
97 const transaction = this.props.fields
98 const accountAlias = transaction.accountAlias.value
99 const accountId = transaction.accountId.value
100 const assetAlias = transaction.assetAlias.value
101 const assetId = transaction.assetId.value
102 const receivers = transaction.receivers
103 const addresses = receivers.map(x => x.address.value)
104 const amounts = receivers.map(x => Number(x.amount.value))
106 const {t, i18n} = this.props
108 const noAccount = !accountAlias && !accountId
109 const noAsset = !assetAlias && !assetId
111 if ( addresses.includes('') || amounts.includes(0)|| noAccount || noAsset) {
112 this.setState({estimateGas: null})
116 const actions = normalTxActionBuilder(transaction, Math.pow(10, 7), 'amount.value' )
118 const body = {actions, ttl: 1}
119 this.connection.request('/build-transaction', body).then(resp => {
120 if (resp.status === 'fail') {
121 this.setState({estimateGas: null})
122 const errorMsg = resp.code && i18n.exists(`btmError.${resp.code}`) && t(`btmError.${resp.code}`) || resp.msg
123 this.props.showError(new Error(errorMsg))
127 return this.connection.request('/estimate-transaction-gas', {
128 transactionTemplate: resp.data
130 if (resp.status === 'fail') {
131 this.setState({estimateGas: null})
132 const errorMsg = resp.code && i18n.exists(`btmError.${resp.code}`) && t(`btmError.${resp.code}`) || resp.msg
133 this.props.showError(new Error(errorMsg))
136 this.setState({estimateGas: Math.ceil(resp.data.totalNeu/100000)*100000})
143 fields: {accountId, accountAlias, assetId, assetAlias, receivers, gasLevel},
147 const t = this.props.t;
148 [accountAlias, accountId, assetAlias, assetId].forEach(key => {
149 key.onBlur = this.estimateNormalTransactionGas.bind(this)
151 (receivers.map(receiver => receiver.amount)).forEach(amount =>{
152 amount.onBlur = this.estimateNormalTransactionGas.bind(this)
155 let submitLabel = t('transaction.new.submit')
157 const assetDecimal = getAssetDecimal(this.props.fields, this.props.asset) || 0
159 const showAvailableBalance = (accountAlias.value || accountId.value) &&
160 (assetAlias.value || assetId.value)
162 const availableBalance = balance(this.props.fields, assetDecimal, this.props.balances, this.props.btmAmountUnit)
164 const showBtmAmountUnit = (assetAlias.value === 'BTM' || assetId.value === btmID)
168 className={styles.container}
169 onSubmit={e => this.confirmedTransaction(e, assetDecimal)}
170 {...disableAutocomplete}
172 <div className={styles.borderBottom}>
173 <label className={styles.title}>{t('transaction.normal.from')}</label>
174 <div className={`${styles.mainBox} ${this.props.tutorialVisible? styles.tutorialItem: styles.item}`}>
176 key='account-selector-field'
177 keyIndex='normaltx-account'
178 title={t('form.account')}
179 aliasField={Autocomplete.AccountAlias}
187 key='asset-selector-field'
188 keyIndex='normaltx-asset'
189 title={ t('form.asset')}
190 aliasField={Autocomplete.AssetAlias}
196 {showAvailableBalance && availableBalance &&
197 <small className={styles.balanceHint}>{t('transaction.normal.availableBalance')} {availableBalance}</small>}
201 <label className={styles.title}>{t('transaction.normal.to')}</label>
203 <div className={styles.mainBox}>
204 {receivers.map((receiver, index) =>
206 className={this.props.tutorialVisible? styles.tutorialItem: styles.item}
207 key={receiver.id.value}>
208 <TextField title={t('form.address')} fieldProps={{
211 receiver.address.onBlur(e)
212 this.estimateNormalTransactionGas()
216 {!showBtmAmountUnit &&
217 <AmountInputMask title={t('form.amount')} fieldProps={receiver.amount} decimal={assetDecimal}
219 {showBtmAmountUnit &&
220 <AmountUnitField title={t('form.amount')} fieldProps={receiver.amount}/>
224 <a href='#' className={styles.receiverBtn} onClick={this.addReceiverItem}>+</a> :
225 <a href='#' className={`${styles.receiverBtn} text-danger`} onClick={()=> this.removeReceiverItem(index)}>-</a>
232 <label className={styles.title}>{t('transaction.normal.selectFee')}</label>
233 <div className={styles.txFeeBox}>
235 gas={this.state.estimateGas}
236 fieldProps={gasLevel}
237 btmAmountUnit={this.props.btmAmountUnit}
239 <span className={styles.feeDescription}> {t('transaction.normal.feeDescription')}</span>
243 <FormSection className={styles.submitSection}>
244 {error && error.message !== 'PasswordWrong' &&
246 title={t('form.errorTitle')}
249 <div className={styles.submit}>
250 <button type='submit' className='btn btn-primary'
251 disabled={submitting || this.disableSubmit()}>
261 const validate = (values, props) => {
262 const errors = {gas: {}}
266 if (values.amount && !/^\d+(\.\d+)?$/i.test(values.amount)) {
267 errors.amount = ( t('errorMessage.amountError') )
272 const asyncValidate = (values, dispatch, props) => {
276 values.receivers.forEach((receiver, idx) => {
277 const address = values.receivers[idx].address
278 if ( !address || address.length === 0)
279 promises.push(Promise.resolve())
282 chainClient().accounts.validateAddresses(address)
285 if (!resp.data.valid) {
286 errors[idx] = {address: props.t('errorMessage.addressError')}
294 return Promise.all(promises).then(() => {
295 if (errors.length > 0) throw {
302 const mapDispatchToProps = (dispatch) => ({
303 showError: err => dispatch({type: 'ERROR', payload: err}),
304 closeModal: () => dispatch(actions.app.hideModal),
305 showModal: (body) => dispatch(actions.app.showModal(
307 actions.app.hideModal,
314 ...BaseNew.mapDispatchToProps('transaction')(dispatch)
317 export default withNamespaces('translations') (BaseNew.connect(
318 BaseNew.mapStateToProps('transaction'),
321 form: 'NormalTransactionForm',
328 'receivers[].amount',
329 'receivers[].address',
333 asyncBlurFields: ['receivers[].address'],