Claim payout from bounty list (#4269)
* Add Claim functionality to bounty list * Apply review suggestions * Apply review suggestions * Add tests for isClaimable * Apply review suggestions
This commit is contained in:
parent
e9fe9ff26c
commit
cd53cf1623
@ -4,6 +4,7 @@
|
||||
"Bond is estimated based on bountyDepositBase and dataDepositPerByte constants.": "Bond is estimated based on bountyDepositBase and dataDepositPerByte constants.",
|
||||
"Bounties": "Bounties",
|
||||
"Calculated bond for bounty": "Calculated bond for bounty",
|
||||
"Claim Bounty": "Claim Bounty",
|
||||
"Claimable": "Claimable",
|
||||
"Curators deposit": "Curators deposit",
|
||||
"Curators fee": "Curators fee",
|
||||
|
||||
@ -28,6 +28,7 @@ function Bounties (): React.ReactElement {
|
||||
[t('beneficiary'), 'start'],
|
||||
[t('payout due'), 'start'],
|
||||
[],
|
||||
[],
|
||||
[]
|
||||
]);
|
||||
|
||||
|
||||
@ -11,7 +11,8 @@ import { AddressSmall, Icon, LinkExternal } from '@polkadot/react-components';
|
||||
import { BlockToTime, FormatBalance } from '@polkadot/react-query';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { getBountyStatus } from './helpers/getBountyStatus';
|
||||
import BountyClaim from './BountyClaim';
|
||||
import { getBountyStatus } from './helpers';
|
||||
import { useTranslation } from './translate';
|
||||
|
||||
interface Props {
|
||||
@ -56,6 +57,15 @@ function Bounty ({ bestNumber, bounty, className = '', description, index }: Pro
|
||||
<td><DueBlocks dueBlocks={blocksUntilUpdate} /></td>
|
||||
<td>{beneficiary ? <AddressSmall value={beneficiary} /> : EMPTY_CELL}</td>
|
||||
<td><DueBlocks dueBlocks={blocksUntilPayout} /></td>
|
||||
<td>
|
||||
{beneficiary && blocksUntilPayout && (
|
||||
<BountyClaim
|
||||
beneficiaryId={beneficiary}
|
||||
index={index}
|
||||
payoutDue={blocksUntilPayout}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td className='table-column-icon'>
|
||||
<LinkExternal
|
||||
data={index}
|
||||
@ -101,6 +111,7 @@ function Bounty ({ bestNumber, bounty, className = '', description, index }: Pro
|
||||
<td />
|
||||
<td />
|
||||
<td />
|
||||
<td />
|
||||
</tr>
|
||||
</>
|
||||
);
|
||||
|
||||
45
packages/page-bounties/src/BountyClaim.tsx
Normal file
45
packages/page-bounties/src/BountyClaim.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2017-2020 @polkadot/app-bounties authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { AccountId } from '@polkadot/types/interfaces';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { TxButton } from '@polkadot/react-components';
|
||||
import { useAccounts, useApi } from '@polkadot/react-hooks';
|
||||
|
||||
import { isClaimable } from './helpers';
|
||||
import { useTranslation } from './translate';
|
||||
|
||||
interface Props {
|
||||
beneficiaryId: AccountId;
|
||||
index: number;
|
||||
payoutDue: BN;
|
||||
}
|
||||
|
||||
function BountyClaim ({ beneficiaryId, index, payoutDue }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const { allAccounts, hasAccounts } = useAccounts();
|
||||
|
||||
const isBountyClaimable = useMemo(() => isClaimable(allAccounts, beneficiaryId, payoutDue), [allAccounts, beneficiaryId, payoutDue]);
|
||||
|
||||
return hasAccounts && isBountyClaimable
|
||||
? (
|
||||
<TxButton
|
||||
accountId={beneficiaryId}
|
||||
icon='plus'
|
||||
label={t<string>('Claim Bounty')}
|
||||
params={[index]}
|
||||
tx={
|
||||
api.tx.bounties
|
||||
? 'bounties.claimBounty'
|
||||
: 'treasury.claimBounty'
|
||||
}
|
||||
/>
|
||||
)
|
||||
: null;
|
||||
}
|
||||
|
||||
export default React.memo(BountyClaim);
|
||||
@ -10,7 +10,7 @@ import { Button, Input, InputAddress, InputBalance, Modal, TxButton } from '@pol
|
||||
import { useApi, useToggle } from '@polkadot/react-hooks';
|
||||
import { BN_ZERO } from '@polkadot/util';
|
||||
|
||||
import { calculateBountyBond } from './helpers/calculateBountyBond';
|
||||
import { calculateBountyBond } from './helpers';
|
||||
import { useTranslation } from './translate';
|
||||
|
||||
function BountyCreate () {
|
||||
|
||||
6
packages/page-bounties/src/helpers/index.tsx
Normal file
6
packages/page-bounties/src/helpers/index.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
// Copyright 2017-2020 @polkadot/app-bounties authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export * from './calculateBountyBond';
|
||||
export * from './getBountyStatus';
|
||||
export * from './isClaimable';
|
||||
42
packages/page-bounties/src/helpers/isClaimable.spec.ts
Normal file
42
packages/page-bounties/src/helpers/isClaimable.spec.ts
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2017-2020 @polkadot/app-bounties authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
import { TypeRegistry } from '@polkadot/types/create';
|
||||
|
||||
import { isClaimable } from './isClaimable';
|
||||
|
||||
describe('Is claimable', () => {
|
||||
const registry = new TypeRegistry();
|
||||
const accountAddress = '5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM';
|
||||
const beneficiaryId = registry.createType('AccountId', [registry, accountAddress]);
|
||||
|
||||
it('returns false, when payout due is greater than 0', () => {
|
||||
const accounts = [accountAddress];
|
||||
const payoutDue = new BN('1');
|
||||
|
||||
expect(isClaimable(accounts, beneficiaryId, payoutDue)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false, when payout due is equal 0', () => {
|
||||
const accounts = [accountAddress];
|
||||
const payoutDue = new BN('0');
|
||||
|
||||
expect(isClaimable(accounts, beneficiaryId, payoutDue)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true, when payout due is lesser than 0 and beneficiary is among accounts', () => {
|
||||
const accounts = [accountAddress];
|
||||
const payoutDue = new BN('-1');
|
||||
|
||||
expect(isClaimable(accounts, beneficiaryId, payoutDue)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false, when beneficiary is not among accounts', () => {
|
||||
const accounts = ['This_is_not_the_treasury_address_27Tt8tkntv6Q7JVPhFsTB'];
|
||||
const payoutDue = new BN('-1');
|
||||
|
||||
expect(isClaimable(accounts, beneficiaryId, payoutDue)).toBe(false);
|
||||
});
|
||||
});
|
||||
10
packages/page-bounties/src/helpers/isClaimable.ts
Normal file
10
packages/page-bounties/src/helpers/isClaimable.ts
Normal file
@ -0,0 +1,10 @@
|
||||
// Copyright 2017-2020 @polkadot/app-council authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { AccountId } from '@polkadot/types/interfaces';
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
export function isClaimable (accounts: string[], beneficiary: AccountId, payoutDue: BN): boolean {
|
||||
return payoutDue.ltn(0) && accounts.includes(beneficiary.toString());
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user