add uptime overview

This commit is contained in:
liangping 2023-05-16 16:19:22 +08:00
parent c35b07ee49
commit 8f1561dd74
3 changed files with 201 additions and 20 deletions

View File

@ -23,16 +23,6 @@ const commits = ref([] as Commit[]);
const keyword = ref('');
const live = ref(true);
// storage local favorite validator ids
const local = ref(
JSON.parse(localStorage.getItem('uptime-validators') || '{}') as Record<
string,
string[]
>
);
const currentPined = local.value[chainStore.chainName]
const selected = ref(currentPined || []); // favorite validators on selected blockchain
const signingInfo = ref({} as Record<string, SigningInfo>);
// filter validators by keywords
@ -90,10 +80,17 @@ onUnmounted(() => {
});
watchEffect(() => {
local.value[chainStore.chainName] = selected.value;
localStorage.setItem('uptime-validators', JSON.stringify(local.value));
});
// watchEffect((x) => {
// const list = selected.value.map(x => {
// const val = stakingStore.validators.find(v => v.operator_address === x)
// return {
// name: val?.description.moniker || "",
// address: x
// }
// })
// local.value[chainStore.chainName] = list;
// localStorage.setItem('uptime-validators', JSON.stringify(local.value));
// });
</script>
<template>
@ -105,10 +102,10 @@ watchEffect(() => {
placeholder="Keywords to filter validators"
class="input input-sm w-full flex-1"
/>
<button class="btn btn-primary btn-sm">
<RouterLink class="btn btn-primary btn-sm" :to="`/${chain}/uptime/overview`">
<Icon icon="mdi-star" class="mr-2 text-lg" />
<span class="">Favorite</span>
</button>
</RouterLink>
</div>
<div
class="grid grid-cols-1 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6 gap-x-4 mt-4"
@ -116,10 +113,6 @@ watchEffect(() => {
<div v-for="(v, i) in validators" :key="i" >
<div class="flex items-center justify-between py-0">
<label class="text-truncate text-sm">
<input type="checkbox"
v-model="selected"
:value="v.operator_address"
/>
<span class="ml-1 text-black dark:text-white">{{ i + 1 }}.{{ v.description.moniker }}</span>
</label>
<div

View File

@ -0,0 +1,188 @@
<script lang="ts" setup>
import { ref, onMounted, computed, watchEffect } from 'vue';
import { fromHex, toBase64 } from '@cosmjs/encoding';
import { Icon } from '@iconify/vue';
import {
useFormatter,
useStakingStore,
useBaseStore,
useBlockchain,
useDashboard,
} from '@/stores';
import UptimeBar from '@/components/UptimeBar.vue';
import type { Block, Commit } from '@/types';
import { consensusPubkeyToHexAddress, valconsToBase64 } from '@/libs';
import type { SigningInfo } from '@/types/slashing';
import { CosmosRestClient } from '@/libs/client';
const props = defineProps(['chain']);
const stakingStore = useStakingStore();
const format = useFormatter();
const chainStore = useBlockchain();
const dashboard = useDashboard()
// storage local favorite validator ids
const local = ref(JSON.parse(localStorage.getItem('uptime-validators') || '{}') as Record<string, {name: string, address: string}[]>)
const signingInfo = ref({} as Record<string, SigningInfo[]>);
const selected = ref([] as string[])
const selectChain = ref(chainStore.chainName)
const validators = ref(stakingStore.validators)
const keyword = ref("")
if(local.value) Object.keys(local.value).map(chainName => {
const chain = dashboard.chains[chainName]
if(chain && chain.endpoints.rest) {
const client = CosmosRestClient.newDefault(chain.endpoints.rest[0].address)
client.getSlashingSigningInfos().then( resp => {
signingInfo.value[chainName] = resp.info
})
}
if(chainName === selectChain.value) {
const vals = local.value[chainName]
if(vals) {
selected.value = vals.map(x => x.address)
}
}
return chain
})
const filterValidators = computed(() => {
if(keyword.value) {
return validators.value.filter(x => x.description.moniker.indexOf(keyword.value) > -1)
}
return validators.value
})
const list = computed(() => {
const list = [] as any[]
if(local.value) Object.keys(local.value).map( chainName => {
const vals = local.value[chainName]
const info = signingInfo.value[chainName]
if(vals && info) {
vals.forEach(v => {
const sigingInfo = info.find(x => valconsToBase64(x.address) === v.address)
list.push( {
chainName,
v,
sigingInfo,
})
})
}
})
return list
})
function add() {
const newList = [] as { name: string; address: string; }[]
selected.value.forEach(x => {
const validator = validators.value.find(v => (consensusPubkeyToHexAddress(v.consensus_pubkey) === x))
if(validator) newList.push({
name: validator.description.moniker || x,
address: x
})
})
if(!local.value) local.value = {}
local.value[selectChain.value] = newList
localStorage.setItem("uptime-validators", JSON.stringify(local.value))
}
function changeChain() {
validators.value = []
const endpoint = dashboard.chains[selectChain.value].endpoints.rest?.at(0)?.address
if(!endpoint) return
const client = CosmosRestClient.newDefault(endpoint)
client.getStakingValidators("BOND_STATUS_BONDED").then(x => {
validators.value = x.validators
})
const vals = local.value[selectChain.value]
if(vals) {
selected.value = vals.map(x => x.address)
} else {
selected.value = []
}
}
function color(v: string) {
if(v) {
const n = Number(v)
if(n < 10) return " badge-success"
if(n > 1000) return " badge-error"
if(n > 0) return " badge-warning"
}
}
</script>
<template>
<div>
<table class="table w-full">
<thead>
<tr>
<th>Blockchain</th>
<th>Validator</th>
<th>Missing Blocks</th>
<th>Signed Blocks</th>
<th>Last Jailed Time</th>
<th>Tombstoned</th>
</tr>
</thead>
<tbody>
<tr v-for="v in list">
<td class=" capitalize">{{ v.chainName }}</td>
<td>{{ v.v.name }}</td>
<td><span class="badge " :class="color( v.sigingInfo?.missed_blocks_counter)">{{ v.sigingInfo?.missed_blocks_counter }}</span></td>
<td><span v-if="v.sigingInfo">{{ Number(v.sigingInfo.index_offset) - Number(v.sigingInfo.start_height) }}</span></td>
<td>
<div v-if="v.sigingInfo && !v.sigingInfo?.jailed_until.startsWith('1970')" class="text-xs flex flex-col">
<div class="badge">{{ format.toDay(v.sigingInfo.jailed_until, 'from') }}</div>
<div>{{v.sigingInfo?.jailed_until }}</div>
</div>
</td>
<td class=" capitalize">{{ v.sigingInfo?.tombstoned }}</td>
</tr>
</tbody>
</table>
<label for="add-validator" class="btn btn-primary mt-5">Add Validators</label>
<!-- Put this part before </body> tag -->
<input type="checkbox" id="add-validator" class="modal-toggle" />
<div class="modal">
<div class="modal-box relative">
<label for="add-validator" class="btn btn-sm btn-circle absolute right-2 top-2"></label>
<h3 class="text-lg font-bold">Add Validators</h3>
<div class="py-4 max-h-60 overflow-y-auto">
<div class="form-control my-5 border-2">
<div class="input-group input-group-md">
<select v-model="selectChain" class="select select-bordered capitalize" @change="changeChain">
<option v-for="v in dashboard.chains" :value="v.chainName">
{{ v.chainName }}
</option>
</select>
<input v-model="keyword" type="text" class="input w-full" placeholder="keywords to filter validator">
</div>
</div>
<table class="table table-compact w-full hover">
<thead>
<tr><th>Validator</th><th></th></tr>
</thead>
<tbody>
<tr v-for="v in filterValidators">
<td><label :for="v.operator_address"><div class=" w-full">{{ v.description.moniker }}</div></label></td>
<td><input :id="v.operator_address" v-model="selected" class="checkbox" type="checkbox" :value="consensusPubkeyToHexAddress(v.consensus_pubkey)"/></td>
</tr>
</tbody>
</table>
</div>
<div class="modal-action">
<label for="add-validator" class="btn" @click="add">add</label>
</div>
</div>
</div>
<div class="h-6"></div>
</div>
</template>