Refresh single fund info (#4956)
* Refresh single fund info * Media queries * className fix * Countdown on ended * s/count/unique/ * loading * only count in state
This commit is contained in:
parent
eb35ead3ed
commit
5c1cfd472d
@ -2,15 +2,16 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { EventRecord, ParaId } from '@polkadot/types/interfaces';
|
||||
import type { Campaign } from '../types';
|
||||
import type { Contributed } from './types';
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { AddressMini, Digits, ParaLink, TxButton } from '@polkadot/react-components';
|
||||
import { useAccounts, useApi } from '@polkadot/react-hooks';
|
||||
import { AddressMini, Digits, Icon, ParaLink, TxButton } from '@polkadot/react-components';
|
||||
import { useAccounts, useApi, useEventTrigger } 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 FundContribute from './FundContribute';
|
||||
@ -18,15 +19,40 @@ import FundContribute from './FundContribute';
|
||||
interface Props {
|
||||
bestNumber?: BN;
|
||||
className?: string;
|
||||
contributed?: Contributed;
|
||||
isOngoing?: boolean;
|
||||
value: Campaign;
|
||||
}
|
||||
|
||||
function Fund ({ bestNumber, className, contributed, isOngoing, value: { info: { cap, depositor, end, firstSlot, lastSlot, raised, retiring }, isCapped, isEnded, isRetired, isWinner, paraId, retireEnd } }: Props): React.ReactElement<Props> {
|
||||
interface Contributions {
|
||||
uniqueCount?: number;
|
||||
myAccounts?: string[];
|
||||
}
|
||||
|
||||
function Fund ({ bestNumber, className, isOngoing, value: { childKey, info: { cap, depositor, end, firstSlot, lastSlot, raised, retiring }, isCapped, isEnded, isRetired, isWinner, paraId, retireEnd } }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const { allAccounts } = useAccounts();
|
||||
const [{ myAccounts, uniqueCount }, setContributors] = useState<Contributions>({});
|
||||
const trigger = useEventTrigger([api.events.crowdloan.Contributed], useCallback(
|
||||
({ event: { data: [, fundIndex] } }: EventRecord) =>
|
||||
(fundIndex as ParaId).eq(paraId),
|
||||
[paraId]
|
||||
));
|
||||
|
||||
useEffect((): void => {
|
||||
trigger &&
|
||||
api.rpc.childstate
|
||||
.getKeys(childKey, '0x')
|
||||
.then((keys) => setContributors((): Contributions => {
|
||||
const contributors = keys.map((k) => encodeAddress(k));
|
||||
|
||||
return {
|
||||
myAccounts: contributors.filter((c) => allAccounts.includes(c)),
|
||||
uniqueCount: contributors.length
|
||||
};
|
||||
}))
|
||||
.catch(console.error);
|
||||
}, [allAccounts, api, childKey, trigger]);
|
||||
|
||||
const isDepositor = useMemo(
|
||||
(): boolean => {
|
||||
@ -83,16 +109,20 @@ function Fund ({ bestNumber, className, contributed, isOngoing, value: { info: {
|
||||
: t<string>('Ended')
|
||||
}
|
||||
</td>
|
||||
<td className='address'><AddressMini value={depositor} /></td>
|
||||
<td className='address media--1400'><AddressMini value={depositor} /></td>
|
||||
{!isOngoing && (
|
||||
<td className='all number together'>
|
||||
{retiringLeft && retiring.isTrue && (
|
||||
<BlockToTime value={retiringLeft} />
|
||||
{isEnded && (
|
||||
<>
|
||||
{retiringLeft && (
|
||||
<BlockToTime value={retiringLeft} />
|
||||
)}
|
||||
#{formatNumber(retireEnd)}
|
||||
</>
|
||||
)}
|
||||
{!blocksLeft && <>#{formatNumber(retireEnd)}</>}
|
||||
</td>
|
||||
)}
|
||||
<td className='all number together'>
|
||||
<td className={`all number together${isOngoing ? '' : ' media--1200'}`}>
|
||||
{blocksLeft && (
|
||||
<BlockToTime value={blocksLeft} />
|
||||
)}
|
||||
@ -109,10 +139,16 @@ function Fund ({ bestNumber, className, contributed, isOngoing, value: { info: {
|
||||
<div>{percentage}</div>
|
||||
</td>
|
||||
<td className='number'>
|
||||
{contributed && (
|
||||
formatNumber(contributed.count)
|
||||
{uniqueCount && (
|
||||
formatNumber(uniqueCount)
|
||||
)}
|
||||
</td>
|
||||
<td className='badge'>
|
||||
<Icon
|
||||
color={myAccounts?.length ? 'green' : 'gray'}
|
||||
icon='asterisk'
|
||||
/>
|
||||
</td>
|
||||
<td className='button'>
|
||||
{canDissolve && (
|
||||
<TxButton
|
||||
|
||||
@ -3,12 +3,10 @@
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { Campaign, LeasePeriod } from '../types';
|
||||
import type { Contributed } from './types';
|
||||
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { useApi, useEventTrigger } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import Fund from './Fund';
|
||||
@ -33,48 +31,32 @@ function extractLists (value: Campaign[] | null, leasePeriod?: LeasePeriod): [Ca
|
||||
|
||||
function Funds ({ bestNumber, className, leasePeriod, value }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const trigger = useEventTrigger([api.events.crowdloan.Contributed]);
|
||||
const [contributors, setContributors] = useState<Contributed[]>([]);
|
||||
|
||||
const headerActiveRef = useRef([
|
||||
[t('ongoing'), 'start', 4],
|
||||
[t('ongoing'), 'start', 2],
|
||||
[undefined, 'media--1400'],
|
||||
[],
|
||||
[t('ending')],
|
||||
[t('periods')],
|
||||
[t('raised')],
|
||||
[t('count')],
|
||||
[t('unique')],
|
||||
[undefined, 'badge'],
|
||||
[]
|
||||
]);
|
||||
|
||||
const headedEndedRef = useRef([
|
||||
[t('completed'), 'start', 4],
|
||||
[t('completed'), 'start', 2],
|
||||
[undefined, 'media--1400'],
|
||||
[],
|
||||
[t('retired')],
|
||||
[t('ending')],
|
||||
[t('ending'), 'media--1200'],
|
||||
[t('periods')],
|
||||
[t('raised')],
|
||||
[t('count')],
|
||||
[t('unique')],
|
||||
[undefined, 'badge'],
|
||||
[]
|
||||
]);
|
||||
|
||||
useEffect((): void => {
|
||||
trigger && value &&
|
||||
Promise
|
||||
.all(value.map(({ childKey }) => api.rpc.childstate.getKeys(childKey, '0x')))
|
||||
.then((all) => setContributors(
|
||||
all.map((keys, index) => ({
|
||||
accountIds: [], // keys.map((a) => encodeAddress(a)),
|
||||
count: keys.length,
|
||||
trieIndex: value[index].info.trieIndex
|
||||
}))
|
||||
))
|
||||
.catch(console.error);
|
||||
}, [api, trigger, value]);
|
||||
|
||||
const findContributions = useCallback(
|
||||
(trieIndex: BN) => contributors.find((c) => trieIndex.eq(c.trieIndex)),
|
||||
[contributors]
|
||||
);
|
||||
|
||||
const [active, ended] = useMemo(
|
||||
() => extractLists(value, leasePeriod),
|
||||
[leasePeriod, value]
|
||||
@ -84,13 +66,12 @@ function Funds ({ bestNumber, className, leasePeriod, value }: Props): React.Rea
|
||||
<>
|
||||
<Table
|
||||
className={className}
|
||||
empty={active && t<string>('No active campaigns found')}
|
||||
empty={value && active && t<string>('No active campaigns found')}
|
||||
header={headerActiveRef.current}
|
||||
>
|
||||
{active?.map((fund) => (
|
||||
<Fund
|
||||
bestNumber={bestNumber}
|
||||
contributed={findContributions(fund.info.trieIndex)}
|
||||
isOngoing
|
||||
key={fund.accountId}
|
||||
value={fund}
|
||||
@ -99,13 +80,12 @@ function Funds ({ bestNumber, className, leasePeriod, value }: Props): React.Rea
|
||||
</Table>
|
||||
<Table
|
||||
className={className}
|
||||
empty={ended && t<string>('No completed campaigns found')}
|
||||
empty={value && ended && t<string>('No completed campaigns found')}
|
||||
header={headedEndedRef.current}
|
||||
>
|
||||
{ended?.map((fund) => (
|
||||
<Fund
|
||||
bestNumber={bestNumber}
|
||||
contributed={findContributions(fund.info.trieIndex)}
|
||||
key={fund.accountId}
|
||||
value={fund}
|
||||
/>
|
||||
|
||||
@ -15,21 +15,23 @@ import { useIsMountedRef } from './useIsMountedRef';
|
||||
|
||||
type EventCheck = AugmentedEvent<'promise'> | string | false | undefined | null;
|
||||
|
||||
export function useEventTrigger (checks: EventCheck[]): string {
|
||||
const IDENTITY_FILTER = () => true;
|
||||
|
||||
export function useEventTrigger (checks: EventCheck[], filter: (record: EventRecord) => boolean = IDENTITY_FILTER): string {
|
||||
const { api } = useApi();
|
||||
const [trigger, setTrigger] = useState('0x00');
|
||||
const mountedRef = useIsMountedRef();
|
||||
const eventRecords = useCall<Vec<EventRecord>>(api.query.system.events);
|
||||
|
||||
useEffect((): void => {
|
||||
mountedRef.current && eventRecords && eventRecords.filter(({ event }) =>
|
||||
event && checks.some((check) => check && (
|
||||
mountedRef.current && eventRecords && eventRecords.filter((r) =>
|
||||
r.event && checks.some((check) => check && (
|
||||
isString(check)
|
||||
? event.section === check
|
||||
: check.is(event)
|
||||
))
|
||||
? r.event.section === check
|
||||
: check.is(r.event)
|
||||
)) && filter(r)
|
||||
).length && setTrigger(() => eventRecords.createdAtHash?.toHex() || '0x00');
|
||||
}, [eventRecords, checks, mountedRef]);
|
||||
}, [eventRecords, checks, filter, mountedRef]);
|
||||
|
||||
return trigger;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user