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
})
}
}
}
+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
const type = 'balance'
class List extends React.Component {
+ componentDidMount() {
+ this.props.getVoteDetail()
+ }
+
rescanWallet(){
this.props.showModal(
<RescanDialog
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>
}
...BaseList.mapStateToProps(type, ListItem)(state, ownProps),
rescanning: state.balance.rescanning,
rescanProgress: state.balance.rescanProgress,
+ voteDetail: state.balance.voteDetail,
skipCreate: true
}
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,
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)} />
}
}
--- /dev/null
+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) )
--- /dev/null
+.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;
+ }
+ }
+}
import List from './List'
+import VoteDetails from './VoteDetails/VoteDetails'
export {
List,
+ VoteDetails
}
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
items: itemsReducer,
queries: queriesReducer,
rescanning,
- rescanProgress
+ rescanProgress,
+ voteDetail
})
-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,
+ },
+ ]
+})
dispatch(receive(resp))
}
}
- )
+ ).catch(error=>{
+ dispatch({type: 'ERROR', payload: { 'message': error.msg}})
+ })
return promise
}
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})
}
})
<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>}
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))
})
}
noCloseBtn: true
}
)),
+ estimateGasFee: actions.transaction.estimateGas,
+ buildTransaction: actions.transaction.buildTransaction,
...BaseNew.mapDispatchToProps('transaction')(dispatch)
})
"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.",
"reissueTitle":"是否可重复发行",
"reissueTrue":"可重复发行",
"reissueFalse":"不可重复发行",
- "vote": "节点公钥"
+ "vote": "节点公钥",
+ "totalVoteAmount":"投票"
},
"xpub":{
"methodOptions" : {
"successMsg":"已经成功扫描资产余额。",
"startMsg":"扫描正在启动...",
"rescanMsg":"钱包正在扫描中..."
- }
+ },
+ "voteDetails":"投票详情"
},
"errorMessage":{
"password": "密码错误,请重新查看密码。",
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}),
}
}
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
)
},
'assetTags' ,
'assetIsLocal' ,
'amount' ,
- 'accountId' ,
+ 'totalVoteAmount',
'accountAlias' ,
'account' ,
'accountTags' ,
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}`),
}
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)
}