From 2b797cf95e15fc46a254d1cdba98c49f0192b05e Mon Sep 17 00:00:00 2001 From: Jaco Date: Wed, 9 Jun 2021 13:42:13 +0300 Subject: [PATCH] Display contribution values (#5527) * Show contribution values * Remove parathread API ping --- .../page-parachains/src/Crowdloan/Fund.tsx | 58 ++-------- .../src/Crowdloan/useContributions.ts | 100 ++++++++++++++++++ .../src/Parathreads/Parathread.tsx | 4 +- 3 files changed, 113 insertions(+), 49 deletions(-) create mode 100644 packages/page-parachains/src/Crowdloan/useContributions.ts diff --git a/packages/page-parachains/src/Crowdloan/Fund.tsx b/packages/page-parachains/src/Crowdloan/Fund.tsx index bc3c0e0891..e7b90acd52 100644 --- a/packages/page-parachains/src/Crowdloan/Fund.tsx +++ b/packages/page-parachains/src/Crowdloan/Fund.tsx @@ -2,21 +2,19 @@ // SPDX-License-Identifier: Apache-2.0 import type BN from 'bn.js'; -import type { StorageKey } from '@polkadot/types'; -import type { EventRecord, ParaId } from '@polkadot/types/interfaces'; import type { Campaign, LeasePeriod } from '../types'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useMemo } from 'react'; import { AddressMini, Expander, Icon, ParaLink, TxButton } from '@polkadot/react-components'; -import { useAccounts, useApi, useEventTrigger, useParaEndpoints } from '@polkadot/react-hooks'; +import { useAccounts, useApi, useParaEndpoints } from '@polkadot/react-hooks'; import { BlockToTime, FormatBalance } from '@polkadot/react-query'; import { formatNumber } from '@polkadot/util'; -import { encodeAddress } from '@polkadot/util-crypto'; import { useTranslation } from '../translate'; import Contribute from './Contribute'; import Refund from './Refund'; +import useContributions from './useContributions'; interface Props { bestNumber?: BN; @@ -27,48 +25,12 @@ interface Props { value: Campaign; } -interface Contributions { - contributors: string[]; - myAccounts: string[]; -} - -const NO_CONTRIB: Contributions = { contributors: [], myAccounts: [] }; - -function extractContributors (allAccountsHex: string[], keys: StorageKey[], ss58Format?: number): Contributions { - const contributors = keys.map((k) => k.toHex()); - - return { - contributors, - myAccounts: contributors - .filter((c) => allAccountsHex.includes(c)) - .map((a) => encodeAddress(a, ss58Format)) - }; -} - function Fund ({ bestNumber, className, isOdd, isOngoing, leasePeriod, value: { childKey, info: { cap, depositor, end, firstPeriod, lastPeriod, raised, verifier }, isCapped, isEnded, isWinner, paraId } }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); - const { allAccountsHex, isAccount } = useAccounts(); + const { isAccount } = useAccounts(); const endpoints = useParaEndpoints(paraId); - const [{ contributors, myAccounts }, setContributors] = useState(NO_CONTRIB); - const trigger = useEventTrigger([api.events.crowdloan.Contributed, api.events.crowdloan.Withdrew, api.events.crowdloan.AllRefunded, api.events.crowdloan.PartiallyRefunded], useCallback( - ({ event: { data } }: EventRecord) => - ((data.length === 1 - ? data[0] // AllRefunded, PartiallyRefunded [ParaId] - : data[1] // Contributed, Withdrew [AccountId, ParaId, Balance] - ) as ParaId).eq(paraId), - [paraId] - )); - - useEffect((): void => { - trigger && - api.rpc.childstate - .getKeys(childKey, '0x') - .then((keys) => setContributors( - extractContributors(allAccountsHex, keys, api.registry.chainSS58)) - ) - .catch(console.error); - }, [allAccountsHex, api, childKey, trigger]); + const { contributorsHex, myAccounts, myAccountsHex, myContributions } = useContributions(paraId, childKey); const isDepositor = useMemo( () => isAccount(depositor.toString()), @@ -139,8 +101,8 @@ function Fund ({ bestNumber, className, isOdd, isOngoing, leasePeriod, value: {
{percentage}
- {contributors.length !== 0 && ( - formatNumber(contributors.length) + {contributorsHex.length !== 0 && ( + formatNumber(contributorsHex.length) )} @@ -150,7 +112,7 @@ function Fund ({ bestNumber, className, isOdd, isOngoing, leasePeriod, value: { /> - {canWithdraw && contributors.length !== 0 && ( + {canWithdraw && contributorsHex.length !== 0 && ( )} {canDissolve && ( @@ -185,10 +147,12 @@ function Fund ({ bestNumber, className, isOdd, isOngoing, leasePeriod, value: { {myAccounts.length !== 0 && ( ('My contributions ({{count}})', { replace: { count: myAccounts.length } })}> - {myAccounts.map((a) => ( + {myAccounts.map((a, index) => ( ))} diff --git a/packages/page-parachains/src/Crowdloan/useContributions.ts b/packages/page-parachains/src/Crowdloan/useContributions.ts new file mode 100644 index 0000000000..a44daf3fac --- /dev/null +++ b/packages/page-parachains/src/Crowdloan/useContributions.ts @@ -0,0 +1,100 @@ +// Copyright 2017-2021 @polkadot/app-parachains authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { StorageKey } from '@polkadot/types'; +import type { Balance, EventRecord, ParaId } from '@polkadot/types/interfaces'; + +import { useCallback, useEffect, useState } from 'react'; + +import { useAccounts, useApi, useEventTrigger } from '@polkadot/react-hooks'; +import { encodeAddress } from '@polkadot/util-crypto'; + +interface Contributions { + contributorsHex: string[]; + myAccounts: string[]; + myAccountsHex: string[]; + myContributions: Record; +} + +const NO_CONTRIB: Contributions = { + contributorsHex: [], + myAccounts: [], + myAccountsHex: [], + myContributions: {} +}; + +function extractContributors (allAccountsHex: string[], keys: StorageKey[], ss58Format?: number): Partial { + const contributorsHex = keys.map((k) => k.toHex()); + const myAccountsHex = contributorsHex.filter((c) => allAccountsHex.includes(c)); + + return { + contributorsHex, + myAccounts: myAccountsHex.map((a) => encodeAddress(a, ss58Format)), + myAccountsHex + }; +} + +function useMyContributions (childKey: string, keys: string[]): Record { + const { api } = useApi(); + const [state, setState] = useState>({}); + + useEffect((): void => { + keys.length && + Promise + .all(keys.map((k) => api.rpc.childstate.getStorage(childKey, k))) + .then((values) => setState( + values + .map((v) => api.createType('Option', v)) + .map((o) => + o.isSome + ? api.createType('Balance', o.unwrap()) + : api.createType('Balance') + ) + .reduce((all, b, index) => ({ ...all, [keys[index]]: b }), {}) + )) + .catch(console.error); + }, [api, childKey, keys]); + + return state; +} + +export default function useContributions (paraId: ParaId, childKey: string): Contributions { + const { api } = useApi(); + const { allAccountsHex } = useAccounts(); + const [state, setState] = useState(() => NO_CONTRIB); + const myContributions = useMyContributions(childKey, state.myAccountsHex); + + const trigger = useEventTrigger([ + api.events.crowdloan.Contributed, + api.events.crowdloan.Withdrew, + api.events.crowdloan.AllRefunded, + api.events.crowdloan.PartiallyRefunded + ], useCallback( + ({ event: { data } }: EventRecord) => + ((data.length === 1 + ? data[0] // AllRefunded, PartiallyRefunded [ParaId] + : data[1] // Contributed, Withdrew [AccountId, ParaId, Balance] + ) as ParaId).eq(paraId), + [paraId] + )); + + useEffect((): void => { + trigger && + api.rpc.childstate + .getKeys(childKey, '0x') + .then((keys) => setState((prev) => ({ + ...prev, + ...extractContributors(allAccountsHex, keys, api.registry.chainSS58) + }))) + .catch(console.error); + }, [allAccountsHex, api, childKey, trigger]); + + useEffect((): void => { + setState((prev) => ({ + ...prev, + myContributions + })); + }, [myContributions]); + + return state; +} diff --git a/packages/page-parachains/src/Parathreads/Parathread.tsx b/packages/page-parachains/src/Parathreads/Parathread.tsx index 6dc187ddb2..382c8fc42f 100644 --- a/packages/page-parachains/src/Parathreads/Parathread.tsx +++ b/packages/page-parachains/src/Parathreads/Parathread.tsx @@ -12,7 +12,7 @@ import { useAccounts, useApi, useCallMulti } from '@polkadot/react-hooks'; import { formatNumber } from '@polkadot/util'; import Lifecycle from '../Overview/Lifecycle'; -import ParachainInfo from '../Overview/ParachainInfo'; +// import ParachainInfo from '../Overview/ParachainInfo'; import Periods from '../Overview/Periods'; import { useTranslation } from '../translate'; import { sliceHex } from '../util'; @@ -82,7 +82,7 @@ function Upcoming ({ id, leasePeriod, leases, nextAction }: Props): React.ReactE - + {/* */} {leasePeriod && leases && periods && (