1 import { BaseNew, FormContainer, FormSection, FieldLabel, JsonField, TextField, Autocomplete, ObjectSelectorField, AmountUnitField } from 'features/shared/components'
2 import { DropdownButton, MenuItem } from 'react-bootstrap'
3 import { reduxForm } from 'redux-form'
4 import ActionItem from './FormActionItem'
5 import React from 'react'
6 import styles from './New.scss'
7 import balanceActions from 'features/balances/actions'
8 import { normalizeBTMAmountUnit, formatBTMAmount, parseBTMAmount } from 'utility/buildInOutDisplay'
10 const rangeOptions = [
28 class Form extends React.Component {
36 this.submitWithValidation = this.submitWithValidation.bind(this)
37 this.addActionItem = this.addActionItem.bind(this)
38 this.removeActionItem = this.removeActionItem.bind(this)
39 this.toggleDropwdown = this.toggleDropwdown.bind(this)
40 this.closeDropdown = this.closeDropdown.bind(this)
41 this.disableSubmit = this.disableSubmit.bind(this)
45 if (!this.props.autocompleteIsLoaded) {
46 this.props.fetchAll().then(() => {
47 this.props.didLoadAutocomplete()
51 this.props.fields.normalTransaction.gas.type.onChange(rangeOptions[0].label)
52 this.props.fields.normalTransaction.gas.price.onChange(rangeOptions[0].value)
55 balanceAmount(normalTransaction) {
56 let balances = this.props.balances
57 let filteredBalances = balances
58 if (normalTransaction.accountAlias.value) {
59 filteredBalances = filteredBalances.filter(balance => balance.accountAlias === normalTransaction.accountAlias.value)
61 if (normalTransaction.accountId.value) {
62 filteredBalances = filteredBalances.filter(balance => balance.accountId === normalTransaction.accountId.value)
64 if (normalTransaction.assetAlias.value) {
65 filteredBalances = filteredBalances.filter(balance => balance.assetAlias === normalTransaction.assetAlias.value)
67 if (normalTransaction.assetId.value) {
68 filteredBalances = filteredBalances.filter(balance => balance.assetId === normalTransaction.assetId.value)
71 return filteredBalances.length === 1 ? normalizeBTMAmountUnit(filteredBalances[0].assetId, filteredBalances[0].amount, this.props.btmAmountUnit) : null
75 this.setState({ showDropdown: !this.state.showDropdown })
79 this.setState({ showDropdown: false })
83 this.props.fields.actions.addField({
85 referenceData: '{\n\t\n}'
90 disableSubmit(actions, normalTransaction) {
91 if (this.state.showAdvanceTx) {
92 return actions.length == 0 && !this.state.showAdvanced
95 const hasValue = target => {
96 return !!(target && target.value)
99 return !((hasValue(normalTransaction.accountId) || hasValue(normalTransaction.accountAlias)) &&
100 (hasValue(normalTransaction.assetId)|| hasValue(normalTransaction.assetAlias)) &&
101 hasValue(normalTransaction.address) && (hasValue(normalTransaction.amount)))
104 removeActionItem(index) {
105 this.props.fields.actions.removeField(index)
108 emptyActions(actions){
109 if(actions.length != 0){
110 actions.map(()=> this.removeActionItem(0))
114 submitWithValidation(data) {
115 return new Promise((resolve, reject) => {
116 this.props.submitForm(data)
121 response.actions = []
123 err.data.forEach((error) => {
124 response.actions[error.data.actionIndex] = {type: error}
128 response['_error'] = err
129 return reject(response)
137 fields: { baseTransaction, actions, submitAction, password, normalTransaction },
142 const lang = this.props.lang
144 let submitLabel = lang === 'zh' ? '提交交易' : 'Submit transaction'
145 if (submitAction.value == 'generate') {
146 submitLabel = lang === 'zh' ? '生成交易hex' : 'Generate transaction hex'
149 const gasOnChange = event => {
150 normalTransaction.gas.type.onChange(event)
152 const range = rangeOptions.find(item => item.label === event.target.value)
153 normalTransaction.gas.price.onChange(range.value)
156 const showAvailableBalance = (normalTransaction.accountAlias.value || normalTransaction.accountId.value) &&
157 (normalTransaction.assetAlias.value || normalTransaction.assetId.value)
158 const availableBalance = this.balanceAmount(normalTransaction)
160 const showBtmAmountUnit = (normalTransaction.assetAlias.value === 'btm' ||
161 normalTransaction.assetId.value === 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
166 label={ lang === 'zh' ? '新建交易' :'New transaction' }
167 submitLabel={submitLabel}
168 onSubmit={handleSubmit(this.submitWithValidation)}
169 showSubmitIndicator={true}
170 submitting={submitting}
171 disabled={this.disableSubmit(actions, normalTransaction)} >
174 <div className={`btn-group ${styles.btnGroup}`} role='group'>
176 className={`btn btn-default ${this.state.showAdvanceTx ? null: 'active'}`}
179 this.emptyActions(actions)
180 this.setState({showAdvanceTx: false})
182 { lang === 'zh' ? '简单交易' : 'Normal' }
185 className={`btn btn-default ${this.state.showAdvanceTx ? 'active': null}`}
188 this.setState({showAdvanceTx: true})
190 { lang === 'zh' ? '高级交易' : 'Advanced' }
194 { !this.state.showAdvanceTx && <FormSection title={ lang === 'zh' ? '简单交易' : 'Normal Trasaction' }>
195 <label className={styles.title}>{ lang === 'zh' ? '从' : 'From' }</label>
196 <div className={styles.main}>
198 title={ lang === 'zh' ? '账户' :'Account' }
199 aliasField={Autocomplete.AccountAlias}
201 id: normalTransaction.accountId,
202 alias: normalTransaction.accountAlias
206 title={ lang === 'zh' ? '资产' : 'Asset' }
207 aliasField={Autocomplete.AssetAlias}
209 id: normalTransaction.assetId,
210 alias: normalTransaction.assetAlias
213 {showAvailableBalance && availableBalance &&
214 <small className={styles.balanceHint}>{availableBalance} available</small>}
217 <label className={styles.title}>{ lang === 'zh' ? '至' : 'To' }</label>
218 <div className={styles.main}>
219 <TextField title='Address' fieldProps={normalTransaction.address}/>
220 {!showBtmAmountUnit && <TextField title='Amount' fieldProps={normalTransaction.amount}
222 {showBtmAmountUnit && <AmountUnitField
225 ...normalTransaction.amount,
227 format={formatBTMAmount}
228 normalize={parseBTMAmount}
232 <label className={styles.title}>Gas</label>
234 <label className={styles.optionsBtnContianer}>
235 {rangeOptions.map((option) =>
239 {...normalTransaction.gas.type}
240 onChange={gasOnChange}
242 checked={option.label == normalTransaction.gas.type.value}
244 { lang === 'zh' ? option.label_zh : option.label }
247 option.label === 'Customize' && normalTransaction.gas.type.value === 'Customize' &&
251 fieldProps={normalTransaction.gas.price}
252 placeholder='Enter gas' />
262 { this.state.showAdvanceTx && <FormSection title='Actions'>
263 {actions.map((action, index) =>
268 accounts={this.props.accounts}
269 assets={this.props.assets}
270 remove={this.removeActionItem}
273 <div className={`btn-group ${styles.addActionContainer} ${this.state.showDropdown && 'open'}`}>
275 className={`btn btn-default ${styles.addAction}`}
276 id='input-dropdown-addon'
278 onSelect={this.addActionItem}
280 <MenuItem eventKey='issue'>Issue</MenuItem>
281 <MenuItem eventKey='spend_account'>Spend from account</MenuItem>
282 {/*<MenuItem eventKey='spend_account_unspent_output'>Spend unspent output</MenuItem>*/}
283 <MenuItem eventKey='control_account'>Control with account</MenuItem>
284 <MenuItem eventKey='control_receiver'>Control with receiver</MenuItem>
285 <MenuItem eventKey='control_address'>Control with address</MenuItem>
286 <MenuItem eventKey='retire'>Retire</MenuItem>
287 {/*<MenuItem eventKey='set_transaction_reference_data'>Set transaction reference data</MenuItem>*/}
292 {this.state.showAdvanceTx && !this.state.showAdvanced &&
295 className={styles.showAdvanced}
298 this.setState({showAdvanced: true})
301 { lang === 'zh' ? '显示高级选项' : 'Show advanced options'}
306 {this.state.showAdvanceTx && this.state.showAdvanced && <FormSection title={ lang === 'zh' ? '高级选项' :'Advanced Options' }>
309 title='Base transaction'
310 placeholder='Paste transaction hex here...'
311 fieldProps={baseTransaction}
314 <FieldLabel>Transaction Build Type</FieldLabel>
315 <table className={styles.submitTable}>
318 <td><input id='submit_action_submit' type='radio' {...submitAction} value='submit' checked={submitAction.value == 'submit'} /></td>
320 <label htmlFor='submit_action_submit'>{ lang === 'zh' ? '向区块链提交交易' :'Submit transaction to blockchain' }</label>
322 <label htmlFor='submit_action_submit' className={styles.submitDescription}>
323 { lang === 'zh' ? '此次交易将通过密钥签名然后提交到区块链。' :
324 'This transaction will be signed by the MockHSM and submitted to the blockchain.' }
329 <td><input id='submit_action_generate' type='radio' {...submitAction} value='generate' checked={submitAction.value == 'generate'} /></td>
331 <label htmlFor='submit_action_generate'>{ lang === 'zh' ? '允许更多actions' : 'Allow additional actions' }</label>
333 <label htmlFor='submit_action_generate' className={styles.submitDescription}>
334 { lang === 'zh' ? '这些actions将通过密钥签名然后作为一个交易 hex 字符串返回, 字符串可以用做base transaction于 multi-party swap。' +
336 'These actions will be signed by the MockHSM and returned as a transaction hex string, ' +
337 'which should be used as the base transaction in a multi-party swap. This transaction ' +
338 'will be valid for one hour.' }
351 const validate = values => {
352 const errors = {actions: {}}
355 let baseTx = values.baseTransaction || ''
356 if (baseTx.trim().match(/[^0-9a-fA-F]/)) {
357 errors.baseTransaction = 'Base transaction must be a hex string.'
362 values.actions.forEach((action, index) => {
363 fieldError = JsonField.validator(values.actions[index].referenceData)
365 errors.actions[index] = {...errors.actions[index], referenceData: fieldError}
372 export default BaseNew.connect(
375 for (let key in state.balance.items) {
376 balances.push(state.balance.items[key])
380 autocompleteIsLoaded: state.key.autocompleteIsLoaded,
381 lang: state.core.lang,
382 btmAmountUnit: state.core.btmAmountUnit,
384 ...BaseNew.mapStateToProps('transaction')(state)
388 didLoadAutocomplete: () => dispatch(balanceActions.didLoadAutocomplete),
389 fetchAll: (cb) => dispatch(balanceActions.fetchAll(cb)),
390 ...BaseNew.mapDispatchToProps('transaction')(dispatch)
393 form: 'NewTransactionForm',
396 'actions[].accountId',
397 'actions[].accountAlias',
399 'actions[].assetAlias',
401 'actions[].receiver',
402 'actions[].outputId',
403 'actions[].referenceData',
406 'actions[].password',
407 'normalTransaction.accountAlias',
408 'normalTransaction.accountId',
409 'normalTransaction.amount',
410 'normalTransaction.assetAlias',
411 'normalTransaction.assetId',
412 'normalTransaction.gas',
413 'normalTransaction.gas.type',
414 'normalTransaction.gas.price',
415 'normalTransaction.address',
421 submitAction: 'submit',