11 } from 'features/shared/components'
12 import {DropdownButton, MenuItem} from 'react-bootstrap'
13 import {reduxForm} from 'redux-form'
14 import ActionItem from './FormActionItem'
15 import React from 'react'
16 import styles from './New.scss'
17 import actions from 'actions'
18 import { normalizeBTMAmountUnit, converIntToDec } from 'utility/buildInOutDisplay'
20 const rangeOptions = [
38 const btmID = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
40 class Form extends React.Component {
48 this.submitWithValidation = this.submitWithValidation.bind(this)
49 this.addActionItem = this.addActionItem.bind(this)
50 this.removeActionItem = this.removeActionItem.bind(this)
51 this.toggleDropwdown = this.toggleDropwdown.bind(this)
52 this.closeDropdown = this.closeDropdown.bind(this)
53 this.disableSubmit = this.disableSubmit.bind(this)
57 if (!this.props.autocompleteIsLoaded) {
58 this.props.fetchAll().then(() => {
59 this.props.didLoadAutocomplete()
62 if (!this.props.autocompleteIsAssetLoaded) {
63 this.props.fetchAssetAll().then(() => {
64 this.props.didLoadAssetAutocomplete()
68 this.props.fields.normalTransaction.gas.type.onChange(rangeOptions[0].label)
69 this.props.fields.normalTransaction.gas.price.onChange(rangeOptions[0].value)
72 balanceAmount(normalTransaction, assetdecimal) {
73 let balances = this.props.balances
74 let filteredBalances = balances
75 if (normalTransaction.accountAlias.value) {
76 filteredBalances = filteredBalances.filter(balance => balance.accountAlias === normalTransaction.accountAlias.value)
78 if (normalTransaction.accountId.value) {
79 filteredBalances = filteredBalances.filter(balance => balance.accountId === normalTransaction.accountId.value)
81 if (normalTransaction.assetAlias.value) {
82 filteredBalances = filteredBalances.filter(balance => balance.assetAlias === normalTransaction.assetAlias.value)
84 if (normalTransaction.assetId.value) {
85 filteredBalances = filteredBalances.filter(balance => balance.assetId === normalTransaction.assetId.value)
88 if(filteredBalances.length === 1){
89 if (filteredBalances[0].assetId === btmID){
90 return normalizeBTMAmountUnit(filteredBalances[0].assetId, filteredBalances[0].amount, this.props.btmAmountUnit)
91 }else if( assetdecimal ){
92 return converIntToDec(filteredBalances[0].amount, assetdecimal)
94 return filteredBalances[0].amount
101 assetDecimal(normalTransaction) {
102 let asset = this.props.asset
103 let filteredAsset = asset
104 if (normalTransaction.assetAlias.value) {
105 filteredAsset = filteredAsset.filter(asset => asset.alias === normalTransaction.assetAlias.value)
107 if (normalTransaction.assetId.value) {
108 filteredAsset = filteredAsset.filter(asset => asset.id === normalTransaction.assetId.value)
111 return (filteredAsset.length === 1 && filteredAsset[0].definition && filteredAsset[0].id !== btmID ) ? filteredAsset[0].definition.decimals : null
115 this.setState({showDropdown: !this.state.showDropdown})
119 this.setState({showDropdown: false})
122 addActionItem(type) {
123 this.props.fields.actions.addField({
125 referenceData: '{\n\t\n}'
130 disableSubmit(actions, normalTransaction) {
131 if (this.state.showAdvanceTx) {
132 return actions.length == 0 && !this.state.showAdvanced
135 const hasValue = target => {
136 return !!(target && target.value)
139 return !((hasValue(normalTransaction.accountId) || hasValue(normalTransaction.accountAlias)) &&
140 (hasValue(normalTransaction.assetId) || hasValue(normalTransaction.assetAlias)) &&
141 hasValue(normalTransaction.address) && (hasValue(normalTransaction.amount)))
144 removeActionItem(index) {
145 this.props.fields.actions.removeField(index)
148 emptyActions(actions) {
149 if (actions.length != 0) {
150 actions.map(() => this.removeActionItem(0))
154 submitWithValidation(data) {
155 return new Promise((resolve, reject) => {
156 this.props.submitForm(Object.assign({}, data, {state: this.state}))
161 response.actions = []
163 err.data.forEach((error) => {
164 response.actions[error.data.actionIndex] = {type: error}
168 response['_error'] = err
169 return reject(response)
177 fields: {baseTransaction, actions, submitAction, password, normalTransaction},
182 const lang = this.props.lang
184 let submitLabel = lang === 'zh' ? '提交交易' : 'Submit transaction'
185 const hasBaseTransaction = ((baseTransaction.value || '').trim()).length > 0
186 if (submitAction.value == 'generate' && !hasBaseTransaction) {
187 submitLabel = lang === 'zh' ? '生成交易JSON' : 'Generate transaction JSON'
190 const gasOnChange = event => {
191 normalTransaction.gas.type.onChange(event)
193 const range = rangeOptions.find(item => item.label === event.target.value)
194 normalTransaction.gas.price.onChange(range.value)
196 const assetDecimal = this.assetDecimal(normalTransaction)
198 const showAvailableBalance = (normalTransaction.accountAlias.value || normalTransaction.accountId.value) &&
199 (normalTransaction.assetAlias.value || normalTransaction.assetId.value)
200 const availableBalance = this.balanceAmount(normalTransaction, assetDecimal)
202 const showBtmAmountUnit = (normalTransaction.assetAlias.value === 'BTM' ||
203 normalTransaction.assetId.value === 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
208 label={lang === 'zh' ? '新建交易' : 'New transaction'}
209 submitLabel={submitLabel}
210 onSubmit={handleSubmit(this.submitWithValidation)}
211 showSubmitIndicator={true}
212 submitting={submitting}
213 disabled={this.disableSubmit(actions, normalTransaction)}>
215 <div className={`btn-group ${styles.btnGroup}`} role='group'>
217 className={`btn btn-default ${this.state.showAdvanceTx ? null : 'active'}`}
220 this.emptyActions(actions)
221 this.setState({showAdvanceTx: false})
223 {lang === 'zh' ? '简单交易' : 'Normal'}
226 className={`btn btn-default ${this.state.showAdvanceTx ? 'active' : null}`}
229 this.setState({showAdvanceTx: true})
231 {lang === 'zh' ? '高级交易' : 'Advanced'}
235 {!this.state.showAdvanceTx && <FormSection title={lang === 'zh' ? '简单交易' : 'Normal Trasaction'}>
236 <label className={styles.title}>{lang === 'zh' ? '从' : 'From'}</label>
237 <div className={styles.main}>
239 key='account-selector-field'
241 title={lang === 'zh' ? '账户' : 'Account'}
242 aliasField={Autocomplete.AccountAlias}
244 id: normalTransaction.accountId,
245 alias: normalTransaction.accountAlias
249 key='asset-selector-field'
251 title={lang === 'zh' ? '资产' : 'Asset'}
252 aliasField={Autocomplete.AssetAlias}
254 id: normalTransaction.assetId,
255 alias: normalTransaction.assetAlias
258 {showAvailableBalance && availableBalance &&
259 <small className={styles.balanceHint}>{availableBalance} {lang === 'zh' ? '可用' : 'available'} </small>}
262 <label className={styles.title}>{lang === 'zh' ? '至' : 'To'}</label>
263 <div className={styles.main}>
264 <TextField title={lang === 'zh' ? '地址' : 'Address'} fieldProps={normalTransaction.address}/>
265 {!showBtmAmountUnit && !assetDecimal &&
266 <TextField title={lang === 'zh' ? '数量' : 'Amount'} fieldProps={normalTransaction.amount}
268 {!showBtmAmountUnit && assetDecimal &&
269 <AmountInputMask title={lang === 'zh' ? '数量' : 'Amount'} fieldProps={normalTransaction.amount} decimal={assetDecimal}
271 {showBtmAmountUnit &&
272 <AmountUnitField title={lang === 'zh' ? '数量' : 'Amount'} fieldProps={normalTransaction.amount}/>
276 <label className={styles.title}>Gas</label>
277 <table className={styles.optionsBtnContianer}>
278 {rangeOptions.map((option) =>
279 <tr className={styles.optionsBtn}>
280 <td className={styles.optionsLabel}>
283 {...normalTransaction.gas.type}
284 onChange={gasOnChange}
286 checked={option.label == normalTransaction.gas.type.value}
288 {lang === 'zh' ? option.label_zh : option.label}
292 {option.label == normalTransaction.gas.type.value && option.label !== 'Customize'
293 && normalizeBTMAmountUnit(btmID, option.value, this.props.btmAmountUnit)}
295 option.label === 'Customize' && normalTransaction.gas.type.value === 'Customize' &&
299 fieldProps={normalTransaction.gas.price}
300 placeholder='Enter gas'/>
308 <label className={styles.title}>{lang === 'zh' ? '密码' : 'Password'}</label>
309 <TextField placeholder={lang === 'zh' ? '请输入密码' : 'Please enter the assword'} fieldProps={password}
310 autoFocus={false} type={'password'}/>
313 {this.state.showAdvanceTx && <FormSection title='Actions'>
314 {actions.map((action, index) =>
319 accounts={this.props.accounts}
320 assets={this.props.assets}
321 remove={this.removeActionItem}
323 decimal={this.assetDecimal(action)}
326 <div className={`btn-group ${styles.addActionContainer} ${this.state.showDropdown && 'open'}`}>
328 className={`btn btn-default ${styles.addAction}`}
329 id='input-dropdown-addon'
331 onSelect={this.addActionItem}
333 <MenuItem eventKey='issue'>Issue</MenuItem>
334 <MenuItem eventKey='spend_account'>Spend from account</MenuItem>
335 <MenuItem eventKey='control_address'>Control with address</MenuItem>
336 <MenuItem eventKey='retire'>Retire</MenuItem>
341 {this.state.showAdvanceTx && !this.state.showAdvanced &&
344 className={styles.showAdvanced}
347 this.setState({showAdvanced: true})
350 {lang === 'zh' ? '显示高级选项' : 'Show advanced options'}
355 {this.state.showAdvanceTx && this.state.showAdvanced &&
356 <FormSection title={lang === 'zh' ? '高级选项' : 'Advanced Options'}>
359 title={lang === 'zh' ? '带签名交易' : 'To sign transaction'}
360 placeholder={lang === 'zh' ? '在这里复制交易 HEX ...' : 'Paste transaction hex here...'}
361 fieldProps={baseTransaction}
364 <FieldLabel>{lang === 'zh' ? '交易构建类型' : 'Transaction Build Type'}</FieldLabel>
365 <table className={styles.submitTable}>
368 <td><input id='submit_action_submit' type='radio' {...submitAction} value='submit'
369 checked={submitAction.value == 'submit'}/></td>
372 htmlFor='submit_action_submit'>{lang === 'zh' ? '向区块链提交交易' : 'Submit transaction to blockchain'}</label>
374 <label htmlFor='submit_action_submit' className={styles.submitDescription}>
375 {lang === 'zh' ? '此次交易将通过密钥签名然后提交到区块链。' :
376 'This transaction will be signed by the MockHSM and submitted to the blockchain.'}
381 <td><input id='submit_action_generate' type='radio' {...submitAction} value='generate'
382 checked={submitAction.value == 'generate'}/></td>
384 <label htmlFor='submit_action_generate'>{lang === 'zh' ? '需要更多签名' : 'Need more signature'}</label>
386 <label htmlFor='submit_action_generate' className={styles.submitDescription}>
387 {lang === 'zh' ? '这些actions将通过密钥签名然后作为一个交易 JSON 字符串返回。 作为多签交易的输入,这个JSON字符串需要更多的签名数据。' :
388 'These actions will be signed by the Key and returned as a transaction JSON string, ' +
389 'which should be used to sign transaction in a multi-sign spend.'}
399 this.state.showAdvanceTx && (actions.length > 0 || this.state.showAdvanced) && <FormSection>
400 <label className={styles.title}>{lang === 'zh' ? '密码' : 'Password'}</label>
401 <TextField placeholder={lang === 'zh' ? '请输入密码' : 'Please enter the assword'} fieldProps={password}
402 autoFocus={false} type={'password'}/>
410 const validate = values => {
411 const errors = {actions: {}, normalTransaction: {gas: {}}}
414 let baseTx = (values.baseTransaction || '').trim()
419 errors.baseTransaction = 'To sign transaction must be a JSON string.'
425 values.actions.forEach((action, index) => {
426 numError = (!/^\d+(\.\d+)?$/i.test(values.actions[index].amount))
428 errors.actions[index] = {...errors.actions[index], amount: 'Invalid amount type'}
433 let normalTx = values.normalTransaction || ''
434 if (normalTx.amount && !/^\d+(\.\d+)?$/i.test(normalTx.amount)) {
435 errors.normalTransaction.amount = 'Invalid amount type'
440 export default BaseNew.connect(
443 for (let key in state.balance.items) {
444 balances.push(state.balance.items[key])
448 autocompleteIsLoaded: state.balance.autocompleteIsLoaded,
449 autocompleteIsAssetLoaded: state.balance.autocompleteIsLoaded,
450 lang: state.core.lang,
451 btmAmountUnit: state.core.btmAmountUnit,
453 asset: Object.keys(state.asset.items).map(k => state.asset.items[k]),
454 ...BaseNew.mapStateToProps('transaction')(state)
458 didLoadAutocomplete: () => dispatch(actions.balance.didLoadAutocomplete),
459 fetchAll: (cb) => dispatch(actions.balance.fetchAll(cb)),
460 didLoadAssetAutocomplete: () => dispatch(actions.asset.didLoadAutocomplete),
461 fetchAssetAll: (cb) => dispatch(actions.asset.fetchAll(cb)),
462 ...BaseNew.mapDispatchToProps('transaction')(dispatch)
465 form: 'NewTransactionForm',
468 'actions[].accountId',
469 'actions[].accountAlias',
471 'actions[].assetAlias',
473 'actions[].receiver',
474 'actions[].outputId',
475 'actions[].referenceData',
478 'actions[].password',
479 'normalTransaction.accountAlias',
480 'normalTransaction.accountId',
481 'normalTransaction.amount',
482 'normalTransaction.assetAlias',
483 'normalTransaction.assetId',
484 'normalTransaction.gas',
485 'normalTransaction.gas.type',
486 'normalTransaction.gas.price',
487 'normalTransaction.address',
494 submitAction: 'submit',