Adjust useBounties hook (#7475)

* Adjust useBounties hook

* Adjust
This commit is contained in:
Jaco 2022-04-28 11:22:47 +02:00 committed by GitHub
parent b07100b8b0
commit 55af72d88f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 121 additions and 96 deletions

View File

@ -463,8 +463,7 @@ describe('Bounties', () => {
{
curator: alice,
status: 'Active',
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
updateDue: mockBountyHooks.bountyApi.bestNumber!.toNumber()
updateDue: mockBountyHooks.bountyApi.bestNumber?.toNumber()
}) });
bountiesPage.renderOne(bounty);

View File

@ -1,7 +1,7 @@
// Copyright 2017-2022 @polkadot/app-bounties authors & contributors
// SPDX-License-Identifier: Apache-2.0
import React, { useRef } from 'react';
import React, { useMemo, useRef } from 'react';
import styled from 'styled-components';
import Summary from '@polkadot/app-bounties/Summary';
@ -18,7 +18,12 @@ interface Props {
function Bounties ({ className }: Props): React.ReactElement {
const { t } = useTranslation();
const { bestNumber, bounties } = useBounties();
const info = useBounties();
const sorted = useMemo(
() => info && info.bounties && [...info.bounties].sort((a, b) => b.index.cmp(a.index)),
[info]
);
const headerRef = useRef([
[t('bounties'), 'start', 3],
@ -29,30 +34,26 @@ function Bounties ({ className }: Props): React.ReactElement {
return (
<div className={className}>
<Summary activeBounties={bounties?.length} />
<Summary info={info} />
<Button.Group>
<BountyCreate />
</Button.Group>
<Table
className='bounties-table-wrapper'
empty={bounties && t<string>('No open bounties')}
empty={sorted && t<string>('No open bounties')}
header={headerRef.current}
withCollapsibleRows
>
{bounties && bestNumber &&
bounties
.sort((a, b) => b.index.cmp(a.index))
.map(({ bounty, description, index, proposals }) => (
<Bounty
bestNumber={bestNumber}
bounty={bounty}
description={description}
index={index}
key={index.toNumber()}
proposals={proposals}
/>
))
}
{sorted && info.bestNumber && sorted.map(({ bounty, description, index, proposals }) => (
<Bounty
bestNumber={info.bestNumber}
bounty={bounty}
description={description}
index={index}
key={index.toNumber()}
proposals={proposals}
/>
))}
</Table>
</div>
);

View File

@ -33,8 +33,6 @@ interface Props {
proposals?: DeriveCollectiveProposal[];
}
const EMPTY_CELL = '-';
function Bounty ({ bestNumber, bounty, className = '', description, index, proposals }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [isExpanded, toggleExpanded] = useToggle(false);
@ -155,18 +153,18 @@ function Bounty ({ bestNumber, bounty, className = '', description, index, propo
<div className='label'>{t('Bond')}</div>
<div className='inline-balance'><FormatBalance value={bond} /></div>
</div>
{curator && (
<div className='label-column-right'>
<div className='label'>{t("Curator's fee")}</div>
<div className='inline-balance'>{<FormatBalance value={fee} />}</div>
</div>
)}
<div className='label-column-right'>
<div className='label'>{t("Curator's fee")}</div>
<div className='inline-balance'>{curator ? <FormatBalance value={fee} /> : EMPTY_CELL}</div>
</div>
<div className='label-column-right'>
{!curatorDeposit.isZero && (
{curator && !curatorDeposit.isZero() && (
<>
<div className='label'>{t("Curator's deposit")}</div>
<div className='inline-balance'>
{curator
? <FormatBalance value={curatorDeposit} />
: EMPTY_CELL}
<FormatBalance value={curatorDeposit} />
</div>
</>
)}

View File

@ -1,24 +1,24 @@
// Copyright 2017-2022 @polkadot/app-bounties authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BountyApi } from './hooks/useBounties';
import React, { useMemo } from 'react';
import { CardSummary, SummaryBox } from '@polkadot/react-components';
import { useTreasury } from '@polkadot/react-hooks';
import { FormatBalance } from '@polkadot/react-query';
import { BN } from '@polkadot/util';
import { BN, formatNumber } from '@polkadot/util';
import { useBounties } from './hooks';
import { useTranslation } from './translate';
interface Props {
activeBounties?: number;
className?: string;
info: BountyApi;
}
function Summary ({ activeBounties, className = '' }: Props): React.ReactElement<Props> {
function Summary ({ className = '', info: { bestNumber, bounties, bountyCount, childCount } }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { bestNumber, bounties, bountyIndex } = useBounties();
const { spendPeriod } = useTreasury();
const totalValue = useMemo(
@ -29,12 +29,19 @@ function Summary ({ activeBounties, className = '' }: Props): React.ReactElement
return (
<SummaryBox className={`ui--BountySummary ${className}`}>
<section>
<CardSummary label={t<string>('active')}>
{activeBounties}
</CardSummary>
{activeBounties !== undefined && (
{bounties && (
<CardSummary label={t<string>('active')}>
{formatNumber(bounties.length)}
</CardSummary>
)}
{bountyCount && bounties && (
<CardSummary label={t<string>('past')}>
{bountyIndex?.subn(activeBounties).toString()}
{formatNumber(bountyCount.subn(bounties.length))}
</CardSummary>
)}
{childCount && (
<CardSummary label={t<string>('children')}>
{formatNumber(childCount)}
</CardSummary>
)}
</section>
@ -47,7 +54,7 @@ function Summary ({ activeBounties, className = '' }: Props): React.ReactElement
</CardSummary>
</section>
<section>
{bestNumber && spendPeriod.gtn(0) && (
{bestNumber && !spendPeriod.isZero() && (
<CardSummary
label={t<string>('funding period')}
progress={{

View File

@ -1,77 +1,98 @@
// Copyright 2017-2022 @polkadot/app-bounties authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ApiPromise } from '@polkadot/api';
import type { SubmittableExtrinsicFunction } from '@polkadot/api/types';
import type { BN } from '@polkadot/util';
import { useMemo } from 'react';
import { DeriveBounties } from '@polkadot/api-derive/types';
import { createNamedHook, useApi, useBestNumber, useCall } from '@polkadot/react-hooks';
import { BalanceOf, BlockNumber, BountyIndex } from '@polkadot/types/interfaces';
import { BalanceOf } from '@polkadot/types/interfaces';
import { BN_ZERO } from '@polkadot/util';
export type BountyApi = {
interface BountyApiTxs {
acceptCurator: SubmittableExtrinsicFunction<'promise'>;
approveBounty: SubmittableExtrinsicFunction<'promise'>;
awardBounty: SubmittableExtrinsicFunction<'promise'>;
bestNumber?: BlockNumber,
bounties?: DeriveBounties,
bountyCuratorDeposit: BN,
bountyDepositBase: BN,
bountyIndex?: BN,
bountyUpdatePeriod?: BN,
bountyValueMinimum: BN,
claimBounty: SubmittableExtrinsicFunction<'promise'>;
closeBounty: SubmittableExtrinsicFunction<'promise'>;
dataDepositPerByte: BN,
extendBountyExpiry: SubmittableExtrinsicFunction<'promise'>;
maximumReasonLength: number,
proposeBounty: SubmittableExtrinsicFunction<'promise'>;
proposeCurator: SubmittableExtrinsicFunction<'promise'>;
unassignCurator: SubmittableExtrinsicFunction<'promise'>;
};
}
interface BountyApiConstants {
bountyCuratorDeposit: BN;
bountyDepositBase: BN;
bountyUpdatePeriod?: BN;
bountyValueMinimum: BN;
dataDepositPerByte: BN;
maximumReasonLength: number;
}
interface BountyApiStatics extends BountyApiConstants, BountyApiTxs {
// nothing additional
}
export interface BountyApi extends BountyApiStatics {
bestNumber?: BN;
bounties?: DeriveBounties;
bountyCount?: BN;
childCount?: BN;
}
function getStatics (api: ApiPromise): BountyApiStatics {
const constsBase = api.consts.bounties || api.consts.treasury;
const txBase = api.tx.bounties || api.tx.treasury;
return {
// constants
bountyCuratorDeposit: (constsBase.bountyCuratorDeposit as BalanceOf || { toBn: () => BN_ZERO }).toBn(),
bountyDepositBase: (constsBase.bountyDepositBase as BalanceOf).toBn(),
bountyUpdatePeriod: constsBase.bountyUpdatePeriod,
bountyValueMinimum: (constsBase.bountyValueMinimum as BalanceOf).toBn(),
dataDepositPerByte: (constsBase.dataDepositPerByte as BalanceOf).toBn(),
maximumReasonLength: constsBase.maximumReasonLength.toNumber(),
// extrinsics
// eslint-disable-next-line sort-keys
acceptCurator: txBase.acceptCurator,
approveBounty: txBase.approveBounty,
awardBounty: txBase.awardBounty,
claimBounty: txBase.claimBounty,
closeBounty: txBase.closeBounty,
extendBountyExpiry: txBase.extendBountyExpiry,
proposeBounty: txBase.proposeBounty,
proposeCurator: txBase.proposeCurator,
unassignCurator: txBase.unassignCurator
};
}
function useBountiesImpl (): BountyApi {
const { api } = useApi();
const bounties = useCall<DeriveBounties>(api.derive.bounties.bounties);
const bountyIndex = useCall<BountyIndex>((api.query.bounties || api.query.treasury).bountyCount);
const bountyCount = useCall<BN>((api.query.bounties || api.query.treasury).bountyCount);
const childCount = useCall<BN>(api.query.childBounties?.childBountyCount);
const bestNumber = useBestNumber();
const constsBase = api.consts.bounties || api.consts.treasury;
const bountyCuratorDeposit = (constsBase.bountyCuratorDeposit as BalanceOf || { toBn: () => BN_ZERO }).toBn();
const bountyDepositBase = (constsBase.bountyDepositBase as BalanceOf).toBn();
const bountyValueMinimum = (constsBase.bountyValueMinimum as BalanceOf).toBn();
const maximumReasonLength = constsBase.maximumReasonLength.toNumber();
const dataDepositPerByte = (constsBase.dataDepositPerByte as BalanceOf).toBn();
const bountyUpdatePeriod = constsBase.bountyUpdatePeriod;
const proposeBounty = (api.tx.bounties || api.tx.treasury).proposeBounty;
const proposeCurator = (api.tx.bounties || api.tx.treasury).proposeCurator;
const claimBounty = (api.tx.bounties || api.tx.treasury).claimBounty;
const acceptCurator = (api.tx.bounties || api.tx.treasury).acceptCurator;
const approveBounty = (api.tx.bounties || api.tx.treasury).approveBounty;
const closeBounty = (api.tx.bounties || api.tx.treasury).closeBounty;
const extendBountyExpiry = (api.tx.bounties || api.tx.treasury).extendBountyExpiry;
const unassignCurator = (api.tx.bounties || api.tx.treasury).unassignCurator;
const awardBounty = (api.tx.bounties || api.tx.treasury).awardBounty;
return {
acceptCurator,
approveBounty,
awardBounty,
bestNumber,
bounties,
bountyCuratorDeposit,
bountyDepositBase,
bountyIndex,
bountyUpdatePeriod,
bountyValueMinimum,
claimBounty,
closeBounty,
dataDepositPerByte,
extendBountyExpiry,
maximumReasonLength,
proposeBounty,
proposeCurator,
unassignCurator
};
const statics = useMemo(
() => getStatics(api),
[api]
);
return useMemo(
(): BountyApi => ({
...statics,
bestNumber,
bounties,
bountyCount,
childCount
}),
[bestNumber, bounties, bountyCount, childCount, statics]
);
}
export const useBounties = createNamedHook('useBounties', useBountiesImpl);

View File

@ -5,8 +5,7 @@ import type { SubmittableExtrinsicFunction } from '@polkadot/api/types';
import { DeriveBounties } from '@polkadot/api-derive/types';
import { balanceOf } from '@polkadot/test-support/creation/balance';
import { BlockNumber } from '@polkadot/types/interfaces';
import { BN } from '@polkadot/util';
import { BN, BN_ONE } from '@polkadot/util';
import { BountyApi } from '../../src/hooks';
@ -16,15 +15,15 @@ export const defaultBountyApi: BountyApi = {
acceptCurator: jest.fn() as unknown as SubmittableExtrinsicFunction<'promise'>,
approveBounty: jest.fn() as unknown as SubmittableExtrinsicFunction<'promise'>,
awardBounty: jest.fn().mockReturnValue('mockAwardExtrinsic') as unknown as SubmittableExtrinsicFunction<'promise'>,
bestNumber: new BN(1) as BlockNumber,
bestNumber: BN_ONE,
bounties: [] as DeriveBounties,
bountyCuratorDeposit: balanceOf(500000),
bountyDepositBase: new BN(1),
bountyDepositBase: BN_ONE,
bountyUpdatePeriod: defaultBountyUpdatePeriod,
bountyValueMinimum: new BN(1),
bountyValueMinimum: BN_ONE,
claimBounty: jest.fn() as unknown as SubmittableExtrinsicFunction<'promise'>,
closeBounty: jest.fn().mockReturnValue({ length: 4 }) as unknown as SubmittableExtrinsicFunction<'promise'>,
dataDepositPerByte: new BN(1),
dataDepositPerByte: BN_ONE,
extendBountyExpiry: jest.fn().mockReturnValue('mockExtendExtrinsic') as unknown as SubmittableExtrinsicFunction<'promise'>,
maximumReasonLength: 100,
proposeBounty: jest.fn() as unknown as SubmittableExtrinsicFunction<'promise'>,