From b6802bfca0046bd4a51b3f4910376d0f8b709caa Mon Sep 17 00:00:00 2001 From: Jaco Date: Fri, 16 Dec 2022 10:06:35 +0200 Subject: [PATCH] Format track selection options (#8555) * Format track selection options * Adjust submit dialog --- .../src/api/params/tracks/kusama.ts | 7 +- .../page-referenda/src/Referenda/Submit.tsx | 239 ++++++++++-------- 2 files changed, 144 insertions(+), 102 deletions(-) diff --git a/packages/apps-config/src/api/params/tracks/kusama.ts b/packages/apps-config/src/api/params/tracks/kusama.ts index 30ce0fceb2..cbfedf1528 100644 --- a/packages/apps-config/src/api/params/tracks/kusama.ts +++ b/packages/apps-config/src/api/params/tracks/kusama.ts @@ -29,7 +29,9 @@ const SPEND_LIMITS = { }; function formatSpend (mul: number, value: BN): string { - return formatBalance(value.muln(mul), FMT_OPTS); + // We lose the decimals here... depending on chain config, this could be non-optimal + // (A simple formatBalance(value.muln(mul), FMT_OPTS) formats to 4 decimals) + return `${formatBalance(value.muln(mul), FMT_OPTS).split('.')[0]} ${FMT_OPTS.withUnit}`; } function compareFellowshipRank (trackId: number): (rank: BN) => boolean { @@ -124,7 +126,8 @@ export const kusama: Record = { { id: 0, name: 'root', - origin: { system: 'Root' } + origin: { system: 'Root' }, + text: 'Origin for the system root' }, { id: 1, diff --git a/packages/page-referenda/src/Referenda/Submit.tsx b/packages/page-referenda/src/Referenda/Submit.tsx index 9dd7ac2aec..35092aa9fa 100644 --- a/packages/page-referenda/src/Referenda/Submit.tsx +++ b/packages/page-referenda/src/Referenda/Submit.tsx @@ -1,6 +1,7 @@ // Copyright 2017-2022 @polkadot/app-referenda authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { ApiPromise } from '@polkadot/api'; import type { RawParam } from '@polkadot/react-params/types'; import type { BN } from '@polkadot/util'; import type { HexString } from '@polkadot/util/types'; @@ -10,12 +11,12 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import usePreimage from '@polkadot/app-preimages/usePreimage'; -import { Button, Dropdown, Input, InputAddress, InputBalance, Modal, TxButton } from '@polkadot/react-components'; +import { Button, Dropdown, Input, InputAddress, InputBalance, InputNumber, Modal, ToggleGroup, TxButton } from '@polkadot/react-components'; import { useApi, useBestNumber, useToggle } from '@polkadot/react-hooks'; import Params from '@polkadot/react-params'; import { Available } from '@polkadot/react-query'; import { getTypeDef } from '@polkadot/types/create'; -import { BN_HUNDRED, BN_ONE, formatNumber, isHex } from '@polkadot/util'; +import { BN_HUNDRED, BN_ONE, BN_THOUSAND, BN_ZERO, isHex } from '@polkadot/util'; import { useTranslation } from '../translate'; import { getTrackInfo, getTrackName } from '../util'; @@ -34,29 +35,33 @@ interface HashState { } interface ImageState { - imageLen: number; + imageLen: BN; + imageLenDefault?: BN; isImageLenValid: boolean; } -interface DefaultAtAfter { - defaults: [{ isValid: boolean, value: { After: BN } }]; - trackId: number; +interface TrackOpt { + text: React.ReactNode; + value: number; } -function getDefaultEnactment (prev: DefaultAtAfter | null, trackId: number): DefaultAtAfter { - if (prev && prev.trackId === trackId) { - return prev; - } +function getTrackOptions (api: ApiPromise, specName: string, palletReferenda: string, tracks?: TrackDescription[]): undefined | TrackOpt[] { + return tracks && tracks.map(({ id, info }): TrackOpt => { + const trackInfo = getTrackInfo(api, specName, palletReferenda, tracks, id.toNumber()); + const trackName = getTrackName(id, info); - return { - defaults: [{ - isValid: true, - value: { - After: BN_HUNDRED - } - }], - trackId - }; + return { + text: trackInfo?.text + ? ( +
+
{trackName}
+
{trackInfo.text}
+
+ ) + : trackName, + value: id.toNumber() + }; + }); } function Submit ({ className = '', isMember, members, palletReferenda, tracks }: Props): React.ReactElement | null { @@ -67,20 +72,26 @@ function Submit ({ className = '', isMember, members, palletReferenda, tracks }: const [accountId, setAccountId] = useState(null); const [trackId, setTrack] = useState(undefined); const [origin, setOrigin] = useState(null); - const [atAfter, setAtAfter] = useState(null); - const [defaultAtAfter, setDefaultAtAfter] = useState(null); const [{ imageHash, isImageHashValid }, setImageHash] = useState({ imageHash: null, isImageHashValid: false }); - const [{ imageLen, isImageLenValid }, setImageLen] = useState({ imageLen: 0, isImageLenValid: false }); + const [{ imageLen, imageLenDefault, isImageLenValid }, setImageLen] = useState({ imageLen: BN_ZERO, isImageLenValid: false }); + const [enactIndex, setEnactIndex] = useState(0); + const [afterBlocks, setAfterBlocks] = useState(BN_HUNDRED); + const [atBlock, setAtBlock] = useState(BN_ONE); + const [initialAt, setInitialAt] = useState(); const preimage = usePreimage(imageHash); useEffect((): void => { - trackId !== undefined && setDefaultAtAfter((prev) => - getDefaultEnactment(prev, trackId) + bestNumber && setInitialAt((prev) => + prev || bestNumber.add(BN_THOUSAND) ); - }, [trackId]); + }, [bestNumber]); useEffect((): void => { - preimage?.proposalLength && setImageLen({ imageLen: preimage?.proposalLength.toNumber(), isImageLenValid: true }); + preimage?.proposalLength && setImageLen((prev) => ({ + imageLen: prev.imageLen, + imageLenDefault: preimage.proposalLength, + isImageLenValid: prev.isImageLenValid + })); }, [preimage]); const trackInfo = useMemo( @@ -89,25 +100,19 @@ function Submit ({ className = '', isMember, members, palletReferenda, tracks }: ); const isInvalidAt = useMemo( - () => !bestNumber || !atAfter || ( - (atAfter as { At: BN }).At - ? (atAfter as { At: BN }).At.lte(bestNumber) - : (atAfter as { After: BN }).After.lt(BN_ONE) + () => !bestNumber || ( + enactIndex === 0 + ? afterBlocks?.lt(BN_ONE) + : atBlock?.lt(bestNumber) ), - [atAfter, bestNumber] + [afterBlocks, atBlock, bestNumber, enactIndex] ); - const [originType, atAfterType] = useMemo( - () => [ - [{ - name: 'origin', - type: getTypeDef(api.tx[palletReferenda as 'referenda'].submit.meta.args[0].type.toString()) - }], - [{ - name: 'enact', - type: getTypeDef(api.tx[palletReferenda as 'referenda'].submit.meta.args[2].type.toString()) - }] - ], + const originType = useMemo( + () => [{ + name: 'origin', + type: getTypeDef(api.tx[palletReferenda as 'referenda'].submit.meta.args[0].type.toString()) + }], [api, palletReferenda] ); @@ -129,11 +134,16 @@ function Submit ({ className = '', isMember, members, palletReferenda, tracks }: ); const trackOpts = useMemo( - () => tracks && tracks.map(({ id, info }) => ({ - text: getTrackName(id, info), - value: id.toNumber() - })), - [tracks] + () => getTrackOptions(api, specName, palletReferenda, tracks), + [api, palletReferenda, specName, tracks] + ); + + const enactOpts = useMemo( + () => [ + { text: t('After delay'), value: 'after' }, + { text: t('At block'), value: 'at' } + ], + [t] ); const _onChangeOriginMulti = useCallback( @@ -151,12 +161,6 @@ function Submit ({ className = '', isMember, members, palletReferenda, tracks }: [] ); - const _onChangeAtAfter = useCallback( - ([{ isValid, value }]: RawParam[]) => - setAtAfter(isValid ? value : null), - [] - ); - const _onChangeImageHash = useCallback( (h?: string) => setImageHash({ @@ -167,20 +171,12 @@ function Submit ({ className = '', isMember, members, palletReferenda, tracks }: ); const _onChangeImageLen = useCallback( - (l?: string): void => { - try { - const imageLen = Number.parseInt(l || '0', 10); - - setImageLen({ - imageLen, - isImageLenValid: Number.isInteger(imageLen) - }); - } catch { - setImageLen({ - imageLen: 0, - isImageLenValid: false - }); - } + (value: BN): void => { + setImageLen((prev) => ({ + imageLen: value, + imageLenDefault: prev.imageLenDefault, + isImageLenValid: !value.isZero() + })); }, [] ); @@ -210,25 +206,6 @@ function Submit ({ className = '', isMember, members, palletReferenda, tracks }: type='account' /> - ('The hash of the preimage for the proposal as previously submitted or intended.')}> - ('The preimage hash of the proposal')} - isError={!isImageHashValid} - label={t('preimage hash')} - onChange={_onChangeImageHash} - value={imageHash || ''} - /> - - ('The value will be populated when a valid on-chain hash exists in preimages')}> - ('The preimage length of the proposal')} - key='inputLength' - label={t('preimage length')} - onChange={_onChangeImageLen} - value={imageLen.toString()} - /> - ('The origin (and by extension track) that you wish to submit for, each has a different period, different root and acceptance criteria.')}> )} - {bestNumber && defaultAtAfter && ( - ('The moment of enactment, either at a specific block, or after a specific number of blocks.', { replace: { bestNumber: formatNumber(bestNumber) } })}> - - - )} + +

{t('The hash of the preimage for the proposal as previously submitted or intended.')}

+

{t('The length value witll be auto-populated from the on-chain value if is is found.')}

+ + } + > + ('The preimage hash of the proposal')} + isError={!isImageHashValid} + label={t('preimage hash')} + onChange={_onChangeImageHash} + value={imageHash || ''} + /> + ('The preimage length of the proposal')} + isDisabled={!!preimage?.proposalLength && !preimage?.proposalLength.isZero() && isImageHashValid && isImageLenValid} + isError={!isImageLenValid} + key='inputLength' + label={t('preimage length')} + onChange={_onChangeImageLen} + value={imageLen} + /> +
+ ('The moment of enactment, either at a specific block, or after a specific number of blocks.')} + > + + + {enactIndex === 0 + ? ( + ('The number of blocks to delay enactment after proposal approval.')}> + ('after number of blocks')} + onChange={setAfterBlocks} + value={afterBlocks} + /> + + ) + : ( + ('A specific block to enact the proposal at.')}> + ('at specific block')} + onChange={setAtBlock} + value={atBlock} + /> + + ) + } ('The deposit for this proposal will be locked for the referendum duration.')}> ('Submit proposal')} onStart={toggleSubmit} params={[ @@ -293,7 +319,9 @@ function Submit ({ className = '', isMember, members, palletReferenda, tracks }: ? { hash: preimage.proposalHash, len: imageLen } : { hash: imageHash, len: imageLen } }, - atAfter + enactIndex === 0 + ? { After: afterBlocks } + : { At: atBlock } ]} tx={api.tx[palletReferenda as 'referenda'].submit} /> @@ -316,4 +344,15 @@ export default React.memo(styled(Submit)` padding-left: 0; } } + + .trackOption { + .faded { + margin-top: 0.25rem; + opacity: 0.5; + } + } + + .ui--Modal-Columns.centerEnactType > div:first-child { + text-align: center; + } `);