Format track selection options (#8555)
* Format track selection options * Adjust submit dialog
This commit is contained in:
parent
707753c75e
commit
b6802bfca0
@ -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<string, TrackInfo[]> = {
|
||||
{
|
||||
id: 0,
|
||||
name: 'root',
|
||||
origin: { system: 'Root' }
|
||||
origin: { system: 'Root' },
|
||||
text: 'Origin for the system root'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
|
||||
@ -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
|
||||
? (
|
||||
<div className='trackOption'>
|
||||
<div className='normal'>{trackName}</div>
|
||||
<div className='faded'>{trackInfo.text}</div>
|
||||
</div>
|
||||
)
|
||||
: trackName,
|
||||
value: id.toNumber()
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function Submit ({ className = '', isMember, members, palletReferenda, tracks }: Props): React.ReactElement<Props> | null {
|
||||
@ -67,20 +72,26 @@ function Submit ({ className = '', isMember, members, palletReferenda, tracks }:
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const [trackId, setTrack] = useState<number | undefined>(undefined);
|
||||
const [origin, setOrigin] = useState<RawParam['value'] | null>(null);
|
||||
const [atAfter, setAtAfter] = useState<RawParam['value'] | null>(null);
|
||||
const [defaultAtAfter, setDefaultAtAfter] = useState<DefaultAtAfter | null>(null);
|
||||
const [{ imageHash, isImageHashValid }, setImageHash] = useState<HashState>({ imageHash: null, isImageHashValid: false });
|
||||
const [{ imageLen, isImageLenValid }, setImageLen] = useState<ImageState>({ imageLen: 0, isImageLenValid: false });
|
||||
const [{ imageLen, imageLenDefault, isImageLenValid }, setImageLen] = useState<ImageState>({ 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<BN | undefined>();
|
||||
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<string>('After delay'), value: 'after' },
|
||||
{ text: t<string>('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'
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t<string>('The hash of the preimage for the proposal as previously submitted or intended.')}>
|
||||
<Input
|
||||
autoFocus
|
||||
help={t<string>('The preimage hash of the proposal')}
|
||||
isError={!isImageHashValid}
|
||||
label={t<string>('preimage hash')}
|
||||
onChange={_onChangeImageHash}
|
||||
value={imageHash || ''}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t<string>('The value will be populated when a valid on-chain hash exists in preimages')}>
|
||||
<Input
|
||||
help={t<string>('The preimage length of the proposal')}
|
||||
key='inputLength'
|
||||
label={t<string>('preimage length')}
|
||||
onChange={_onChangeImageLen}
|
||||
value={imageLen.toString()}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t<string>('The origin (and by extension track) that you wish to submit for, each has a different period, different root and acceptance criteria.')}>
|
||||
<Dropdown
|
||||
defaultValue={trackOpts[0] && trackOpts[0].value}
|
||||
@ -259,18 +236,67 @@ function Submit ({ className = '', isMember, members, palletReferenda, tracks }:
|
||||
/>
|
||||
)}
|
||||
</Modal.Columns>
|
||||
{bestNumber && defaultAtAfter && (
|
||||
<Modal.Columns hint={t<string>('The moment of enactment, either at a specific block, or after a specific number of blocks.', { replace: { bestNumber: formatNumber(bestNumber) } })}>
|
||||
<Params
|
||||
className='timeSelect'
|
||||
isError={isInvalidAt}
|
||||
key={`after:${defaultAtAfter.trackId.toString()}`}
|
||||
onChange={_onChangeAtAfter}
|
||||
params={atAfterType}
|
||||
values={defaultAtAfter.defaults}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
)}
|
||||
<Modal.Columns
|
||||
hint={
|
||||
<>
|
||||
<p>{t<string>('The hash of the preimage for the proposal as previously submitted or intended.')}</p>
|
||||
<p>{t<string>('The length value witll be auto-populated from the on-chain value if is is found.')}</p>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Input
|
||||
autoFocus
|
||||
help={t<string>('The preimage hash of the proposal')}
|
||||
isError={!isImageHashValid}
|
||||
label={t<string>('preimage hash')}
|
||||
onChange={_onChangeImageHash}
|
||||
value={imageHash || ''}
|
||||
/>
|
||||
<InputNumber
|
||||
defaultValue={imageLenDefault}
|
||||
help={t<string>('The preimage length of the proposal')}
|
||||
isDisabled={!!preimage?.proposalLength && !preimage?.proposalLength.isZero() && isImageHashValid && isImageLenValid}
|
||||
isError={!isImageLenValid}
|
||||
key='inputLength'
|
||||
label={t<string>('preimage length')}
|
||||
onChange={_onChangeImageLen}
|
||||
value={imageLen}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns
|
||||
className='centerEnactType'
|
||||
hint={t<string>('The moment of enactment, either at a specific block, or after a specific number of blocks.')}
|
||||
>
|
||||
<ToggleGroup
|
||||
onChange={setEnactIndex}
|
||||
options={enactOpts}
|
||||
value={enactIndex}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
{enactIndex === 0
|
||||
? (
|
||||
<Modal.Columns hint={t<string>('The number of blocks to delay enactment after proposal approval.')}>
|
||||
<InputNumber
|
||||
defaultValue={BN_HUNDRED}
|
||||
isError={isInvalidAt}
|
||||
label={t<string>('after number of blocks')}
|
||||
onChange={setAfterBlocks}
|
||||
value={afterBlocks}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
)
|
||||
: (
|
||||
<Modal.Columns hint={t<string>('A specific block to enact the proposal at.')}>
|
||||
<InputNumber
|
||||
defaultValue={initialAt}
|
||||
isError={isInvalidAt}
|
||||
label={t<string>('at specific block')}
|
||||
onChange={setAtBlock}
|
||||
value={atBlock}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
)
|
||||
}
|
||||
<Modal.Columns hint={t<string>('The deposit for this proposal will be locked for the referendum duration.')}>
|
||||
<InputBalance
|
||||
defaultValue={api.consts[palletReferenda as 'referenda'].submissionDeposit}
|
||||
@ -283,7 +309,7 @@ function Submit ({ className = '', isMember, members, palletReferenda, tracks }:
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
icon='plus'
|
||||
isDisabled={!selectedOrigin || !atAfter || !isImageHashValid || !isImageLenValid || !accountId || isInvalidAt || !preimage?.proposalHash}
|
||||
isDisabled={!selectedOrigin || !isImageHashValid || !isImageLenValid || !accountId || isInvalidAt || !preimage?.proposalHash}
|
||||
label={t<string>('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;
|
||||
}
|
||||
`);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user