OSDN Git Service

add the asset decimal function in transaction page.
[bytom/bytom-electron.git] / src / features / transactions / components / New / New.jsx
index 6d432d7..015c896 100644 (file)
@@ -1,26 +1,42 @@
-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)
@@ -43,12 +59,17 @@ class Form extends React.Component {
         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) {
@@ -63,15 +84,39 @@ class Form extends React.Component {
     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) {
@@ -83,8 +128,8 @@ class Form extends React.Component {
   }
 
   disableSubmit(actions, normalTransaction) {
-    if (this.showAdvance) {
-      return actions.length == 0 & !this.state.showAdvanced
+    if (this.state.showAdvanceTx) {
+      return actions.length == 0 && !this.state.showAdvanced
     }
 
     const hasValue = target => {
@@ -92,7 +137,7 @@ class Form extends React.Component {
     }
 
     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)))
   }
 
@@ -100,15 +145,15 @@ class Form extends React.Component {
     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 = {}
 
@@ -129,15 +174,17 @@ class Form extends React.Component {
 
   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 => {
@@ -146,47 +193,53 @@ class Form extends React.Component {
       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,
@@ -194,28 +247,38 @@ class Form extends React.Component {
               }}
             />
             <ObjectSelectorField
-              title='Asset'
+              key='asset-selector-field'
+              lang={lang}
+              title={lang === 'zh' ? '资产' : 'Asset'}
               aliasField={Autocomplete.AssetAlias}
               fieldProps={{
                 id: normalTransaction.assetId,
                 alias: normalTransaction.assetAlias
               }}
             />
+            {showAvailableBalance && availableBalance &&
+            <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}/>
-            {showAvailableBalance && availableBalance &&
-            <small className='value-balance'>{availableBalance} available</small>}
+            <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}
@@ -223,25 +286,32 @@ class Form extends React.Component {
                            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}
@@ -250,6 +320,7 @@ class Form extends React.Component {
               accounts={this.props.accounts}
               assets={this.props.assets}
               remove={this.removeActionItem}
+              lang={lang}
             />)}
 
           <div className={`btn-group ${styles.addActionContainer} ${this.state.showDropdown && 'open'}`}>
@@ -261,61 +332,61 @@ class Form extends React.Component {
             >
               <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>
@@ -323,29 +394,46 @@ class Form extends React.Component {
             </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
 }
 
@@ -357,14 +445,20 @@ export default BaseNew.connect(
     }
 
     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({
@@ -395,6 +489,7 @@ export default BaseNew.connect(
       'password'
     ],
     validate,
+    touchOnChange: true,
     initialValues: {
       submitAction: 'submit',
     },