Add preimage buttons to proposals (#2192)

* Add preimage buttons to proposals

* Styling overrides

* InputAddress filter
This commit is contained in:
Jaco Greeff 2020-01-24 13:34:38 +01:00 committed by GitHub
parent 8be974f500
commit 52a2aa275a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 107 additions and 25 deletions

View File

@ -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>
);
}

View File

@ -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}
/>

View 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}
/>
)}
</>
);
}

View File

@ -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>
);

View File

@ -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>
);

View File

@ -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={

View File

@ -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

View File

@ -76,6 +76,11 @@ export default css`
}
}
&.disabled.error input {
background-color: #fff6f6;
border-color: #e0b4b4;
}
> input {
width: 0;
}