Merge pull request #18 from ping-pub/master

merge
This commit is contained in:
Alisa
2023-06-08 17:22:19 +08:00
committed by GitHub
29 changed files with 388 additions and 110 deletions
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { Icon } from '@iconify/vue';
import { controlledComputed } from '@vueuse/core'
interface Props {
title: string;
+5 -4
View File
@@ -2,7 +2,7 @@
import { computed, ref } from 'vue';
const props = defineProps({
total: { type: Number },
total: { type: String },
limit: { type: Number },
callback: { type: Function, required: true },
});
@@ -10,12 +10,13 @@ const current = ref(1)
const showSize = 3
const pages = computed(() => {
const pages: { color: string, page: number }[] = []
if (props.total && props.limit && props.total > props.limit) {
const total = Number(props.total || 0)
if (total > 0 && props.limit && total > props.limit) {
let page = 0
while (true) {
if (page * props.limit > props.total) break
if (page * props.limit > total) break
page += 1
if (props.total / props.limit > 10 && page > showSize && page < (props.total / props.limit - showSize + 1)) {
if (total / props.limit > 10 && page > showSize && page < (total / props.limit - showSize + 1)) {
if (!(page >= current.value - 1 && page <= current.value + 1)) {
continue
}
+1 -1
View File
@@ -7,7 +7,7 @@ const store = useTxDialog();
:type="store.type"
:sender="store.sender"
:endpoint="store.endpoint"
:params="store.params"
:params='store.params'
@view="store.view"
@confirmed="store.confirmed"
></ping-tx-dialog>
+1
View File
@@ -1,6 +1,7 @@
<script lang="ts" setup>
import { toBase64, toHex } from '@cosmjs/encoding';
import { computed } from '@vue/reactivity';
import { ref } from 'vue';
const props = defineProps(['value']);
const format = ref('base64');
+2
View File
@@ -1,4 +1,6 @@
<script lang="ts">
import { defineComponent, resolveComponent, h } from 'vue'
export default defineComponent({
setup() {
const routerView = resolveComponent('router-view');
+29 -16
View File
@@ -13,6 +13,7 @@ import { useBlockchain } from '@/stores';
import NavBarI18n from './NavBarI18n.vue';
import NavBarWallet from './NavBarWallet.vue';
import type { NavGroup, NavLink, NavSectionTitle, VerticalNavItems } from '../types';
const dashboard = useDashboard();
dashboard.initial();
@@ -35,6 +36,20 @@ const changeOpen = (index: Number) => {
}
};
const showDiscord = window.location.host.search('ping.pub') > -1;
function isNavGroup(nav: VerticalNavItems | any): nav is NavGroup {
return (<NavGroup>nav).children !== undefined;
}
function isNavLink(nav: VerticalNavItems | any): nav is NavLink {
return (<NavLink>nav).to !== undefined;
}
function isNavTitle(nav: VerticalNavItems | any): nav is NavSectionTitle {
return (<NavSectionTitle>nav).heading !== undefined;
}
function selected(route: any, nav: NavLink) {
const b = route.path === nav.to?.path || route.path.startsWith(nav.to?.path) && nav.title.indexOf('dashboard') === -1
return b
}
</script>
<template>
@@ -45,10 +60,12 @@ const showDiscord = window.location.host.search('ping.pub') > -1;
:class="{ block: sidebarShow, 'hidden xl:!block': !sidebarShow }"
>
<div class="flex items-center pl-4 py-4 mb-1">
<img class="w-10 h-10" src="../../assets/logo.svg" />
<h1 class="flex-1 ml-3 text-2xl font-semibold dark:text-white">
Ping.pub
</h1>
<RouterLink to="/" class="flex items-center">
<img class="w-10 h-10" src="../../assets/logo.svg" />
<h1 class="flex-1 ml-3 text-2xl font-semibold dark:text-white">
Ping.pub
</h1>
</RouterLink>
<div
class="pr-4 cursor-pointer xl:!hidden"
@click="sidebarShow = false"
@@ -62,7 +79,7 @@ const showDiscord = window.location.host.search('ping.pub') > -1;
class="px-2"
>
<div
v-if="item?.title && item?.children?.length"
v-if="isNavGroup(item)"
:tabindex="index"
class="collapse"
:class="{
@@ -107,17 +124,15 @@ const showDiscord = window.location.host.search('ping.pub') > -1;
</div>
</div>
<div class="collapse-content">
<div class="menu bg-base-100 w-full">
<div v-for="(el, key) of item?.children" class="menu bg-base-100 w-full">
<RouterLink
v-for="(el, key) of item?.children"
v-if="isNavLink(el)"
@click="sidebarShow = false"
:key="key"
class="hover:bg-gray-100 dark:hover:bg-[#373f59] rounded cursor-pointer px-3 py-2 flex items-center"
:to="el?.to"
:class="{
'!bg-primary':
$route.path === el?.to?.path && item?.title !== 'Favorite',
'!bg-primary': selected($route, el),
}"
:to="el.to"
>
<Icon
v-if="!el?.icon?.image"
@@ -137,9 +152,7 @@ const showDiscord = window.location.host.search('ping.pub') > -1;
<div
class="text-base capitalize text-gray-500 dark:text-gray-300"
:class="{
'text-white':
$route.path === el?.to?.path &&
item?.title !== 'Favorite',
'text-white': item?.title !== 'Favorite',
}"
>
{{ item?.title === 'Favorite' ? el?.title : $t(el?.title) }}
@@ -150,8 +163,8 @@ const showDiscord = window.location.host.search('ping.pub') > -1;
</div>
<RouterLink
v-if="isNavLink(item)"
:to="item?.to"
v-if="item?.title && !item?.children?.length && item?.to"
@click="sidebarShow = false"
class="cursor-pointer rounded-lg px-4 flex items-center py-2 hover:bg-gray-100 dark:hover:bg-[#373f59]"
>
@@ -183,7 +196,7 @@ const showDiscord = window.location.host.search('ping.pub') > -1;
</div>
</RouterLink>
<div
v-if="item?.heading"
v-if="isNavTitle(item)"
class="px-4 text-sm text-gray-400 pb-2 uppercase"
>
{{ item?.heading }}
+3 -1
View File
@@ -1,6 +1,8 @@
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { Icon } from '@iconify/vue';
import { useI18n } from 'vue-i18n';
const i18nLangs: Array<{ label: string; i18nLang: string }> = [
{
label: 'English',
@@ -13,7 +15,7 @@ const i18nLangs: Array<{ label: string; i18nLang: string }> = [
];
let locale = ref(useI18n({ useScope: 'global' }).locale);
watch(locale, (val: string) => {
watch(locale, (val) => {
document.documentElement.setAttribute('lang', val as string);
});
+1
View File
@@ -108,6 +108,7 @@ const tipMsg = computed(() => {
<ping-connect-wallet
:chain-id="baseStore.currentChainId"
:hd-path="chainStore.defaultHDPath"
:prefix="chainStore.current?.bech32Prefix"
@connect="walletStateChange"
@keplr-config="walletStore.suggestChain()"
/>
+59
View File
@@ -0,0 +1,59 @@
// 👉 Vertical nav section title
export interface NavSectionTitle extends Partial<AclProperties> {
heading: string
}
// 👉 Vertical nav link
declare type ATagTargetAttrValues = '_blank' | '_self' | '_parent' | '_top' | 'framename'
declare type ATagRelAttrValues =
| 'alternate'
| 'author'
| 'bookmark'
| 'external'
| 'help'
| 'license'
| 'next'
| 'nofollow'
| 'noopener'
| 'noreferrer'
| 'prev'
| 'search'
| 'tag'
export interface NavLinkProps {
to?: RouteLocationRaw | string | null
href?: string
target?: ATagTargetAttrValues
rel?: ATagRelAttrValues
i18n?: boolean
}
export interface Icon {
icon?: string,
image?: string,
size: string,
}
export interface NavLink extends NavLinkProps {
title: string
icon?: Icon
badgeContent?: string | number
badgeClass?: string
disable?: boolean
order?: number
}
// 👉 Vertical nav group
export interface NavGroup {
title: string
icon?: Icon
badgeContent?: string | number
badgeClass?: string
children: (NavLink | NavGroup)[]
disable?: boolean
order?: number
i18n?: boolean
}
export declare type VerticalNavItems = (NavLink | NavGroup | NavSectionTitle)[]
export declare type HorizontalNavItems = (NavLink | NavGroup)[]
+4 -57
View File
@@ -51,9 +51,10 @@ export function formatTokenAmount(
tokenDenom = 'uatom',
format = true
) {
const denom = tokenDenom?.denom_trace
? tokenDenom?.denom_trace?.base_denom
: tokenDenom;
const denom = typeof tokenDenom === 'string'
? tokenDenom
// @ts-ignore
: tokenDenom?.denom_trace?.base_denom;
let amount = 0;
const asset = assets.find((a: any) => a.base === denom);
let exp = asset
@@ -79,60 +80,6 @@ export function numberWithCommas(x: any) {
return parts.join('.');
}
export function tokenFormatter(tokens: any, denoms = {}, decimal = 2) {
if (Array.isArray(tokens)) {
return tokens.map((t) => formatToken(t, denoms, decimal)).join(', ');
}
return formatToken(tokens, denoms, 2);
}
export function formatToken(
token: any,
IBCDenom = {},
decimals = 2,
withDenom = true
) {
if (token) {
const denom = IBCDenom[token.denom] || token.denom;
if (withDenom) {
return `${formatTokenAmount(
token.amount,
decimals,
denom
)} ${formatTokenDenom(denom)}`;
}
return formatTokenAmount(token.amount, decimals, denom);
}
return token;
}
export function formatTokenDenom(tokenDenom: any) {
if (tokenDenom && tokenDenom.code === undefined) {
let denom = tokenDenom.denom_trace
? tokenDenom.denom_trace.base_denom
: tokenDenom;
const chains = getLocalChains();
const selected = localStorage.getItem('selected_chain');
const selChain = chains[selected];
const nativeAsset = selChain.assets.find((a) => a.base === denom);
if (nativeAsset) {
denom = nativeAsset.symbol;
} else {
const config = Object.values(chains);
config.forEach((x) => {
if (x.assets) {
const asset = x.assets.find((a) => a.base === denom);
if (asset) denom = asset.symbol;
}
});
}
return denom.length > 10
? `${denom.substring(0, 7).toUpperCase()}..${denom.substring(
denom.length - 3
)}`
: denom.toUpperCase();
}
return '';
}
export function isToken(value: string) {
let is = false;
if (Array.isArray(value)) {
+12 -5
View File
@@ -7,11 +7,12 @@ import {
useDashboard,
useStakingStore,
} from '@/stores';
import { consensusPubkeyToHexAddress } from '@/libs';
const format = useFormatter();
const chainStore = useBlockchain();
const dashboard = useDashboard();
const stakingStore = useStakingStore();
import { consensusPubkeyToHexAddress } from '@/libs';
const rpcList = ref(
chainStore.current?.endpoints?.rpc || [{ address: '', provider: '' }]
);
@@ -42,21 +43,24 @@ onUnmounted(() => {
});
const newTime = computed(() => {
return format.toDay(updatetime.value || '', 'time');
return format.toDay(updatetime.value?.toDateString(), 'time');
});
const vals = computed(() => {
return validators.value.map((x) => {
const x2 = x;
// @ts-ignore
x2.hex = consensusPubkeyToHexAddress(x.consensus_pubkey);
return x2;
});
});
function showName(i, text) {
function showName(i:number, text: string) {
if (text === 'nil-Vote') {
// @ts-ignore
if (positions.value?.[i]?.address) {
const val = vals.value.find(
// @ts-ignore
(x) => x.hex === positions.value?.[i]?.address
);
return val?.description?.moniker || i;
@@ -65,10 +69,11 @@ function showName(i, text) {
}
const txt = text.substring(text.indexOf(':') + 1, text.indexOf(' '));
const sig = text.split(' ');
// @ts-ignore
const val = validators.value.find((x) => x?.hex?.startsWith(txt));
return `${val?.description?.moniker || txt} - ${sig[2]}`;
}
function color(i, txt) {
function color(i: number, txt: string) {
if (i === roundState.value?.proposer?.index) {
return txt === 'nil-Vote' ? 'warning' : 'primary';
}
@@ -99,7 +104,9 @@ async function fetchPosition() {
const data = await response.json();
positions.value = data.result.round_state.validators.validators;
} catch (error) {
// @ts-ignore
httpstatus.value = error?.status || 500;
// @ts-ignore
httpStatusText.value = error?.message || 'Error';
}
}
@@ -125,7 +132,7 @@ async function update() {
step.value = raw[2];
// find the highest onboard rate
roundState.value?.height_vote_set?.forEach((element) => {
roundState.value?.height_vote_set?.forEach((element: any) => {
const rates = Number(
element.prevotes_bit_array.substring(
element.prevotes_bit_array.length - 4
@@ -158,7 +158,7 @@ const result = ref('');
</table>
<div class="flex justify-between">
<PaginationBar
:limit="50"
:limit="pageRequest.limit"
:total="response.pagination?.total"
:callback="loadContract"
/>
+1 -1
View File
@@ -201,7 +201,7 @@ function updateState() {
</div>
</div>
<div class="grid grid-cols-2 gap-4 md:!grid-cols-3 lg:!grid-cols-6">
<div class="grid grid-cols-1 gap-4 md:!grid-cols-3 lg:!grid-cols-6">
<div v-for="(item, key) in store.stats" :key="key">
<CardStatisticsVertical v-bind="item" />
</div>
+5 -2
View File
@@ -9,7 +9,7 @@ import {
import { useDistributionStore } from '@/stores/useDistributionStore';
import { useMintStore } from '@/stores/useMintStore';
import { useStakingStore } from '@/stores/useStakingStore';
import type { Tally } from '@/types';
import type { Coin, Tally } from '@/types';
import numeral from 'numeral';
import { defineStore } from 'pinia';
@@ -175,6 +175,7 @@ export const useIndexModule = defineStore('module-index', {
color: 'warning',
icon: 'mdi-lock',
stats: formatter.formatTokenAmount({
// @ts-ignore
amount: this.pool.bonded_tokens,
denom: staking.params.bond_denom,
}),
@@ -192,8 +193,9 @@ export const useIndexModule = defineStore('module-index', {
color: 'primary',
icon: 'mdi-bank',
stats: formatter.formatTokens(
// @ts-ignore
this.communityPool?.filter(
(x) => x.denom === staking.params.bond_denom
(x: Coin) => x.denom === staking.params.bond_denom
)
),
change: 0,
@@ -203,6 +205,7 @@ export const useIndexModule = defineStore('module-index', {
coingeckoId() {
this.tickerIndex = 0;
// @ts-ignore
const [firstAsset] = this.blockchain?.assets || [];
return firstAsset.coingecko_id
}
+6 -6
View File
@@ -26,11 +26,11 @@ const chains = computed(() => {
</script>
<template>
<div class="">
<div class="flex items-center justify-center mb-6 mt-14">
<div class="w-8 md:!w-16 rounded-full mr-3">
<div class="flex flex-col md:flex-row items-center justify-center mb-6 mt-14 gap-3">
<div class="w-16 rounded-full">
<img src="/logo.svg" />
</div>
<h1 class="text-primary text-3xl md:!text-6xl font-bold mr-2">
<h1 class="text-primary text-3xl md:!text-6xl font-bold">
Ping dashboard
</h1>
<div class="badge badge-primary badge-outline mt-1 text-sm md:!mt-8">
@@ -50,14 +50,14 @@ const chains = computed(() => {
<progress class="progress progress-info w-80 h-1"></progress>
</div>
<div class="flex items-center rounded-full bg-base-100 border border-gray-200 dark:border-gray-700 mt-10">
<div class="flex items-center rounded-lg bg-base-100 border border-gray-200 dark:border-gray-700 mt-10">
<Icon icon="mdi:magnify" class="text-2xl text-gray-400 ml-3"/>
<input :placeholder="$t('index.search_placeholder')" class="px-4 h-10 bg-transparent flex-1 outline-none text-base" v-model="keywords" />
<div class="px-4 text-base">{{ chains.length }}/{{ dashboard.length }}</div>
<div class="px-4 text-base hidden md:block">{{ chains.length }}/{{ dashboard.length }}</div>
</div>
<div
class="grid grid-cols-2 gap-4 mt-6 md:!grid-cols-3 lg:!grid-cols-4 2xl:!grid-cols-5"
class="grid grid-cols-1 gap-4 mt-6 md:!grid-cols-3 lg:!grid-cols-4 2xl:!grid-cols-5"
>
<ChainSummary
v-for="(chain, index) in chains"
+3 -1
View File
@@ -1,6 +1,8 @@
import { useBlockchain } from "@/stores";
import { setupLayouts } from "virtual:generated-layouts";
import { createRouter, createWebHistory } from "vue-router";
// @ts-ignore
import { setupLayouts } from "virtual:generated-layouts";
// @ts-ignore
import routes from "~pages";
const router = createRouter({
+1
View File
@@ -10,6 +10,7 @@ export const useBankStore = defineStore('bankstore', {
supply: {} as Coin,
balances: {} as Record<string, Coin[]>,
totalSupply: { supply: [] as Coin[] },
ibcDenoms: {} as Record<string, DenomTrace>
};
},
getters: {
+4 -4
View File
@@ -5,7 +5,7 @@ import {
type Endpoint,
EndpointType,
} from './useDashboard';
import type { VerticalNavItems } from '@/@layouts/types';
import type { NavGroup, NavLink, NavSectionTitle, VerticalNavItems } from '@/layouts/types';
import { useRouter } from 'vue-router';
import { CosmosRestClient } from '@/libs/client';
import {
@@ -107,7 +107,7 @@ export const useBlockchain = defineStore('blockchain', {
// combine all together
return [
...currNavItem,
{ heading: 'Ecosystem' },
{ heading: 'Ecosystem' } as NavSectionTitle,
{
title: 'Favorite',
children: favNavItems,
@@ -115,7 +115,7 @@ export const useBlockchain = defineStore('blockchain', {
badgeClass: 'bg-primary',
i18n: true,
icon: { icon: 'mdi-star', size: '22' },
},
} as NavGroup,
{
title: 'All Blockchains',
to: { path: '/' },
@@ -123,7 +123,7 @@ export const useBlockchain = defineStore('blockchain', {
badgeClass: 'bg-primary',
i18n: true,
icon: { icon: 'mdi-grid', size: '22' },
},
} as NavLink,
];
},
},
+2 -1
View File
@@ -147,6 +147,7 @@ export const useFormatter = defineStore('formatter', {
}
const conf = mode === 'local'? this.blockchain.current?.assets?.find(
// @ts-ignore
(x) => x.base === token.denom || x.base.denom === token.denom
): this.findGlobalAssetConfig(token.denom)
@@ -226,7 +227,7 @@ export const useFormatter = defineStore('formatter', {
numberAndSign(input: number, fmt = '+0,0') {
return numeral(input).format(fmt);
},
toDay(time?: string, format = 'long') {
toDay(time?: string | number, format = 'long') {
if (!time) return '';
if (format === 'long') {
return dayjs(time).format('YYYY-MM-DD HH:mm');
+4 -4
View File
@@ -17,7 +17,7 @@ export const useWalletStore = defineStore('walletStore', {
balances: [] as Coin[],
delegations: [] as Delegation[],
unbonding: [] as UnbondingResponses[],
rewards: {} as DelegatorRewards,
rewards: {total: [], rewards: []} as DelegatorRewards,
walletIsConnected: {} as WalletConnected
};
},
@@ -28,7 +28,7 @@ export const useWalletStore = defineStore('walletStore', {
connectedWallet() {
const chainStore = useBlockchain();
const key = chainStore.defaultHDPath;
let connected = this.walletIsConnected
let connected = {} as WalletConnected
if (!this.walletIsConnected?.cosmosAddress){
connected = JSON.parse(localStorage.getItem(key) || '{}');
}
@@ -54,8 +54,9 @@ export const useWalletStore = defineStore('walletStore', {
},
rewardAmount() {
const stakingStore = useStakingStore();
// @ts-ignore
const reward = this.rewards.total?.find(
(x) => x.denom === stakingStore.params.bond_denom
(x: Coin) => x.denom === stakingStore.params.bond_denom
);
return reward || { amount: '0', denom: stakingStore.params.bond_denom };
},
@@ -125,7 +126,6 @@ export const useWalletStore = defineStore('walletStore', {
console.log(key, 'key')
console.log(localStorage.getItem(key))
localStorage.removeItem(key);
this.walletIsConnected = null
this.$reset()
},
setConnectedWallet(value: any) {