forked from cerc-io/cosmos-explorer
add pinned mode
This commit is contained in:
parent
cfa55105a1
commit
d618eae534
@ -52,8 +52,8 @@ export default class ChainFetch {
|
||||
return this.get('/blocks/latest', config).then(data => Block.create(data))
|
||||
}
|
||||
|
||||
async getBlockByHeight(height) {
|
||||
return this.get(`/blocks/${height}`).then(data => Block.create(data))
|
||||
async getBlockByHeight(height, config = null) {
|
||||
return this.get(`/blocks/${height}`, config).then(data => Block.create(data))
|
||||
}
|
||||
|
||||
async getSlashingSigningInfo() {
|
||||
|
@ -126,6 +126,24 @@ const router = new VueRouter({
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/:chain/uptime/my',
|
||||
name: 'uptime',
|
||||
component: () => import('@/views/UptimeMyValidators.vue'),
|
||||
meta: {
|
||||
pageTitle: 'Uptime',
|
||||
breadcrumb: [
|
||||
{
|
||||
text: 'Uptime',
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
text: 'My Validators',
|
||||
active: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/:chain/account/:address',
|
||||
name: 'chain-account',
|
||||
|
@ -13,6 +13,12 @@
|
||||
no-body
|
||||
class="mb-1"
|
||||
>
|
||||
<b-button
|
||||
:href="`./uptime/my`"
|
||||
variant="primary"
|
||||
>
|
||||
Browse favourate only
|
||||
</b-button>
|
||||
<b-form-input
|
||||
v-model="query"
|
||||
placeholder="Keywords to filter validators"
|
||||
@ -26,24 +32,28 @@
|
||||
md="4"
|
||||
class="text-truncate"
|
||||
>
|
||||
<span class="d-inline-block text-truncate font-weight-bold align-bottom">{{ index+1 }} {{ x.validator.moniker }}</span>
|
||||
<template>
|
||||
<div class="d-flex justify-content-between align-self-stretch flex-wrap">
|
||||
<b-form-checkbox
|
||||
v-model="pinned"
|
||||
:value="`${chain}#${x.address}`"
|
||||
class="custom-control-warning"
|
||||
@change="pinValidator(`${chain}#${x.address}`)"
|
||||
><span class="d-inline-block text-truncate font-weight-bold align-bottom">{{ index+1 }} {{ x.validator.moniker }}</span>
|
||||
</b-form-checkbox>
|
||||
<div class="d-flex justify-content-between align-self-stretch flex-wrap">
|
||||
<div
|
||||
v-for="(b,i) in blocks"
|
||||
:key="i"
|
||||
style="width:1.5%;"
|
||||
><router-link :to="`./blocks/${b.height}`">
|
||||
<div
|
||||
v-for="(b,i) in blocks"
|
||||
:key="i"
|
||||
style="width:1.5%;"
|
||||
><router-link :to="`./blocks/${b.height}`">
|
||||
<div
|
||||
v-b-tooltip.hover.v-second
|
||||
:title="b.height"
|
||||
:class="b.sigs && b.sigs[x.address] ? b.sigs[x.address] : 'bg-light-success'"
|
||||
class="m-auto"
|
||||
> </div>
|
||||
</router-link>
|
||||
</div>
|
||||
v-b-tooltip.hover.v-second
|
||||
:title="b.height"
|
||||
:class="b.sigs && b.sigs[x.address] ? b.sigs[x.address] : 'bg-light-success'"
|
||||
class="m-auto"
|
||||
> </div>
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-card>
|
||||
@ -52,7 +62,7 @@
|
||||
|
||||
<script>
|
||||
import {
|
||||
BRow, BCol, VBTooltip, BFormInput, BCard, BAlert,
|
||||
BRow, BCol, VBTooltip, BFormInput, BCard, BAlert, BFormCheckbox, BButton,
|
||||
} from 'bootstrap-vue'
|
||||
|
||||
import {
|
||||
@ -66,12 +76,18 @@ export default {
|
||||
BFormInput,
|
||||
BCard,
|
||||
BAlert,
|
||||
BButton,
|
||||
BFormCheckbox,
|
||||
},
|
||||
directives: {
|
||||
'b-tooltip': VBTooltip,
|
||||
},
|
||||
data() {
|
||||
const { chain } = this.$route.params
|
||||
const pinned = localStorage.getItem('pinned') ? localStorage.getItem('pinned').split(',') : ''
|
||||
return {
|
||||
pinned,
|
||||
chain,
|
||||
query: '',
|
||||
validators: [],
|
||||
missing: {},
|
||||
@ -108,6 +124,9 @@ export default {
|
||||
clearInterval(this.timer)
|
||||
},
|
||||
methods: {
|
||||
pinValidator() {
|
||||
localStorage.setItem('pinned', this.pinned)
|
||||
},
|
||||
initBlocks() {
|
||||
this.$http.getLatestBlock().then(d => {
|
||||
const { height } = d.block.last_commit
|
||||
|
182
src/views/UptimeMyChainBlocks.vue
Normal file
182
src/views/UptimeMyChainBlocks.vue
Normal file
@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<div class="px-0">
|
||||
<b-card>
|
||||
<b-card-title class="text-uppercase">
|
||||
{{ chain }}
|
||||
</b-card-title>
|
||||
<b-alert
|
||||
variant="danger"
|
||||
:show="syncing"
|
||||
>
|
||||
<div class="alert-body">
|
||||
<span>No new block is produced since <strong>{{ latestTime }}</strong> </span>
|
||||
</div>
|
||||
</b-alert>
|
||||
<b-row>
|
||||
<b-col
|
||||
v-for="(x,index) in uptime"
|
||||
:key="index"
|
||||
sm="12"
|
||||
class="text-truncate"
|
||||
>
|
||||
<b-form-checkbox
|
||||
v-model="pinned"
|
||||
:value="`${chain}#${x.address}`"
|
||||
class="custom-control-warning"
|
||||
@change="pinValidator(`${chain}#${x.address}`)"
|
||||
>
|
||||
<span class="d-inline-block text-truncate font-weight-bold align-bottom"> {{ x.validator.moniker }}</span>
|
||||
</b-form-checkbox>
|
||||
<div class="d-flex justify-content-between align-self-stretch flex-wrap">
|
||||
<div
|
||||
v-for="(b,i) in blocks"
|
||||
:key="i"
|
||||
style="width:1.5%;"
|
||||
><router-link :to="`./blocks/${b.height}`">
|
||||
<div
|
||||
v-b-tooltip.hover.v-second
|
||||
:title="b.height"
|
||||
:class="b.sigs && b.sigs[x.address] ? b.sigs[x.address] : 'bg-light-success'"
|
||||
class="m-auto"
|
||||
> </div>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
BRow, BCol, VBTooltip, BCard, BAlert, BCardTitle, BFormCheckbox,
|
||||
} from 'bootstrap-vue'
|
||||
|
||||
import {
|
||||
getLocalChains, timeIn, toDay,
|
||||
} from '@/libs/data'
|
||||
|
||||
export default {
|
||||
name: 'Blocks',
|
||||
components: {
|
||||
BRow,
|
||||
BCol,
|
||||
BCard,
|
||||
BAlert,
|
||||
BCardTitle,
|
||||
BFormCheckbox,
|
||||
},
|
||||
directives: {
|
||||
'b-tooltip': VBTooltip,
|
||||
},
|
||||
props: {
|
||||
chain: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
validators: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
const chains = getLocalChains()
|
||||
const pinned = localStorage.getItem('pinned') ? localStorage.getItem('pinned').split(',') : ''
|
||||
return {
|
||||
pinned,
|
||||
config: chains[this.chain],
|
||||
missing: {},
|
||||
blocks: Array.from('0'.repeat(50)).map(x => ({ sigs: {}, height: Number(x) })),
|
||||
syncing: false,
|
||||
latestTime: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
uptime() {
|
||||
const vals = this.validators
|
||||
return vals
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.initBlocks()
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.blocks = [] // clear running tasks if it is not finish
|
||||
this.syncing = false
|
||||
clearInterval(this.timer)
|
||||
},
|
||||
methods: {
|
||||
pinValidator() {
|
||||
localStorage.setItem('pinned', this.pinned)
|
||||
},
|
||||
initBlocks() {
|
||||
this.$http.getLatestBlock(this.config).then(d => {
|
||||
const { height } = d.block.last_commit
|
||||
if (timeIn(d.block.header.time, 3, 'm')) {
|
||||
this.syncing = true
|
||||
} else {
|
||||
this.syncing = false
|
||||
}
|
||||
this.latestTime = toDay(d.block.header.time, 'long')
|
||||
const blocks = []
|
||||
// update height
|
||||
let promise = Promise.resolve()
|
||||
for (let i = height - 1; i > height - 50; i -= 1) {
|
||||
blocks.unshift({ sigs: {}, height: i > 0 ? i : 0 })
|
||||
if (i > height - 48 && i > 0) {
|
||||
promise = promise.then(() => new Promise(resolve => {
|
||||
this.fetch_status(i, resolve)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
const sigs = this.initColor()
|
||||
d.block.last_commit.signatures.forEach(x => {
|
||||
if (x.validator_address) sigs[x.validator_address] = 'bg-success'
|
||||
})
|
||||
blocks.push({ sigs, height })
|
||||
this.blocks = blocks
|
||||
|
||||
this.timer = setInterval(this.fetch_latest, 6000)
|
||||
})
|
||||
},
|
||||
initColor() {
|
||||
const sigs = {}
|
||||
this.validators.forEach(x => {
|
||||
sigs[x.address] = 'bg-danger'
|
||||
})
|
||||
return sigs
|
||||
},
|
||||
fetch_status(height, resolve) {
|
||||
const block = this.blocks.find(b => b.height === height)
|
||||
if (block) {
|
||||
this.$http.getBlockByHeight(height, this.config).then(res => {
|
||||
resolve()
|
||||
const sigs = this.initColor()
|
||||
res.block.last_commit.signatures.forEach(x => {
|
||||
if (x.validator_address) sigs[x.validator_address] = 'bg-success'
|
||||
})
|
||||
this.$set(block, 'sigs', sigs)
|
||||
})
|
||||
}
|
||||
},
|
||||
fetch_latest() {
|
||||
this.$http.getLatestBlock(this.config).then(res => {
|
||||
const sigs = this.initColor()
|
||||
res.block.last_commit.signatures.forEach(x => {
|
||||
if (x.validator_address) sigs[x.validator_address] = 'bg-success'
|
||||
})
|
||||
const block = this.blocks.find(b => b[1] === res.block.last_commit.height)
|
||||
if (typeof block === 'undefined') { // mei
|
||||
// this.$set(block, 0, typeof sigs !== 'undefined')
|
||||
if (this.blocks.length >= 50) this.blocks.shift()
|
||||
this.blocks.push({ sigs, height: res.block.last_commit.height })
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
66
src/views/UptimeMyValidators.vue
Normal file
66
src/views/UptimeMyValidators.vue
Normal file
@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="container-md px-0">
|
||||
<b-row>
|
||||
<b-col
|
||||
v-for="(x,index) in Object.keys(chainVals)"
|
||||
:key="index"
|
||||
sm="12"
|
||||
md="4"
|
||||
class="text-truncate"
|
||||
>
|
||||
<uptime-my-chain-blocks
|
||||
:chain="x"
|
||||
:validators="chainVals[x]"
|
||||
/>
|
||||
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
BRow, BCol, VBTooltip,
|
||||
} from 'bootstrap-vue'
|
||||
import { consensusPubkeyToHexAddress, getCachedValidators } from '@/libs/data'
|
||||
import UptimeMyChainBlocks from './UptimeMyChainBlocks.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BRow,
|
||||
BCol,
|
||||
UptimeMyChainBlocks,
|
||||
},
|
||||
directives: {
|
||||
'b-tooltip': VBTooltip,
|
||||
},
|
||||
data() {
|
||||
const pinned = (localStorage.getItem('pinned') || '').split(',').map(x => x.split('#')).reduce((a1, b) => {
|
||||
const a = a1
|
||||
if (a[b[0]]) {
|
||||
a[b[0]].push(b[1])
|
||||
} else {
|
||||
a[b[0]] = [b[1]]
|
||||
}
|
||||
return a
|
||||
}, {})
|
||||
|
||||
const chainVals = {}
|
||||
Object.keys(pinned).forEach(x => {
|
||||
const cached = JSON.parse(getCachedValidators(x))
|
||||
const validators = []
|
||||
pinned[x].forEach(address => {
|
||||
const val = cached.find(v => address === consensusPubkeyToHexAddress(v.consensus_pubkey))
|
||||
validators.push({ address, validator: val.description })
|
||||
})
|
||||
chainVals[x] = validators
|
||||
})
|
||||
|
||||
return {
|
||||
chainVals,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
@ -48,7 +48,7 @@ module.exports = {
|
||||
devServer: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'https://lcd-cosmos.cosmostation.io/',
|
||||
target: 'https://cosmos.api.ping.pub/',
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
'^/api': '',
|
||||
|
Loading…
Reference in New Issue
Block a user