Bounties - Extract "give up curator" role modal, prioritize unhappy path motions to display and organize files into folders (#4596)
This commit is contained in:
parent
bcadef3868
commit
36eb0b2e6a
@ -402,7 +402,7 @@ describe('Bounties', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('slash curator modal', () => {
|
||||
describe('give up curator modal', () => {
|
||||
it('gives up on the Curator role of an Active bounty', async () => {
|
||||
const bounty = aBounty({ status: bountyStatusWith({ curator: alice }) });
|
||||
|
||||
@ -410,12 +410,14 @@ describe('Bounties', () => {
|
||||
await bountiesPage.openGiveUpCuratorsRole();
|
||||
|
||||
bountiesPage.enterProposingAccount(alice);
|
||||
await bountiesPage.clickButton('Approve');
|
||||
await bountiesPage.clickButton('Give Up');
|
||||
|
||||
bountiesPage.expectExtrinsicQueued({ accountId: alice, extrinsic: 'mockUnassignExtrinsic' });
|
||||
expect(mockBountyHooks.bountyApi.unassignCurator).toHaveBeenCalledWith(aBountyIndex(0));
|
||||
});
|
||||
});
|
||||
|
||||
describe('slash curator modal', () => {
|
||||
it('creates a motion when slashing a PendingPayout bounty', async () => {
|
||||
bountiesPage.renderOne(bountyWith({ status: 'PendingPayout' }));
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ import { BlockToTime, FormatBalance } from '@polkadot/react-query';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { getProposalToDisplay } from './helpers/extendedStatuses';
|
||||
import VotingResultsColumn from './Voting/VotersColumn';
|
||||
import VotersColumn from './Voting/VotersColumn';
|
||||
import { BountyActions } from './BountyActions';
|
||||
import BountyExtraActions from './BountyExtraActions';
|
||||
import BountyInfos from './BountyInfos';
|
||||
@ -190,12 +190,12 @@ function Bounty ({ bestNumber, bounty, className = '', description, index, propo
|
||||
<td>
|
||||
{proposals && (
|
||||
<div className='votes-table'>
|
||||
<VotingResultsColumn
|
||||
<VotersColumn
|
||||
option={'ayes'}
|
||||
proposals={proposals}
|
||||
status={status}
|
||||
/>
|
||||
<VotingResultsColumn
|
||||
<VotersColumn
|
||||
option={'nays'}
|
||||
proposals={proposals}
|
||||
status={status}
|
||||
|
||||
@ -8,7 +8,7 @@ import React from 'react';
|
||||
import { useBounties } from '@polkadot/app-bounties/hooks';
|
||||
import { InputAddress, Modal, TxButton } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from './translate';
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
curatorId: AccountId;
|
||||
62
packages/page-bounties/src/BountyExtraActions/GiveUp.tsx
Normal file
62
packages/page-bounties/src/BountyExtraActions/GiveUp.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright 2017-2021 @polkadot/app-bounties authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { AccountId, BountyIndex } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { useBounties } from '@polkadot/app-bounties/hooks';
|
||||
import { InputAddress, Modal, TxButton } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
curatorId: AccountId;
|
||||
index: BountyIndex;
|
||||
toggleOpen: () => void;
|
||||
}
|
||||
|
||||
function BountyGiveUpCurator ({ curatorId, index, toggleOpen }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const { unassignCurator } = useBounties();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
header={t<string>("Give up curator's role")}
|
||||
size='large'
|
||||
>
|
||||
<Modal.Content>
|
||||
<Modal.Column>
|
||||
<p>{t<string>('This action will unassign you from the curator role.')}</p>
|
||||
</Modal.Column>
|
||||
<Modal.Columns>
|
||||
<Modal.Column>
|
||||
<InputAddress
|
||||
help={t<string>('The Curator account that will give up on it\'s role.')}
|
||||
isDisabled
|
||||
label={t<string>('curator account')}
|
||||
type='account'
|
||||
value={curatorId.toString()}
|
||||
withLabel
|
||||
/>
|
||||
</Modal.Column>
|
||||
<Modal.Column>
|
||||
<p>{t<string>('You are giving up your curator role, the bounty will return to the Funded state. You will get your deposit back.')}</p>
|
||||
</Modal.Column>
|
||||
</Modal.Columns>
|
||||
</Modal.Content>
|
||||
<Modal.Actions onCancel={toggleOpen}>
|
||||
<TxButton
|
||||
accountId={curatorId}
|
||||
icon='check'
|
||||
label={t<string>('Give Up')}
|
||||
onStart={toggleOpen}
|
||||
params={[index]}
|
||||
tx={unassignCurator}
|
||||
/>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(BountyGiveUpCurator);
|
||||
@ -53,16 +53,6 @@ function SlashCurator ({ action, curatorId, description, index, toggleOpen }: Pr
|
||||
const unassignCuratorProposal = useMemo(() => unassignCurator(index), [index, unassignCurator]);
|
||||
|
||||
const actionProperties = useMemo<Record<ValidUnassignCuratorAction, ActionProperties>>(() => ({
|
||||
GiveUp: {
|
||||
filter: [curatorId.toString()],
|
||||
header: t('This action will unassign you from a curator role.'),
|
||||
helpMessage: t("The Curator account that will give up on it's role"),
|
||||
params: [index],
|
||||
proposingAccountTip: t('The account that will create the transaction.'),
|
||||
tip: t('You are giving up your curator role, the bounty will return to the Funded state. You will get your deposit back.'),
|
||||
title: t("Give up curator's role"),
|
||||
tx: unassignCurator
|
||||
},
|
||||
SlashCuratorAction: {
|
||||
filter: allAccounts,
|
||||
header: t('This action will Slash the Curator.'),
|
||||
@ -93,7 +83,7 @@ function SlashCurator ({ action, curatorId, description, index, toggleOpen }: Pr
|
||||
title: t('Unassign curator'),
|
||||
tx: api.tx.council.propose
|
||||
}
|
||||
}), [t, curatorId, index, unassignCurator, api.tx.council.propose, allAccounts, members, threshold, unassignCuratorProposal]);
|
||||
}), [t, index, unassignCurator, api.tx.council.propose, allAccounts, members, threshold, unassignCuratorProposal]);
|
||||
|
||||
const { filter, header, helpMessage, params, proposingAccountTip, tip, title, tx } = actionProperties[action];
|
||||
|
||||
@ -10,15 +10,16 @@ import { ThemeProps } from '@polkadot/react-components/types';
|
||||
import { useMembers, useToggle } from '@polkadot/react-hooks';
|
||||
import { BlockNumber, BountyIndex, BountyStatus } from '@polkadot/types/interfaces';
|
||||
|
||||
import CloseBounty from './BountyActions/CloseBounty';
|
||||
import ExtendBountyExpiryAction from './BountyActions/ExtendBountyExpiryAction';
|
||||
import SlashCurator from './BountyActions/SlashCurator';
|
||||
import { determineUnassignCuratorAction } from '../helpers';
|
||||
import { useBountyStatus, useUserRole } from '../hooks';
|
||||
import { bountySvgColor } from '../theme';
|
||||
import { useTranslation } from '../translate';
|
||||
import { ValidUnassignCuratorAction } from '../types';
|
||||
import BountyRejectCurator from './BountyRejectCurator';
|
||||
import { determineUnassignCuratorAction } from './helpers';
|
||||
import { useBountyStatus, useUserRole } from './hooks';
|
||||
import { bountySvgColor } from './theme';
|
||||
import { useTranslation } from './translate';
|
||||
import { ValidUnassignCuratorAction } from './types';
|
||||
import CloseBounty from './CloseBounty';
|
||||
import ExtendBountyExpiryAction from './ExtendBountyExpiryAction';
|
||||
import GiveUp from './GiveUp';
|
||||
import SlashCurator from './SlashCurator';
|
||||
|
||||
interface Props {
|
||||
bestNumber: BlockNumber;
|
||||
@ -29,12 +30,13 @@ interface Props {
|
||||
status: BountyStatus;
|
||||
}
|
||||
|
||||
function BountyExtraActions ({ bestNumber, className, description, index, proposals, status }: Props): React.ReactElement<Props> | null {
|
||||
function Index ({ bestNumber, className, description, index, proposals, status }: Props): React.ReactElement<Props> | null {
|
||||
const [isPopupOpen, togglePopupOpen] = useToggle();
|
||||
const [isCloseBountyOpen, toggleCloseBounty] = useToggle();
|
||||
const [isRejectCuratorOpen, toggleRejectCurator] = useToggle();
|
||||
const [isSlashCuratorOpen, toggleSlashCurator] = useToggle();
|
||||
const [isExtendExpiryOpen, toggleExtendExpiry] = useToggle();
|
||||
const [isGiveUpCuratorOpen, toggleGiveUpCurator] = useToggle();
|
||||
const [selectedAction, setSlashAction] = useState<ValidUnassignCuratorAction>();
|
||||
const { t } = useTranslation();
|
||||
const { isMember } = useMembers();
|
||||
@ -46,7 +48,6 @@ function BountyExtraActions ({ bestNumber, className, description, index, propos
|
||||
const availableSlashActions = determineUnassignCuratorAction(roles, status, blocksUntilUpdate);
|
||||
|
||||
const slashCuratorActionNames = useRef<Record<ValidUnassignCuratorAction, string>>({
|
||||
GiveUp: t('Give Up'),
|
||||
SlashCuratorAction: t('Slash Curator'),
|
||||
SlashCuratorMotion: t('Slash Curator (Council)'),
|
||||
UnassignCurator: t('Unassign Curator')
|
||||
@ -57,10 +58,11 @@ function BountyExtraActions ({ bestNumber, className, description, index, propos
|
||||
|
||||
const showCloseBounty = (status.isFunded || status.isActive || status.isCuratorProposed) && isMember && !existingCloseBountyProposal;
|
||||
const showRejectCurator = status.isCuratorProposed && isCurator;
|
||||
const showGiveUpCurator = status.isActive && isCurator;
|
||||
const showExtendExpiry = status.isActive && isCurator;
|
||||
const showSlashCurator = (status.isCuratorProposed || status.isActive || status.isPendingPayout) && !existingUnassignCuratorProposal && availableSlashActions.length !== 0;
|
||||
|
||||
const hasNoItems = !(showCloseBounty || showRejectCurator || showExtendExpiry || showSlashCurator);
|
||||
const hasNoItems = !(showCloseBounty || showRejectCurator || showExtendExpiry || showSlashCurator || showGiveUpCurator);
|
||||
|
||||
function slashCuratorClicked (action: ValidUnassignCuratorAction) {
|
||||
setSlashAction(action);
|
||||
@ -91,6 +93,13 @@ function BountyExtraActions ({ bestNumber, className, description, index, propos
|
||||
toggleOpen={toggleExtendExpiry}
|
||||
/>
|
||||
}
|
||||
{isGiveUpCuratorOpen && curator &&
|
||||
<GiveUp
|
||||
curatorId={curator}
|
||||
index={index}
|
||||
toggleOpen={toggleGiveUpCurator}
|
||||
/>
|
||||
}
|
||||
{isSlashCuratorOpen && curator && selectedAction &&
|
||||
<SlashCurator
|
||||
action={selectedAction}
|
||||
@ -142,6 +151,14 @@ function BountyExtraActions ({ bestNumber, className, description, index, propos
|
||||
{t<string>('Extend Expiry')}
|
||||
</Menu.Item>
|
||||
}
|
||||
{showGiveUpCurator &&
|
||||
<Menu.Item
|
||||
key='giveUpCurator'
|
||||
onClick={toggleGiveUpCurator}
|
||||
>
|
||||
{t<string>('Give Up')}
|
||||
</Menu.Item>
|
||||
}
|
||||
{showSlashCurator && availableSlashActions.map((actionName) =>
|
||||
<Menu.Item
|
||||
key={actionName}
|
||||
@ -157,7 +174,7 @@ function BountyExtraActions ({ bestNumber, className, description, index, propos
|
||||
: null;
|
||||
}
|
||||
|
||||
export default React.memo(styled(BountyExtraActions)(({ theme }: ThemeProps) => `
|
||||
export default React.memo(styled(Index)(({ theme }: ThemeProps) => `
|
||||
&& .ui--Button:not(.isDisabled):not(.isIcon).settings-button {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
@ -7,8 +7,8 @@ import styled from 'styled-components';
|
||||
import { Icon } from '@polkadot/react-components';
|
||||
import { ThemeProps } from '@polkadot/react-components/types';
|
||||
|
||||
import { bountyColor, bountyLabelColor, bountySvgColor } from './theme';
|
||||
import { useTranslation } from './translate';
|
||||
import { bountyColor, bountyLabelColor, bountySvgColor } from '../theme';
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
className: '';
|
||||
@ -10,11 +10,11 @@ import React from 'react';
|
||||
import { AddressSmall } from '@polkadot/react-components';
|
||||
import { BN_HUNDRED } from '@polkadot/util';
|
||||
|
||||
import VotingSummary from './Voting/VotingSummary';
|
||||
import Description from '../Description';
|
||||
import { useBounties } from '../hooks';
|
||||
import { useTranslation } from '../translate';
|
||||
import BountyInfo from './BountyInfo';
|
||||
import Description from './Description';
|
||||
import { useBounties } from './hooks';
|
||||
import { useTranslation } from './translate';
|
||||
import VotingSummary from './VotingSummary';
|
||||
|
||||
interface Props {
|
||||
beneficiary?: AccountId;
|
||||
@ -29,12 +29,6 @@ describe('adjust slash curator component for', () => {
|
||||
expect(displayAs).toEqual(['SlashCuratorMotion']);
|
||||
});
|
||||
|
||||
it('Curator in Active state', () => {
|
||||
const displayAs = determineUnassignCuratorAction(['Curator'], aBountyStatus('Active'));
|
||||
|
||||
expect(displayAs).toEqual(['GiveUp']);
|
||||
});
|
||||
|
||||
it('User in Active state with update due blocks remaining', () => {
|
||||
const displayAs = determineUnassignCuratorAction(['User'], aBountyStatus('Active'), new BN('1'));
|
||||
|
||||
|
||||
@ -17,10 +17,6 @@ export function determineUnassignCuratorAction (roles: UserRole[], status: Bount
|
||||
}
|
||||
|
||||
if (status.isActive) {
|
||||
if (roles.includes('Curator')) {
|
||||
actions.push('GiveUp');
|
||||
}
|
||||
|
||||
if (roles.includes('Member')) {
|
||||
actions.push('SlashCuratorMotion');
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import { BountyVotingStatuses, StatusName } from '../types';
|
||||
const validProposalNames: BountyVotingStatuses = {
|
||||
Active: ['closeBounty', 'unassignCurator'],
|
||||
Approved: [],
|
||||
CuratorProposed: ['unassignCurator', 'closeBounty'],
|
||||
CuratorProposed: ['closeBounty', 'unassignCurator'],
|
||||
Funded: ['proposeCurator', 'closeBounty'],
|
||||
PendingPayout: ['unassignCurator'],
|
||||
Proposed: ['approveBounty', 'closeBounty']
|
||||
|
||||
@ -15,6 +15,6 @@ export type HelpMessages = Record<StatusName, string>;
|
||||
|
||||
export type StatusName = 'Active' | 'Approved' | 'CuratorProposed' | 'Funded' | 'PendingPayout' | 'Proposed';
|
||||
export type BountyVotingStatuses = { [status in StatusName]: string[] };
|
||||
export type ValidUnassignCuratorAction = 'GiveUp' | 'UnassignCurator' | 'SlashCuratorMotion' | 'SlashCuratorAction';
|
||||
export type ValidUnassignCuratorAction = 'UnassignCurator' | 'SlashCuratorMotion' | 'SlashCuratorAction';
|
||||
export type UnassignCuratorAction = ValidUnassignCuratorAction | 'None';
|
||||
export type UserRole = 'User' | 'Member' | 'Curator' | 'None';
|
||||
|
||||
@ -281,7 +281,7 @@ export class BountiesPage {
|
||||
async openGiveUpCuratorsRole (): Promise<void> {
|
||||
await this.openExtraActions();
|
||||
await this.clickButtonByText('Give Up');
|
||||
await this.expectText('This action will unassign you from a curator role.');
|
||||
await this.expectText('This action will unassign you from the curator role.');
|
||||
}
|
||||
|
||||
async openSlashCuratorByCouncil (): Promise<void> {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user