atomatically load latest

This commit is contained in:
liangping 2023-05-14 09:23:18 +08:00
parent e7a21a9db9
commit eb79884c07
8 changed files with 81 additions and 57 deletions

View File

@ -86,8 +86,8 @@ chainStore.$subscribe((m, s) => {
<div <div
class="capitalize whitespace-nowrap text-base font-semibold text-gray-600 dark:text-gray-200" class="capitalize whitespace-nowrap text-base font-semibold text-gray-600 dark:text-gray-200"
> >
{{ #{{
baseStore.latest.block?.header?.chain_id || chainStore.chainName || '' baseStore.latest?.block?.header?.height || chainStore.chainName || ''
}} }}
</div> </div>
<div <div

View File

@ -31,5 +31,5 @@ app.use(LazyLoad, { component: true });
app.mount('#app'); app.mount('#app');
// fetch latest block every 6s // fetch latest block every 6s
// const blockStore = useBaseStore() const blockStore = useBaseStore()
// setInterval(() => {blockStore.fetchLatest()}, 6000) setInterval(() => {blockStore.fetchLatest()}, 6000)

View File

@ -5,19 +5,23 @@ import { useBlockModule } from './block';
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue'; import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
import { computed } from '@vue/reactivity'; import { computed } from '@vue/reactivity';
import { onBeforeRouteUpdate } from 'vue-router'; import { onBeforeRouteUpdate } from 'vue-router';
import { useBaseStore } from '@/stores';
import { ref } from 'vue';
import type { Block } from '@/types';
const props = defineProps(['height', 'chain']); const props = defineProps(['height', 'chain']);
const store = useBlockModule(); const store = useBaseStore();
store.fetchBlock(props.height);
const tab = ref('summary'); const tab = ref('summary');
const current = ref({} as Block)
store.fetchBlock(props.height).then(x => current.value = x)
const height = computed(() => { const height = computed(() => {
return Number(store.current.block?.header?.height || props.height || 0); return Number(current.value.block?.header?.height || props.height || 0);
}); });
onBeforeRouteUpdate(async (to, from, next) => { onBeforeRouteUpdate(async (to, from, next) => {
if (from.path !== to.path) { if (from.path !== to.path) {
store.fetchBlock(String(to.params.height)); store.fetchBlock(String(to.params.height)).then(x => current.value = x);
next(); next();
} }
}); });
@ -26,7 +30,7 @@ onBeforeRouteUpdate(async (to, from, next) => {
<div> <div>
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow"> <div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<h2 class="card-title flex flex-row justify-between"> <h2 class="card-title flex flex-row justify-between">
<p class="">#{{ store.current.block?.header?.height }}</p> <p class="">#{{ current.block?.header?.height }}</p>
<div class="" v-if="props.height"> <div class="" v-if="props.height">
<RouterLink <RouterLink
:to="`/${store.blockchain.chainName}/block/${height - 1}`" :to="`/${store.blockchain.chainName}/block/${height - 1}`"
@ -43,23 +47,23 @@ onBeforeRouteUpdate(async (to, from, next) => {
</div> </div>
</h2> </h2>
<div> <div>
<DynamicComponent :value="store.current.block_id" /> <DynamicComponent :value="current.block_id" />
</div> </div>
</div> </div>
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow"> <div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<h2 class="card-title flex flex-row justify-between">Block Header</h2> <h2 class="card-title flex flex-row justify-between">Block Header</h2>
<DynamicComponent :value="store.current.block?.header" /> <DynamicComponent :value="current.block?.header" />
</div> </div>
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow"> <div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<h2 class="card-title flex flex-row justify-between">Transactions</h2> <h2 class="card-title flex flex-row justify-between">Transactions</h2>
<TxsElement :value="store.current.block?.data?.txs" /> <TxsElement :value="current.block?.data?.txs" />
</div> </div>
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow"> <div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
<h2 class="card-title flex flex-row justify-between">Last Commit</h2> <h2 class="card-title flex flex-row justify-between">Last Commit</h2>
<DynamicComponent :value="store.current.block?.last_commit" /> <DynamicComponent :value="current.block?.last_commit" />
</div> </div>
</div> </div>
</template> </template>

View File

@ -1,13 +1,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useBlockModule } from './block';
import { computed, ref } from '@vue/reactivity'; import { computed, ref } from '@vue/reactivity';
import { useFormatter } from '@/stores'; import { useBaseStore, useFormatter } from '@/stores';
const props = defineProps(['height', 'chain']); const props = defineProps(['height', 'chain']);
const store = useBlockModule();
// store.fetchBlock(props.height)
const tab = ref('blocks'); const tab = ref('blocks');
const base = useBaseStore()
const format = useFormatter(); const format = useFormatter();
</script> </script>
<template> <template>
@ -38,8 +37,8 @@ const format = useFormatter();
<th>Time</th> <th>Time</th>
</tr> </tr>
</thead> </thead>
<tbody v-if="store.recents && store.recents.length > 0" > <tbody v-if="base.recents && base.recents.length > 0" >
<tr v-for="(item, index) in store.recents" :key="index"> <tr v-for="(item, index) in base.recents" :key="index">
<td class="text-sm text-primary"> <td class="text-sm text-primary">
<RouterLink <RouterLink
:to="`/${props.chain}/block/${item.block?.header?.height}`" :to="`/${props.chain}/block/${item.block?.header?.height}`"
@ -64,19 +63,21 @@ const format = useFormatter();
<table class="table w-full"> <table class="table w-full">
<thead> <thead>
<tr> <tr>
<th style="position: relative">Height</th>
<th style="position: relative">Hash</th> <th style="position: relative">Hash</th>
<th>Messages</th> <th>Messages</th>
<th>Fees</th> <th>Fees</th>
</tr> </tr>
</thead> </thead>
<tbody v-if="store.txsInRecents && store.txsInRecents.length > 0" > <tbody >
<tr v-for="(item,index) in store.txsInRecents" :index="index"> <tr v-for="(item,index) in base.txsInRecents" :index="index">
<td> <td>{{ item.height }}</td>
<td class="text-xs truncate" width="50%">
<RouterLink :to="`/${props.chain}/tx/${item.hash}`">{{ <RouterLink :to="`/${props.chain}/tx/${item.hash}`">{{
item.hash item.hash
}}</RouterLink> }}</RouterLink>
</td> </td>
<td>{{ format.messages(item.tx.body.messages as any) }}</td> <td>{{ format.messages(item.tx.body.messages) }}</td>
<td>{{ format.formatTokens(item.tx.authInfo.fee?.amount) }}</td> <td>{{ format.formatTokens(item.tx.authInfo.fee?.amount) }}</td>
</tr> </tr>
</tbody> </tbody>

View File

@ -258,13 +258,10 @@ const color = computed(() => {
<div class="text-lg font-semibold text-main"> <div class="text-lg font-semibold text-main">
{{ format.formatToken(walletStore.balanceOfStakingToken) }} {{ format.formatToken(walletStore.balanceOfStakingToken) }}
</div> </div>
<div class="text-sm"> <div class="text-sm" :class="color">
<span :class="color" <span class="ml-1">
>{{ format.showChanges(change) }}<small>%</small> ${{ format.tokenValue(walletStore.balanceOfStakingToken) }}
</span> </span>
<span class="ml-1">{{
format.tokenValue(walletStore.balanceOfStakingToken)
}}</span>
</div> </div>
</div> </div>
<div class="bg-gray-100 dark:bg-[#373f59] rounded-sm px-4 py-3"> <div class="bg-gray-100 dark:bg-[#373f59] rounded-sm px-4 py-3">
@ -272,8 +269,8 @@ const color = computed(() => {
<div class="text-lg font-semibold text-main"> <div class="text-lg font-semibold text-main">
{{ format.formatToken(walletStore.stakingAmount) }} {{ format.formatToken(walletStore.stakingAmount) }}
</div> </div>
<div class="text-sm"> <div class="text-sm" :class="color">
{{ format.tokenValue(walletStore.stakingAmount) }} ${{ format.tokenValue(walletStore.stakingAmount) }}
</div> </div>
</div> </div>
<div class="bg-gray-100 dark:bg-[#373f59] rounded-sm px-4 py-3"> <div class="bg-gray-100 dark:bg-[#373f59] rounded-sm px-4 py-3">
@ -281,8 +278,8 @@ const color = computed(() => {
<div class="text-lg font-semibold text-main"> <div class="text-lg font-semibold text-main">
{{ format.formatToken(walletStore.rewardAmount) }} {{ format.formatToken(walletStore.rewardAmount) }}
</div> </div>
<div class="text-sm"> <div class="text-sm" :class="color">
{{ format.tokenValue(walletStore.rewardAmount) }} ${{ format.tokenValue(walletStore.rewardAmount) }}
</div> </div>
</div> </div>
<div class="bg-gray-100 dark:bg-[#373f59] rounded-sm px-4 py-3"> <div class="bg-gray-100 dark:bg-[#373f59] rounded-sm px-4 py-3">
@ -290,8 +287,8 @@ const color = computed(() => {
<div class="text-lg font-semibold text-main"> <div class="text-lg font-semibold text-main">
{{ format.formatToken(walletStore.unbondingAmount) }} {{ format.formatToken(walletStore.unbondingAmount) }}
</div> </div>
<div class="text-sm"> <div class="text-sm" :class="color">
{{ format.tokenValue(walletStore.unbondingAmount) }} ${{ format.tokenValue(walletStore.unbondingAmount) }}
</div> </div>
</div> </div>
</div> </div>

View File

@ -82,7 +82,7 @@ const change24 = (key: Key) => {
const change24Text = (key?: Key) => { const change24Text = (key?: Key) => {
if (!key) return ''; if (!key) return '';
const v = change24(key); const v = change24(key);
return v !== 0 ? format.showChanges(v) : ''; return v && v !== 0 ? format.showChanges(v) : '';
}; };
const change24Color = (key?: Key) => { const change24Color = (key?: Key) => {
@ -202,7 +202,7 @@ const rank = function (position: number) {
<th scope="col" class="text-right">VOTING POWER</th> <th scope="col" class="text-right">VOTING POWER</th>
<th scope="col" class="text-right">24h CHANGES</th> <th scope="col" class="text-right">24h CHANGES</th>
<th scope="col" class="text-right">COMMISSION</th> <th scope="col" class="text-right">COMMISSION</th>
<th scope="col">ACTIONS</th> <th scope="col" class="text-center">ACTIONS</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -298,18 +298,9 @@ const rank = function (position: number) {
:class="change24Color(v.consensus_pubkey)" :class="change24Color(v.consensus_pubkey)"
> >
{{ change24Text(v.consensus_pubkey) }} {{ change24Text(v.consensus_pubkey) }}
<div
v-if="v.jailed"
class="text-xs truncate relative py-2 px-4 rounded-full w-fit text-error"
>
<span
class="inset-x-0 inset-y-0 opacity-10 absolute bg-error"
></span>
Jailed
</div>
</td> </td>
<!-- 👉 commission --> <!-- 👉 commission -->
<td class="text-right"> <td class="text-right text-xs">
{{ {{
format.formatCommissionRate( format.formatCommissionRate(
v.commission?.commission_rates?.rate v.commission?.commission_rates?.rate
@ -317,8 +308,12 @@ const rank = function (position: number) {
}} }}
</td> </td>
<!-- 👉 Action --> <!-- 👉 Action -->
<td> <td class="text-center">
<div v-if="v.jailed" class="badge badge-error gap-2 text-white">
Jailed
</div>
<label <label
v-else
for="delegate" for="delegate"
class="btn btn-xs bg-primary" class="btn btn-xs bg-primary"
@click=" @click="
@ -326,8 +321,8 @@ const rank = function (position: number) {
validator_address: v.operator_address, validator_address: v.operator_address,
}) })
" "
>Delegate</label >Delegate</label>
>
</td> </td>
</tr> </tr>
</tbody> </tbody>

View File

@ -1,7 +1,10 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { useBlockchain } from '@/stores'; import { useBlockchain } from '@/stores';
import { decodeTxRaw, type DecodedTxRaw } from '@cosmjs/proto-signing';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import type { Block } from '@/types'; import type { Block } from '@/types';
import { hashTx } from '@/libs';
import { fromBase64 } from '@cosmjs/encoding';
export const useBaseStore = defineStore('baseStore', { export const useBaseStore = defineStore('baseStore', {
state: () => { state: () => {
@ -29,6 +32,26 @@ export const useBaseStore = defineStore('baseStore', {
blockchain() { blockchain() {
return useBlockchain(); return useBlockchain();
}, },
txsInRecents() {
const txs = [] as { height: string, hash: string; tx: DecodedTxRaw }[];
this.recents.forEach((b) =>
b.block?.data?.txs.forEach((tx: string) => {
if (tx) {
const raw = fromBase64(tx)
try {
txs.push({
height: b.block.header.height,
hash: hashTx(raw),
tx: decodeTxRaw(raw),
});
} catch (e) {
console.error(e)
}
}
})
);
return txs;
},
}, },
actions: { actions: {
async initial() { async initial() {
@ -48,10 +71,13 @@ export const useBaseStore = defineStore('baseStore', {
this.earlest = this.latest; this.earlest = this.latest;
this.recents = []; this.recents = [];
} }
if (this.recents.length >= 50) { //check if the block exists in recents
this.recents.pop(); if(this.recents.findIndex(x => x.block_id.hash === this.latest.block_id.hash) === -1 ) {
if (this.recents.length >= 50) {
this.recents.pop();
}
this.recents.push(this.latest);
} }
this.recents.push(this.latest);
return this.latest; return this.latest;
}, },
@ -61,7 +87,7 @@ export const useBaseStore = defineStore('baseStore', {
async fetchLatestValidators(offset = 0) { async fetchLatestValidators(offset = 0) {
return this.blockchain.rpc.getBaseValidatorsetLatest(offset); return this.blockchain.rpc.getBaseValidatorsetLatest(offset);
}, },
async fetchBlock(height?: number) { async fetchBlock(height?: number|string) {
return this.blockchain.rpc.getBaseBlockAt(String(height)); return this.blockchain.rpc.getBaseBlockAt(String(height));
}, },
async fetchAbciInfo() { async fetchAbciInfo() {

View File

@ -211,12 +211,13 @@ export const useFormatter = defineStore('formatter', {
} }
return dayjs(time).format('YYYY-MM-DD HH:mm:ss'); return dayjs(time).format('YYYY-MM-DD HH:mm:ss');
}, },
messages(msgs: { '@type': string }[]) { messages(msgs: { '@type'?: string, typeUrl?: string }[]) {
if (msgs) { if (msgs) {
const sum: Record<string, number> = msgs const sum: Record<string, number> = msgs
.map((msg) => { .map((msg) => {
return msg['@type'] const msgType = msg['@type'] || msg.typeUrl || 'unknown'
.substring(msg['@type'].lastIndexOf('.') + 1) return msgType
.substring(msgType.lastIndexOf('.') + 1)
.replace('Msg', ''); .replace('Msg', '');
}) })
.reduce((s, c) => { .reduce((s, c) => {