Add bounties summary (#4336)

* Add bounty summary INIT

* Add timed displays

* WIP

* Add locales

* Add chain colors and progress display

* Extract useTreasury hook

* Pull spendPeriod into useTreasury

* Apply review suggestions

* Apply review suggestion

* Add check if activeBounties exists

* Add dark theme to bounty summary

* Add border-radius to HighlightedSection

* Apply review suggestions

Co-authored-by: Jaco Greeff <jacogr@gmail.com>
This commit is contained in:
MiZiet 2021-01-09 12:46:52 +01:00 committed by GitHub
parent 5ce8c40916
commit 75023bb4f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 148 additions and 14 deletions

View File

@ -31,15 +31,21 @@
"Title too long": "Title too long",
"Use this account to propose bounty from. This can be a normal or council account.": "Use this account to propose bounty from. This can be a normal or council account.",
"Value": "Value",
"active": "active",
"beneficiary": "beneficiary",
"bounties": "bounties",
"bounty bond": "bounty bond",
"bounty requested allocation": "bounty requested allocation",
"bounty title": "bounty title",
"curator": "curator",
"next bounty funding in": "next bounty funding in",
"next burn": "next burn",
"past": "past",
"payout due": "payout due",
"spend period": "spend period",
"submit with account": "submit with account",
"title": "title",
"treasury": "treasury",
"update due": "update due",
"value": "value",
"vote with account": "vote with account",

View File

@ -6,6 +6,7 @@ import type { BlockNumber } from '@polkadot/types/interfaces';
import React, { useRef } from 'react';
import Summary from '@polkadot/app-bounties/Summary';
import { Button, Table } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
@ -34,6 +35,7 @@ function Bounties (): React.ReactElement {
return (
<>
<Summary activeBounties={deriveBounties?.length}/>
<Button.Group>
<BountyCreate />
</Button.Group>

View File

@ -0,0 +1,91 @@
// Copyright 2017-2020 @polkadot/app-bounties authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BountyIndex } from '@polkadot/types/interfaces';
import React from 'react';
import { useTranslation } from '@polkadot/app-treasury/translate';
import { CardSummary, SummaryBox } from '@polkadot/react-components';
import Progress from '@polkadot/react-components/Progress';
import { useApi, useCall } from '@polkadot/react-hooks';
import { useTreasury } from '@polkadot/react-hooks/useTreasury';
import { BlockToTime, FormatBalance } from '@polkadot/react-query';
import { Balance } from '@polkadot/types/interfaces';
interface Props {
activeBounties?: number;
className?: string;
}
function Summary ({ activeBounties, className = '' }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const bountyIndex = useCall<BountyIndex>((api.query.bounties || api.query.treasury).bountyCount);
const bestNumber = useCall<Balance>(api.derive.chain.bestNumber);
const { burn, spendPeriod, value } = useTreasury();
return (
<SummaryBox className={`ui--BountySummary ${className}`}>
<section>
<CardSummary label={t<string>('active')}>
{activeBounties}
</CardSummary>
{activeBounties !== undefined && (
<CardSummary label={t<string>('past')}>
{bountyIndex?.subn(activeBounties).toString()}
</CardSummary>
)}
{bestNumber && spendPeriod.gtn(0) && (
<CardSummary label={t<string>('next bounty funding in')}>
<BlockToTime
blocks={spendPeriod.sub(bestNumber.mod(spendPeriod))}
className='timer'
/>
</CardSummary>
)}
</section>
<section>
{value && (
<CardSummary label={t<string>('treasury')}>
<FormatBalance
value={value}
withSi
/>
</CardSummary>
)}
{burn && (
<CardSummary
label={t<string>('next burn')}
>
<FormatBalance
value={burn}
withSi
/>
</CardSummary>
)}
{spendPeriod.gtn(0) && (
<CardSummary
label={t<string>('spend period')}
>
<BlockToTime
blocks={spendPeriod}
className='timer'
/>
</CardSummary>
)}
{bestNumber && spendPeriod.gtn(0) && (
<Progress
className='media--1000'
total={spendPeriod}
value={bestNumber.mod(spendPeriod)}
/>
)}
</section>
</SummaryBox>
);
}
export default React.memo(Summary);

View File

@ -1,7 +1,6 @@
// Copyright 2017-2021 @polkadot/app-treasury authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { DeriveBalancesAccount } from '@polkadot/api-derive/types';
import type { Balance } from '@polkadot/types/interfaces';
import BN from 'bn.js';
@ -9,34 +8,24 @@ import React from 'react';
import { CardSummary, SummaryBox } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
import { useTreasury } from '@polkadot/react-hooks/useTreasury';
import { FormatBalance } from '@polkadot/react-query';
import { formatNumber, stringToU8a } from '@polkadot/util';
import { formatNumber } from '@polkadot/util';
import { useTranslation } from '../translate';
const TREASURY_ACCOUNT = stringToU8a('modlpy/trsry'.padEnd(32, '\0'));
interface Props {
approvalCount?: number;
proposalCount?: number;
}
const PM_DIV = new BN(1000000);
function Summary ({ approvalCount, proposalCount }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const bestNumber = useCall<Balance>(api.derive.chain.bestNumber);
const totalProposals = useCall<BN>(api.query.treasury.proposalCount);
const treasuryBalance = useCall<DeriveBalancesAccount>(api.derive.balances.account, [TREASURY_ACCOUNT]);
const spendPeriod = api.consts.treasury.spendPeriod;
const value = treasuryBalance?.freeBalance.gtn(0)
? treasuryBalance.freeBalance
: null;
const burn = treasuryBalance?.freeBalance.gtn(0) && !api.consts.treasury.burn.isZero()
? api.consts.treasury.burn.mul(treasuryBalance?.freeBalance).div(PM_DIV)
: null;
const { burn, spendPeriod, value } = useTreasury();
return (
<SummaryBox>

View File

@ -0,0 +1,46 @@
// Copyright 2017-2020 @polkadot/react-hooks authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Balance } from '@polkadot/types/interfaces';
import BN from 'bn.js';
import { useEffect, useState } from 'react';
import { DeriveBalancesAccount } from '@polkadot/api-derive/types';
import { useApi } from '@polkadot/react-hooks/useApi';
import { useCall } from '@polkadot/react-hooks/useCall';
import { BN_ZERO, stringToU8a } from '@polkadot/util';
const PM_DIV = new BN(1000000);
const TREASURY_ACCOUNT = stringToU8a('modlpy/trsry'.padEnd(32, '\0'));
interface Treasury {
value?: Balance;
burn?: BN;
spendPeriod: BN;
}
export function useTreasury (): Treasury {
const { api } = useApi();
const [value, setValue] = useState<Balance>();
const [burn, setBurn] = useState<BN>();
const treasuryBalance = useCall<DeriveBalancesAccount>(api.derive.balances.account, [TREASURY_ACCOUNT]);
const spendPeriod = api.consts.treasury.spendPeriod;
useEffect(() => {
setValue(treasuryBalance?.freeBalance.gtn(0)
? treasuryBalance.freeBalance
: undefined);
setBurn(treasuryBalance?.freeBalance.gtn(0) && !api.consts.treasury.burn.isZero()
? api.consts.treasury.burn.mul(treasuryBalance?.freeBalance).div(PM_DIV)
: BN_ZERO);
}, [api, treasuryBalance, spendPeriod]);
return {
burn,
spendPeriod,
value
};
}