-import { BaseNew, FormContainer, FormSection, FieldLabel, JsonField, TextField, Autocomplete, ObjectSelectorField, SelectField } from 'features/shared/components'
-import { DropdownButton, MenuItem } from 'react-bootstrap'
-import { reduxForm } from 'redux-form'
+import {
+ BaseNew,
+ FormContainer,
+ FormSection,
+ FieldLabel,
+ TextField,
+ Autocomplete,
+ ObjectSelectorField,
+ AmountUnitField,
+ AmountInputMask
+} from 'features/shared/components'
+import {DropdownButton, MenuItem} from 'react-bootstrap'
+import {reduxForm} from 'redux-form'
import ActionItem from './FormActionItem'
import React from 'react'
import styles from './New.scss'
-import balanceActions from 'features/balances/actions'
+import actions from 'actions'
+import { normalizeBTMAmountUnit, converIntToDec } from 'utility/buildInOutDisplay'
const rangeOptions = [
{
label: 'Standard',
+ label_zh: '标准',
value: '20000000'
},
{
label: 'Fast',
+ label_zh: '快速',
value: '25000000'
},
{
label: 'Customize',
+ label_zh: '自定义',
value: ''
}
]
+const btmID = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
+
class Form extends React.Component {
constructor(props) {
super(props)
this.props.didLoadAutocomplete()
})
}
+ if (!this.props.autocompleteIsAssetLoaded) {
+ this.props.fetchAssetAll().then(() => {
+ this.props.didLoadAssetAutocomplete()
+ })
+ }
this.props.fields.normalTransaction.gas.type.onChange(rangeOptions[0].label)
this.props.fields.normalTransaction.gas.price.onChange(rangeOptions[0].value)
}
- balanceAmount(normalTransaction) {
+ balanceAmount(normalTransaction, assetdecimal) {
let balances = this.props.balances
let filteredBalances = balances
if (normalTransaction.accountAlias.value) {
if (normalTransaction.assetId.value) {
filteredBalances = filteredBalances.filter(balance => balance.assetId === normalTransaction.assetId.value)
}
- return filteredBalances.length === 1 ? filteredBalances[0].amount : null
+
+ if(filteredBalances.length === 1){
+ if (filteredBalances[0].assetId === btmID){
+ return normalizeBTMAmountUnit(filteredBalances[0].assetId, filteredBalances[0].amount, this.props.btmAmountUnit)
+ }else if( assetdecimal ){
+ return converIntToDec(filteredBalances[0].amount, assetdecimal)
+ }else{
+ return filteredBalances[0].amount
+ }
+ }else {
+ return null
+ }
+ }
+
+ assetDecimal(normalTransaction) {
+ let asset = this.props.asset
+ let filteredAsset = asset
+ if (normalTransaction.assetAlias.value) {
+ filteredAsset = filteredAsset.filter(asset => asset.alias === normalTransaction.assetAlias.value)
+ }
+ if (normalTransaction.assetId.value) {
+ filteredAsset = filteredAsset.filter(asset => asset.id === normalTransaction.assetId.value)
+ }
+
+ return (filteredAsset.length === 1 && filteredAsset[0].definition && filteredAsset[0].id !== btmID ) ? filteredAsset[0].definition.decimals : null
}
toggleDropwdown() {
- this.setState({ showDropdown: !this.state.showDropdown })
+ this.setState({showDropdown: !this.state.showDropdown})
}
closeDropdown() {
- this.setState({ showDropdown: false })
+ this.setState({showDropdown: false})
}
addActionItem(type) {
}
disableSubmit(actions, normalTransaction) {
- if (this.state.showAdvance) {
+ if (this.state.showAdvanceTx) {
return actions.length == 0 && !this.state.showAdvanced
}
}
return !((hasValue(normalTransaction.accountId) || hasValue(normalTransaction.accountAlias)) &&
- (hasValue(normalTransaction.assetId)|| hasValue(normalTransaction.assetAlias)) &&
+ (hasValue(normalTransaction.assetId) || hasValue(normalTransaction.assetAlias)) &&
hasValue(normalTransaction.address) && (hasValue(normalTransaction.amount)))
}
this.props.fields.actions.removeField(index)
}
- emptyActions(actions){
- if(actions.length != 0){
- actions.map(()=> this.removeActionItem(0))
+ emptyActions(actions) {
+ if (actions.length != 0) {
+ actions.map(() => this.removeActionItem(0))
}
}
submitWithValidation(data) {
return new Promise((resolve, reject) => {
- this.props.submitForm(data)
+ this.props.submitForm(Object.assign({}, data, {state: this.state}))
.catch((err) => {
const response = {}
render() {
const {
- fields: { baseTransaction, actions, submitAction, password, normalTransaction },
+ fields: {baseTransaction, actions, submitAction, password, normalTransaction},
error,
handleSubmit,
submitting
} = this.props
+ const lang = this.props.lang
- let submitLabel = 'Submit transaction'
- if (submitAction.value == 'generate') {
- submitLabel = 'Generate transaction hex'
+ let submitLabel = lang === 'zh' ? '提交交易' : 'Submit transaction'
+ const hasBaseTransaction = ((baseTransaction.value || '').trim()).length > 0
+ if (submitAction.value == 'generate' && !hasBaseTransaction) {
+ submitLabel = lang === 'zh' ? '生成交易JSON' : 'Generate transaction JSON'
}
const gasOnChange = event => {
const range = rangeOptions.find(item => item.label === event.target.value)
normalTransaction.gas.price.onChange(range.value)
}
+ const assetDecimal = this.assetDecimal(normalTransaction)
const showAvailableBalance = (normalTransaction.accountAlias.value || normalTransaction.accountId.value) &&
(normalTransaction.assetAlias.value || normalTransaction.assetId.value)
- const availableBalance = this.balanceAmount(normalTransaction)
+ const availableBalance = this.balanceAmount(normalTransaction, assetDecimal)
- return(
+ const showBtmAmountUnit = (normalTransaction.assetAlias.value === 'BTM' ||
+ normalTransaction.assetId.value === 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
+
+
+ return (
<FormContainer
error={error}
- label='New transaction'
+ label={lang === 'zh' ? '新建交易' : 'New transaction'}
submitLabel={submitLabel}
onSubmit={handleSubmit(this.submitWithValidation)}
showSubmitIndicator={true}
submitting={submitting}
- disabled={this.disableSubmit(actions, normalTransaction)} >
-
+ disabled={this.disableSubmit(actions, normalTransaction)}>
<div className={`btn-group ${styles.btnGroup}`} role='group'>
<button
- className={`btn btn-default ${this.state.showAdvance ? null: 'active'}`}
+ className={`btn btn-default ${this.state.showAdvanceTx ? null : 'active'}`}
onClick={(e) => {
e.preventDefault()
this.emptyActions(actions)
- this.setState({showAdvance: false})
- }} >
- Normal
+ this.setState({showAdvanceTx: false})
+ }}>
+ {lang === 'zh' ? '简单交易' : 'Normal'}
</button>
<button
- className={`btn btn-default ${this.state.showAdvance ? 'active': null}`}
+ className={`btn btn-default ${this.state.showAdvanceTx ? 'active' : null}`}
onClick={(e) => {
e.preventDefault()
- this.setState({showAdvance: true})
- }} >
- Advanced
+ this.setState({showAdvanceTx: true})
+ }}>
+ {lang === 'zh' ? '高级交易' : 'Advanced'}
</button>
</div>
- { !this.state.showAdvance && <FormSection title='Normal Trasaction'>
- <label className={styles.title}>From</label>
+ {!this.state.showAdvanceTx && <FormSection title={lang === 'zh' ? '简单交易' : 'Normal Trasaction'}>
+ <label className={styles.title}>{lang === 'zh' ? '从' : 'From'}</label>
<div className={styles.main}>
<ObjectSelectorField
- title='Account'
+ key='account-selector-field'
+ lang={lang}
+ title={lang === 'zh' ? '账户' : 'Account'}
aliasField={Autocomplete.AccountAlias}
fieldProps={{
id: normalTransaction.accountId,
}}
/>
<ObjectSelectorField
- title='Asset'
+ key='asset-selector-field'
+ lang={lang}
+ title={lang === 'zh' ? '资产' : 'Asset'}
aliasField={Autocomplete.AssetAlias}
fieldProps={{
id: normalTransaction.assetId,
}}
/>
{showAvailableBalance && availableBalance &&
- <small className={styles.balanceHint}>{availableBalance} available</small>}
+ <small className={styles.balanceHint}>{availableBalance} {lang === 'zh' ? '可用' : 'available'} </small>}
</div>
- <label className={styles.title}>To</label>
+ <label className={styles.title}>{lang === 'zh' ? '至' : 'To'}</label>
<div className={styles.main}>
- <TextField title='Address' fieldProps={normalTransaction.address}/>
- <TextField title='Amount' fieldProps={normalTransaction.amount}/>
+ <TextField title={lang === 'zh' ? '地址' : 'Address'} fieldProps={normalTransaction.address}/>
+ {!showBtmAmountUnit && !assetDecimal &&
+ <TextField title={lang === 'zh' ? '数量' : 'Amount'} fieldProps={normalTransaction.amount}
+ />}
+ {!showBtmAmountUnit && assetDecimal &&
+ <AmountInputMask title={lang === 'zh' ? '数量' : 'Amount'} fieldProps={normalTransaction.amount} decimal={assetDecimal}
+ />}
+ {showBtmAmountUnit &&
+ <AmountUnitField title={lang === 'zh' ? '数量' : 'Amount'} fieldProps={normalTransaction.amount}/>
+ }
</div>
<label className={styles.title}>Gas</label>
- <div>
- <label className={styles.optionsBtnContianer}>
- {rangeOptions.map((option) =>
- <label>
+ <table className={styles.optionsBtnContianer}>
+ {rangeOptions.map((option) =>
+ <tr className={styles.optionsBtn}>
+ <td className={styles.optionsLabel}>
<label>
<input type='radio'
{...normalTransaction.gas.type}
value={option.label}
checked={option.label == normalTransaction.gas.type.value}
/>
- { option.label }
+ {lang === 'zh' ? option.label_zh : option.label}
</label>
+ </td>
+ <td>
+ {option.label == normalTransaction.gas.type.value && option.label !== 'Customize'
+ && normalizeBTMAmountUnit(btmID, option.value, this.props.btmAmountUnit)}
{
option.label === 'Customize' && normalTransaction.gas.type.value === 'Customize' &&
- <label>
- <TextField
+ <div>
+ <AmountUnitField
autoFocus={true}
fieldProps={normalTransaction.gas.price}
- placeholder='Enter gas' />
- </label>
+ placeholder='Enter gas'/>
+ </div>
}
- </label>
- )}
- </label>
- </div>
+ </td>
+ </tr>
+ )}
+ </table>
+ <label className={styles.title}>{lang === 'zh' ? '密码' : 'Password'}</label>
+ <TextField placeholder={lang === 'zh' ? '请输入密码' : 'Please enter the assword'} fieldProps={password}
+ autoFocus={false} type={'password'}/>
</FormSection>}
- { this.state.showAdvance && <FormSection title='Actions'>
+ {this.state.showAdvanceTx && <FormSection title='Actions'>
{actions.map((action, index) =>
<ActionItem
key={index}
accounts={this.props.accounts}
assets={this.props.assets}
remove={this.removeActionItem}
+ lang={lang}
/>)}
<div className={`btn-group ${styles.addActionContainer} ${this.state.showDropdown && 'open'}`}>
>
<MenuItem eventKey='issue'>Issue</MenuItem>
<MenuItem eventKey='spend_account'>Spend from account</MenuItem>
- {/*<MenuItem eventKey='spend_account_unspent_output'>Spend unspent output</MenuItem>*/}
- <MenuItem eventKey='control_account'>Control with account</MenuItem>
- <MenuItem eventKey='control_receiver'>Control with receiver</MenuItem>
<MenuItem eventKey='control_address'>Control with address</MenuItem>
<MenuItem eventKey='retire'>Retire</MenuItem>
- {/*<MenuItem eventKey='set_transaction_reference_data'>Set transaction reference data</MenuItem>*/}
</DropdownButton>
</div>
</FormSection>}
- {this.state.showAdvance && !this.state.showAdvanced &&
- <FormSection>
- <a href='#'
- className={styles.showAdvanced}
- onClick={(e) => {
- e.preventDefault()
- this.setState({showAdvanced: true})
- }}
- >
- Show advanced options
- </a>
- </FormSection>
+ {this.state.showAdvanceTx && !this.state.showAdvanced &&
+ <FormSection>
+ <a href='#'
+ className={styles.showAdvanced}
+ onClick={(e) => {
+ e.preventDefault()
+ this.setState({showAdvanced: true})
+ }}
+ >
+ {lang === 'zh' ? '显示高级选项' : 'Show advanced options'}
+ </a>
+ </FormSection>
}
- {this.state.showAdvance && this.state.showAdvanced && <FormSection title='Advanced Options'>
+ {this.state.showAdvanceTx && this.state.showAdvanced &&
+ <FormSection title={lang === 'zh' ? '高级选项' : 'Advanced Options'}>
<div>
<TextField
- title='Base transaction'
- placeholder='Paste transaction hex here...'
+ title={lang === 'zh' ? '带签名交易' : 'To sign transaction'}
+ placeholder={lang === 'zh' ? '在这里复制交易 HEX ...' : 'Paste transaction hex here...'}
fieldProps={baseTransaction}
- autoFocus={true} />
+ autoFocus={true}/>
- <FieldLabel>Transaction Build Type</FieldLabel>
+ <FieldLabel>{lang === 'zh' ? '交易构建类型' : 'Transaction Build Type'}</FieldLabel>
<table className={styles.submitTable}>
<tbody>
<tr>
- <td><input id='submit_action_submit' type='radio' {...submitAction} value='submit' checked={submitAction.value == 'submit'} /></td>
+ <td><input id='submit_action_submit' type='radio' {...submitAction} value='submit'
+ checked={submitAction.value == 'submit'}/></td>
<td>
- <label htmlFor='submit_action_submit'>Submit transaction to blockchain</label>
- <br />
+ <label
+ htmlFor='submit_action_submit'>{lang === 'zh' ? '向区块链提交交易' : 'Submit transaction to blockchain'}</label>
+ <br/>
<label htmlFor='submit_action_submit' className={styles.submitDescription}>
- This transaction will be signed by the MockHSM and submitted to the blockchain.
+ {lang === 'zh' ? '此次交易将通过密钥签名然后提交到区块链。' :
+ 'This transaction will be signed by the MockHSM and submitted to the blockchain.'}
</label>
</td>
</tr>
<tr>
- <td><input id='submit_action_generate' type='radio' {...submitAction} value='generate' checked={submitAction.value == 'generate'} /></td>
+ <td><input id='submit_action_generate' type='radio' {...submitAction} value='generate'
+ checked={submitAction.value == 'generate'}/></td>
<td>
- <label htmlFor='submit_action_generate'>Allow additional actions</label>
- <br />
+ <label htmlFor='submit_action_generate'>{lang === 'zh' ? '需要更多签名' : 'Need more signature'}</label>
+ <br/>
<label htmlFor='submit_action_generate' className={styles.submitDescription}>
- These actions will be signed by the MockHSM and returned as a
- transaction hex string, which should be used as the base
- transaction in a multi-party swap. This transaction will be
- valid for one hour.
+ {lang === 'zh' ? '这些actions将通过密钥签名然后作为一个交易 JSON 字符串返回。 作为多签交易的输入,这个JSON字符串需要更多的签名数据。' :
+ 'These actions will be signed by the Key and returned as a transaction JSON string, ' +
+ 'which should be used to sign transaction in a multi-sign spend.'}
</label>
</td>
</tr>
</table>
</div>
</FormSection>}
+
+ {
+ this.state.showAdvanceTx && (actions.length > 0 || this.state.showAdvanced) && <FormSection>
+ <label className={styles.title}>{lang === 'zh' ? '密码' : 'Password'}</label>
+ <TextField placeholder={lang === 'zh' ? '请输入密码' : 'Please enter the assword'} fieldProps={password}
+ autoFocus={false} type={'password'}/>
+ </FormSection>
+ }
</FormContainer>
)
}
}
const validate = values => {
- const errors = {actions: {}}
+ const errors = {actions: {}, normalTransaction: {gas: {}}}
// Base transaction
- let baseTx = values.baseTransaction || ''
- if (baseTx.trim().match(/[^0-9a-fA-F]/)) {
- errors.baseTransaction = 'Base transaction must be a hex string.'
+ let baseTx = (values.baseTransaction || '').trim()
+ try {
+ JSON.parse(baseTx)
+ } catch (e) {
+ if (baseTx && e) {
+ errors.baseTransaction = 'To sign transaction must be a JSON string.'
+ }
}
// Actions
- let fieldError
+ let numError
values.actions.forEach((action, index) => {
- fieldError = JsonField.validator(values.actions[index].referenceData)
- if (fieldError) {
- errors.actions[index] = {...errors.actions[index], referenceData: fieldError}
+ numError = (!/^\d+(\.\d+)?$/i.test(values.actions[index].amount))
+ if (numError) {
+ errors.actions[index] = {...errors.actions[index], amount: 'Invalid amount type'}
}
})
+ // Numerical
+ let normalTx = values.normalTransaction || ''
+ if (normalTx.amount && !/^\d+(\.\d+)?$/i.test(normalTx.amount)) {
+ errors.normalTransaction.amount = 'Invalid amount type'
+ }
return errors
}
}
return {
- autocompleteIsLoaded: state.key.autocompleteIsLoaded,
+ autocompleteIsLoaded: state.balance.autocompleteIsLoaded,
+ autocompleteIsAssetLoaded: state.balance.autocompleteIsLoaded,
+ lang: state.core.lang,
+ btmAmountUnit: state.core.btmAmountUnit,
balances,
+ asset: Object.keys(state.asset.items).map(k => state.asset.items[k]),
...BaseNew.mapStateToProps('transaction')(state)
}
},
(dispatch) => ({
- didLoadAutocomplete: () => dispatch(balanceActions.didLoadAutocomplete),
- fetchAll: (cb) => dispatch(balanceActions.fetchAll(cb)),
+ didLoadAutocomplete: () => dispatch(actions.balance.didLoadAutocomplete),
+ fetchAll: (cb) => dispatch(actions.balance.fetchAll(cb)),
+ didLoadAssetAutocomplete: () => dispatch(actions.asset.didLoadAutocomplete),
+ fetchAssetAll: (cb) => dispatch(actions.asset.fetchAll(cb)),
...BaseNew.mapDispatchToProps('transaction')(dispatch)
}),
reduxForm({
'password'
],
validate,
+ touchOnChange: true,
initialValues: {
submitAction: 'submit',
},