finish staking list
This commit is contained in:
parent
e02cac6154
commit
df6a20ee67
@ -12,7 +12,14 @@
|
||||
class="d-flex align-items-center"
|
||||
@click="() => updateGroupOpen(!isOpen)"
|
||||
>
|
||||
<feather-icon :icon="item.icon || 'CircleIcon'" />
|
||||
<b-avatar
|
||||
variant="info"
|
||||
:src="item.icon"
|
||||
icon="people-fill"
|
||||
size="sm"
|
||||
class="mr-1"
|
||||
/>
|
||||
|
||||
<span class="menu-title text-truncate">{{ t(item.title) }}</span>
|
||||
<b-badge
|
||||
v-if="item.tag"
|
||||
@ -40,7 +47,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BLink, BBadge, BCollapse } from 'bootstrap-vue'
|
||||
import {
|
||||
BLink, BBadge, BCollapse, BAvatar,
|
||||
} from 'bootstrap-vue'
|
||||
import { resolveVerticalNavMenuItemComponent as resolveNavItemComponent } from '@core/layouts/utils'
|
||||
import { useUtils as useI18nUtils } from '@core/libs/i18n'
|
||||
import { useUtils as useAclUtils } from '@core/libs/acl'
|
||||
@ -56,6 +65,7 @@ export default {
|
||||
components: {
|
||||
VerticalNavMenuHeader,
|
||||
VerticalNavMenuLink,
|
||||
BAvatar,
|
||||
BLink,
|
||||
BBadge,
|
||||
BCollapse,
|
||||
|
@ -11,8 +11,8 @@
|
||||
v-bind="linkProps"
|
||||
class="d-flex align-items-center"
|
||||
>
|
||||
<feather-icon :icon="item.icon || 'CircleIcon'" />
|
||||
<span class="menu-title text-truncate">{{ t(item.title) }}</span>
|
||||
<feather-icon :icon="item.icon || 'ChevronRightIcon'" />
|
||||
<span class="text-truncate">{{ t(item.title) }}</span>
|
||||
<b-badge
|
||||
v-if="item.tag"
|
||||
pill
|
||||
|
@ -9,6 +9,20 @@
|
||||
"staking": "Staking",
|
||||
"governance": "Governance",
|
||||
"summary": "Summary",
|
||||
"blockchains": "Blockchains"
|
||||
"blockchains": "Blockchains",
|
||||
|
||||
"proposal_id": "Proposal ID",
|
||||
"proposal_type": "Proposal Type",
|
||||
"proposal_proposer": "Proposer",
|
||||
"proposal_submit_time": "Submited Time",
|
||||
"proposal_voting_start_time": "Voting Begin Time",
|
||||
"proposal_voting_end_time": "Voting End Time",
|
||||
"proposal_description": "Description",
|
||||
"proposal_content": "Content",
|
||||
"proposal_total_deposit": "Total Deposit",
|
||||
|
||||
"btn_vote": "Vote",
|
||||
"btn_detail": "Detail",
|
||||
"btn_back_list": "Back To List"
|
||||
|
||||
}
|
||||
|
@ -33,7 +33,7 @@
|
||||
<h6 class="mb-0 text-uppercase">
|
||||
{{ selected_chain.chain_name }}
|
||||
</h6>
|
||||
<small v-b-tooltip.hover.bottom="'Data Provider'">{{ selected_chain.api }}</small>
|
||||
<small v-b-tooltip.hover.bottom="'Data Provider'">{{ selected_chain.api }} {{ selected_chain.sdk_version }}</small>
|
||||
</b-media-body>
|
||||
</b-media>
|
||||
</div>
|
||||
@ -94,11 +94,10 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
selected_chain() {
|
||||
const c = this.$route.params.chain
|
||||
const has = Object.keys(store.state.chains.chains).findIndex(i => i === c)
|
||||
const selected = (has > -1) ? store.state.chains.chains[c] : store.state.chains.chains.cosmos
|
||||
localStorage.setItem('selected_chain', selected)
|
||||
return selected
|
||||
// const c = this.$route.params.chain
|
||||
// const has = Object.keys(store.state.chains.config).findIndex(i => i === c)
|
||||
// const selected = (has > -1) ? store.state.chains.config[c] : store.state.chains.config.cosmos
|
||||
return store.state.chains.selected
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<layout-vertical>
|
||||
|
||||
<router-view />
|
||||
<router-view :key="$route.fullPath" />
|
||||
|
||||
<template #navbar="{ toggleVerticalMenuActive }">
|
||||
<navbar :toggle-vertical-menu-active="toggleVerticalMenuActive" />
|
||||
|
@ -1,16 +1,14 @@
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
export function toDay(time) {
|
||||
return dayjs(time).format('YYYY-MM-DD')
|
||||
return dayjs(time).format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
|
||||
export function percent(num) {
|
||||
return (num * 100).toFixed(2)
|
||||
return parseFloat((num * 100).toFixed(2))
|
||||
}
|
||||
|
||||
export function formatToken(token) {
|
||||
// token.denom
|
||||
// token.amount
|
||||
let denom = token.denom.toUpperCase()
|
||||
if (denom.charAt(0) === 'U') {
|
||||
denom = denom.substring(1)
|
||||
@ -18,6 +16,13 @@ export function formatToken(token) {
|
||||
return `${(token.amount / 1000000).toFixed()} ${denom}`
|
||||
}
|
||||
|
||||
export function tokenFormatter(tokens) {
|
||||
if (Array.isArray(tokens)) {
|
||||
return tokens.map(t => formatToken(t))
|
||||
}
|
||||
return formatToken(tokens)
|
||||
}
|
||||
|
||||
export * from 'compare-versions'
|
||||
|
||||
export class Data {
|
||||
|
18
src/libs/data/deposit.js
Normal file
18
src/libs/data/deposit.js
Normal file
@ -0,0 +1,18 @@
|
||||
export default class Deposit {
|
||||
constructor() {
|
||||
this.proposal_id = ''
|
||||
this.depositor = ''
|
||||
this.amount = ''
|
||||
}
|
||||
|
||||
init(element) {
|
||||
this.proposal_id = element.proposal_id
|
||||
this.depositor = element.depositor
|
||||
this.amount = element.amount
|
||||
return this
|
||||
}
|
||||
|
||||
debug() {
|
||||
return console.log(this)
|
||||
}
|
||||
}
|
@ -1,5 +1,10 @@
|
||||
export { default as Proposal } from './proposal'
|
||||
export { default as ProposalTally } from './proposal-tally'
|
||||
export { default as StakingPool } from './staking-pool'
|
||||
export { default as Proposer } from './proposer'
|
||||
export { default as Votes } from './votes'
|
||||
export { default as Deposit } from './deposit'
|
||||
export { default as Validator } from './validator'
|
||||
export * from './data'
|
||||
|
||||
export default class Test {}
|
||||
|
@ -1,14 +1,29 @@
|
||||
import { percent } from './data'
|
||||
|
||||
export default class ProposalTally {
|
||||
constructor(element, total) {
|
||||
this.yes = percent(Number(element.yes) / total)
|
||||
this.no = percent(Number(element.no) / total)
|
||||
this.veto = percent(Number(element.no_with_veto) / total)
|
||||
this.abstain = percent(Number(element.abstain) / total)
|
||||
this.turnout = percent((Number(element.yes) + Number(element.no) + Number(element.abstain) + Number(element.no_with_veto)) / total)
|
||||
constructor() {
|
||||
this.total = 0
|
||||
this.yes = 0
|
||||
this.no = 0
|
||||
this.veto = 0
|
||||
this.abstain = 0
|
||||
this.turnout = 0
|
||||
}
|
||||
|
||||
init(element, total) {
|
||||
const subtotal = Number(element.yes) + Number(element.no) + Number(element.abstain) + Number(element.no_with_veto)
|
||||
if (total < 1) {
|
||||
this.total = subtotal + 1
|
||||
} else {
|
||||
this.total = total
|
||||
}
|
||||
this.yes = percent(Number(element.yes) / this.total)
|
||||
this.no = percent(Number(element.no) / this.total)
|
||||
this.veto = percent(Number(element.no_with_veto) / this.total)
|
||||
this.abstain = percent(Number(element.abstain) / this.total)
|
||||
this.turnout = percent(subtotal / this.total)
|
||||
return this
|
||||
}
|
||||
|
||||
total() {
|
||||
return this.total
|
||||
|
@ -1,31 +1,43 @@
|
||||
import compareVersions from 'compare-versions'
|
||||
import { toDay, formatToken, percent } from './data'
|
||||
import { toDay, formatToken } from './data'
|
||||
import ProposalTally from './proposal-tally'
|
||||
|
||||
export default class Proposal {
|
||||
constructor(element, total) {
|
||||
constructor() {
|
||||
this.element = null
|
||||
|
||||
this.id = 0
|
||||
this.status = 1
|
||||
this.type = '-'
|
||||
this.title = '-'
|
||||
this.description = '-'
|
||||
this.tally = new ProposalTally()
|
||||
this.submit_time = ' - '
|
||||
this.voting_end_time = '0000-00-00'
|
||||
this.voting_start_time = '-'
|
||||
this.total_deposit = '-'
|
||||
this.contents = null
|
||||
}
|
||||
|
||||
init(element, total) {
|
||||
this.element = element
|
||||
const yes = Number(element.final_tally_result.yes)
|
||||
const no = Number(element.final_tally_result.no)
|
||||
const abstain = Number(element.final_tally_result.abstain)
|
||||
const veto = Number(element.final_tally_result.no_with_veto)
|
||||
|
||||
this.id = element.id
|
||||
this.status = element.status
|
||||
this.type = element.content.type
|
||||
this.title = element.content.value.title
|
||||
this.description = element.content.value.description
|
||||
this.tally = {
|
||||
yes: percent(yes / total),
|
||||
no: percent(no / total),
|
||||
abstain: percent(abstain / total),
|
||||
veto: percent(veto / total),
|
||||
turnout: percent((yes + no + abstain + veto) / total),
|
||||
total,
|
||||
}
|
||||
this.tally = new ProposalTally().init(element.final_tally_result, total)
|
||||
this.submit_time = toDay(element.submit_time)
|
||||
this.voting_end_time = toDay(element.voting_end_time)
|
||||
this.voting_start_time = toDay(element.voting_start_time)
|
||||
this.total_deposit = formatToken(element.total_deposit[0])
|
||||
this.contents = element.content.value
|
||||
return this
|
||||
}
|
||||
|
||||
hello() {
|
||||
return this.title
|
||||
}
|
||||
|
||||
updateTally(newTally) {
|
||||
@ -33,7 +45,6 @@ export default class Proposal {
|
||||
}
|
||||
|
||||
versionFixed(ver) {
|
||||
console.log(ver, compareVersions(ver, '0.4'))
|
||||
if (compareVersions(ver, '0.40') >= 0) {
|
||||
// do nothing
|
||||
} else if (compareVersions(ver, '0.37') >= 0) {
|
||||
@ -50,7 +61,6 @@ export default class Proposal {
|
||||
default:
|
||||
this.status = 1
|
||||
}
|
||||
console.log(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
src/libs/data/proposer.js
Normal file
18
src/libs/data/proposer.js
Normal file
@ -0,0 +1,18 @@
|
||||
export default class Proposer {
|
||||
constructor() {
|
||||
this.proposal_id = ''
|
||||
this.proposer = ''
|
||||
}
|
||||
|
||||
init(element) {
|
||||
if (element != null) {
|
||||
this.proposal_id = element.proposal_id
|
||||
this.proposer = element.proposer
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
debug() {
|
||||
return console.log(this)
|
||||
}
|
||||
}
|
@ -1,10 +1,17 @@
|
||||
import { percent } from './data'
|
||||
|
||||
export default class StakingPool {
|
||||
constructor(element) {
|
||||
constructor() {
|
||||
this.element = null
|
||||
this.bondedToken = 0
|
||||
this.notBondedToken = 0
|
||||
}
|
||||
|
||||
init(element) {
|
||||
this.element = element
|
||||
this.bondedToken = Number(element.bonded_tokens)
|
||||
this.notBondedToken = Number(element.not_bonded_tokens)
|
||||
return this
|
||||
}
|
||||
|
||||
total() {
|
||||
@ -12,6 +19,6 @@ export default class StakingPool {
|
||||
}
|
||||
|
||||
bondedRatio() {
|
||||
return percent((this.bondedToken * 100) / this.total)
|
||||
return percent((this.bondedToken * 100) / this.total())
|
||||
}
|
||||
}
|
||||
|
24
src/libs/data/valdiator-description.js
Normal file
24
src/libs/data/valdiator-description.js
Normal file
@ -0,0 +1,24 @@
|
||||
export default class ValidatorDescription {
|
||||
constructor() {
|
||||
this.moniker = 'string'
|
||||
this.identity = 'string'
|
||||
this.website = 'string'
|
||||
this.security_contact = 'string'
|
||||
this.details = 'string'
|
||||
}
|
||||
|
||||
init(element) {
|
||||
if (element != null) {
|
||||
this.moniker = element.moniker
|
||||
this.identity = element.identity
|
||||
this.website = element.website
|
||||
this.security_contact = element.security_contact
|
||||
this.details = element.details
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
debug() {
|
||||
return console.log(this)
|
||||
}
|
||||
}
|
22
src/libs/data/validator-commission.js
Normal file
22
src/libs/data/validator-commission.js
Normal file
@ -0,0 +1,22 @@
|
||||
export default class ValidatorCommission {
|
||||
constructor() {
|
||||
this.rate = '0'
|
||||
this.max_rate = '0'
|
||||
this.max_change_rate = '0'
|
||||
this.update_time = '1970-01-01T00:00:00Z'
|
||||
}
|
||||
|
||||
init(element) {
|
||||
if (element != null) {
|
||||
this.rate = element.commission_rates.rate
|
||||
this.max_rate = element.commission_rates.max_rate
|
||||
this.max_change_rate = element.commission_rates.max_change_rate
|
||||
this.update_time = element.update_time
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
debug() {
|
||||
return console.log(this)
|
||||
}
|
||||
}
|
35
src/libs/data/validator.js
Normal file
35
src/libs/data/validator.js
Normal file
@ -0,0 +1,35 @@
|
||||
import ValidatorCommission from './validator-commission'
|
||||
import ValidatorDescription from './valdiator-description'
|
||||
|
||||
export default class Validator {
|
||||
constructor() {
|
||||
this.operator_address = 'cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l'
|
||||
this.consensus_pubkey = 'cosmosvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cs3k9cuf'
|
||||
this.jailed = true
|
||||
this.status = 0
|
||||
this.tokens = '-'
|
||||
this.delegator_shares = 0
|
||||
this.description = new ValidatorDescription()
|
||||
this.bond_height = 0
|
||||
this.bond_intra_tx_counter = 0
|
||||
this.unbonding_height = 0
|
||||
this.unbonding_time = '1970-01-01T00:00:00Z'
|
||||
this.commission = new ValidatorCommission()
|
||||
}
|
||||
|
||||
init(element) {
|
||||
this.operator_address = element.operator_address
|
||||
this.consensus_pubkey = element.consensus_pubkey
|
||||
this.jailed = element.jailed
|
||||
this.status = element.status
|
||||
this.tokens = element.tokens
|
||||
this.delegator_shares = element.delegator_shares
|
||||
this.description = new ValidatorDescription().init(element.description)
|
||||
this.bond_height = element.bond_height
|
||||
this.bond_intra_tx_counter = element.bond_intra_tx_counter
|
||||
this.unbonding_height = element.unbonding_height
|
||||
this.unbonding_time = element.unbonding_time
|
||||
this.commission = new ValidatorCommission().init(element.commission)
|
||||
return this
|
||||
}
|
||||
}
|
18
src/libs/data/votes.js
Normal file
18
src/libs/data/votes.js
Normal file
@ -0,0 +1,18 @@
|
||||
export default class Votes {
|
||||
constructor() {
|
||||
this.proposal_id = 0
|
||||
this.voter = ''
|
||||
this.option = ''
|
||||
}
|
||||
|
||||
init(element) {
|
||||
this.proposal_id = element.proposal_id
|
||||
this.voter = element.voter
|
||||
this.option = element.option
|
||||
return this
|
||||
}
|
||||
|
||||
debug() {
|
||||
return console.log(this)
|
||||
}
|
||||
}
|
@ -1,50 +1,67 @@
|
||||
import fetch from 'node-fetch'
|
||||
import store from '@/store'
|
||||
import DefaultHandler from '@/libs/default-handler'
|
||||
import { Proposal, ProposalTally, StakingPool } from './data'
|
||||
|
||||
const requestVersion = async (c, url) => {
|
||||
const response = await fetch(url)
|
||||
const json = await response.json()
|
||||
const sdk = json.application_version.build_deps.find(e => e.startsWith('github.com/cosmos/cosmos-sdk'))
|
||||
const re = /(\d+(\.\d+)*)/i
|
||||
const version = sdk.match(re)
|
||||
console.log(`${c} sdk version:`, version[0])
|
||||
store.commit('setup_sdk_version', { chain_name: c, version: version[0] })
|
||||
return version[0]
|
||||
}
|
||||
import {
|
||||
Proposal, ProposalTally, Proposer, StakingPool, Votes, Deposit,
|
||||
Validator,
|
||||
} from './data'
|
||||
|
||||
function commonProcess(res) {
|
||||
return res.result
|
||||
}
|
||||
|
||||
// 头像
|
||||
export async function keybase(identity) {
|
||||
return fetch(`https://keybase.io/_/api/1.0/user/lookup.json?key_suffix=${identity}&fields=pictures`)
|
||||
.then(res => res.json())
|
||||
}
|
||||
|
||||
const chainAPI = class ChainFetch {
|
||||
constructor(route) {
|
||||
const c = route.params.chain
|
||||
const has = Object.keys(store.state.chains.chains).findIndex(i => i === c)
|
||||
const selected = (has > -1) ? store.state.chains.chains[c] : store.state.chains.chains.cosmos
|
||||
if (selected.sdk_version === undefined) {
|
||||
const v = requestVersion(c, `${selected.api}/node_info`)
|
||||
selected.sdk_version = v
|
||||
}
|
||||
this.config = selected
|
||||
this.handler = new DefaultHandler()
|
||||
getSelectedConfig() {
|
||||
this.config = store.state.chains.selected
|
||||
return this.config
|
||||
}
|
||||
|
||||
async getStakingPool() {
|
||||
return this.get('/staking/pool').then(data => new StakingPool(commonProcess(data)))
|
||||
return this.get('/staking/pool').then(data => new StakingPool().init(commonProcess(data)))
|
||||
}
|
||||
|
||||
async getValidatorList() {
|
||||
return this.get('/staking/validators').then(data => commonProcess(data).map(i => new Validator().init(i)))
|
||||
}
|
||||
|
||||
async getGovernanceTally(pid, total) {
|
||||
return this.get(`/gov/proposals/${pid}/tally`).then(data => new ProposalTally(commonProcess(data), total))
|
||||
return this.get(`/gov/proposals/${pid}/tally`).then(data => new ProposalTally().init(commonProcess(data), total))
|
||||
}
|
||||
|
||||
getGovernance(pid) {
|
||||
return this.get(`/gov/proposals/${pid}`).then(data => {
|
||||
const p = new Proposal().init(commonProcess(data), 0)
|
||||
p.versionFixed(this.config.sdk_version)
|
||||
return p
|
||||
})
|
||||
}
|
||||
|
||||
async getGovernanceProposer(pid) {
|
||||
return this.get(`/gov/proposals/${pid}/proposer`).then(data => new Proposer().init(commonProcess(data))).catch(e => console.log(e))
|
||||
}
|
||||
|
||||
async getGovernanceDeposits(pid) {
|
||||
return this.get(`/gov/proposals/${pid}/deposits`).then(data => {
|
||||
const result = commonProcess(data)
|
||||
return Array.isArray(result) ? result.reverse().map(d => new Deposit().init(d)) : result
|
||||
})
|
||||
}
|
||||
|
||||
async getGovernanceVotes(pid) {
|
||||
return this.get(`/gov/proposals/${pid}/votes`).then(data => commonProcess(data).map(d => new Votes().init(d)))
|
||||
}
|
||||
|
||||
async getGovernanceList() {
|
||||
return Promise.all([this.get('/gov/proposals'), this.get('/staking/pool')]).then(data => {
|
||||
const pool = new StakingPool(commonProcess(data[1]))
|
||||
const pool = new StakingPool().init(commonProcess(data[1]))
|
||||
const ret = []
|
||||
commonProcess(data[0]).forEach(e => {
|
||||
const g = new Proposal(e, pool.bondedToken)
|
||||
const g = new Proposal().init(e, pool.bondedToken)
|
||||
g.versionFixed(this.config.sdk_version)
|
||||
ret.push(g)
|
||||
})
|
||||
@ -53,8 +70,9 @@ const chainAPI = class ChainFetch {
|
||||
}
|
||||
|
||||
async get(url) {
|
||||
const response = await fetch(this.config.api + url)
|
||||
return response.json()
|
||||
this.getSelectedConfig()
|
||||
const ret = await fetch(this.config.api + url).then(response => response.json()).catch(e => console.log(e))
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,13 +6,14 @@ import messages from '@/lang'
|
||||
|
||||
import VueI18n from 'vue-i18n'
|
||||
|
||||
import ChainAPI from '@/libs/fetch'
|
||||
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import App from './App.vue'
|
||||
|
||||
// Global Components
|
||||
import './global-components'
|
||||
import '@/libs/fetch'
|
||||
|
||||
// 3rd party plugins
|
||||
import '@/libs/portal-vue'
|
||||
@ -41,6 +42,7 @@ require('@core/scss/core.scss')
|
||||
require('@/assets/scss/style.scss')
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.prototype.$http = new ChainAPI()
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
|
@ -29,10 +29,10 @@ function processMenu() {
|
||||
header: 'blockchains',
|
||||
},
|
||||
]
|
||||
Object.keys(store.state.chains.chains).forEach(chain => {
|
||||
Object.keys(store.state.chains.config).forEach(chain => {
|
||||
const menu = {
|
||||
title: chain,
|
||||
icon: 'PieChartIcon',
|
||||
icon: store.state.chains.config[chain].logo,
|
||||
}
|
||||
const children = []
|
||||
modules.forEach(m => {
|
||||
|
@ -1,5 +1,7 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import fetch from 'node-fetch'
|
||||
import store from '../store'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
@ -24,20 +26,6 @@ const router = new VueRouter({
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/second-page',
|
||||
name: 'second-page',
|
||||
component: () => import('@/views/Info.vue'),
|
||||
meta: {
|
||||
pageTitle: 'Second Page',
|
||||
breadcrumb: [
|
||||
{
|
||||
text: 'Second Page',
|
||||
active: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// chain modules
|
||||
{
|
||||
path: '/:chain/info',
|
||||
@ -79,6 +67,28 @@ const router = new VueRouter({
|
||||
text: 'Governance',
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
text: 'Proposals',
|
||||
active: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/:chain/gov/:proposalid',
|
||||
name: 'proposal',
|
||||
component: () => import('@/views/ProposalView.vue'),
|
||||
meta: {
|
||||
pageTitle: 'Governance',
|
||||
breadcrumb: [
|
||||
{
|
||||
text: 'Governance',
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
text: 'Proposal Detail',
|
||||
active: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -105,6 +115,26 @@ const router = new VueRouter({
|
||||
],
|
||||
})
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const c = to.params.chain
|
||||
const has = Object.keys(store.state.chains.config).findIndex(i => i === c)
|
||||
if (has > -1) {
|
||||
const chain = store.state.chains.config[c]
|
||||
store.commit('select', { chain_name: c })
|
||||
if (chain.sdk_version === undefined) {
|
||||
fetch(`${chain.api}/node_info`)
|
||||
.then(res => res.json())
|
||||
.then(json => {
|
||||
const sdk = json.application_version.build_deps.find(e => e.startsWith('github.com/cosmos/cosmos-sdk'))
|
||||
const re = /(\d+(\.\d+)*)/i
|
||||
const version = sdk.match(re)
|
||||
store.commit('setup_sdk_version', { chain_name: c, version: version[0] })
|
||||
})
|
||||
}
|
||||
}
|
||||
next()
|
||||
})
|
||||
|
||||
// ? For splash screen
|
||||
// Remove afterEach hook if you are not using splash screen
|
||||
router.afterEach(() => {
|
||||
|
7
src/store/chains/crypto.json
Normal file
7
src/store/chains/crypto.json
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
{
|
||||
"chain_name": "crypto",
|
||||
"chain_id": "akashnet-1",
|
||||
"api": "https://mainnet.crypto.org:1317",
|
||||
"logo": "https://look.ping.pub/static/chains/cosmoshub.svg"
|
||||
}
|
@ -9,14 +9,18 @@ configs.keys().forEach(k => {
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
chains,
|
||||
config: chains,
|
||||
selected: {},
|
||||
},
|
||||
getters: {
|
||||
getchains: state => state.chains,
|
||||
},
|
||||
mutations: {
|
||||
setup_sdk_version(state, info) {
|
||||
state.chains.chains[info.chain_name].sdk_version = info.version
|
||||
state.chains.config[info.chain_name].sdk_version = info.version
|
||||
},
|
||||
select(state, args) {
|
||||
state.chains.selected = state.chains.config[args.chain_name]
|
||||
},
|
||||
|
||||
},
|
||||
|
@ -37,7 +37,7 @@
|
||||
>
|
||||
Rejected
|
||||
</b-badge>
|
||||
#{{ p.id }}. {{ p.title }}
|
||||
#{{ p.id }}. <a :href="`./gov/${p.id}`">{{ p.title }}</a>
|
||||
</b-card-title>
|
||||
<b-card-body md="12">
|
||||
<div class="gov-wrapper">
|
||||
@ -57,14 +57,6 @@
|
||||
{{ p.voting_end_time }}
|
||||
</h6>
|
||||
</div>
|
||||
<!-- <div class="gov">
|
||||
<p class="card-text mb-25">
|
||||
Proposer
|
||||
</p>
|
||||
<h6 class="mb-0">
|
||||
Ping.pub
|
||||
</h6>
|
||||
</div> -->
|
||||
<div class="gov">
|
||||
<p class="card-text mb-25">
|
||||
Deposit
|
||||
@ -136,17 +128,26 @@
|
||||
{{ p.tally.abstain }}% voted Abstain
|
||||
</b-tooltip>
|
||||
<b-card-footer>
|
||||
<router-link
|
||||
v-ripple.400="'rgba(113, 102, 240, 0.15)'"
|
||||
:to="`./gov/${p.id}`"
|
||||
variant="outline-primary"
|
||||
class="btn"
|
||||
>
|
||||
<b-button
|
||||
v-ripple.400="'rgba(113, 102, 240, 0.15)'"
|
||||
:href="`./gov/${p.id}`"
|
||||
variant="outline-primary"
|
||||
>
|
||||
Detail
|
||||
{{ $t('btn_detail') }}
|
||||
</b-button>
|
||||
</router-link>
|
||||
<b-button
|
||||
:disabled="p.status!=2"
|
||||
variant="primary"
|
||||
class="btn float-right mg-2"
|
||||
>
|
||||
Vote
|
||||
{{ $t('btn_vote') }}
|
||||
</b-button>
|
||||
</b-card-footer>
|
||||
</b-card>
|
||||
@ -158,7 +159,7 @@ import {
|
||||
BCard, BCardTitle, BCardBody, BCardFooter, BButton, BProgressBar, BProgress, BBadge, BTooltip,
|
||||
} from 'bootstrap-vue'
|
||||
import Ripple from 'vue-ripple-directive'
|
||||
import ChainAPI from '@/libs/fetch'
|
||||
import { Proposal } from '@/libs/data'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -177,26 +178,22 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
proposals: [new Proposal()],
|
||||
values: [15, 50, 10, 5],
|
||||
max: 1,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selected_c() {
|
||||
console.log(localStorage.getItem('selected_chain'))
|
||||
return localStorage.getItem('selected_chain')
|
||||
mounted() {
|
||||
this.getList()
|
||||
},
|
||||
// this.$http.get()
|
||||
},
|
||||
asyncComputed: {
|
||||
proposals: {
|
||||
get() {
|
||||
const api = new ChainAPI(this.$route)
|
||||
return api.getGovernanceList().then(res => {
|
||||
methods: {
|
||||
getList() {
|
||||
// this.$http.setup(this.$route)
|
||||
this.$http.getGovernanceList().then(res => {
|
||||
const voting = res.filter(i => i.status === 2)
|
||||
if (voting.length > 0) {
|
||||
let i = 0
|
||||
Promise.all(voting.reverse().map(p => api.getGovernanceTally(p.id, p.tally.total))).then(update => {
|
||||
Promise.all(voting.reverse().map(p => this.$http.getGovernanceTally(p.id, p.tally.total))).then(update => {
|
||||
this.proposals.map(x => {
|
||||
if (x.status === 2) {
|
||||
const xh = x
|
||||
@ -208,12 +205,37 @@ export default {
|
||||
})
|
||||
})
|
||||
}
|
||||
return res.reverse()
|
||||
this.proposals = res.reverse()
|
||||
})
|
||||
},
|
||||
// default: 'loading...',
|
||||
},
|
||||
},
|
||||
// asyncComputed: {
|
||||
// proposals: {
|
||||
// get() {
|
||||
// const api = new ChainAPI(this.$route)
|
||||
// api.setup(this.$route)
|
||||
// return api.getGovernanceList().then(res => {
|
||||
// const voting = res.filter(i => i.status === 2)
|
||||
// if (voting.length > 0) {
|
||||
// let i = 0
|
||||
// Promise.all(voting.reverse().map(p => api.getGovernanceTally(p.id, p.tally.total))).then(update => {
|
||||
// this.proposals.map(x => {
|
||||
// if (x.status === 2) {
|
||||
// const xh = x
|
||||
// xh.tally = update[i]
|
||||
// i += 1
|
||||
// return xh
|
||||
// }
|
||||
// return x
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
// return res.reverse()
|
||||
// })
|
||||
// },
|
||||
// // default: 'loading...',
|
||||
// },
|
||||
// },
|
||||
// watch: {
|
||||
// proposals(val, newdata) {
|
||||
// console.log('In watch', val, newdata)
|
||||
@ -251,6 +273,6 @@ section {
|
||||
}
|
||||
|
||||
.gov-wrapper .gov {
|
||||
width: 8.4rem;
|
||||
width: 9.8rem;
|
||||
}
|
||||
</style>
|
||||
|
@ -8,7 +8,6 @@
|
||||
<script>
|
||||
import { BCard, BCardText } from 'bootstrap-vue'
|
||||
// import fetch from 'node-fetch'
|
||||
import '@/libs/fetch'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -26,14 +25,6 @@ export default {
|
||||
// }).catch(e => {
|
||||
// console.log(e)
|
||||
// })
|
||||
this.fetchdata('http://lcd.akash.forbole.com/node_info', {
|
||||
Headers: {
|
||||
'Content-Type': 'text/html',
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
console.log(response)
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
357
src/views/ProposalView.vue
Normal file
357
src/views/ProposalView.vue
Normal file
@ -0,0 +1,357 @@
|
||||
<template>
|
||||
<section>
|
||||
<b-card>
|
||||
<b-card-header>
|
||||
<b-card-title>
|
||||
<b-badge
|
||||
v-if="proposal.status == 1"
|
||||
pill
|
||||
variant="light-info"
|
||||
class="text-right"
|
||||
>
|
||||
Deposit
|
||||
</b-badge>
|
||||
<b-badge
|
||||
v-if="proposal.status == 2"
|
||||
pill
|
||||
variant="light-primary"
|
||||
class="text-right"
|
||||
>
|
||||
Voting
|
||||
</b-badge>
|
||||
<b-badge
|
||||
v-if="proposal.status == 3"
|
||||
pill
|
||||
variant="light-success"
|
||||
class="text-right"
|
||||
>
|
||||
Passed
|
||||
</b-badge>
|
||||
<b-badge
|
||||
v-if="proposal.status == 4"
|
||||
pill
|
||||
variant="light-danger"
|
||||
class="text-right"
|
||||
>
|
||||
Rejected
|
||||
</b-badge>
|
||||
#{{ proposal.id }} {{ proposal.title }}
|
||||
</b-card-title>
|
||||
</b-card-header>
|
||||
<b-card-body>
|
||||
<b-table-simple>
|
||||
<b-tr>
|
||||
<b-td lg="2">
|
||||
{{ $t('proposal_id') }}
|
||||
</b-td><b-td>{{ proposal.id }}</b-td>
|
||||
</b-tr>
|
||||
<b-tr>
|
||||
<b-td>
|
||||
{{ $t('proposal_proposer') }}
|
||||
</b-td><b-td>{{ proposer.proposer }} </b-td>
|
||||
</b-tr>
|
||||
<b-tr>
|
||||
<b-td>
|
||||
{{ $t('proposal_total_deposit') }}
|
||||
</b-td><b-td>{{ proposal.total_deposit }} </b-td>
|
||||
</b-tr>
|
||||
<b-tr>
|
||||
<b-td>
|
||||
{{ $t('proposal_submit_time') }}
|
||||
</b-td><b-td>{{ proposal.submit_time }}</b-td>
|
||||
</b-tr>
|
||||
<b-tr>
|
||||
<b-td>
|
||||
{{ $t('proposal_voting_start_time') }}
|
||||
</b-td><b-td>{{ proposal.voting_start_time }}</b-td>
|
||||
</b-tr>
|
||||
<b-tr>
|
||||
<b-td>
|
||||
{{ $t('proposal_voting_end_time') }}
|
||||
</b-td><b-td>{{ proposal.voting_end_time }}</b-td>
|
||||
</b-tr>
|
||||
<b-tr>
|
||||
<b-td>
|
||||
{{ $t('proposal_type') }}
|
||||
</b-td><b-td>{{ proposal.type }}</b-td>
|
||||
</b-tr>
|
||||
<b-tr
|
||||
v-for="(value, name) in proposal.contents"
|
||||
:key="name"
|
||||
>
|
||||
<b-td style="text-transform: capitalize; vertical-align: top;">
|
||||
{{ name.replaceAll('_',' ') }}
|
||||
</b-td>
|
||||
<b-td v-if="!Array.isArray(value)">
|
||||
{{ value }}
|
||||
</b-td>
|
||||
<b-td v-if="Array.isArray(value) && name === 'amount'">
|
||||
<span
|
||||
v-for="token in value"
|
||||
:key="token.amount"
|
||||
>
|
||||
{{ token.amount }} {{ token.denom }}
|
||||
</span>
|
||||
</b-td>
|
||||
<b-td v-if="Array.isArray(value) && name != 'amount'">
|
||||
<b-table :items="value">
|
||||
<!-- A custom formatted column -->
|
||||
<template #cell(amount)="data">
|
||||
<span
|
||||
v-for="token in data.value"
|
||||
:key="token.amount"
|
||||
>
|
||||
{{ token.amount }} {{ token.denom }}
|
||||
</span>
|
||||
</template>
|
||||
</b-table>
|
||||
</b-td>
|
||||
</b-tr>
|
||||
</b-table-simple>
|
||||
</b-card-body>
|
||||
<b-card-footer>
|
||||
<router-link :to="`../gov`">
|
||||
<b-button
|
||||
variant="outline-primary"
|
||||
>
|
||||
{{ $t('btn_back_list') }}
|
||||
</b-button>
|
||||
</router-link>
|
||||
<b-button
|
||||
:disabled="proposer.status!=2"
|
||||
variant="primary"
|
||||
class="btn float-right mg-2"
|
||||
>
|
||||
{{ $t('btn_vote') }}
|
||||
</b-button>
|
||||
</b-card-footer>
|
||||
</b-card>
|
||||
|
||||
<b-card>
|
||||
<b-card-header>
|
||||
<b-card-title>
|
||||
Votes
|
||||
</b-card-title>
|
||||
</b-card-header>
|
||||
<b-card-body>
|
||||
<b-progress
|
||||
:max="100"
|
||||
height="2rem"
|
||||
class="mb-2"
|
||||
show-progress
|
||||
>
|
||||
<b-progress-bar
|
||||
:id="'vote-yes'+proposal.id"
|
||||
variant="success"
|
||||
:value="proposal.tally.yes"
|
||||
show-progress
|
||||
/>
|
||||
<b-progress-bar
|
||||
:id="'vote-no'+proposal.id"
|
||||
variant="warning"
|
||||
:value="proposal.tally.no"
|
||||
show-progress
|
||||
/>
|
||||
<b-progress-bar
|
||||
:id="'vote-veto'+proposal.id"
|
||||
variant="danger"
|
||||
:value="proposal.tally.veto"
|
||||
show-progress
|
||||
/>
|
||||
<b-progress-bar
|
||||
:id="'vote-abstain'+proposal.id"
|
||||
variant="info"
|
||||
:value="proposal.tally.abstain"
|
||||
show-progress
|
||||
/>
|
||||
</b-progress>
|
||||
<b-tooltip
|
||||
:target="'vote-yes'+proposal.id"
|
||||
>
|
||||
{{ proposal.tally.yes }}% voted Yes
|
||||
</b-tooltip>
|
||||
<b-tooltip
|
||||
:target="'vote-no'+proposal.id"
|
||||
>
|
||||
{{ proposal.tally.no }}% voted No
|
||||
</b-tooltip>
|
||||
<b-tooltip
|
||||
:target="'vote-veto'+proposal.id"
|
||||
>
|
||||
{{ proposal.tally.veto }}% voted No With Veta
|
||||
</b-tooltip>
|
||||
<b-tooltip
|
||||
:target="'vote-abstain'+proposal.id"
|
||||
>
|
||||
{{ proposal.tally.abstain }}% voted Abstain
|
||||
</b-tooltip>
|
||||
<b-table
|
||||
:fields="votes_fields"
|
||||
:items="votes"
|
||||
/>
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
<b-card>
|
||||
<b-card-header>
|
||||
<b-card-title>
|
||||
Deposits ({{ proposal.total_deposit }})
|
||||
</b-card-title>
|
||||
</b-card-header>
|
||||
<b-card-body>
|
||||
<b-table
|
||||
:items="deposits"
|
||||
:fields="deposit_fields"
|
||||
/>
|
||||
</b-card-body>
|
||||
<b-card-footer>
|
||||
<router-link :to="`../gov`">
|
||||
<b-button
|
||||
variant="outline-primary"
|
||||
>
|
||||
{{ $t('btn_back_list') }}
|
||||
</b-button>
|
||||
</router-link>
|
||||
<b-button
|
||||
:disabled="proposer.status!=2"
|
||||
variant="primary"
|
||||
class="btn float-right mg-2"
|
||||
>
|
||||
{{ $t('btn_vote') }}
|
||||
</b-button>
|
||||
</b-card-footer>
|
||||
</b-card>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
BCard, BCardBody, BCardFooter, BButton, BTable, BTableSimple, BTr, BTd, BCardTitle, BCardHeader,
|
||||
BProgressBar, BProgress, BTooltip, BBadge,
|
||||
} from 'bootstrap-vue'
|
||||
// import fetch from 'node-fetch'
|
||||
|
||||
import { tokenFormatter } from '@/libs/data/data'
|
||||
import { Proposal, Proposer } from '@/libs/data'
|
||||
// import { formatToken } from '@/libs/data/data'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BCard,
|
||||
BCardTitle,
|
||||
BCardBody,
|
||||
BCardFooter,
|
||||
BButton,
|
||||
BTable,
|
||||
BTableSimple,
|
||||
BCardHeader,
|
||||
BTr,
|
||||
BTd,
|
||||
BProgressBar,
|
||||
BProgress,
|
||||
BTooltip,
|
||||
BBadge,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
proposal: new Proposal(),
|
||||
proposer: new Proposer(),
|
||||
deposits: [],
|
||||
votes: [],
|
||||
votes_fields: [
|
||||
{ key: 'voter', sortable: true },
|
||||
{
|
||||
key: 'option',
|
||||
sortable: true,
|
||||
formatter: value => {
|
||||
switch (value) {
|
||||
case 1:
|
||||
return 'Yes'
|
||||
case 2:
|
||||
return 'Abstain'
|
||||
case 3:
|
||||
return 'No'
|
||||
case 4:
|
||||
return 'No With Veto'
|
||||
default:
|
||||
return value
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
deposit_fields: [
|
||||
'depositor',
|
||||
{
|
||||
key: 'amount',
|
||||
sortable: true,
|
||||
formatter: tokenFormatter,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
const pid = this.$route.params.proposalid
|
||||
|
||||
this.$http.getGovernance(pid).then(p => {
|
||||
if (p.status === 2) {
|
||||
this.$http.getGovernanceTally(pid, 0).then(t => p.updateTally(t)).catch(e => console.log('failed on update voting tally:', e))
|
||||
}
|
||||
this.proposal = p
|
||||
})
|
||||
|
||||
this.$http.getGovernanceProposer(pid).then(res => {
|
||||
this.proposer = res
|
||||
})
|
||||
this.$http.getGovernanceDeposits(pid).then(res => {
|
||||
this.deposits = res
|
||||
})
|
||||
this.$http.getGovernanceVotes(pid).then(res => {
|
||||
this.votes = res
|
||||
})
|
||||
},
|
||||
|
||||
// asyncComputed: {
|
||||
// proposal: {
|
||||
// get() {
|
||||
// const pid = this.$route.params.proposalid
|
||||
// // const api = new ChainAPI(this.$route)
|
||||
// return this.$http.getGovernance(pid).then(p => {
|
||||
// if (p.status === 2) {
|
||||
// this.$http.getGovernanceTally(pid, 0).then(t => p.updateTally(t)).catch(e => console.log('failed on update voting tally:', e))
|
||||
// }
|
||||
// return p
|
||||
// })
|
||||
// },
|
||||
// default: new Proposal(),
|
||||
// },
|
||||
// proposer: {
|
||||
// get() {
|
||||
// const pid = this.$route.params.proposalid
|
||||
// // const api = new ChainAPI(this.$route)
|
||||
// return this.$http.getGovernanceProposer(pid)
|
||||
// },
|
||||
// default: new Proposer(),
|
||||
// },
|
||||
// deposits: {
|
||||
// get() {
|
||||
// const pid = this.$route.params.proposalid
|
||||
// // const api = new ChainAPI(this.$route)
|
||||
// return this.$http.getGovernanceDeposits(pid)
|
||||
// },
|
||||
// default: [],
|
||||
// },
|
||||
// votes: {
|
||||
// get() {
|
||||
// const pid = this.$route.params.proposalid
|
||||
// // const api = new ChainAPI(this.$route)
|
||||
// return this.$http.getGovernanceVotes(pid)
|
||||
// },
|
||||
// default: [],
|
||||
// },
|
||||
// },
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
@ -6,7 +6,8 @@
|
||||
>
|
||||
<b-table
|
||||
responsive="sm"
|
||||
:items="items"
|
||||
:items="delegations"
|
||||
:fields="validator_fields"
|
||||
/>
|
||||
</b-card-code>
|
||||
|
||||
@ -16,41 +17,90 @@
|
||||
>
|
||||
<b-table
|
||||
responsive="sm"
|
||||
:items="items"
|
||||
striped
|
||||
:items="validators"
|
||||
:fields="validator_fields"
|
||||
:sort-desc="true"
|
||||
sort-by="tokens"
|
||||
>
|
||||
<!-- Column: Validator -->
|
||||
<template #cell(description)="data">
|
||||
<b-media vertical-align="center">
|
||||
<template #aside>
|
||||
<b-avatar
|
||||
size="32"
|
||||
icon="ChevronRightIcon"
|
||||
variant="light-primary"
|
||||
:src="data.item.avatar"
|
||||
/>
|
||||
</template>
|
||||
<span class="font-weight-bold d-block text-nowrap">
|
||||
{{ data.item.description.moniker }}
|
||||
</span>
|
||||
<small class="text-muted">{{ data.item.description.website || data.item.description.identity }}</small>
|
||||
</b-media>
|
||||
</template>
|
||||
</b-table>
|
||||
</b-card-code>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BTable } from 'bootstrap-vue'
|
||||
import { BTable, BMedia, BAvatar } from 'bootstrap-vue'
|
||||
import BCardCode from '@core/components/b-card-code/BCardCode.vue'
|
||||
import { Validator, percent } from '@/libs/data'
|
||||
import { keybase } from '@/libs/fetch'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BCardCode,
|
||||
BTable,
|
||||
BMedia,
|
||||
BAvatar,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [
|
||||
|
||||
sortBy: 'tokens',
|
||||
sortDesc: true,
|
||||
validators: [new Validator()],
|
||||
delegations: [new Validator()],
|
||||
validator_fields: [
|
||||
{ key: 'description', label: 'Validator', sortable: true },
|
||||
{
|
||||
age: 40, first_name: 'Dickerson', last_name: 'Macdonald', Occupation: 'Job',
|
||||
key: 'tokens',
|
||||
sortable: true,
|
||||
formatter: value => parseInt(value / 100000, 0),
|
||||
tdClass: 'text-right',
|
||||
thClass: 'text-right',
|
||||
sortByFormatted: true,
|
||||
},
|
||||
{
|
||||
age: 21, first_name: 'Larsen', last_name: 'Shaw', Occupation: 'Job',
|
||||
},
|
||||
{
|
||||
age: 89, first_name: 'Geneva', last_name: 'Wilson', Occupation: 'Bussiness',
|
||||
},
|
||||
{
|
||||
age: 38, first_name: 'Jami', last_name: 'Carney', Occupation: 'Bussiness',
|
||||
},
|
||||
{
|
||||
age: 40, first_name: 'James', last_name: 'Thomson', Occupation: 'Job',
|
||||
key: 'commission',
|
||||
sortable: true,
|
||||
formatter: value => `${percent(value.rate)}%`,
|
||||
tdClass: 'text-right',
|
||||
thClass: 'text-right',
|
||||
},
|
||||
{ key: 'delegator_shares', sortable: true },
|
||||
],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$http.getValidatorList().then(res => {
|
||||
this.validators = res
|
||||
this.validators.forEach(i => {
|
||||
if (i.description.identity) {
|
||||
keybase(i.description.identity).then(d => {
|
||||
if (Array.isArray(d.them) && d.them.length > 0) {
|
||||
console.log(d.them[0].pictures.primary.url)
|
||||
const validator = this.validators.find(u => u.description.identity === i.description.identity)
|
||||
validator.avatar = d.them[0].pictures.primary.url
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user