OSDN Git Service

update the vote display under the balance page.
authorZhiting Lin <zlin035@uottawa.ca>
Tue, 18 Jun 2019 05:49:23 +0000 (13:49 +0800)
committerZhiting Lin <zlin035@uottawa.ca>
Tue, 18 Jun 2019 05:49:23 +0000 (13:49 +0800)
18 files changed:
src/features/accounts/components/AccountShow.jsx
src/features/balances/actions.js
src/features/balances/components/List.jsx
src/features/balances/components/ListItem.jsx
src/features/balances/components/VoteDetails/VoteDetails.jsx [new file with mode: 0644]
src/features/balances/components/VoteDetails/VoteDetails.scss [new file with mode: 0644]
src/features/balances/components/index.js
src/features/balances/reducers.js
src/features/balances/routes.js
src/features/shared/actions/list.js
src/features/shared/components/BaseShow.jsx
src/features/shared/components/KeyValueTable/KeyValueTable.jsx
src/features/transactions/components/New/NormalTransactionForm.jsx
src/locales/en/translation.json
src/locales/zh/translation.json
src/sdk/api/accounts.js
src/sdk/shared.js
src/utility/buildInOutDisplay.js

index f1953c0..68cfd25 100644 (file)
@@ -29,6 +29,8 @@ class AccountShow extends BaseShow {
       const changeAddresses = data.data.filter(address => address.change).map((address) => {return {address: address.address, program: address.controlProgram}})
 
       this.setState({addresses: normalAddresses, changeAddresses})
+    }).catch(error =>{
+      return
     })
   }
 
index 56c6a99..85cb47c 100644 (file)
@@ -41,10 +41,23 @@ const walletInfo = () => {
   }
 }
 
+const getVoteDetail = () => {
+  return (dispatch) => {
+    return chainClient().accounts.listAccountVotes({})
+      .then(resp => {
+        dispatch({type: 'UPDATE_VOTE_DETAILS', param:resp.data})
+      })
+      .catch((err) => {
+        dispatch({type: 'ERROR', payload: err})
+      })
+  }
+}
+
 let actions = {
   updateInfo,
   rescan,
   walletInfo,
+  getVoteDetail,
   ...baseListActions('balance')
 }
 export default actions
