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'
22 class NormalTxForm extends React.Component {
25 this.connection = chainClient().connection
31 this.submitWithValidation = this.submitWithValidation.bind(this)
32 this.disableSubmit = this.disableSubmit.bind(this)
33 this.addReceiverItem = this.addReceiverItem.bind(this)
36 disableSubmit(props) {
37 const hasValue = target => {
38 return !!(target && target.value)
41 return !( (this.state.estimateGas) &&
42 (hasValue(props.accountId) || hasValue(props.accountAlias)) &&
43 (hasValue(props.assetId) || hasValue(props.assetAlias)) &&
44 hasValue(props.address) && (hasValue(props.amount))
48 submitWithValidation(data) {
49 return new Promise((resolve, reject) => {
50 this.props.submitForm(Object.assign({}, data, {state: this.state, form: 'normalTx'}))
52 this.props.closeModal()
53 this.props.destroyForm()
56 if(err.message !== 'PasswordWrong'){
57 this.props.closeModal()
64 confirmedTransaction(e, assetDecimal){
68 cancel={this.props.closeModal}
69 onSubmit={this.submitWithValidation}
70 gas={this.state.estimateGas}
71 btmAmountUnit={this.props.btmAmountUnit}
72 assetDecimal={assetDecimal}
73 asset={this.props.asset}
74 lang = {this.props.lang}
80 const counter = this.state.counter
81 this.props.fields.receivers.addField({
90 removeReceiverItem(index) {
91 const receiver = this.props.fields.receivers
92 const promise = new Promise(function(resolve, reject) {
94 receiver.removeField(index)
102 () => this.estimateNormalTransactionGas()
106 estimateNormalTransactionGas() {
107 const transaction = this.props.fields
108 const accountAlias = transaction.accountAlias.value
109 const accountId = transaction.accountId.value
110 const assetAlias = transaction.assetAlias.value
111 const assetId = transaction.assetId.value
112 const receivers = transaction.receivers
113 const addresses = receivers.map(x => x.address.value)
114 const amounts = receivers.map(x => Number(x.amount.value))
116 const noAccount = !accountAlias && !accountId
117 const noAsset = !assetAlias && !assetId
119 if ( addresses.includes('') || amounts.includes(0)|| noAccount || noAsset) {
120 this.setState({estimateGas: null})
124 const totalAmount = amounts.reduce((prev, next) => prev + next)
126 const spendAction = {
132 type: 'spend_account'
139 amount: Math.pow(10, 7),
140 type: 'spend_account'
143 const actions = [spendAction, gasAction]
144 receivers.forEach((receiver)=>{
147 address: receiver.address.value,
150 amount:Number(receiver.amount.value),
151 type: 'control_address'
156 const body = {actions, ttl: 1}
157 this.connection.request('/build-transaction', body).then(resp => {
158 if (resp.status === 'fail') {
159 this.setState({estimateGas: null})
160 this.props.showError(new Error(resp.msg))
164 return this.connection.request('/estimate-transaction-gas', {
165 transactionTemplate: resp.data
167 if (resp.status === 'fail') {
168 this.setState({estimateGas: null})
169 this.props.showError(new Error(resp.msg))
172 this.setState({estimateGas: Math.ceil(resp.data.totalNeu/100000)*100000})
179 fields: {accountId, accountAlias, assetId, assetAlias, receivers, gasLevel},
183 const lang = this.props.lang;
184 [accountAlias, accountId, assetAlias, assetId].forEach(key => {
185 key.onBlur = this.estimateNormalTransactionGas.bind(this)
187 (receivers.map(receiver => receiver.amount)).forEach(amount =>{
188 amount.onBlur = this.estimateNormalTransactionGas.bind(this)
191 let submitLabel = lang === 'zh' ? '提交交易' : 'Submit transaction'
193 const assetDecimal = this.props.assetDecimal(this.props.fields) || 0
195 const showAvailableBalance = (accountAlias.value || accountId.value) &&
196 (assetAlias.value || assetId.value)
198 const availableBalance = this.props.balanceAmount(this.props.fields, assetDecimal)
200 const showBtmAmountUnit = (assetAlias.value === 'BTM' || assetId.value === btmID)
204 className={styles.container}
205 onSubmit={e => this.confirmedTransaction(e, assetDecimal)}
206 {...disableAutocomplete}
209 <label className={styles.title}>{lang === 'zh' ? '从' : 'From'}</label>
210 <div className={`${styles.mainBox} ${styles.item}`}>
212 key='account-selector-field'
213 keyIndex='normaltx-account'
215 title={lang === 'zh' ? '账户' : 'Account'}
216 aliasField={Autocomplete.AccountAlias}
224 key='asset-selector-field'
225 keyIndex='normaltx-asset'
227 title={lang === 'zh' ? '资产' : 'Asset'}
228 aliasField={Autocomplete.AssetAlias}
234 {showAvailableBalance && availableBalance &&
235 <small className={styles.balanceHint}>{lang === 'zh' ? '可用余额:' : 'Available balance:'} {availableBalance}</small>}
239 <label className={styles.title}>{lang === 'zh' ? '至' : 'To'}</label>
241 <div className={styles.mainBox}>
242 {receivers.map((receiver, index) =>
244 className={styles.item}
245 key={receiver.id.value}>
246 <TextField title={lang === 'zh' ? '地址' : 'Address'} fieldProps={{
249 receiver.address.onBlur(e)
250 this.estimateNormalTransactionGas()
254 {!showBtmAmountUnit &&
255 <AmountInputMask title={lang === 'zh' ? '数量' : 'Amount'} fieldProps={receiver.amount} decimal={assetDecimal}
257 {showBtmAmountUnit &&
258 <AmountUnitField title={lang === 'zh' ? '数量' : 'Amount'} fieldProps={receiver.amount}/>
261 <a href='#' className='btn btn-sm ' onClick={this.addReceiverItem}>+</a> :
262 <a href='#' className='btn btn-sm btn-danger' onClick={() => this.removeReceiverItem(index)}>-</a>
269 <label className={styles.title}>{lang === 'zh' ? '选择手续费' : 'Select Fee'}</label>
271 gas={this.state.estimateGas}
272 fieldProps={gasLevel}
273 btmAmountUnit={this.props.btmAmountUnit}
277 <FormSection className={styles.submitSection}>
278 {error && error.message !== 'PasswordWrong' &&
280 title='Error submitting form'
283 <div className={styles.submit}>
284 <button type='submit' className='btn btn-primary'
285 disabled={submitting || this.disableSubmit(this.props.fields)}>
295 const validate = (values, props) => {
296 const errors = {gas: {}}
297 const lang = props.lang
300 if (values.amount && !/^\d+(\.\d+)?$/i.test(values.amount)) {
301 errors.amount = ( lang === 'zh' ? '请输入数字' : 'Invalid amount type' )
306 const asyncValidate = (values) => {
310 values.receivers.forEach((receiver, idx) => {
311 const address = values.receivers[idx].address
312 if ( !address || address.length === 0)
313 promises.push(Promise.resolve())
316 chainClient().accounts.validateAddresses(address)
319 if (!resp.data.valid) {
320 errors[idx] = {address: 'invalid address'}
328 return Promise.all(promises).then(() => {
329 if (errors.length > 0) throw {
337 export default BaseNew.connect(
338 BaseNew.mapStateToProps('transaction'),
340 showError: err => dispatch({type: 'ERROR', payload: err}),
341 closeModal: () => dispatch(actions.app.hideModal),
342 showModal: (body) => dispatch(actions.app.showModal(
344 actions.app.hideModal,
351 ...BaseNew.mapDispatchToProps('transaction')(dispatch)
354 form: 'NormalTransactionForm',
361 'receivers[].amount',
362 'receivers[].address',
366 asyncBlurFields: ['receivers[].address'],