forked from cerc-io/cosmos-explorer
atomatically load latest
This commit is contained in:
parent
e7a21a9db9
commit
eb79884c07
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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() {
|
||||||
|
@ -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) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user