diff --git a/packages/apps/public/locales/en/app-bounties.json b/packages/apps/public/locales/en/app-bounties.json index 546a7f53ff..3a6d97efb7 100644 --- a/packages/apps/public/locales/en/app-bounties.json +++ b/packages/apps/public/locales/en/app-bounties.json @@ -3,6 +3,7 @@ "Accept": "Accept", "Account does not have enough funds.": "Account does not have enough funds.", "Add Bounty": "Add Bounty", + "After a bounty was proposed the council decides whether to fund it or not.": "After a bounty was proposed the council decides whether to fund it or not.", "Allocation value is smaller than the minimum bounty value.": "Allocation value is smaller than the minimum bounty value.", "Approval under voting": "Approval under voting", "Approve": "Approve", @@ -12,6 +13,7 @@ "Choose a curator whose background and expertise is such that they are capable of determining when the task is complete.": "Choose a curator whose background and expertise is such that they are capable of determining when the task is complete.", "Claim": "Claim", "Claimable": "Claimable", + "Curator has been proposed by council. The bounty is waiting for curator to accept the role.": "Curator has been proposed by council. The bounty is waiting for curator to accept the role.", "Curator under voting": "Curator under voting", "Curator's deposit": "Curator's deposit", "Curator's fee": "Curator's fee", @@ -43,6 +45,10 @@ "This action will create a Council motion to either approve or reject the Bounty.": "This action will create a Council motion to either approve or reject the Bounty.", "This action will extend expiry time of the selected bounty.": "This action will extend expiry time of the selected bounty.", "This amount will be reserved from origin account and returned on approval or slashed upon rejection.": "This amount will be reserved from origin account and returned on approval or slashed upon rejection.", + "This bounty has received approval and is currently being implemented.": "This bounty has received approval and is currently being implemented.", + "This bounty is approved and funded.": "This bounty is approved and funded.", + "This bounty was approved by the council. When next treasury spend period comes, it will get funded.": "This bounty was approved by the council. When next treasury spend period comes, it will get funded.", + "This bounty was completed and the beneficiary was awarded by the curator. Claiming the payout will be possible after delay has passed.": "This bounty was completed and the beneficiary was awarded by the curator. Claiming the payout will be possible after delay has passed.", "Title too long": "Title too long", "Unassign curator under voting": "Unassign curator under voting", "Value": "Value", diff --git a/packages/apps/public/locales/en/translation.json b/packages/apps/public/locales/en/translation.json index 1b97f5c4f2..63c214406b 100644 --- a/packages/apps/public/locales/en/translation.json +++ b/packages/apps/public/locales/en/translation.json @@ -9,13 +9,13 @@ "Add an existing contract": "", "Add proxy": "", "Address index {{index}}": "", + "After a bounty was proposed the council decides whether to fund it or not.": "", "All stashes": "", "All voters may alter their votes any number of times prior to the close of the poll.": "", "An account that is to receive the rewards": "", "Any combination of the four options may be approved of by the voter. There is no need to select only one option!": "", "Approval under voting": "", "Approving of all or none of the options is equivalent and will not affect the outcome of the poll.": "", - "Asset ID": "", "Authorize transaction": "", "Bond": "", "Cancel": "", @@ -30,6 +30,7 @@ "Contracts": "", "Controller account": "", "Create a backup file for this account": "", + "Curator has been proposed by council. The bounty is waiting for curator to accept the role.": "", "Curator under voting": "", "Curator's deposit": "", "Curator's fee": "", @@ -49,7 +50,6 @@ "Governance": "", "If you have not claimed rewards straight after the end of the era, the validator is in the active set and you are seeing no rewards, this would mean that the reward payout transaction was made by another account on your behalf. Always check your favorite explorer to see any historic payouts made to your accounts.": "", "Inactive": "", - "Info": "", "Initiate recovery for another": "", "Light theme (default)": "", "Locked funds (e.g. for staking) are counted.": "", @@ -66,7 +66,6 @@ "No change": "", "No change from the original 2017 sale definitions; will mean a total of 10 million DOT from genesis.": "", "No discretionary lock-voting is in place; all DOT used to vote counts the same.": "", - "No items": "", "No sub identities set.": "", "Nominating": "", "Nominators": "", @@ -82,7 +81,6 @@ "Retrieving nominators": "", "Retrieving validators": "", "Save": "", - "Select the account to use for this action.": "", "Set on-chain identity": "", "Set on-chain sub-identities": "", "Settings": "", @@ -98,7 +96,6 @@ "Split of 10x from the original sale; will mean a total of 100 million DOT from genesis. Apparent DOT price would be 10x lower and apparent account balances 10x higher.": "", "Stash account (do not increase the amount at stake)": "", "Stash account (increase the amount at stake)": "", - "Submit": "", "Submit (no signature)": "", "Swap to a non-executing approval type, with subsequent calls providing the actual call data.": "", "The account type (derivation) to use": "", @@ -123,6 +120,10 @@ "The sending account that will be used to send this transaction. Any applicable fees will be paid by this account.": "", "The signatory is one of the allowed accounts on the multisig, making a recorded approval for the transaction.": "", "The specified value is too large and does not allow funds to pay future transaction fees.": "", + "This bounty has received approval and is currently being implemented.": "", + "This bounty is approved and funded.": "", + "This bounty was approved by the council. When next treasury spend period comes, it will get funded.": "", + "This bounty was completed and the beneficiary was awarded by the curator. Claiming the payout will be possible after delay has passed.": "", "This could either be an approval for the hash or with full call details. The call as last approval triggers execution.": "", "This is a developer tool that allows you to execute selected snippets in a limited context.": "", "This is not a valid JSON object.": "", @@ -153,7 +154,6 @@ "activate": "", "address copied": "", "address index": "", - "address {{index}}": "", "available": "", "available to be unlocked": "", "aye": "", @@ -220,7 +220,6 @@ "overall/validator": "", "own": "", "own stake": "", - "paid": "", "parachain": "", "parachains": "", "parent": "", @@ -241,14 +240,12 @@ "recent blocks": "", "recently": "", "referenda": "", - "relay parent": "", "remaining": "", "reporters": "", "rewards": "", "role": "", "runners up": "", "scheduled": "", - "scheduling": "", "secret": "", "sending from my account": "", "signer": "", @@ -257,8 +254,6 @@ "state": "", "status": "", "strikes": "", - "sub name": "", - "swap to id": "", "tags": "", "the payment account": "", "threshold": "", @@ -274,7 +269,6 @@ "upcoming": "", "update due": "", "upgrade": "", - "using my account": "", "validation": "", "validator {{index}}": "", "validators": "", @@ -282,7 +276,6 @@ "vote using my account": "", "votes": "", "voting end": "", - "watermark": "", "yesterday": "", "{{balance}} voted": "", "{{count}} key/value pairs encoded for submission": "", diff --git a/packages/page-bounties/src/Bounty.tsx b/packages/page-bounties/src/Bounty.tsx index 737f5a51c1..bb7030c2de 100644 --- a/packages/page-bounties/src/Bounty.tsx +++ b/packages/page-bounties/src/Bounty.tsx @@ -13,9 +13,9 @@ import { BlockToTime, FormatBalance } from '@polkadot/react-query'; import { formatNumber } from '@polkadot/util'; import { BountyActions } from './BountyActions'; +import BountyStatusView from './BountyStatusView'; import { getBountyStatus, truncateTitle } from './helpers'; import { useTranslation } from './translate'; -import VotingDescription from './VotingDescription'; interface Props { bestNumber: BlockNumber; @@ -54,17 +54,11 @@ function Bounty ({ bestNumber, bounty, className = '', description, index, propo <> -
-
{bountyStatus}
- {proposals && ( -
- -
- )} -
+ {truncateTitle(description, 30)} diff --git a/packages/page-bounties/src/BountyStatusView.tsx b/packages/page-bounties/src/BountyStatusView.tsx new file mode 100644 index 0000000000..ba4564cecd --- /dev/null +++ b/packages/page-bounties/src/BountyStatusView.tsx @@ -0,0 +1,61 @@ +// Copyright 2017-2021 @polkadot/app-bounties authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { DeriveCollectiveProposal } from '@polkadot/api-derive/types'; +import type { BountyStatus } from '@polkadot/types/interfaces'; + +import React, { useRef } from 'react'; +import styled from 'styled-components'; + +import { LabelHelp } from '@polkadot/react-components'; + +import { insertSpaceBeforeCapitalLetter } from './helpers'; +import { useTranslation } from './translate'; +import { HelpMessages, StatusName } from './types'; +import VotingDescription from './VotingDescription'; + +interface Props { + bountyStatus: StatusName; + className?: string; + proposals?: DeriveCollectiveProposal[]; + status: BountyStatus; +} + +function BountyStatusView ({ bountyStatus, className = '', proposals, status }: Props): React.ReactElement { + const { t } = useTranslation(); + + const statusHelpMessages = useRef({ + Active: t('This bounty has received approval and is currently being implemented.'), + Approved: t('This bounty was approved by the council. When next treasury spend period comes, it will get funded.'), + CuratorProposed: t('Curator has been proposed by council. The bounty is waiting for curator to accept the role.'), + Funded: t('This bounty is approved and funded.'), + PendingPayout: t('This bounty was completed and the beneficiary was awarded by the curator. Claiming the payout will be possible after delay has passed.'), + Proposed: t('After a bounty was proposed the council decides whether to fund it or not.') + }); + + return ( +
+
+ {insertSpaceBeforeCapitalLetter(bountyStatus)} + +
+ {proposals && ( +
+ +
+ )} +
+ ); +} + +export default React.memo(styled(BountyStatusView)` + .bountyStatus { + display: flex; + align-items: flex-end; + } +`); diff --git a/packages/page-bounties/src/VotingDescription.tsx b/packages/page-bounties/src/VotingDescription.tsx index aecb18a3cf..fef6ae351c 100644 --- a/packages/page-bounties/src/VotingDescription.tsx +++ b/packages/page-bounties/src/VotingDescription.tsx @@ -4,7 +4,7 @@ import type { DeriveCollectiveProposal } from '@polkadot/api-derive/types'; import type { BountyStatus } from '@polkadot/types/interfaces'; -import React, { useMemo } from 'react'; +import React, { useRef } from 'react'; import styled from 'styled-components'; import { bestValidProposalName } from './helpers/extendedStatuses'; @@ -19,12 +19,12 @@ interface Props { function VotingDescription ({ className = '', proposals, status }: Props): React.ReactElement { const bestProposalName = bestValidProposalName(proposals, status); const { t } = useTranslation(); - const descriptions: Record = useMemo(() => ({ + const descriptions = useRef>({ approveBounty: t('Approval under voting'), closeBounty: t('Rejection under voting'), proposeCurator: t('Curator under voting'), unassignCurator: t('Unassign curator under voting') - }), [t]); + }); return ( <> @@ -33,7 +33,7 @@ function VotingDescription ({ className = '', proposals, status }: Props): React className={className} data-testid='extendedStatus' > - <>{descriptions[bestProposalName] || null} + <>{descriptions.current[bestProposalName] || null} )} diff --git a/packages/page-bounties/src/helpers/extendedStatuses.ts b/packages/page-bounties/src/helpers/extendedStatuses.ts index 598c573b95..a81374efa8 100644 --- a/packages/page-bounties/src/helpers/extendedStatuses.ts +++ b/packages/page-bounties/src/helpers/extendedStatuses.ts @@ -4,8 +4,7 @@ import type { DeriveCollectiveProposal } from '@polkadot/api-derive/types'; import type { BountyStatus } from '@polkadot/types/interfaces'; -type BountyVotingStatus = 'Active' | 'Approved' | 'CuratorProposed' | 'Funded' | 'PendingPayout' | 'Proposed'; -type BountyVotingStatuses = { [status in BountyVotingStatus]: string[] }; +import { BountyVotingStatuses, StatusName } from '../types'; const validProposalNames: BountyVotingStatuses = { Active: ['closeBounty', 'unassignCurator'], @@ -17,7 +16,7 @@ const validProposalNames: BountyVotingStatuses = { }; function validMethods (status: BountyStatus): string[] { - return validProposalNames[status.type as BountyVotingStatus]; + return validProposalNames[status.type as StatusName]; } function getProposalByMethod (bountyProposals: DeriveCollectiveProposal[], method: string | undefined): DeriveCollectiveProposal | undefined { diff --git a/packages/page-bounties/src/helpers/getBountyStatus.spec.ts b/packages/page-bounties/src/helpers/getBountyStatus.spec.ts index 0a0ecc8740..0f8855c180 100644 --- a/packages/page-bounties/src/helpers/getBountyStatus.spec.ts +++ b/packages/page-bounties/src/helpers/getBountyStatus.spec.ts @@ -15,7 +15,7 @@ describe('get bounty status', () => { it('for CuratorProposed state it has curator defined', () => { const bountyStatus = getBountyStatus(registry.createType('BountyStatus', 'CuratorProposed')); - expect(bountyStatus.bountyStatus).toEqual('Curator Proposed'); + expect(bountyStatus.bountyStatus).toEqual('CuratorProposed'); expect(bountyStatus.curator).toBeDefined(); }); @@ -30,7 +30,7 @@ describe('get bounty status', () => { it('for PendingPayout state it has curator, beneficiary and unlock_at defined', () => { const bountyStatus = getBountyStatus(registry.createType('BountyStatus', 'PendingPayout')); - expect(bountyStatus.bountyStatus).toEqual('Pending Payout'); + expect(bountyStatus.bountyStatus).toEqual('PendingPayout'); expect(bountyStatus.curator).toBeDefined(); expect(bountyStatus.beneficiary).toBeDefined(); expect(bountyStatus.unlockAt).toBeDefined(); diff --git a/packages/page-bounties/src/helpers/getBountyStatus.ts b/packages/page-bounties/src/helpers/getBountyStatus.ts index 09b83dc8a1..63f0cb7fc3 100644 --- a/packages/page-bounties/src/helpers/getBountyStatus.ts +++ b/packages/page-bounties/src/helpers/getBountyStatus.ts @@ -3,10 +3,10 @@ import type { BountyStatus } from '@polkadot/types/interfaces'; -import { BountyStatusType } from '../types'; +import { BountyStatusType, StatusName } from '../types'; export const getBountyStatus = (status: BountyStatus): BountyStatusType => { - const statusAsString = status.type; + const statusAsString = status.type as StatusName; let result: BountyStatusType = { beneficiary: undefined, @@ -19,7 +19,7 @@ export const getBountyStatus = (status: BountyStatus): BountyStatusType => { if (status.isCuratorProposed) { result = { ...result, - bountyStatus: 'Curator Proposed', + bountyStatus: 'CuratorProposed', curator: status.asCuratorProposed.curator }; } @@ -36,7 +36,7 @@ export const getBountyStatus = (status: BountyStatus): BountyStatusType => { result = { ...result, beneficiary: status.asPendingPayout.beneficiary, - bountyStatus: 'Pending Payout', + bountyStatus: 'PendingPayout', curator: status.asPendingPayout.curator, unlockAt: status.asPendingPayout.unlockAt }; diff --git a/packages/page-bounties/src/helpers/index.tsx b/packages/page-bounties/src/helpers/index.tsx index 408016d294..d10aa42c9d 100644 --- a/packages/page-bounties/src/helpers/index.tsx +++ b/packages/page-bounties/src/helpers/index.tsx @@ -4,4 +4,4 @@ export * from './calculateBountyBond'; export * from './getBountyStatus'; export * from './isClaimable'; -export * from './truncateTitle'; +export * from './stringHelpers'; diff --git a/packages/page-bounties/src/helpers/truncateTitle.ts b/packages/page-bounties/src/helpers/stringHelpers.ts similarity index 71% rename from packages/page-bounties/src/helpers/truncateTitle.ts rename to packages/page-bounties/src/helpers/stringHelpers.ts index a7e334c57d..e1a052b64d 100644 --- a/packages/page-bounties/src/helpers/truncateTitle.ts +++ b/packages/page-bounties/src/helpers/stringHelpers.ts @@ -1,6 +1,10 @@ // Copyright 2017-2021 @polkadot/app-bounties authors & contributors // SPDX-License-Identifier: Apache-2.0 +export function insertSpaceBeforeCapitalLetter (str: string): string { + return str.replace(/([a-z])([A-Z])/g, '$1 $2'); +} + export function truncateTitle (str: string, maxLength: number): string { const ellipsis = String.fromCharCode(8230); diff --git a/packages/page-bounties/src/helpers/truncateTitle.spec.ts b/packages/page-bounties/src/helpers/truncateTitle.spec.ts index 065c15ae8b..09f849c1a6 100644 --- a/packages/page-bounties/src/helpers/truncateTitle.spec.ts +++ b/packages/page-bounties/src/helpers/truncateTitle.spec.ts @@ -1,7 +1,7 @@ // Copyright 2017-2021 @polkadot/app-bounties authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { truncateTitle } from '@polkadot/app-bounties/helpers/truncateTitle'; +import { truncateTitle } from './stringHelpers'; describe('Truncate title', () => { it('does not truncate short title', () => { diff --git a/packages/page-bounties/src/types.ts b/packages/page-bounties/src/types.ts index b1a1cb4512..230d04fbe9 100644 --- a/packages/page-bounties/src/types.ts +++ b/packages/page-bounties/src/types.ts @@ -5,8 +5,13 @@ import type { AccountId, BlockNumber } from '@polkadot/types/interfaces/runtime' export interface BountyStatusType { beneficiary: AccountId | undefined; - bountyStatus: string; + bountyStatus: StatusName; curator: AccountId | undefined; unlockAt: BlockNumber | undefined; updateDue: BlockNumber | undefined; } + +export type HelpMessages = Record; + +export type StatusName = 'Active' | 'Approved' | 'CuratorProposed' | 'Funded' | 'PendingPayout' | 'Proposed'; +export type BountyVotingStatuses = { [status in StatusName]: string[] };