From 7cd2b815831ded0c262ae0e372148a0b2ff2a84e Mon Sep 17 00:00:00 2001 From: Jaco Date: Mon, 5 Dec 2022 14:13:12 +0200 Subject: [PATCH] Display individual ranked votes (#8480) * Display individual ranked votes * Adjust --- packages/page-assets/src/useAssetIds.ts | 2 +- packages/page-nfts/src/useCollectionIds.ts | 2 +- packages/page-parachains/src/useFunds.ts | 2 +- packages/page-parachains/src/useOwnedIds.ts | 2 +- packages/page-parachains/src/useProposals.ts | 4 +- .../page-parachains/src/useUpcomingIds.ts | 2 +- packages/page-preimages/src/usePreimages.ts | 2 +- packages/page-ranked/src/useMemberIds.ts | 2 +- .../page-referenda/src/Referenda/Votes.tsx | 46 +++++++++++++- .../page-referenda/src/Referenda/useVotes.ts | 60 +++++++++++++++++++ .../page-referenda/src/useReferendaIds.ts | 2 +- packages/page-staking/src/Bags/useBagsList.ts | 2 +- packages/page-staking/src/Pools/useMembers.ts | 2 +- packages/page-staking/src/Pools/usePoolIds.ts | 2 +- packages/page-treasury/src/useTipHashes.ts | 2 +- packages/page-whitelist/src/useHashes.ts | 2 +- packages/react-hooks/src/useEventChanges.ts | 6 +- packages/react-hooks/src/useMapEntries.ts | 8 +-- packages/react-hooks/src/useMapKeys.ts | 8 +-- 19 files changed, 129 insertions(+), 29 deletions(-) create mode 100644 packages/page-referenda/src/Referenda/useVotes.ts diff --git a/packages/page-assets/src/useAssetIds.ts b/packages/page-assets/src/useAssetIds.ts index e3cdb11ea4..9a1bb6a322 100644 --- a/packages/page-assets/src/useAssetIds.ts +++ b/packages/page-assets/src/useAssetIds.ts @@ -29,7 +29,7 @@ function filter (records: EventRecord[]): Changes { function useAssetIdsImpl (): u32[] | undefined { const { api } = useApi(); - const startValue = useMapKeys(api.query.assets.asset, OPT_KEY); + const startValue = useMapKeys(api.query.assets.asset, [], OPT_KEY); return useEventChanges([ api.events.assets.Created, diff --git a/packages/page-nfts/src/useCollectionIds.ts b/packages/page-nfts/src/useCollectionIds.ts index 260dc417a6..82fa48be13 100644 --- a/packages/page-nfts/src/useCollectionIds.ts +++ b/packages/page-nfts/src/useCollectionIds.ts @@ -31,7 +31,7 @@ function filter (records: EventRecord[]): Changes { function useCollectionIdsImpl (): u32[] | undefined { const { api } = useApi(); - const startValue = useMapKeys(api.query.uniques.class, OPT_KEYS); + const startValue = useMapKeys(api.query.uniques.class, [], OPT_KEYS); return useEventChanges([ api.events.uniques.Created, diff --git a/packages/page-parachains/src/useFunds.ts b/packages/page-parachains/src/useFunds.ts index 2044ae3a0d..de23a47e66 100644 --- a/packages/page-parachains/src/useFunds.ts +++ b/packages/page-parachains/src/useFunds.ts @@ -157,7 +157,7 @@ function useFundsImpl (): Campaigns { const bestNumber = useBestNumber(); const mountedRef = useIsMountedRef(); const trigger = useEventTrigger([api.events.crowdloan?.Created]); - const paraIds = useMapKeys(api.query.crowdloan?.funds, OPT_FUNDS, trigger.blockHash); + const paraIds = useMapKeys(api.query.crowdloan?.funds, [], OPT_FUNDS, trigger.blockHash); const campaigns = useCall(api.query.crowdloan?.funds.multi, [paraIds], OPT_FUNDS_MULTI); const leases = useCall(api.query.slots.leases.multi, [paraIds], OPT_LEASE); const [result, setResult] = useState(EMPTY); diff --git a/packages/page-parachains/src/useOwnedIds.ts b/packages/page-parachains/src/useOwnedIds.ts index 477315f2ae..e18a410997 100644 --- a/packages/page-parachains/src/useOwnedIds.ts +++ b/packages/page-parachains/src/useOwnedIds.ts @@ -61,7 +61,7 @@ function useOwnedIdsImpl (): OwnedId[] { api.events.registrar.Registered, api.events.registrar.Reserved ]); - const unfiltered = useMapEntries(api.query.registrar.paras, OPT_ENTRIES, trigger.blockHash); + const unfiltered = useMapEntries(api.query.registrar.paras, [], OPT_ENTRIES, trigger.blockHash); const hashes = useCall(api.query.paras.currentCodeHash.multi, [unfiltered ? unfiltered.ids : []], OPT_HASHES); return useMemo( diff --git a/packages/page-parachains/src/useProposals.ts b/packages/page-parachains/src/useProposals.ts index 6c71dbc340..53781e2413 100644 --- a/packages/page-parachains/src/useProposals.ts +++ b/packages/page-parachains/src/useProposals.ts @@ -37,8 +37,8 @@ function useProposalsImpl (): Proposals | undefined { const { api } = useApi(); const mountedRef = useIsMountedRef(); const trigger = useEventTrigger([api.events.proposeParachain?.ProposeParachain]); - const proposalIds = useMapKeys(api.query.proposeParachain?.proposals, OPT_IDS, trigger.blockHash); - const scheduled = useMapEntries(api.query.proposeParachain?.scheduledProposals, OPT_SCHED, trigger.blockHash); + const proposalIds = useMapKeys(api.query.proposeParachain?.proposals, [], OPT_IDS, trigger.blockHash); + const scheduled = useMapEntries(api.query.proposeParachain?.scheduledProposals, [], OPT_SCHED, trigger.blockHash); const [sessionIndex, approvedIds] = useCallMulti([ api.query.session.currentIndex, api.query.proposeParachain?.approvedProposals diff --git a/packages/page-parachains/src/useUpcomingIds.ts b/packages/page-parachains/src/useUpcomingIds.ts index 7d499531ab..e74fefd56d 100644 --- a/packages/page-parachains/src/useUpcomingIds.ts +++ b/packages/page-parachains/src/useUpcomingIds.ts @@ -33,7 +33,7 @@ function useUpomingIdsImpl (): ParaId[] | undefined { api.events.registrar.Registered ]); - return useMapEntries(api.query.paras.paraLifecycles, OPT_ENTRIES, trigger.blockHash); + return useMapEntries(api.query.paras.paraLifecycles, [], OPT_ENTRIES, trigger.blockHash); } export default createNamedHook('useUpomingIds', useUpomingIdsImpl); diff --git a/packages/page-preimages/src/usePreimages.ts b/packages/page-preimages/src/usePreimages.ts index 4a6e4cb1c5..770036233f 100644 --- a/packages/page-preimages/src/usePreimages.ts +++ b/packages/page-preimages/src/usePreimages.ts @@ -32,7 +32,7 @@ function filter (records: EventRecord[]): Changes { function usePreimagesImpl (): HexString[] | undefined { const { api } = useApi(); - const startValue = useMapKeys(api.query.preimage.statusFor, OPT_HASH); + const startValue = useMapKeys(api.query.preimage.statusFor, [], OPT_HASH); const hashes = useEventChanges([ api.events.preimage.Cleared, api.events.preimage.Noted diff --git a/packages/page-ranked/src/useMemberIds.ts b/packages/page-ranked/src/useMemberIds.ts index 41a250ad0e..ddad28afa2 100644 --- a/packages/page-ranked/src/useMemberIds.ts +++ b/packages/page-ranked/src/useMemberIds.ts @@ -30,7 +30,7 @@ function filter (records: EventRecord[]): Changes { function useMemberIdsImpl (collective: PalletColl): AccountId32[] | undefined { const { api } = useApi(); - const startValue = useMapKeys(api.query[collective].members, OPT_ID); + const startValue = useMapKeys(api.query[collective].members, [], OPT_ID); return useEventChanges([ api.events[collective].MemberAdded, diff --git a/packages/page-referenda/src/Referenda/Votes.tsx b/packages/page-referenda/src/Referenda/Votes.tsx index 832788ff35..5ea36b0e0c 100644 --- a/packages/page-referenda/src/Referenda/Votes.tsx +++ b/packages/page-referenda/src/Referenda/Votes.tsx @@ -1,16 +1,18 @@ // Copyright 2017-2022 @polkadot/app-referenda authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { u32 } from '@polkadot/types'; import type { PalletConvictionVotingTally, PalletRankedCollectiveTally } from '@polkadot/types/lookup'; import type { BN } from '@polkadot/util'; import type { PalletVote } from '../types'; -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; -import { Expander } from '@polkadot/react-components'; +import { AddressMini, Expander } from '@polkadot/react-components'; import { FormatBalance } from '@polkadot/react-query'; import { useTranslation } from '../translate'; +import useVotes from './useVotes'; interface Props { className?: string; @@ -20,12 +22,49 @@ interface Props { tally: PalletConvictionVotingTally | PalletRankedCollectiveTally; } -function Votes ({ className = '', isConvictionVote, tally }: Props): React.ReactElement { +function Votes ({ className = '', id, isConvictionVote, palletVote, tally }: Props): React.ReactElement { const { t } = useTranslation(); + const votes = useVotes(palletVote, id, isConvictionVote); + + const [ayes, nays] = useMemo( + () => votes + ? Object.entries(votes).reduce<[[string, u32][], [string, u32][]]>(([ayes, nays], [accountId, vote]) => { + if (vote.isAye) { + ayes.push([accountId, vote.asAye]); + } else { + nays.push([accountId, vote.asNay]); + } + + return [ayes, nays]; + }, [[], []]) + : [], + [votes] + ); + + const renderAyes = useCallback( + () => ayes?.map(([a]) => ( + + )), + [ayes] + ); + + const renderNays = useCallback( + () => nays?.map(([a]) => ( + + )), + [nays] + ); return ( []): AccountId[] => + keys.map(({ args: [, accountId] }) => accountId) +}; + +const OPT_VOTES = { + transform: ([[params], votes]: [[[[BN, AccountId][]]], Option[]]): Record => + params.reduce>((all, [, a], i) => { + if (votes[i] && votes[i].isSome) { + all[a.toString()] = votes[i].unwrap(); + } + + return all; + }, {}), + withParamsTransform: true +}; + +function filterEvents (records: EventRecord[], _: ApiPromise, id?: BN): Changes { + const added: AccountId[] = []; + + records.forEach(({ event: { data: [who, pollId] } }): void => { + if (id && pollId.eq(id)) { + added.push(who as AccountId); + } + }); + + return { added }; +} + +function useVotesImpl (palletVote: PalletVote, id: BN, isConvictionVote: boolean): Record | undefined { + const { api } = useApi(); + const startAccounts = useMapKeys(isConvictionVote === false && api.query[palletVote].voting, [id], OPT_ACCOUNTID); + const allAccounts = useEventChanges([ + api.events[palletVote].Voted + ], filterEvents, startAccounts, id); + + const params = useMemo( + () => allAccounts?.map((a) => [id, a]), + [allAccounts, id] + ); + + return useCall(params && api.query[palletVote].voting.multi, [params], OPT_VOTES); +} + +export default createNamedHook('useVotes', useVotesImpl); diff --git a/packages/page-referenda/src/useReferendaIds.ts b/packages/page-referenda/src/useReferendaIds.ts index 7a4f9f1130..6ac8a4edf1 100644 --- a/packages/page-referenda/src/useReferendaIds.ts +++ b/packages/page-referenda/src/useReferendaIds.ts @@ -31,7 +31,7 @@ function filter (records: EventRecord[]): Changes { function useReferendaIdsImpl (palletReferenda: PalletReferenda): BN[] | undefined { const { api } = useApi(); - const startValue = useMapKeys(api.query[palletReferenda].referendumInfoFor, OPT_ID); + const startValue = useMapKeys(api.query[palletReferenda].referendumInfoFor, [], OPT_ID); return useEventChanges([ api.events[palletReferenda].Submitted diff --git a/packages/page-staking/src/Bags/useBagsList.ts b/packages/page-staking/src/Bags/useBagsList.ts index 08aa9af76d..b4d8f52769 100644 --- a/packages/page-staking/src/Bags/useBagsList.ts +++ b/packages/page-staking/src/Bags/useBagsList.ts @@ -55,7 +55,7 @@ function merge (prev: BagInfo[] | undefined, curr: BagInfo[]): BagInfo[] { function useBagsListImpl (): BagInfo[] | undefined { const mod = useQueryModule(); const [result, setResult] = useState(); - const ids = useMapKeys(mod.listBags, KEY_OPTS); + const ids = useMapKeys(mod.listBags, [], KEY_OPTS); const query = useCall(ids && ids.length !== 0 && mod.listBags.multi, [ids], MULTI_OPTS); useEffect((): void => { diff --git a/packages/page-staking/src/Pools/useMembers.ts b/packages/page-staking/src/Pools/useMembers.ts index 3fb6706871..c6f7a4acbf 100644 --- a/packages/page-staking/src/Pools/useMembers.ts +++ b/packages/page-staking/src/Pools/useMembers.ts @@ -86,7 +86,7 @@ function interleave (prev: MembersMap, additions: MembersMapEntry[]): MembersMap function useMembersImpl (): MembersMap | undefined { const { api } = useApi(); const [membersMap, setMembersMap] = useState(); - const queryMap = useMapEntries(api.query.nominationPools.poolMembers, OPT_ENTRIES); + const queryMap = useMapEntries(api.query.nominationPools.poolMembers, [], OPT_ENTRIES); const ids = useEventChanges([ api.events.nominationPools.Bonded ], filterEvents, EMPTY_START); diff --git a/packages/page-staking/src/Pools/usePoolIds.ts b/packages/page-staking/src/Pools/usePoolIds.ts index 705685d98f..46345008c1 100644 --- a/packages/page-staking/src/Pools/usePoolIds.ts +++ b/packages/page-staking/src/Pools/usePoolIds.ts @@ -31,7 +31,7 @@ function filterEvents (records: EventRecord[]): Changes { function usePoolIdsImpl (): u32[] | undefined { const { api } = useApi(); - const startValue = useMapKeys(api.query.nominationPools.bondedPools, OPT_KEYS); + const startValue = useMapKeys(api.query.nominationPools.bondedPools, [], OPT_KEYS); return useEventChanges([ api.events.nominationPools.Created, diff --git a/packages/page-treasury/src/useTipHashes.ts b/packages/page-treasury/src/useTipHashes.ts index 78d3a423b3..b30831e07f 100644 --- a/packages/page-treasury/src/useTipHashes.ts +++ b/packages/page-treasury/src/useTipHashes.ts @@ -19,7 +19,7 @@ function useTipHashesImpl (): string[] | undefined { api.events.tips?.TipRetracted ]); - return useMapKeys((api.query.tips || api.query.treasury)?.tips, OPT, trigger.blockHash); + return useMapKeys((api.query.tips || api.query.treasury)?.tips, [], OPT, trigger.blockHash); } export default createNamedHook('useTipHashes', useTipHashesImpl); diff --git a/packages/page-whitelist/src/useHashes.ts b/packages/page-whitelist/src/useHashes.ts index 738b38348a..4fe0801227 100644 --- a/packages/page-whitelist/src/useHashes.ts +++ b/packages/page-whitelist/src/useHashes.ts @@ -32,7 +32,7 @@ function filter (records: EventRecord[]): Changes { function useHashesImpl (): HexString[] | undefined { const { api } = useApi(); - const startValue = useMapKeys(api.query.whitelist.whitelistedCall, OPT_HASH); + const startValue = useMapKeys(api.query.whitelist.whitelistedCall, [], OPT_HASH); const hashes = useEventChanges([ api.events.whitelist.CallWhitelisted, api.events.whitelist.WhitelistedCallRemoved diff --git a/packages/react-hooks/src/useEventChanges.ts b/packages/react-hooks/src/useEventChanges.ts index d9426d24b4..1935ff5514 100644 --- a/packages/react-hooks/src/useEventChanges.ts +++ b/packages/react-hooks/src/useEventChanges.ts @@ -47,7 +47,7 @@ function interleave (existing: T[] = [], { added = [], removed .map(([, v]) => v); } -export function useEventChanges (checks: EventCheck[], filter: (records: EventRecord[], api: ApiPromise) => Changes, startValue?: T[]): T[] | undefined { +export function useEventChanges (checks: EventCheck[], filter: (records: EventRecord[], api: ApiPromise, additional?: A) => Changes, startValue?: T[], additional?: A): T[] | undefined { const { api } = useApi(); const [state, setState] = useState(); const { blockHash, events } = useEventTrigger(checks); @@ -59,8 +59,8 @@ export function useEventChanges (checks: EventCheck[], filter: // add/remove any additional items detected (only when actual events occur) useEffect((): void => { - blockHash && setState((prev) => interleave(prev, filter(events, api))); - }, [api, blockHash, events, filter]); + blockHash && setState((prev) => interleave(prev, filter(events, api, additional))); + }, [additional, api, blockHash, events, filter]); return state; } diff --git a/packages/react-hooks/src/useMapEntries.ts b/packages/react-hooks/src/useMapEntries.ts index a9194f86ed..5a05090ed8 100644 --- a/packages/react-hooks/src/useMapEntries.ts +++ b/packages/react-hooks/src/useMapEntries.ts @@ -10,20 +10,20 @@ interface Options { } // FIXME This is generic, we cannot really use createNamedHook -export function useMapEntries (entry?: QueryableStorageEntry<'promise'> | null | false, { transform }: Options = {}, at?: string | null | false): T | undefined { +export function useMapEntries (entry: QueryableStorageEntry<'promise'> | null | false | undefined, params: unknown[], { transform }: Options = {}, at?: string | null | false): T | undefined { const [state, setState] = useState(); useEffect((): void => { entry && ( at && at !== '0' - ? entry.entriesAt(at) - : entry.entries() + ? entry.entriesAt(at, ...params) + : entry.entries(...params) ).then((entries) => setState( transform ? transform(entries) : entries as unknown as T )).catch(console.error); - }, [at, entry, transform]); + }, [at, entry, params, transform]); return state; } diff --git a/packages/react-hooks/src/useMapKeys.ts b/packages/react-hooks/src/useMapKeys.ts index 12f0e17f19..45d4112b62 100644 --- a/packages/react-hooks/src/useMapKeys.ts +++ b/packages/react-hooks/src/useMapKeys.ts @@ -10,20 +10,20 @@ interface Options { } // FIXME This is generic, we cannot really use createNamedHook -export function useMapKeys (entry?: QueryableStorageEntry<'promise'> | null | false, { transform }: Options = {}, at?: string | null | false): T[] | undefined { +export function useMapKeys (entry: QueryableStorageEntry<'promise'> | null | false | undefined, params: unknown[], { transform }: Options = {}, at?: string | null | false): T[] | undefined { const [state, setState] = useState(); useEffect((): void => { entry && ( at && at !== '0' - ? entry.keysAt(at) - : entry.keys() + ? entry.keysAt(at, ...params) + : entry.keys(...params) ).then((keys) => setState( transform ? transform(keys) : keys as unknown as T[] )).catch(console.error); - }, [at, entry, transform]); + }, [at, entry, params, transform]); return state; }