forked from cerc-io/cosmos-explorer
Merge pull request #377 from alisaweb3/v3-single
UI refactor: Editor,Staking,Statesync, Connect wallet, Image lazy load
This commit is contained in:
commit
009ae1c5e6
@ -35,11 +35,12 @@
|
|||||||
"cross-fetch": "^3.1.5",
|
"cross-fetch": "^3.1.5",
|
||||||
"daisyui": "^2.51.6",
|
"daisyui": "^2.51.6",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
|
"lazy-load-vue3": "^1.3.0",
|
||||||
"long": "^5.2.1",
|
"long": "^5.2.1",
|
||||||
"md-editor-v3": "^2.8.1",
|
"md-editor-v3": "^2.8.1",
|
||||||
"numeral": "^2.0.6",
|
"numeral": "^2.0.6",
|
||||||
"osmojs": "^14.0.0-rc.0",
|
"osmojs": "^14.0.0-rc.0",
|
||||||
"ping-widget": "^0.0.4",
|
"ping-widget": "git+https://github.com/ping-pub/widget.git",
|
||||||
"pinia": "^2.0.28",
|
"pinia": "^2.0.28",
|
||||||
"postcss": "^8.4.23",
|
"postcss": "^8.4.23",
|
||||||
"prismjs": "^1.29.0",
|
"prismjs": "^1.29.0",
|
||||||
|
@ -19,6 +19,50 @@ function isMD() {
|
|||||||
v-if="isMD()"
|
v-if="isMD()"
|
||||||
:model-value="format.multiLine(value)"
|
:model-value="format.multiLine(value)"
|
||||||
previewOnly
|
previewOnly
|
||||||
|
class="md-editor-recover"
|
||||||
></MdEditor>
|
></MdEditor>
|
||||||
<span v-else>{{ value }}</span>
|
<span v-else>{{ value }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.md-editor-recover {
|
||||||
|
.h1,h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
.h2,h2 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
display: block;
|
||||||
|
list-style-type: disc;
|
||||||
|
margin-block-start: 1em;
|
||||||
|
margin-block-end: 1em;
|
||||||
|
margin-inline-start: 0px;
|
||||||
|
margin-inline-end: 0px;
|
||||||
|
padding-inline-start: 40px;
|
||||||
|
}
|
||||||
|
dl, ol, ul {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
address, dl, ol, ul {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #666cff !important;
|
||||||
|
}
|
||||||
|
.h1 > a, .h2> a, .h3> a, .h4> a, .h5> a, .h6> a, h1> a, h2> a, h3> a, h4> a, h5> a, h6> a {
|
||||||
|
color: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
1
src/components/icons/IconWeb.vue
Normal file
1
src/components/icons/IconWeb.vue
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" tag="i" class="v-icon notranslate v-theme--light v-icon--size-default iconify iconify--mdi" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M16.36 14c.08-.66.14-1.32.14-2c0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2m-5.15 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95a8.03 8.03 0 0 1-4.33 3.56M14.34 14H9.66c-.1-.66-.16-1.32-.16-2c0-.68.06-1.35.16-2h4.68c.09.65.16 1.32.16 2c0 .68-.07 1.34-.16 2M12 19.96c-.83-1.2-1.5-2.53-1.91-3.96h3.82c-.41 1.43-1.08 2.76-1.91 3.96M8 8H5.08A7.923 7.923 0 0 1 9.4 4.44C8.8 5.55 8.35 6.75 8 8m-2.92 8H8c.35 1.25.8 2.45 1.4 3.56A8.008 8.008 0 0 1 5.08 16m-.82-2C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2c0 .68.06 1.34.14 2M12 4.03c.83 1.2 1.5 2.54 1.91 3.97h-3.82c.41-1.43 1.08-2.77 1.91-3.97M18.92 8h-2.95a15.65 15.65 0 0 0-1.38-3.56c1.84.63 3.37 1.9 4.33 3.56M12 2C6.47 2 2 6.5 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2Z"></path></svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -1,16 +1,23 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { useWalletStore } from '@/stores';
|
||||||
import { useBlockchain, useWalletStore } from '@/stores';
|
|
||||||
|
|
||||||
const walletStore = useWalletStore()
|
const walletStore = useWalletStore();
|
||||||
walletStore.$subscribe((m, s) => {
|
walletStore.$subscribe((m, s) => {
|
||||||
console.log(m, s)
|
console.log(m, s);
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<span v-if="walletStore.currentAddress">{{ walletStore.currentAddress }}</span>
|
<span v-if="walletStore.currentAddress">{{
|
||||||
<ping-connect-wallet class="mt-5" :chain-id="'juno-1'" :hd-path="`m/44'/118/0'/0/0`" />
|
walletStore.currentAddress
|
||||||
|
}}</span>
|
||||||
|
|
||||||
|
<label
|
||||||
|
v-else
|
||||||
|
for="PingConnectWallet"
|
||||||
|
class="btn btn-sm ml-4 ping-connect-btn"
|
||||||
|
>Connect Wallet</label
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -31,4 +31,13 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
<div class="footer-modal">
|
||||||
|
<ping-connect-wallet :chain-id="'juno-1'" :hd-path="`m/44'/118/0'/0/0`" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.footer-modal .ping-connect-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -11,6 +11,7 @@ import '@/plugins/vuetify/styles/styles.scss';
|
|||||||
import '@/style.css';
|
import '@/style.css';
|
||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
import { createPinia } from 'pinia';
|
import { createPinia } from 'pinia';
|
||||||
|
import LazyLoad from 'lazy-load-vue3';
|
||||||
// import router from "@/plugins/vuetify/router";
|
// import router from "@/plugins/vuetify/router";
|
||||||
import router from './router';
|
import router from './router';
|
||||||
import { useBaseStore } from './stores/useBaseStore';
|
import { useBaseStore } from './stores/useBaseStore';
|
||||||
@ -25,7 +26,7 @@ app.use(vuetify);
|
|||||||
app.use(createPinia());
|
app.use(createPinia());
|
||||||
app.use(layoutsPlugin);
|
app.use(layoutsPlugin);
|
||||||
app.use(router);
|
app.use(router);
|
||||||
|
app.use(LazyLoad, { component: true });
|
||||||
// Mount vue app
|
// Mount vue app
|
||||||
app.mount('#app');
|
app.mount('#app');
|
||||||
|
|
||||||
|
@ -136,7 +136,16 @@ function color(v: string) {
|
|||||||
<td>{{ v.version }}</td>
|
<td>{{ v.version }}</td>
|
||||||
<td>{{ v.ordering }}</td>
|
<td>{{ v.ordering }}</td>
|
||||||
<td>
|
<td>
|
||||||
<VChip :color="color(v.state)">{{ v.state }}</VChip>
|
<div
|
||||||
|
class="text-xs truncate relative py-2 px-4 rounded-full w-fit"
|
||||||
|
:class="`text-${color(v.state)}`"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="inset-x-0 inset-y-0 opacity-10 absolute"
|
||||||
|
:class="`bg-${color(v.state)}`"
|
||||||
|
></span>
|
||||||
|
{{ v.state }}
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
useStakingStore,
|
useStakingStore,
|
||||||
} from '@/stores';
|
} from '@/stores';
|
||||||
import { onMounted, computed, ref } from 'vue';
|
import { onMounted, computed, ref } from 'vue';
|
||||||
|
import { Icon } from '@iconify/vue';
|
||||||
import ValidatorCommissionRate from '@/components/ValidatorCommissionRate.vue';
|
import ValidatorCommissionRate from '@/components/ValidatorCommissionRate.vue';
|
||||||
import {
|
import {
|
||||||
consensusPubkeyToHexAddress,
|
consensusPubkeyToHexAddress,
|
||||||
@ -132,29 +133,47 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<VCard class="card-box">
|
<div style="border-width:1px" class="bg-base-100 px-4 pt-3 pb-4 rounded shadow border-indigo-500">
|
||||||
<VCardItem>
|
<div class="flex flex-col lg:flex-row">
|
||||||
<VRow>
|
<div class="flex-1">
|
||||||
<VCol cols="12" md="6">
|
<div class="flex">
|
||||||
<div class="d-flex pl-2">
|
<!-- avator -->
|
||||||
<VAvatar
|
<!-- <VAvatar
|
||||||
icon="mdi-help-circle-outline"
|
icon="mdi-help-circle-outline"
|
||||||
:image="avatars[identity]"
|
:image="avatars[identity]"
|
||||||
size="90"
|
size="90"
|
||||||
rounded
|
rounded
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
|
/> -->
|
||||||
|
<div
|
||||||
|
class="avatar mr-4 relative w-24 rounded-lg overflow-hidden"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="w-24 rounded-lg absolute opacity-10"
|
||||||
|
></div>
|
||||||
|
<div class="w-24 rounded-lg">
|
||||||
|
<img
|
||||||
|
v-if="avatars[identity] !== 'undefined'"
|
||||||
|
:src="`https://s3.amazonaws.com/keybase_processed_uploads/${avatars[identity]}`"
|
||||||
|
class="object-contain"
|
||||||
/>
|
/>
|
||||||
|
<Icon
|
||||||
|
v-else
|
||||||
|
class="text-4xl"
|
||||||
|
:icon="`mdi-help-circle-outline`"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="mx-2">
|
<div class="mx-2">
|
||||||
<h4>{{ v.description?.moniker }}</h4>
|
<h4>{{ v.description?.moniker }}</h4>
|
||||||
<div class="text-sm mb-2">
|
<div class="text-sm mb-2">
|
||||||
{{ v.description?.identity || '-' }}
|
{{ v.description?.identity || '-' }}
|
||||||
</div>
|
</div>
|
||||||
<VBtn>Delegate</VBtn>
|
<button class="btn btn-primary btn-sm w-full">Delegate</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<VSpacer />
|
<div class="m-4 text-sm">
|
||||||
<VCardText>
|
|
||||||
<p class="text-md">About Us</p>
|
<p class="text-md">About Us</p>
|
||||||
<VList class="card-list">
|
<VList class="card-list">
|
||||||
<VListItem prepend-icon="mdi-web">
|
<VListItem prepend-icon="mdi-web">
|
||||||
@ -166,7 +185,6 @@ onMounted(() => {
|
|||||||
><span> {{ v.description?.security_contact }}</span>
|
><span> {{ v.description?.security_contact }}</span>
|
||||||
</VListItem>
|
</VListItem>
|
||||||
</VList>
|
</VList>
|
||||||
|
|
||||||
<p class="text-md mt-3">Validator Status</p>
|
<p class="text-md mt-3">Validator Status</p>
|
||||||
<VList class="card-list">
|
<VList class="card-list">
|
||||||
<VListItem prepend-icon="mdi-shield-account-outline">
|
<VListItem prepend-icon="mdi-shield-account-outline">
|
||||||
@ -179,9 +197,9 @@ onMounted(() => {
|
|||||||
<span>Jailed: </span><span> {{ v.jailed || '-' }} </span>
|
<span>Jailed: </span><span> {{ v.jailed || '-' }} </span>
|
||||||
</VListItem>
|
</VListItem>
|
||||||
</VList>
|
</VList>
|
||||||
</VCardText>
|
</div>
|
||||||
</VCol>
|
</div>
|
||||||
<VCol cols="12" md="6">
|
<div class="flex-1">
|
||||||
<div class="d-flex flex-column py-3 justify-space-between">
|
<div class="d-flex flex-column py-3 justify-space-between">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<VAvatar
|
<VAvatar
|
||||||
@ -272,12 +290,11 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</VCol>
|
</div>
|
||||||
</VRow>
|
</div>
|
||||||
<VDivider />
|
<div class="divider"></div>
|
||||||
<VCardText>{{ v.description?.details }}</VCardText>
|
<div class="text-sm px-4">{{ v.description?.details }}</div>
|
||||||
</VCardItem>
|
</div>
|
||||||
</VCard>
|
|
||||||
|
|
||||||
<VRow class="mt-3">
|
<VRow class="mt-3">
|
||||||
<VCol md="4" sm="12" class="h-100">
|
<VCol md="4" sm="12" class="h-100">
|
||||||
|
@ -3,7 +3,7 @@ import { useBaseStore, useFormatter, useStakingStore } from '@/stores';
|
|||||||
import { toBase64, toHex } from '@cosmjs/encoding';
|
import { toBase64, toHex } from '@cosmjs/encoding';
|
||||||
import { computed } from '@vue/reactivity';
|
import { computed } from '@vue/reactivity';
|
||||||
import { onMounted, ref, type DebuggerEvent } from 'vue';
|
import { onMounted, ref, type DebuggerEvent } from 'vue';
|
||||||
import { consensusPubkeyToHexAddress } from '@/libs';
|
import { Icon } from '@iconify/vue';
|
||||||
import type { Key, Validator } from '@/types';
|
import type { Key, Validator } from '@/types';
|
||||||
const staking = useStakingStore();
|
const staking = useStakingStore();
|
||||||
const format = useFormatter();
|
const format = useFormatter();
|
||||||
@ -22,57 +22,56 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function fetchChange() {
|
async function fetchChange() {
|
||||||
console.log('fetch changes')
|
console.log('fetch changes');
|
||||||
let page = 0
|
let page = 0;
|
||||||
|
|
||||||
let height = Number(base.latest?.block?.header?.height || 0)
|
let height = Number(base.latest?.block?.header?.height || 0);
|
||||||
if (height > 14400) {
|
if (height > 14400) {
|
||||||
height -= 14400
|
height -= 14400;
|
||||||
} else {
|
} else {
|
||||||
height = 1
|
height = 1;
|
||||||
}
|
}
|
||||||
// voting power in 24h ago
|
// voting power in 24h ago
|
||||||
while (page < staking.validators.length && height > 0) {
|
while (page < staking.validators.length && height > 0) {
|
||||||
await base.fetchValidatorByHeight(height, page).then(x => {
|
await base.fetchValidatorByHeight(height, page).then((x) => {
|
||||||
x.validators.forEach(v => {
|
x.validators.forEach((v) => {
|
||||||
yesterday.value[v.pub_key.key] = Number(v.voting_power)
|
yesterday.value[v.pub_key.key] = Number(v.voting_power);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
page += 100
|
page += 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
page = 0
|
page = 0;
|
||||||
// voting power for now
|
// voting power for now
|
||||||
while (page < staking.validators.length) {
|
while (page < staking.validators.length) {
|
||||||
await base.fetchLatestValidators(page).then(x => {
|
await base.fetchLatestValidators(page).then((x) => {
|
||||||
x.validators.forEach(v => {
|
x.validators.forEach((v) => {
|
||||||
latest.value[v.pub_key.key] = Number(v.voting_power)
|
latest.value[v.pub_key.key] = Number(v.voting_power);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
page += 100
|
page += 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchChange();
|
fetchChange();
|
||||||
|
|
||||||
const changes = computed(() => {
|
const changes = computed(() => {
|
||||||
const changes = {} as Record<string, number>
|
const changes = {} as Record<string, number>;
|
||||||
Object.keys(latest.value).forEach(k => {
|
Object.keys(latest.value).forEach((k) => {
|
||||||
const l = latest.value[k] || 0
|
const l = latest.value[k] || 0;
|
||||||
const y = yesterday.value[k] || 0
|
const y = yesterday.value[k] || 0;
|
||||||
changes[k] = l - y
|
changes[k] = l - y;
|
||||||
})
|
});
|
||||||
return changes
|
return changes;
|
||||||
})
|
});
|
||||||
|
|
||||||
const change24 = (key: Key) => {
|
const change24 = (key: Key) => {
|
||||||
// console.log('hex key:', consensusPubkeyToHexAddress(key))
|
|
||||||
const txt = key.key;
|
const txt = key.key;
|
||||||
// const n: number = latest.value[txt];
|
// const n: number = latest.value[txt];
|
||||||
// const o: number = yesterday.value[txt];
|
// const o: number = yesterday.value[txt];
|
||||||
// // console.log( txt, n, o)
|
// // console.log( txt, n, o)
|
||||||
// return n > 0 && o > 0 ? n - o : 0;
|
// return n > 0 && o > 0 ? n - o : 0;
|
||||||
return changes.value[txt]
|
return changes.value[txt];
|
||||||
};
|
};
|
||||||
|
|
||||||
const change24Text = (key?: Key) => {
|
const change24Text = (key?: Key) => {
|
||||||
@ -187,12 +186,13 @@ const rank = function (position: number) {
|
|||||||
{{ list.length }}/{{ staking.params.max_validators }}
|
{{ list.length }}/{{ staking.params.max_validators }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<VCard>
|
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
|
||||||
<VTable class="text-no-wrap table-header-bg rounded-0">
|
<div class="overflow-x-auto">
|
||||||
|
<table class="table w-full">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" style="width: 3rem">#</th>
|
<th scope="col" style="width: 3rem; position: relative">#</th>
|
||||||
<th scope="col">VALIDATOR</th>
|
<th scope="col">VALIDATOR</th>
|
||||||
<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>
|
||||||
@ -204,24 +204,43 @@ const rank = function (position: number) {
|
|||||||
<tr v-for="(v, i) in list" :key="v.operator_address">
|
<tr v-for="(v, i) in list" :key="v.operator_address">
|
||||||
<!-- 👉 rank -->
|
<!-- 👉 rank -->
|
||||||
<td>
|
<td>
|
||||||
<VChip label :color="rank(i)">
|
<div
|
||||||
|
class="text-xs truncate relative py-2 px-4 rounded-full w-fit"
|
||||||
|
:class="`text-${rank(i)}`"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="inset-x-0 inset-y-0 opacity-10 absolute"
|
||||||
|
:class="`bg-${rank(i)}`"
|
||||||
|
></span>
|
||||||
{{ i + 1 }}
|
{{ i + 1 }}
|
||||||
</VChip>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- 👉 Validator -->
|
<!-- 👉 Validator -->
|
||||||
<td>
|
<td>
|
||||||
<div
|
<div
|
||||||
class="d-flex align-center overflow-hidden"
|
class="d-flex align-center overflow-hidden"
|
||||||
style="max-width: 400px"
|
style="max-width: 400px"
|
||||||
>
|
>
|
||||||
<VAvatar
|
<div
|
||||||
variant="tonal"
|
class="avatar mr-4 relative w-9 rounded-full overflow-hidden"
|
||||||
class="me-3"
|
>
|
||||||
size="34"
|
<div
|
||||||
icon="mdi-help-circle-outline"
|
class="w-9 rounded-full bg-gray-400 absolute opacity-10"
|
||||||
:image="logo(v.description?.identity)"
|
></div>
|
||||||
|
<div class="w-9 rounded-full">
|
||||||
|
<img
|
||||||
|
v-if="logo(v.description?.identity) !== ''"
|
||||||
|
v-lazy="logo(v.description?.identity)"
|
||||||
|
class="object-contain"
|
||||||
/>
|
/>
|
||||||
|
<Icon
|
||||||
|
v-else
|
||||||
|
class="text-4xl"
|
||||||
|
:icon="`mdi-help-circle-outline`"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="d-flex flex-column">
|
<div class="d-flex flex-column">
|
||||||
<h6 class="text-sm text-primary">
|
<h6 class="text-sm text-primary">
|
||||||
<RouterLink
|
<RouterLink
|
||||||
@ -270,7 +289,15 @@ const rank = function (position: number) {
|
|||||||
:class="change24Color(v.consensus_pubkey)"
|
:class="change24Color(v.consensus_pubkey)"
|
||||||
>
|
>
|
||||||
{{ change24Text(v.consensus_pubkey) }}
|
{{ change24Text(v.consensus_pubkey) }}
|
||||||
<VChip label v-if="v.jailed" color="error">Jailed</VChip>
|
<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">
|
||||||
@ -286,13 +313,26 @@ const rank = function (position: number) {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</VTable>
|
</table>
|
||||||
<VDivider />
|
</div>
|
||||||
<VCardActions class="py-2">
|
|
||||||
<VChip label color="error">Top 33%</VChip>
|
<div class="divider"></div>
|
||||||
<VChip label color="warning" class="mx-2">Top 67%</VChip>
|
<div class="flex flex-row">
|
||||||
</VCardActions>
|
<div
|
||||||
</VCard>
|
class="text-xs truncate relative py-2 px-4 rounded-md w-fit text-error mr-2"
|
||||||
|
>
|
||||||
|
<span class="inset-x-0 inset-y-0 opacity-10 absolute bg-error"></span>
|
||||||
|
Top 33%
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-xs truncate relative py-2 px-4 rounded-md w-fit text-warning"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="inset-x-0 inset-y-0 opacity-10 absolute bg-warning"
|
||||||
|
></span>
|
||||||
|
Top 67%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -38,9 +38,11 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<VCard>
|
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||||
<VCardTitle>What's State Sync?</VCardTitle>
|
<h2 class="card-title truncate mb-2">
|
||||||
<VCardText>
|
What's State Sync?
|
||||||
|
</h2>
|
||||||
|
<div class="text-sm">
|
||||||
The Tendermint Core 0.34 release includes support for state sync, which
|
The Tendermint Core 0.34 release includes support for state sync, which
|
||||||
allows a new node to join a network by fetching a snapshot of the
|
allows a new node to join a network by fetching a snapshot of the
|
||||||
application state at a recent height instead of fetching and replaying
|
application state at a recent height instead of fetching and replaying
|
||||||
@ -52,36 +54,39 @@ onMounted(() => {
|
|||||||
>here</a
|
>here</a
|
||||||
>
|
>
|
||||||
for more infomation.
|
for more infomation.
|
||||||
</VCardText>
|
</div>
|
||||||
</VCard>
|
</div>
|
||||||
|
|
||||||
<VCard class="my-5">
|
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||||
<VCardTitle>Starting New Node From State Sync</VCardTitle>
|
<h2 class="card-title truncate mb-2">Starting New Node From State Sync</h2>
|
||||||
<VCardItem>
|
<div class="text-sm">
|
||||||
1. Install Binary ({{ appName }} Version:
|
1. Install Binary ({{ appName }} Version:
|
||||||
{{ nodeInfo.application_version?.version || '' }})
|
{{ nodeInfo.application_version?.version || '' }})
|
||||||
<br />
|
<br />
|
||||||
We need to install the binary first and make sure that the version is
|
We need to install the binary first and make sure that the version is
|
||||||
the one currently in use on mainnet.
|
the one currently in use on mainnet.
|
||||||
<br />
|
<br /> <br />
|
||||||
2. Enable State Sync<br />
|
2. Enable State Sync<br />
|
||||||
We can configure Tendermint to use state sync in
|
We can configure Tendermint to use state sync in
|
||||||
$DAEMON_HOME/config/config.toml.
|
$DAEMON_HOME/config/config.toml.
|
||||||
|
<br /><br />
|
||||||
<VTextarea auto-grow :model-value="state"></VTextarea>
|
<VTextarea auto-grow :model-value="state"></VTextarea>
|
||||||
|
<br />
|
||||||
3. Start the daemon: <code>{{ appName }} start</code>
|
3. Start the daemon: <code>{{ appName }} start</code>
|
||||||
<br />
|
<br />
|
||||||
If you are resetting node, run
|
If you are resetting node, run
|
||||||
<code>{{ appName }} unsafe-reset-all</code> or
|
<code>{{ appName }} unsafe-reset-all</code> or
|
||||||
<code>{{ appName }} tendermint unsafe-reset-all --home ~/.HOME</code>
|
<code>{{ appName }} tendermint unsafe-reset-all --home ~/.HOME</code>
|
||||||
before you start the daemon.
|
before you start the daemon.
|
||||||
</VCardItem>
|
</div>
|
||||||
</VCard>
|
</div>
|
||||||
|
|
||||||
<VCard>
|
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
|
||||||
<VCardTitle>Enable Snapshot For State Sync</VCardTitle>
|
<h2 class="card-title truncate mb-2">Enable Snapshot For State Sync</h2>
|
||||||
<VCardItem>
|
<div class="text-sm">
|
||||||
To make state sync works, we can enable snapshot in
|
To make state sync works, we can enable snapshot in
|
||||||
$DAEMON_HOME/config/app.toml
|
$DAEMON_HOME/config/app.toml
|
||||||
|
<br/><br/>
|
||||||
<VTextarea
|
<VTextarea
|
||||||
auto-grow
|
auto-grow
|
||||||
model-value="[state-sync]
|
model-value="[state-sync]
|
||||||
@ -93,8 +98,8 @@ snapshot-interval = 1000
|
|||||||
snapshot-keep-recent = 2"
|
snapshot-keep-recent = 2"
|
||||||
>
|
>
|
||||||
</VTextarea>
|
</VTextarea>
|
||||||
</VCardItem>
|
</div>
|
||||||
</VCard>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -12,7 +12,8 @@ const dashboard = useDashboard();
|
|||||||
|
|
||||||
dashboard.$subscribe((mutation, state) => {
|
dashboard.$subscribe((mutation, state) => {
|
||||||
localStorage.setItem('favorite', JSON.stringify(state.favorite));
|
localStorage.setItem('favorite', JSON.stringify(state.favorite));
|
||||||
dashboard.loadingPrices()
|
// TODO: cause endless loop
|
||||||
|
// dashboard.loadingPrices()
|
||||||
});
|
});
|
||||||
const keywords = ref('');
|
const keywords = ref('');
|
||||||
const chains = computed(() => {
|
const chains = computed(() => {
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { useBlockchain } from './useBlockchain';
|
import { useBlockchain } from './useBlockchain';
|
||||||
import { fromBech32, toBech32 } from '@cosmjs/encoding';
|
import { fromBech32, toBech32 } from '@cosmjs/encoding';
|
||||||
import type { Delegation, Coin, UnbondingResponses, DelegatorRewards } from '@/types';
|
import type {
|
||||||
|
Delegation,
|
||||||
|
Coin,
|
||||||
|
UnbondingResponses,
|
||||||
|
DelegatorRewards,
|
||||||
|
} from '@/types';
|
||||||
import { useStakingStore } from './useStakingStore';
|
import { useStakingStore } from './useStakingStore';
|
||||||
|
|
||||||
export const useWalletStore = defineStore('walletStore', {
|
export const useWalletStore = defineStore('walletStore', {
|
||||||
@ -18,77 +23,87 @@ export const useWalletStore = defineStore('walletStore', {
|
|||||||
return useBlockchain();
|
return useBlockchain();
|
||||||
},
|
},
|
||||||
connectedWallet() {
|
connectedWallet() {
|
||||||
const chainStore = useBlockchain()
|
const chainStore = useBlockchain();
|
||||||
const key = chainStore.defaultHDPath
|
const key = chainStore.defaultHDPath;
|
||||||
|
|
||||||
const connected = JSON.parse(localStorage.getItem(key)||"{}")
|
const connected = JSON.parse(localStorage.getItem(key) || '{}');
|
||||||
return connected
|
return connected;
|
||||||
},
|
},
|
||||||
balanceOfStakingToken(): Coin {
|
balanceOfStakingToken(): Coin {
|
||||||
const stakingStore = useStakingStore()
|
const stakingStore = useStakingStore();
|
||||||
return this.balances.find( x => x.denom === stakingStore.params.bond_denom) || {amount: "0", denom: stakingStore.params.bond_denom}
|
return (
|
||||||
|
this.balances.find(
|
||||||
|
(x) => x.denom === stakingStore.params.bond_denom
|
||||||
|
) || { amount: '0', denom: stakingStore.params.bond_denom }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
stakingAmount() {
|
stakingAmount() {
|
||||||
let amt = 0
|
let amt = 0;
|
||||||
let denom = ''
|
let denom = '';
|
||||||
this.delegations.forEach((i) => {
|
this.delegations.forEach((i) => {
|
||||||
amt += Number(i.balance.amount)
|
amt += Number(i.balance.amount);
|
||||||
denom = i.balance.denom
|
denom = i.balance.denom;
|
||||||
})
|
});
|
||||||
return {amount: String(amt), denom}
|
return { amount: String(amt), denom };
|
||||||
},
|
},
|
||||||
rewardAmount() {
|
rewardAmount() {
|
||||||
|
const stakingStore = useStakingStore();
|
||||||
const stakingStore = useStakingStore()
|
const reward = this.rewards.total?.find(
|
||||||
const reward = this.rewards.total?.find(x => x.denom === stakingStore.params.bond_denom)
|
(x) => x.denom === stakingStore.params.bond_denom
|
||||||
return reward || {amount: "0", denom: stakingStore.params.bond_denom}
|
);
|
||||||
|
return reward || { amount: '0', denom: stakingStore.params.bond_denom };
|
||||||
},
|
},
|
||||||
unbondingAmount() {
|
unbondingAmount() {
|
||||||
let amt = 0
|
let amt = 0;
|
||||||
let denom = ''
|
let denom = '';
|
||||||
this.unbonding.forEach((i) => {
|
this.unbonding.forEach((i) => {
|
||||||
i.entries.forEach(e => {
|
i.entries.forEach((e) => {
|
||||||
amt += Number(e.balance)
|
amt += Number(e.balance);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
const stakingStore = useStakingStore()
|
const stakingStore = useStakingStore();
|
||||||
return {amount: String(amt), denom: stakingStore.params.bond_denom}
|
return { amount: String(amt), denom: stakingStore.params.bond_denom };
|
||||||
},
|
},
|
||||||
currentAddress() {
|
currentAddress() {
|
||||||
if(this.connectedWallet.cosmosAddress) {
|
if (!this.connectedWallet?.cosmosAddress) return '';
|
||||||
const { prefix, data } = fromBech32(this.connectedWallet.cosmosAddress);
|
const { prefix, data } = fromBech32(this.connectedWallet.cosmosAddress);
|
||||||
const chainStore = useBlockchain()
|
const chainStore = useBlockchain();
|
||||||
return toBech32(chainStore.current?.bech32Prefix || prefix, data)
|
return toBech32(chainStore.current?.bech32Prefix || prefix, data);
|
||||||
} else {
|
},
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async loadMyAsset() {
|
async loadMyAsset() {
|
||||||
if(!this.currentAddress) return
|
if (!this.currentAddress) return;
|
||||||
this.blockchain.rpc.getBankBalances(this.currentAddress).then(x => {
|
this.blockchain.rpc.getBankBalances(this.currentAddress).then((x) => {
|
||||||
this.balances = x.balances
|
this.balances = x.balances;
|
||||||
})
|
});
|
||||||
this.blockchain.rpc.getStakingDelegations(this.currentAddress).then(x => {
|
this.blockchain.rpc
|
||||||
this.delegations = x.delegation_responses
|
.getStakingDelegations(this.currentAddress)
|
||||||
})
|
.then((x) => {
|
||||||
this.blockchain.rpc.getStakingDelegatorUnbonding(this.currentAddress).then(x => {
|
this.delegations = x.delegation_responses;
|
||||||
this.unbonding = x.unbonding_responses
|
});
|
||||||
})
|
this.blockchain.rpc
|
||||||
this.blockchain.rpc.getDistributionDelegatorRewards(this.currentAddress).then(x => {
|
.getStakingDelegatorUnbonding(this.currentAddress)
|
||||||
this.rewards = x
|
.then((x) => {
|
||||||
})
|
this.unbonding = x.unbonding_responses;
|
||||||
|
});
|
||||||
|
this.blockchain.rpc
|
||||||
|
.getDistributionDelegatorRewards(this.currentAddress)
|
||||||
|
.then((x) => {
|
||||||
|
this.rewards = x;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
myBalance() {
|
myBalance() {
|
||||||
return this.blockchain.rpc.getBankBalances(this.currentAddress)
|
return this.blockchain.rpc.getBankBalances(this.currentAddress);
|
||||||
},
|
},
|
||||||
myDelegations() {
|
myDelegations() {
|
||||||
return this.blockchain.rpc.getStakingDelegations(this.currentAddress)
|
return this.blockchain.rpc.getStakingDelegations(this.currentAddress);
|
||||||
},
|
},
|
||||||
myUnbonding() {
|
myUnbonding() {
|
||||||
return this.blockchain.rpc.getStakingDelegatorUnbonding(this.currentAddress)
|
return this.blockchain.rpc.getStakingDelegatorUnbonding(
|
||||||
}
|
this.currentAddress
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
12
yarn.lock
12
yarn.lock
@ -5977,6 +5977,11 @@ knitwork@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/knitwork/-/knitwork-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/knitwork/-/knitwork-1.0.0.tgz"
|
||||||
integrity sha512-dWl0Dbjm6Xm+kDxhPQJsCBTxrJzuGl0aP9rhr+TG8D3l+GL90N8O8lYUi7dTSAN2uuDqCtNgb6aEuQH5wsiV8Q==
|
integrity sha512-dWl0Dbjm6Xm+kDxhPQJsCBTxrJzuGl0aP9rhr+TG8D3l+GL90N8O8lYUi7dTSAN2uuDqCtNgb6aEuQH5wsiV8Q==
|
||||||
|
|
||||||
|
lazy-load-vue3@^1.3.0:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lazy-load-vue3/-/lazy-load-vue3-1.3.0.tgz#6aaf573d1d09626ac11fec6b2de9fd07c0130ac0"
|
||||||
|
integrity sha512-IcyeMiDZrxzuIqqwkYkC+6RRQBdm0yTR0onIj6NITrWd6LaUlzzRmbvAmuzGB0g4H1HeVm/Oa0FokxD2lz9UAA==
|
||||||
|
|
||||||
ledger-cosmos-js@^2.1.8:
|
ledger-cosmos-js@^2.1.8:
|
||||||
version "2.1.8"
|
version "2.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/ledger-cosmos-js/-/ledger-cosmos-js-2.1.8.tgz#b409ecd1e77f630e6fb212a9f602fe5c6e8f054b"
|
resolved "https://registry.yarnpkg.com/ledger-cosmos-js/-/ledger-cosmos-js-2.1.8.tgz#b409ecd1e77f630e6fb212a9f602fe5c6e8f054b"
|
||||||
@ -6709,10 +6714,9 @@ pify@^3.0.0:
|
|||||||
resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz"
|
resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz"
|
||||||
integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==
|
integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==
|
||||||
|
|
||||||
ping-widget@^0.0.4:
|
"ping-widget@git+https://github.com/ping-pub/widget.git":
|
||||||
version "0.0.4"
|
version "0.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/ping-widget/-/ping-widget-0.0.4.tgz#b42487cca64fe0be1a4e89c6f300630e3bcae987"
|
resolved "git+https://github.com/ping-pub/widget.git#11de05f773e60430f332fb77b4a37e5ae8c681b2"
|
||||||
integrity sha512-FLzhgd/b+QZt2H7TucmLu8ZXiQh6Op07Usw78XlXBYVwXFtaI5PisKXzV6TftkEa5l8Q1YKMA5d8a8FwLe17HA==
|
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cosmjs/amino" "^0.30.1"
|
"@cosmjs/amino" "^0.30.1"
|
||||||
"@cosmjs/ledger-amino" "^0.30.1"
|
"@cosmjs/ledger-amino" "^0.30.1"
|
||||||
|
Loading…
Reference in New Issue
Block a user