index 24b4481..8a2783e 100644 (file)
@@ -9,6 +9,10 @@ import {withNamespaces} from 'react-i18next'
 const type = 'balance'
 
 class List extends React.Component {
+  componentDidMount() {
+    this.props.getVoteDetail()
+  }
+
   rescanWallet(){
     this.props.showModal(
       <RescanDialog
@@ -22,10 +26,22 @@ class List extends React.Component {
     const newButton = <button key='showRescan' className='btn btn-primary' onClick={() => this.rescanWallet()}>
       {this.props.t('balances.rescan')}
     </button>
+
+    let items
+    if(this.props.items.length !== 0){
+      const mergeById = (a1, a2) =>
+        a1.map(itm => ({
+          ...a2.find((item) => (item.accountId === itm.accountId) && item),
+          ...itm
+        }))
+      items =  this.props.voteDetail.length === 0?  this.props.items: mergeById(this.props.items, this.props.voteDetail)
+    }
+
     return <div>
       <ItemList
         actions={[newButton]}
       {...this.props}
+        items = {items}
       />
     </div>
   }
@@ -36,6 +52,7 @@ const newStateToProps = (state, ownProps) => {
     ...BaseList.mapStateToProps(type, ListItem)(state, ownProps),
     rescanning: state.balance.rescanning,
     rescanProgress: state.balance.rescanProgress,
+    voteDetail: state.balance.voteDetail,
     skipCreate: true
   }
 
@@ -45,6 +62,7 @@ const newStateToProps = (state, ownProps) => {
 const mapDispatchToProps = ( dispatch ) => ({
   rescan: () => dispatch(actions.balance.rescan()),
   info: () => dispatch(actions.balance.walletInfo()),
+  getVoteDetail: () => dispatch(actions.balance.getVoteDetail()),
   showModal: (body) => dispatch(actions.app.showModal(
     body,
     actions.app.hideModal,
index a31a13f..4aaac76 100644 (file)
@@ -1,11 +1,33 @@
 import React from 'react'
 import { KeyValueTable } from 'features/shared/components'
-import { buildBalanceDisplay } from 'utility/buildInOutDisplay'
+import { buildBalanceDisplay, converIntToDec } from 'utility/buildInOutDisplay'
 import {withNamespaces} from 'react-i18next'
+import {btmID} from '../../../utility/environment'
 
 class ListItem extends React.Component {
   render() {
-    return <KeyValueTable items={buildBalanceDisplay(this.props.item, this.props.btmAmountUnit, this.props.t)} />
+    const item  = this.props.item
+    let balanceItem
+    if(item) {
+      const convertAmount = (amount) =>{
+        return (item.assetDefinition && item.assetDefinition.decimals && item.assetId !== btmID) ?
+          converIntToDec(amount, item.assetDefinition.decimals) : amount
+      }
+      let amount = convertAmount(item.amount)
+
+      balanceItem = {
+        amount: amount,
+        assetId: item.assetId,
+        assetAlias: item.assetAlias,
+        accountAlias: item.accountAlias,
+        accountId: item.accountId
+      }
+      if(item.totalVoteAmount){
+        balanceItem.totalVoteAmount = convertAmount(item.totalVoteAmount)
+      }
+    }
+
+    return <KeyValueTable items={buildBalanceDisplay(balanceItem, this.props.btmAmountUnit, this.props.t)} />
   }
 }
 
diff --git a/src/features/balances/components/VoteDetails/VoteDetails.jsx b/src/features/balances/components/VoteDetails/VoteDetails.jsx
new file mode 100644 (file)
index 0000000..5a1a38c
--- /dev/null
@@ -0,0 +1,89 @@
+import React from 'react'
+import { connect } from 'react-redux'
+import { PageContent, PageTitle, Section, BaseShow} from 'features/shared/components'
+import styles from './VoteDetails.scss'
+import {withNamespaces} from 'react-i18next'
+import actions from 'actions'
+import { normalizeGlobalBTMAmount } from 'utility/buildInOutDisplay'
+import { btmID } from 'utility/environment'
+
+class VoteDetails extends BaseShow {
+  constructor(props) {
+    super(props)
+  }
+
+  render() {
+    const t = this.props.t
+    const account = this.props.account
+    const btmAmountUnit = this.props.btmAmountUnit
+
+    let view
+
+
+    if(account){
+      const voteDetails = account.voteDetails
+      const title = <span>
+          {t('balances.voteDetails')}
+        <code>{account.accountAlias ? account.accountAlias : account.accountId}</code>
+        </span>
+
+      let tokenList
+      if(voteDetails){
+        tokenList =
+          <table className={ styles.main }>
+            <thead>
+            <tr>
+              <th>{t('form.vote')}</th><th>{t('form.amount')}</th>
+            </tr>
+            </thead>
+            <tbody>
+            {(voteDetails).map(item =>
+              <tr>
+                <td>{item.vote}</td>
+                <td>{normalizeGlobalBTMAmount(btmID, item.voteAmount, btmAmountUnit)}</td>
+              </tr>
+            )}
+            </tbody>
+          </table>
+
+      }
+
+
+      view =  (
+        <div>
+          <PageTitle title={title} />
+
+          <PageContent>
+            <Section
+              title={t('balances.voteDetails')}
+            >
+              {tokenList}
+            </Section>
+
+
+          </PageContent>
+        </div>
+      )
+    }
+
+    return this.renderIfFound(view)
+  }
+}
+
+
+const mapDispatchToProps = (dispatch) => ({
+  fetchItem: (id) => dispatch(actions.balance.getVoteDetail()),
+})
+
+export default connect(
+  (state, ownProps) => {
+    const generated = (state.balance || {}).voteDetail || []
+    const account = generated.find(i => i.accountId == ownProps.params.id)
+    if (account) return {
+      account,
+      btmAmountUnit: state.core.btmAmountUnit
+    }
+    return {}
+  },
+  mapDispatchToProps
+)(withNamespaces('translations') ( VoteDetails) )
diff --git a/src/features/balances/components/VoteDetails/VoteDetails.scss b/src/features/balances/components/VoteDetails/VoteDetails.scss
new file mode 100644 (file)
index 0000000..1490e72
--- /dev/null
@@ -0,0 +1,48 @@
+.main {
+  background: $background-color;
+  color: $text-strong-color;
+  width: 100%;
+  table-layout: fixed;
+
+  code {
+    padding: 0;
+    font-size: $font-size-code;
+  }
+
+  td {
+    border-top: 1px solid $border-light-color;
+    word-wrap: break-word;
+    color: $text-color;
+    line-height: 20px;
+    vertical-align: top;
+  }
+
+  th {
+    color: $text-strong-color;
+    text-transform: uppercase;
+    font-weight: 500;
+    font-size: $font-size-chrome;
+  }
+
+  td, th  {
+    padding: 13px $gutter-size 13px 0;
+  }
+  th, td {
+
+    &:first-child {
+      padding-left: $gutter-size * 2;
+    }
+
+    &:last-child {
+      width: 30%;
+    }
+  }
+
+  :global {
+    .btn-link {
+      padding-top: 0;
+      padding-bottom: 0;
+      line-height: 1;
+    }
+  }
+}
index ac005ec..f14e1d5 100644 (file)
@@ -1,5 +1,7 @@
 import List from './List'
+import VoteDetails from './VoteDetails/VoteDetails'
 
 export {
   List,
+  VoteDetails
 }
index 007cb65..359cfa6 100644 (file)
@@ -33,6 +33,13 @@ const rescanProgress = (state = {}, action) => {
   return state
 }
 
+const voteDetail = (state = [], action) => {
+  if (action.type == 'UPDATE_VOTE_DETAILS') {
+    return action.param
+  }
+  return state
+}
+
 
 const rescanning = (state = {}, action) => {
   if (action.type == 'START_RESCAN') return true
@@ -45,5 +52,6 @@ export default combineReducers({
   items: itemsReducer,
   queries: queriesReducer,
   rescanning,
-  rescanProgress
+  rescanProgress,
+  voteDetail
 })
index 71f11fb..dd5e588 100644 (file)
@@ -1,4 +1,11 @@
-import { List } from './components'
+import { List , VoteDetails } from './components'
 import { makeRoutes } from 'features/shared'
 
-export default (store) => makeRoutes(store, 'balance', List)
+export default (store) => makeRoutes(store, 'balance', List, null, null,  {
+  childRoutes: [
+    {
+      path: 'vote/:id',
+      component: VoteDetails,
+    },
+  ]
+})
index ad0e817..95b8bc0 100644 (file)
@@ -28,7 +28,9 @@ export default function(type, options = {}) {
             dispatch(receive(resp))
           }
         }
-      )
+      ).catch(error=>{
+        dispatch({type: 'ERROR', payload: { 'message': error.msg}})
+      })
 
       return promise
     }
index 74b558f..679c71c 100644 (file)
@@ -4,13 +4,16 @@ import NotFound from './NotFound'
 export default class BaseShow extends React.Component {
   constructor(props) {
     super(props)
-
     this.state = {}
   }
 
   componentDidMount() {
     this.props.fetchItem(this.props.params.id).then(resp => {
-      if (resp.status == 'fail') {
+      if (resp && ((resp.status == 'fail') || (resp.data && resp.data.length === 0))) {
+        this.setState({notFound: true})
+      }
+    }).catch(error =>{
+      if(error.status === 'fail'){
         this.setState({notFound: true})
       }
     })
index dd71d03..a479691 100644 (file)
@@ -62,6 +62,9 @@ class KeyValueTable extends React.Component {
                   <span
                     className={`${styles.pencil} glyphicon glyphicon-pencil`}></span>{t('form.edit')}
                 </Link>}
+                {item.details && <Link to={item.details} className={styles.edit}>
+                 {t('form.detail')}
+                </Link>}
                 {item.program && <button  onClick={item.program} className={`${styles.detail} ${styles.edit} btn btn-link`}>
                   { t('commonWords.program')}
                 </button>}
index 7065c30..b264474 100644 (file)
@@ -113,25 +113,14 @@ class NormalTxForm extends React.Component {
     const actions = normalTxActionBuilder(transaction, Math.pow(10, 7), 'amount.value' )
 
     const body = {actions, ttl: 1}
-    this.connection.request('/build-transaction', body).then(resp => {
-      if (resp.status === 'fail') {
-        this.setState({estimateGas: null})
-        const errorMsg =  resp.code && i18n.exists(`btmError.${resp.code}`) && t(`btmError.${resp.code}`) || resp.msg
-        this.props.showError(new Error(errorMsg))
-        return
-      }
-
-      return this.connection.request('/estimate-transaction-gas', {
-        transactionTemplate: resp.data
-      }).then(resp => {
-        if (resp.status === 'fail') {
-          this.setState({estimateGas: null})
-          const errorMsg =  resp.code && i18n.exists(`btmError.${resp.code}`) && t(`btmError.${resp.code}`) || resp.msg
-          this.props.showError(new Error(errorMsg))
-          return
-        }
+    this.props.buildTransaction(body).then(resp => {
+      return this.props.estimateGasFee(resp.data).then(resp => {
         this.setState({estimateGas: Math.ceil(resp.data.totalNeu/100000)*100000})
       })
+    }).catch(err =>{
+      this.setState({estimateGas: null, address: null})
+      const errorMsg =  err.code && i18n.exists(`btmError.${err.code}`) && t(`btmError.${err.code}`) || err.msg
+      this.props.showError(new Error(errorMsg))
     })
   }
 
@@ -307,6 +296,8 @@ const mapDispatchToProps = (dispatch) => ({
       noCloseBtn: true
     }
   )),
+  estimateGasFee: actions.transaction.estimateGas,
+  buildTransaction: actions.transaction.buildTransaction,
   ...BaseNew.mapDispatchToProps('transaction')(dispatch)
 })
 
index 7349cfa..6b747d9 100644 (file)
     "reissueTitle":"Token reissuable",
     "reissueTrue":"Able to be reissued",
     "reissueFalse":"Unable to be reissued",
-    "vote": "Node Public Key"
+    "vote": "Node Public Key",
+    "totalVoteAmount":"Vote"
   },
   "xpub":{
     "methodOptions" : {
       "successMsg":"Successfully rescanned spent outputs.",
       "startMsg":"Rescan is starting...",
       "rescanMsg":"Wallet Rescanning..."
-    }
+    },
+    "voteDetails":"Vote Details"
   },
   "errorMessage":{
     "password": "Your password is wrong, please check your password.",
index 3405cff..7894580 100644 (file)
     "reissueTitle":"是否可重复发行",
     "reissueTrue":"可重复发行",
     "reissueFalse":"不可重复发行",
-    "vote": "节点公钥"
+    "vote": "节点公钥",
+    "totalVoteAmount":"投票"
   },
   "xpub":{
     "methodOptions" : {
       "successMsg":"已经成功扫描资产余额。",
       "startMsg":"扫描正在启动...",
       "rescanMsg":"钱包正在扫描中..."
-    }
+    },
+    "voteDetails":"投票详情"
   },
   "errorMessage":{
     "password": "密码错误,请重新查看密码。",
index ebe46a4..f06effc 100644 (file)
@@ -35,7 +35,9 @@ const accountsAPI = (client) => {
 
     listAddresses: (params) => shared.query(client, 'accounts', '/list-addresses', params),
 
-    validateAddresses: (address, cb) => shared.query(client, 'accounts', '/validate-address', {'address': address},  {cb})
+    validateAddresses: (address, cb) => shared.query(client, 'accounts', '/validate-address', {'address': address},  {cb}),
+
+    listAccountVotes: (params, cb) => shared.query(client, 'accounts', '/list-account-votes', params, {cb}),
   }
 }
 
index 3715ad0..3477690 100644 (file)
@@ -77,7 +77,7 @@ module.exports = {
 
   query: (client, memberPath, path, params = {}, opts = {}) => {
     return tryCallback(
-      client.request(path, params).then(data => new Page(data, client, memberPath)),
+      client.request(path, params).then(data => new Page(data, client, memberPath)).catch(error => {throw error} ),
       opts.cb
     )
   },
index bf7c6e7..9760e05 100644 (file)
@@ -13,7 +13,7 @@ const balanceFields = [
   'assetTags' ,
   'assetIsLocal' ,
   'amount' ,
-  'accountId' ,
+  'totalVoteAmount',
   'accountAlias' ,
   'account' ,
   'accountTags' ,
@@ -80,11 +80,17 @@ const buildDisplay = (item, fields, btmAmountUnit, t) => {
     item.assetDefinition.decimals: null
   fields.forEach(key => {
     if (item.hasOwnProperty(key)) {
-      if(key === 'amount'){
+      if(key === 'amount' ){
         details.push({
           label: t(`form.${key}`),
           value: decimals? formatIntNumToPosDecimal(item[key], decimals) :normalizeGlobalBTMAmount(item['assetId'], item[key], btmAmountUnit)
         })
+      }else if(key === 'totalVoteAmount' ){
+        details.push({
+          label: t(`form.${key}`),
+          value: decimals? formatIntNumToPosDecimal(item[key], decimals) :normalizeGlobalBTMAmount(item['assetId'], item[key], btmAmountUnit),
+          details:`/balances/vote/${item.accountId}`
+        })
       }else if(key === 'asset' && item.assetId !=='0000000000000000000000000000000000000000000000000000000000000000'){
         details.push({
           label:  t(`form.${key}`),
@@ -262,12 +268,5 @@ export function buildUnspentDisplay(output, btmAmountUnit, t) {
 }
 
 export function buildBalanceDisplay(balance, btmAmountUnit, t) {
-  let amount = (balance.assetDefinition && balance.assetDefinition.decimals && balance.assetId !== btmID)?
-    formatIntNumToPosDecimal(balance.amount, balance.assetDefinition.decimals): balance.amount
-  return buildDisplay({
-    amount: amount,
-    assetId: balance.assetId,
-    assetAlias: balance.assetAlias,
-    accountAlias: balance.accountAlias
-  }, balanceFields, btmAmountUnit, t)
+  return buildDisplay(balance, balanceFields, btmAmountUnit, t)
 }