Merge pull request #382 from alisaweb3/v3-single

Update ProposalListItem
This commit is contained in:
ping
2023-05-18 06:45:48 +08:00
committed by GitHub
17 changed files with 306 additions and 150 deletions
+3 -3
View File
@@ -1,17 +1,17 @@
<script lang="ts" setup>
import VueCountdown from '@chenfengyuan/vue-countdown';
import Countdown from '@chenfengyuan/vue-countdown';
const props = defineProps({
time: { type: Number },
});
</script>
<template>
<vue-countdown
<Countdown
v-if="time"
:time="time > 0 ? time : 0"
v-slot="{ days, hours, minutes, seconds }"
>
Time Remaining:{{ days }} days, {{ hours }} hours, {{ minutes }} minutes,
{{ seconds }} seconds.
</vue-countdown>
</Countdown>
</template>
+80 -35
View File
@@ -1,13 +1,19 @@
<script lang="ts" setup>
import { useBlockchain, useFormatter, useStakingStore } from '@/stores';
import {
useBlockchain,
useFormatter,
useStakingStore,
useWalletStore,
useTxDialog
} from '@/stores';
import { select } from '@/components/dynamic/index';
import type { PaginatedProposals } from '@/types';
import ProposalProcess from './ProposalProcess.vue';
import type { PropType } from 'vue';
import { ref } from 'vue';
const dialog = useTxDialog();
defineProps({
proposals: { type: Object as PropType<PaginatedProposals> },
votable: { type: Boolean, default: false }
});
const format = useFormatter();
@@ -25,16 +31,25 @@ const statusMap: Record<string, string> = {
PROPOSAL_STATUS_PASSED: 'PASSED',
PROPOSAL_STATUS_REJECTED: 'REJECTED',
};
const voterStatusMap: Record<string, string> = {
No_With_Veto: '',
VOTE_OPTION_YES: 'success',
VOTE_OPTION_NO: 'error',
VOTE_OPTION_ABSTAIN: 'warning',
};
const proposalInfo = ref();
</script>
<template>
<div class="bg-white dark:bg-[#28334e] rounded text-sm">
<table class="table-compact w-full table-fixed lg:table">
<table class="table-compact w-full table-fixed hidden lg:table">
<tbody>
<tr v-for="(item, index) in proposals?.proposals" :key="index">
<td class="px-4 w-20">
<label
for=""
for="proposal-detail-modal"
class="text-main text-base hover:text-indigo-400 cursor-pointer"
@click="proposalInfo = item"
>
#{{ item?.proposal_id }}</label
>
@@ -94,9 +109,25 @@ const statusMap: Record<string, string> = {
</div>
</td>
<td v-if="votable">
<div>
<button class="btn btn-xs btn-primary rounded-sm">Vote</button>
<td v-if="statusMap?.[item?.status] === 'VOTING'">
<div class="" v-show="item?.voterStatus === 'No With Veto'">
<label
for="vote"
class="btn btn-xs btn-primary rounded-sm"
@click="dialog.open('vote', { proposal_id: item?.proposal_id })"
>Vote</label
>
<div
class="text-xs truncate relative py-1 px-3 rounded-full w-fit"
:class="`text-${voterStatusMap?.[item?.voterStatus]}`"
v-show="item?.voterStatus !== 'No With Veto'"
>
<span
class="inset-x-0 inset-y-0 opacity-10 absolute"
:class="`bg-${voterStatusMap?.[item?.voterStatus]}`"
></span>
{{ item?.voterStatus }}
</div>
</div>
</td>
</tr>
@@ -118,35 +149,12 @@ const statusMap: Record<string, string> = {
>{{ item?.content?.title }}</RouterLink
>
<label
for="proposal-detail-modals"
for="proposal-detail-modal"
class="text-main text-base hover:text-indigo-400 cursor-pointer"
@click="proposalInfo = item"
>
#{{ item?.proposal_id }}</label
>
<input
type="checkbox"
id="proposal-detail-modals"
class="modal-toggle"
/>
<div class="modal modal-bottom sm:modal-middle">
<div class="modal-box">
<h3 class="font-bold text-lg">Description</h3>
<p class="py-4">
<Component
v-if="item.content?.description"
:is="select(item.content?.description, 'horizontal')"
:value="item.content?.description"
></Component>
</p>
<div class="modal-action">
<label
for="proposal-detail-modals"
class="btn btn-sm btn-primary"
>Close</label
>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-4 mt-2 mb-2">
@@ -195,10 +203,47 @@ const statusMap: Record<string, string> = {
></ProposalProcess>
</div>
<div class="mt-4">
<button class="btn btn-xs btn-primary rounded-sm px-4">Vote</button>
<div class="mt-4" v-if="statusMap?.[item?.status] === 'VOTING'">
<div class="" v-show="item?.voterStatus === 'No With Veto'">
<label
for="vote"
class="btn btn-xs btn-primary rounded-sm"
@click="dialog.open('vote', { proposal_id: item?.proposal_id })"
>Vote</label
>
<div
class="text-xs truncate relative py-1 px-3 rounded-full w-fit"
:class="`text-${voterStatusMap?.[item?.voterStatus]}`"
v-show="item?.voterStatus !== 'No With Veto'"
>
<span
class="inset-x-0 inset-y-0 opacity-10 absolute"
:class="`bg-${voterStatusMap?.[item?.voterStatus]}`"
></span>
{{ item?.voterStatus }}
</div>
</div>
</div>
</div>
</div>
<input type="checkbox" id="proposal-detail-modal" class="modal-toggle" />
<label for="proposal-detail-modal" class="modal sm:modal-middle">
<label class="modal-box relative" for="">
<label
for="proposal-detail-modal"
class="btn btn-sm btn-circle absolute right-2 top-2"
>✕</label
>
<h3 class="font-bold text-lg">Description</h3>
<p class="py-4">
<Component
v-if="proposalInfo?.content?.description"
:is="select(proposalInfo?.content?.description, 'horizontal')"
:value="proposalInfo?.content?.description"
></Component>
</p>
</label>
</label>
</div>
</template>
+6 -4
View File
@@ -110,11 +110,15 @@ const changeOpen = (index: Number) => {
$route.path === el?.to?.path && item?.title !== 'Favorite',
}"
>
<Icon icon="mdi:chevron-right" class="mr-2 ml-3"/>
<Icon
v-if="!el?.icon?.image"
icon="mdi:chevron-right"
class="mr-2 ml-3"
/>
<img
v-if="el?.icon?.image"
:src="el?.icon?.image"
class="w-6 h-6 rounded-full mr-3"
class="w-6 h-6 rounded-full mr-3 ml-4"
/>
<div
class="text-base text-gray-500 dark:text-gray-300"
@@ -194,8 +198,6 @@ const changeOpen = (index: Number) => {
<NavBarWallet
class="block truncate md:inline-block text-xs md:text-sm"
/>
</div>
<!-- 👉 Pages -->
+20
View File
@@ -5,6 +5,9 @@ const walletStore = useWalletStore();
walletStore.$subscribe((m, s) => {
console.log(m, s);
});
function walletStateChange(res: any) {
walletStore.setConnectedWallet(res.detail?.value);
}
let showCopyToast = ref(0);
async function copyAdress(address: string) {
try {
@@ -89,6 +92,7 @@ const addressChange = computed(() => {
</div>
</div>
</div>
</div>
<label
v-if="!walletStore?.currentAddress"
@@ -96,4 +100,20 @@ const addressChange = computed(() => {
class="btn btn-sm ml-4 ping-connect-btn"
>Connect Wallet</label
>
<div class="footer-modal">
<Teleport to="body">
<ping-connect-wallet
:chain-id="'juno-1'"
:hd-path="`m/44'/118/0'/0/0`"
@change="walletStateChange"
/>
</Teleport>
</div>
</template>
<style>
.footer-modal .ping-connect-btn,
.footer-modal .ping-connect-dropdown {
display: none;
}
</style>
+1 -17
View File
@@ -1,11 +1,3 @@
<script setup lang="ts">
import { useWalletStore } from '@/stores';
const walletStore = useWalletStore();
function walletStateChange(res:any){
console.log(res.detail?.value, 8888888)
walletStore.setConnectedWallet(res.detail?.value)
}
</script>
<template>
<footer class="footer items-center p-4 text-sm mb-4">
<div class="items-center grid-flow-col">
@@ -39,14 +31,6 @@ function walletStateChange(res:any){
>
</div>
</footer>
<div class="footer-modal">
<ping-connect-wallet :chain-id="'juno-1'" :hd-path="`m/44'/118/0'/0/0`" @change="walletStateChange"/>
</div>
</template>
<style>
.footer-modal .ping-connect-btn,
.footer-modal .ping-connect-dropdown {
display: none;
}
</style>
@@ -29,17 +29,14 @@ const selected = ref('');
function showInfo(address: string) {
wasmStore.wasmClient.getWasmContracts(address).then((x) => {
info.value = x.contract_info;
// infoDialog.value = true;
});
}
function showState(address: string) {
wasmStore.wasmClient.getWasmContractStates(address).then((x) => {
state.value = x;
stateDialog.value = true;
});
}
function showQuery(address: string) {
queryDialog.value = true;
selected.value = address;
query.value = '';
result.value = '';
@@ -114,18 +111,20 @@ const result = ref('');
>contract</label
>
<button
<label
class="btn btn-primary btn-sm text-xs mr-2"
for="modal-contract-states"
@click="showState(v)"
>
States
</button>
<button
</label>
<label
for="modal-contract-query"
class="btn btn-primary btn-sm text-xs"
@click="showQuery(v)"
>
Query
</button>
</label>
</td>
</tr>
</tbody>
@@ -153,47 +152,92 @@ const result = ref('');
</label>
</label>
<v-dialog v-model="stateDialog" width="auto">
<v-card>
<VCardTitle>Contract States</VCardTitle>
<VList>
<VListItem v-for="v in state.models">
<VListItemTitle>
{{ format.hexToString(v.key) }}
</VListItemTitle>
<VListItemSubtitle :title="format.base64ToString(v.value)">
{{ format.base64ToString(v.value) }}
</VListItemSubtitle>
</VListItem>
</VList>
<v-card-actions>
<v-btn color="primary" block @click="stateDialog = false"
>Close Dialog</v-btn
>
</v-card-actions>
</v-card>
</v-dialog>
<input type="checkbox" id="modal-contract-states" class="modal-toggle" />
<label for="modal-contract-states" class="modal cursor-pointer">
<label class="modal-box relative p-2" for="">
<div>
<div class="flex items-center justify-between px-3 pt-2">
<div class="text-lg">Contract States</div>
<label
@click="infoDialog = false"
for="modal-contract-states"
class="btn btn-sm btn-circle"
>✕</label
>
</div>
<div class="overflow-auto">
<table class="table table-compact w-full text-sm">
<tr v-for="(v, index) in state.models" :key="index">
<td class="">
{{ format.hexToString(v.key) }}
</td>
<td class="" :title="format.base64ToString(v.value)">
{{ format.base64ToString(v.value) }}
</td>
</tr>
</table>
</div>
</div>
</label>
</label>
<v-dialog v-model="queryDialog" width="auto">
<v-card>
<VCardTitle>Query Contract</VCardTitle>
<v-card-text>
<CustomRadios
v-model:selected-radio="selectedRadio"
:radio-content="radioContent"
:grid-column="{ sm: '6', cols: '12' }"
/>
<input type="checkbox" id="modal-contract-query" class="modal-toggle" />
<label for="modal-contract-states" class="modal cursor-pointer">
<label class="modal-box relative p-2" for="">
<div>
<div class="flex items-center justify-between px-3 pt-2 mb-4">
<div class="text-lg font-semibold">Query Contract</div>
<label
@click="infoDialog = false"
for="modal-contract-query"
class="btn btn-sm btn-circle"
>✕</label
>
</div>
<div class="px-3">
<div>
<div class="grid grid-cols-2 gap-4 mb-4">
<div
class="form-control border rounded px-4"
v-for="(item, index) of radioContent"
:key="index"
:class="{ 'pt-2': index === 0 }"
>
<label
class="label cursor-pointer justify-start"
@click="selectedRadio = item?.value"
>
<input
type="radio"
name="radio-10"
class="radio radio-sm radio-primary mr-4"
:checked="item?.value === selectedRadio"
style="border: 1px solid #d2d6dc"
/>
<div>
<div class="text-base font-semibold">
{{ item?.title }}
</div>
<div class="text-xs">{{ item?.desc }}</div>
</div>
</label>
</div>
</div>
<VTextarea v-model="query" label="Query String" class="my-2" />
<VTextarea v-model="result" label="Result" />
</v-card-text>
<v-card-actions>
<v-btn color="primary" @click="queryDialog = false"
>Close Dialog</v-btn
>
<v-btn color="success" @click="queryContract()">Query Contract</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<VTextarea v-model="query" label="Query String" class="my-2" />
<VTextarea v-model="result" label="Result" />
</div>
<div class="mt-4 mb-4">
<button
class="btn btn-success px-4 text-white"
@click="queryContract()"
>
Query Contract
</button>
</div>
</div>
</div>
</label>
</label>
</div>
</template>
+6 -5
View File
@@ -17,10 +17,10 @@ wasmStore.wasmClient.getWasmCodeList().then((x) => {
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<h2 class="card-title truncate w-full">Cosmos Wasm Smart Contracts</h2>
<div class="overflow-x-auto">
<table class="table w-full mt-4 text-sm">
<table class="table table-compact w-full mt-4 text-sm">
<thead>
<tr>
<th style="position: relative; z-index: 2;">Code Id</th>
<th>Code Id</th>
<th>Code Hash</th>
<th>Creator</th>
<th>Permissions</th>
@@ -32,10 +32,11 @@ wasmStore.wasmClient.getWasmCodeList().then((x) => {
<td>
<RouterLink
:to="`/${props.chain}/cosmwasm/${v.code_id}/contracts`"
><div class="text-truncate" style="max-width: 200px">
{{ v.data_hash }}
</div></RouterLink
class="text-truncate max-w-[200px] block text-primary"
:title="v.data_hash"
>
{{ v.data_hash }}
</RouterLink>
</td>
<td>{{ v.creator }}</td>
<td>
+1 -1
View File
@@ -41,7 +41,7 @@ const changeTab = (val: '2' | '3' | '4') => {
>Rejected</a
>
</div>
<ProposalListItem :proposals="store?.proposals[tab]" :votable="tab === '2'" />
<ProposalListItem :proposals="store?.proposals[tab]"/>
</div>
</template>
<route>
+13 -8
View File
@@ -85,7 +85,7 @@ function color(v: string) {
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<h2 class="card-title mb-4">IBC Client State</h2>
<div class="overflow-x-auto">
<table class="table text-sm w-full">
<table class="table table-compact text-sm w-full">
<tbody>
<tr>
<td class="w-52">update after expiry:</td>
@@ -93,12 +93,17 @@ function color(v: string) {
</tr>
<tr>
<td class="w-52">allow_update_after_misbehaviour:</td>
<td>{{ clientState.client_state?.allow_update_after_misbehaviour }}</td>
<td>
{{ clientState.client_state?.allow_update_after_misbehaviour }}
</td>
</tr>
<tr>
<td class="w-52">trust_level:</td>
<td> {{ clientState.client_state?.trust_level?.numerator }}/{{
clientState.client_state?.trust_level?.denominator}}</td>
<td>
{{ clientState.client_state?.trust_level?.numerator }}/{{
clientState.client_state?.trust_level?.denominator
}}
</td>
</tr>
<tr>
<td class="w-52">trusting_period:</td>
@@ -106,11 +111,11 @@ function color(v: string) {
</tr>
<tr>
<td class="w-52">unbonding_period:</td>
<td> {{ clientState.client_state?.unbonding_period }}</td>
<td>{{ clientState.client_state?.unbonding_period }}</td>
</tr>
<tr>
<td class="w-52">frozen_height:</td>
<td>{{ clientState.client_state?.frozen_height }} </td>
<td>{{ clientState.client_state?.frozen_height }}</td>
</tr>
<tr>
<td class="w-52">latest_height:</td>
@@ -122,11 +127,11 @@ function color(v: string) {
</tr>
<tr>
<td class="w-52">upgrade_path:</td>
<td>{{ clientState.client_state?.upgrade_path }} </td>
<td>{{ clientState.client_state?.upgrade_path }}</td>
</tr>
<tr>
<td class="w-52">max_clock_drift:</td>
<td>{{ clientState.client_state?.max_clock_drift }} </td>
<td>{{ clientState.client_state?.max_clock_drift }}</td>
</tr>
</tbody>
</table>
+2 -2
View File
@@ -25,7 +25,7 @@ function color(v: string) {
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
<h2 class="card-title">IBC Connections</h2>
<div class="overflow-x-auto mt-4">
<table class="table w-full table-zebra">
<table class="table table-compact w-full table-zebra">
<thead>
<tr>
<th class="py-3">Connection Id</th>
@@ -49,7 +49,7 @@ function color(v: string) {
<td class="py-2">{{ v.delay_period }}</td>
<td class="py-2">
<div
class="text-xs truncate relative py-1 px-3 rounded-full w-fit"
class="text-xs truncate relative py-[2px] px-3 rounded-full w-fit"
:class="`text-${color(v.state)}`"
>
<span
+22 -11
View File
@@ -23,7 +23,6 @@ const walletStore = useWalletStore();
const format = useFormatter();
const dialog = useTxDialog();
const stakingStore = useStakingStore();
const coinInfo = computed(() => {
return store.coinInfo;
});
@@ -32,7 +31,6 @@ onMounted(() => {
store.loadDashboard();
walletStore.loadMyAsset();
});
const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]);
blockchain.$subscribe((m, s) => {
@@ -246,10 +244,11 @@ const color = computed(() => {
<div class="bg-base-100 rounded mt-4 shadow">
<div class="px-4 pt-4 pb-2 text-lg font-semibold text-main">
{{ walletStore.currentAddress || 'Not Connected' }}
<span
<RouterLink
v-if="walletStore.currentAddress"
class="float-right font-light text-sm"
>More</span
class="float-right font-light text-sm cursor-pointert link link-primary no-underline font-medium"
to="/wallet/portfolio"
>More</RouterLink
>
</div>
<div
@@ -317,10 +316,13 @@ const color = computed(() => {
<td>{{ format.formatToken(item?.balance) }}</td>
<td>
{{
format.formatToken({
denom: item?.balance?.denom,
amount: item?.delegation?.shares,
})
format.formatToken(
walletStore?.rewards?.rewards?.find(
(el) =>
el?.validator_address ===
item?.delegation?.validator_address
)?.reward?.[0]
)
}}
</td>
<td>
@@ -343,12 +345,21 @@ const color = computed(() => {
</div>
<div class="grid grid-cols-3 gap-4 px-4 pb-6 mt-4">
<button class="btn btn-success text-white">Send</button>
<label for="send" class="btn btn-success text-white">Send</label>
<RouterLink to="/wallet/receive" class="btn btn-info text-white"
>Receive</RouterLink
>
<button class="btn btn-primary text-white">Convert</button>
<label for="PingTokenConvert" class="btn btn-primary text-white"
>Convert</label
>
</div>
<Teleport to="body">
<ping-token-convert
:chain-name="blockchain?.chainName"
:endpoint="blockchain?.endpoint?.address"
:params="walletStore?.connectedWallet?.hdPath"
></ping-token-convert>
</Teleport>
</div>
</div>
</template>
+2 -5
View File
@@ -1,6 +1,5 @@
<script setup lang="ts">
import {
useBankStore,
useBlockchain,
useFormatter,
useMintStore,
@@ -9,7 +8,7 @@ import {
} from '@/stores';
import { onMounted, computed, ref } from 'vue';
import { Icon } from '@iconify/vue';
import ValidatorCommissionRate from '@/components/ValidatorCommissionRate.vue';
import CommissionRate from '@/components/ValidatorCommissionRate.vue';
import {
consensusPubkeyToHexAddress,
operatorAddressToAccount,
@@ -303,9 +302,7 @@ onMounted(() => {
<div class="mt-3 grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="h-100">
<ValidatorCommissionRate
:commission="v.commission"
></ValidatorCommissionRate>
<CommissionRate :commission="v.commission"></CommissionRate>
</div>
<div>
<div class="h-100 bg-base-100 rounded shadow">
+2 -2
View File
@@ -3,7 +3,7 @@ import { useBlockchain, useFormatter } from '@/stores';
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
import { computed, ref } from '@vue/reactivity';
import type { Tx, TxResponse } from '@/types';
import VueJsonPretty from 'vue-json-pretty';
import JsonPretty from 'vue-json-pretty';
import 'vue-json-pretty/lib/styles.css';
const props = defineProps(['hash', 'chain']);
@@ -119,7 +119,7 @@ const messages = computed(() => {
class="bg-base-100 px-4 pt-3 pb-4 rounded shadow"
>
<h2 class="card-title truncate mb-2">JSON</h2>
<vue-json-pretty :data="tx" :deep="3" />
<JsonPretty :data="tx" :deep="3" />
</div>
</div>
</template>
+22 -3
View File
@@ -2,6 +2,7 @@ import { defineStore } from 'pinia';
import { useBlockchain } from './useBlockchain';
import type { PageRequest, PaginatedProposals } from '@/types';
import { LoadingStatus } from './useDashboard';
import { useWalletStore } from './useWalletStore'
import { reactive } from 'vue';
export const useGovStore = defineStore('govStore', {
@@ -20,6 +21,9 @@ export const useGovStore = defineStore('govStore', {
blockchain() {
return useBlockchain();
},
walletstore() {
return useWalletStore();
}
},
actions: {
initial() {
@@ -34,12 +38,24 @@ export const useGovStore = defineStore('govStore', {
await this.blockchain.rpc?.getGovProposals(status)
);
if (status === '2') {
proposals?.proposals?.forEach(async (x1) => {
await this.fetchTally(x1.proposal_id).then((res) => {
x1.final_tally_result = res?.tally;
proposals?.proposals?.forEach((item) => {
this.fetchTally(item.proposal_id).then((res) => {
item.final_tally_result = res?.tally;
});
if (this.walletstore.currentAddress) {
try {
this.fetchProposalVotesVoter(item.proposal_id,this.walletstore.currentAddress).then((res) => {
item.voterStatus = res?.vote?.option || 'No With Veto'
});
} catch (error) {
item.voterStatus = 'No With Veto'
}
} else {
item.voterStatus = 'No With Veto'
}
});
}
this.loading[status] = LoadingStatus.Loaded;
this.proposals[status] = proposals;
}
@@ -62,5 +78,8 @@ export const useGovStore = defineStore('govStore', {
async fetchProposalVotes(proposalId: string, next_key?: string) {
return this.blockchain.rpc.getGovProposalVotes(proposalId, next_key);
},
async fetchProposalVotesVoter(proposalId: string, voter: string) {
return this.blockchain.rpc.getGovProposalVotesVoter(proposalId, voter);
},
},
});
+2 -1
View File
@@ -44,7 +44,8 @@ export interface GovProposal {
"total_deposit": Coin[],
"voting_start_time": string,
"voting_end_time": string,
"is_expedited": boolean
"is_expedited": boolean,
"voterStatus"?: string,
}
export interface Tally {