OSDN Git Service

add the ConfirmModal.
[bytom/bytom-electron.git] / src / features / transactions / components / New / NormalTransactionForm.jsx
1 import {
2   BaseNew,
3   FormSection,
4   TextField,
5   Autocomplete,
6   ObjectSelectorField,
7   AmountUnitField,
8   AmountInputMask,
9   ErrorBanner,
10   SubmitIndicator,
11   GasField
12 } from 'features/shared/components'
13 import {chainClient} from 'utility/environment'
14 import {reduxForm} from 'redux-form'
15 import React from 'react'
16 import styles from './New.scss'
17 import disableAutocomplete from 'utility/disableAutocomplete'
18 import { btmID } from 'utility/environment'
19 import actions from 'actions'
20 import ConfirmModal from './ConfirmModal/ConfirmModal'
21
22
23 class NormalTxForm extends React.Component {
24   constructor(props) {
25     super(props)
26     this.connection = chainClient().connection
27     this.state = {
28       estimateGas:null
29     }
30
31     this.submitWithValidation = this.submitWithValidation.bind(this)
32     this.disableSubmit = this.disableSubmit.bind(this)
33   }
34
35   disableSubmit(props) {
36     const hasValue = target => {
37       return !!(target && target.value)
38     }
39
40     return !( (this.state.estimateGas) &&
41       (hasValue(props.accountId) || hasValue(props.accountAlias)) &&
42       (hasValue(props.assetId) || hasValue(props.assetAlias)) &&
43       hasValue(props.address) && (hasValue(props.amount))
44     )
45   }
46
47   submitWithValidation(data) {
48     return new Promise((resolve, reject) => {
49       this.props.submitForm(Object.assign({}, data, {state: this.state, form: 'normalTx'}))
50         .then(
51           this.props.closeModal()
52         )
53         .catch((err) => {
54           const response = {}
55
56           response['_error'] = err
57           return reject(response)
58         })
59     })
60   }
61
62   confirmedTransaction(e){
63     e.preventDefault()
64     this.props.showModal(
65       <ConfirmModal
66         onSubmit={this.submitWithValidation}
67         gas={this.state.estimateGas}
68       />
69     )
70   }
71
72   estimateNormalTransactionGas() {
73     const transaction = this.props.fields
74     const address = transaction.address.value
75     const amount = transaction.amount.value
76     const accountAlias = transaction.accountAlias.value
77     const accountId = transaction.accountId.value
78     const assetAlias = transaction.assetAlias.value
79     const assetId = transaction.assetId.value
80
81     const noAccount = !accountAlias && !accountId
82     const noAsset = !assetAlias && !assetId
83
84     if (!address || !amount || noAccount || noAsset) {
85       this.setState({estimateGas: null})
86       return
87     }
88
89     const spendAction = {
90       accountAlias,
91       accountId,
92       assetAlias,
93       assetId,
94       amount: Number(amount),
95       type: 'spend_account'
96     }
97     const receiveAction = {
98       address,
99       assetAlias,
100       assetId,
101       amount: Number(amount),
102       type: 'control_address'
103     }
104
105     const gasAction = {
106       accountAlias,
107       accountId,
108       assetAlias: 'BTM',
109       amount: Math.pow(10, 7),
110       type: 'spend_account'
111     }
112
113     const actions = [spendAction, receiveAction, gasAction]
114     const body = {actions, ttl: 1}
115     this.connection.request('/build-transaction', body).then(resp => {
116       if (resp.status === 'fail') {
117         this.setState({estimateGas: null})
118         this.props.showError(new Error(resp.msg))
119         return
120       }
121
122       return this.connection.request('/estimate-transaction-gas', {
123         transactionTemplate: resp.data
124       }).then(resp => {
125         if (resp.status === 'fail') {
126           this.setState({estimateGas: null})
127           this.props.showError(new Error(resp.msg))
128           return
129         }
130         this.setState({estimateGas: Math.ceil(resp.data.totalNeu/100000)*100000})
131       })
132     })
133   }
134
135   render() {
136     const {
137       fields: {accountId, accountAlias, assetId, assetAlias, address, amount, gasLevel},
138       error,
139       submitting
140     } = this.props
141     const lang = this.props.lang;
142     [amount, accountAlias, accountId, assetAlias, assetId].forEach(key => {
143       key.onBlur = this.estimateNormalTransactionGas.bind(this)
144     })
145
146     let submitLabel = lang === 'zh' ? '提交交易' : 'Submit transaction'
147
148     const assetDecimal = this.props.assetDecimal(this.props.fields) || 0
149
150     const showAvailableBalance = (accountAlias.value || accountId.value) &&
151       (assetAlias.value || assetId.value)
152
153     const availableBalance = this.props.balanceAmount(this.props.fields, assetDecimal)
154
155     const showBtmAmountUnit = (assetAlias.value === 'BTM' || assetId.value === btmID)
156
157     return (
158         <form
159           className={styles.container}
160           onSubmit={e => this.confirmedTransaction(e)}
161           {...disableAutocomplete}
162           // onKeyDown={(e) => { this.props.handleKeyDown(e, handleSubmit(this.submitWithValidation), this.disableSubmit(this.props.fields)) }}
163         >
164           <div>
165             <label className={styles.title}>{lang === 'zh' ? '从' : 'From'}</label>
166             <div className={styles.main}>
167               <ObjectSelectorField
168                 key='account-selector-field'
169                 keyIndex='normaltx-account'
170                 lang={lang}
171                 title={lang === 'zh' ? '账户' : 'Account'}
172                 aliasField={Autocomplete.AccountAlias}
173                 fieldProps={{
174                   id: accountId,
175                   alias: accountAlias
176                 }}
177               />
178               <div>
179                 <ObjectSelectorField
180                   key='asset-selector-field'
181                   keyIndex='normaltx-asset'
182                   lang={lang}
183                   title={lang === 'zh' ? '资产' : 'Asset'}
184                   aliasField={Autocomplete.AssetAlias}
185                   fieldProps={{
186                     id: assetId,
187                     alias: assetAlias
188                   }}
189                 />
190                 {showAvailableBalance && availableBalance &&
191                 <small className={styles.balanceHint}>{lang === 'zh' ? '可用余额:' : 'Available balance:'} {availableBalance}</small>}
192               </div>
193             </div>
194
195             <label className={styles.title}>{lang === 'zh' ? '至' : 'To'}</label>
196             <div className={styles.main}>
197               <TextField title={lang === 'zh' ? '地址' : 'Address'} fieldProps={{
198                 ...address,
199                 onBlur: (e) => {
200                   address.onBlur(e)
201                   this.estimateNormalTransactionGas()
202                 },
203               }}/>
204               {!showBtmAmountUnit &&
205               <AmountInputMask title={lang === 'zh' ? '数量' : 'Amount'} fieldProps={amount} decimal={assetDecimal}
206               />}
207               {showBtmAmountUnit &&
208               <AmountUnitField title={lang === 'zh' ? '数量' : 'Amount'} fieldProps={amount}/>
209               }
210             </div>
211
212             <label className={styles.title}>{lang === 'zh' ? '选择手续费' : 'Select Fee'}</label>
213             <GasField
214               gas={this.state.estimateGas}
215               fieldProps={gasLevel}
216               btmAmountUnit={this.props.btmAmountUnit}
217             />
218           </div>
219
220           <FormSection className={styles.submitSection}>
221             {error &&
222             <ErrorBanner
223               title='Error submitting form'
224               error={error} />}
225
226             <div className={styles.submit}>
227               <button type='submit' className='btn btn-primary'
228                       disabled={submitting || this.disableSubmit(this.props.fields)}
229               >
230                 {submitLabel}
231               </button>
232
233               {submitting &&
234               <SubmitIndicator />
235               }
236             </div>
237           </FormSection>
238         </form>
239     )
240   }
241 }
242
243 const validate = (values, props) => {
244   const errors = {gas: {}}
245   const lang = props.lang
246
247   // Numerical
248   if (values.amount && !/^\d+(\.\d+)?$/i.test(values.amount)) {
249     errors.amount = ( lang === 'zh' ? '请输入数字' : 'Invalid amount type' )
250   }
251   return errors
252 }
253
254 const asyncValidate = (values) => {
255   return new Promise((resolve, reject) => {
256     const address = values.address
257     chainClient().accounts.validateAddresses(address)
258       .then(
259         (resp) => {
260           if(!resp.data.valid){
261             reject({ address: 'invalid address'})
262           }else {
263             resolve()
264           }
265         }
266       ).catch((err) => {
267         reject({ address: err })
268       })
269   })
270 }
271
272
273 export default BaseNew.connect(
274   BaseNew.mapStateToProps('transaction'),
275   (dispatch) => ({
276     showError: err => dispatch({type: 'ERROR', payload: err}),
277     closeModal: () => dispatch(actions.app.hideModal),
278     showModal: (body) => dispatch(actions.app.showModal(
279       body,
280       actions.app.hideModal,
281       null,
282       {
283         // dialog: true,
284         noCloseBtn: true
285       }
286     )),
287     ...BaseNew.mapDispatchToProps('transaction')(dispatch)
288   }),
289   reduxForm({
290     form: 'NormalTransactionForm',
291     fields: [
292       'accountAlias',
293       'accountId',
294       'amount',
295       'assetAlias',
296       'assetId',
297       'gasLevel',
298       'address',
299     ],
300     asyncValidate,
301     asyncBlurFields: [ 'address'],
302     validate,
303     destroyOnUnmount: false,
304     touchOnChange: true,
305     initialValues: {
306       gasLevel: '1'
307     },
308   })(NormalTxForm)
309 )
310
311