Add preimage buttons to proposals (#2192)
* Add preimage buttons to proposals * Styling overrides * InputAddress filter
This commit is contained in:
parent
8be974f500
commit
52a2aa275a
@ -12,6 +12,7 @@ import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import ProposalCell from './ProposalCell';
|
||||
import PreImageButton from './PreImageButton';
|
||||
|
||||
interface Props {
|
||||
blockNumber?: BlockNumber;
|
||||
@ -47,6 +48,13 @@ export default function DispatchEntry ({ blockNumber, hash, referendumIndex }: P
|
||||
proposalHash={hash}
|
||||
proposal={proposal}
|
||||
/>
|
||||
<td className='together number top'>
|
||||
<PreImageButton
|
||||
hash={hash}
|
||||
isImminent
|
||||
proposal={proposal}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
|
||||
import { Hash } from '@polkadot/types/interfaces';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
@ -15,25 +16,31 @@ import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
isImminent?: boolean;
|
||||
matchHash?: Hash;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const ZERO_HASH = blake2AsHex('');
|
||||
|
||||
function PreImage ({ className, onClose }: Props): React.ReactElement<Props> {
|
||||
function PreImage ({ className, isImminent: propsIsImminent, matchHash, onClose }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { apiDefaultTxSudo } = useApi();
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const [isImminent, setIsImminent] = useState(false);
|
||||
const [{ hex, hash }, setHash] = useState<{ hex: string; hash: string }>({ hex: '', hash: ZERO_HASH });
|
||||
const [isImminent, setIsImminent] = useState(propsIsImminent || false);
|
||||
const [{ encodedProposal, encodedHash }, setHash] = useState<{ encodedProposal: string; encodedHash: string }>({ encodedProposal: '', encodedHash: ZERO_HASH });
|
||||
const [proposal, setProposal] = useState<any>();
|
||||
|
||||
useEffect((): void => {
|
||||
const hex = (proposal as SubmittableExtrinsic)?.method.toHex() || '';
|
||||
const encodedProposal = (proposal as SubmittableExtrinsic)?.method.toHex() || '';
|
||||
|
||||
setHash({ hex, hash: blake2AsHex(hex) });
|
||||
setHash({ encodedProposal, encodedHash: blake2AsHex(encodedProposal) });
|
||||
}, [proposal]);
|
||||
|
||||
const isMatched = matchHash
|
||||
? matchHash.eq(encodedHash)
|
||||
: true;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className={className}
|
||||
@ -55,8 +62,9 @@ function PreImage ({ className, onClose }: Props): React.ReactElement<Props> {
|
||||
<Input
|
||||
help={t('The hash of the selected proposal, use it for submitting the proposal')}
|
||||
isDisabled
|
||||
isDisabledError={!isMatched}
|
||||
label={t('preimage hash')}
|
||||
value={hash}
|
||||
value={encodedHash}
|
||||
/>
|
||||
<Toggle
|
||||
className='toggleImminent'
|
||||
@ -68,12 +76,12 @@ function PreImage ({ className, onClose }: Props): React.ReactElement<Props> {
|
||||
<Modal.Actions onCancel={onClose}>
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
isDisabled={!proposal || !accountId}
|
||||
isDisabled={!proposal || !accountId || !isMatched || !encodedProposal}
|
||||
isPrimary
|
||||
label={t('Submit preimage')}
|
||||
icon='add'
|
||||
onStart={onClose}
|
||||
params={[hex]}
|
||||
params={[encodedProposal]}
|
||||
tx={isImminent ? 'democracy.noteImminentPreimage' : 'democracy.notePreimage'}
|
||||
withSpinner={false}
|
||||
/>
|
||||
|
||||
47
packages/app-democracy/src/Overview/PreImageButton.tsx
Normal file
47
packages/app-democracy/src/Overview/PreImageButton.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2017-2020 @polkadot/app-democracy authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Proposal, Hash } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import { Button } from '@polkadot/react-components';
|
||||
import { useToggle } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import PreImage from './PreImage';
|
||||
|
||||
interface Props {
|
||||
hash: Hash;
|
||||
isImminent?: boolean;
|
||||
proposal?: Proposal;
|
||||
withoutOr?: boolean;
|
||||
}
|
||||
|
||||
export default function PreImageButton ({ hash, isImminent, proposal, withoutOr }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const [isPreimageOpen, togglePreimage] = useToggle();
|
||||
|
||||
if (proposal) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{!withoutOr && <Button.Or />}
|
||||
<Button
|
||||
icon='plus'
|
||||
isPrimary
|
||||
label={t('Preimage')}
|
||||
onClick={togglePreimage}
|
||||
/>
|
||||
{isPreimageOpen && (
|
||||
<PreImage
|
||||
isImminent={isImminent}
|
||||
matchHash={hash}
|
||||
onClose={togglePreimage}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -6,11 +6,12 @@ import { DeriveProposal } from '@polkadot/api-derive/types';
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { AddressMini, AddressSmall } from '@polkadot/react-components';
|
||||
import { AddressMini, AddressSmall, Button } from '@polkadot/react-components';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import PreImageButton from './PreImageButton';
|
||||
import ProposalCell from './ProposalCell';
|
||||
import Seconding from './Seconding';
|
||||
|
||||
@ -56,11 +57,17 @@ function Proposal ({ className, value: { balance, hash, index, proposal, propose
|
||||
)}
|
||||
</td>
|
||||
<td className='together number top'>
|
||||
<Seconding
|
||||
depositors={seconds || []}
|
||||
proposalId={index}
|
||||
proposal={proposal}
|
||||
/>
|
||||
<Button.Group>
|
||||
<Seconding
|
||||
depositors={seconds || []}
|
||||
proposalId={index}
|
||||
proposal={proposal}
|
||||
/>
|
||||
<PreImageButton
|
||||
hash={hash}
|
||||
proposal={proposal}
|
||||
/>
|
||||
</Button.Group>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
@ -9,11 +9,13 @@ import { BlockNumber } from '@polkadot/types/interfaces';
|
||||
import BN from 'bn.js';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
import { Button } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import PreImageButton from './PreImageButton';
|
||||
import ProposalCell from './ProposalCell';
|
||||
import Voting from './Voting';
|
||||
|
||||
@ -109,10 +111,16 @@ function Referendum ({ className, idNumber, value }: Props): React.ReactElement<
|
||||
<FormatBalance value={votedNay} />
|
||||
</td>
|
||||
<td className='number together top'>
|
||||
<Voting
|
||||
proposal={value.proposal}
|
||||
referendumId={value.index}
|
||||
/>
|
||||
<Button.Group>
|
||||
<Voting
|
||||
proposal={value.proposal}
|
||||
referendumId={value.index}
|
||||
/>
|
||||
<PreImageButton
|
||||
hash={value.hash}
|
||||
proposal={value.proposal}
|
||||
/>
|
||||
</Button.Group>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
@ -20,6 +20,7 @@ interface Props extends BareProps {
|
||||
icon?: React.ReactNode;
|
||||
isAction?: boolean;
|
||||
isDisabled?: boolean;
|
||||
isDisabledError?: boolean;
|
||||
isEditable?: boolean;
|
||||
isError?: boolean;
|
||||
isFull?: boolean;
|
||||
@ -88,7 +89,7 @@ const isSelectAll = (key: string, isPreKeyDown: boolean): boolean =>
|
||||
|
||||
let counter = 0;
|
||||
|
||||
export default function Input ({ autoFocus = false, children, className, defaultValue, help, icon, isEditable = false, isAction = false, isDisabled = false, isError = false, isFull = false, isHidden = false, isReadOnly = false, label, labelExtra, max, maxLength, min, name, onBlur, onChange, onEnter, onEscape, onKeyDown, onKeyUp, onPaste, placeholder, style, tabIndex, type = 'text', value, withEllipsis, withLabel }: Props): React.ReactElement<Props> {
|
||||
export default function Input ({ autoFocus = false, children, className, defaultValue, help, icon, isEditable = false, isAction = false, isDisabled = false, isDisabledError = false, isError = false, isFull = false, isHidden = false, isReadOnly = false, label, labelExtra, max, maxLength, min, name, onBlur, onChange, onEnter, onEscape, onKeyDown, onKeyUp, onPaste, placeholder, style, tabIndex, type = 'text', value, withEllipsis, withLabel }: Props): React.ReactElement<Props> {
|
||||
const [stateName] = useState(`in_${counter++}_at_${Date.now()}`);
|
||||
|
||||
const _onBlur = (): void => {
|
||||
@ -142,7 +143,7 @@ export default function Input ({ autoFocus = false, children, className, default
|
||||
: undefined
|
||||
}
|
||||
disabled={isDisabled}
|
||||
error={!isDisabled && isError}
|
||||
error={(!isDisabled && isError) || isDisabledError}
|
||||
hidden={isHidden}
|
||||
id={name}
|
||||
iconPosition={
|
||||
|
||||
@ -132,7 +132,7 @@ class InputAddress extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { className, defaultValue, filter = [], help, hideAddress = false, isDisabled = false, isError, isMultiple, label, labelExtra, options, optionsAll, placeholder, type = DEFAULT_TYPE, style, withEllipsis, withLabel } = this.props;
|
||||
const { className, defaultValue, help, hideAddress = false, isDisabled = false, isError, isMultiple, label, labelExtra, options, optionsAll, placeholder, type = DEFAULT_TYPE, style, withEllipsis, withLabel } = this.props;
|
||||
const { value } = this.state;
|
||||
const hasOptions = (options && options.length !== 0) || (optionsAll && Object.keys(optionsAll[type]).length !== 0);
|
||||
|
||||
@ -156,9 +156,7 @@ class InputAddress extends React.PureComponent<Props, State> {
|
||||
: (
|
||||
isDisabled && actualValue
|
||||
? [createOption(actualValue)]
|
||||
: optionsAll
|
||||
? optionsAll[type].filter(({ value }): boolean => !filter.length || filter.includes(value || ''))
|
||||
: []
|
||||
: this.getFiltered()
|
||||
);
|
||||
const _defaultValue = (isMultiple || !isUndefined(value))
|
||||
? undefined
|
||||
|
||||
@ -76,6 +76,11 @@ export default css`
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled.error input {
|
||||
background-color: #fff6f6;
|
||||
border-color: #e0b4b4;
|
||||
}
|
||||
|
||||
> input {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user