Adjust council modals with column layout (#2666)

* Adjust council modals with column layout

* Adjust to council icon

* Tip endorsement columns, remove duplicated Transfer modal
This commit is contained in:
Jaco Greeff 2020-05-01 11:39:03 +02:00 committed by GitHub
parent 478910b32b
commit 92a6af7983
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 161 additions and 210 deletions

View File

@ -8,11 +8,12 @@ import { ActionStatus } from '@polkadot/react-components/Status/types';
import React, { useCallback, useEffect, useState } from 'react';
import styled from 'styled-components';
import { AddressSmall, AddressInfo, Button, ChainLock, Icon, LinkExternal, Forget, Menu, Popup, Tag, Transfer } from '@polkadot/react-components';
import { AddressSmall, AddressInfo, Button, ChainLock, Icon, LinkExternal, Forget, Menu, Popup, Tag } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
import keyring from '@polkadot/ui-keyring';
import { formatNumber } from '@polkadot/util';
import Transfer from '../Accounts/modals/Transfer';
import { useTranslation } from '../translate';
interface Props {

View File

@ -9,9 +9,10 @@ import styled from 'styled-components';
import { useAccountInfo, useApi, useRegistrars, useToggle } from '@polkadot/react-hooks';
import { classes } from '@polkadot/react-components/util';
import { colorLink } from '@polkadot/react-components/styles/theme';
import { AccountName, AddressMini, AvatarItem, Button, Icon, IconLink, IdentityIcon, Input, InputTags, LinkExternal, Tag, Transfer } from '@polkadot/react-components';
import { AccountName, AddressMini, AvatarItem, Button, Icon, IconLink, IdentityIcon, Input, InputTags, LinkExternal, Tag } from '@polkadot/react-components';
import { isHex } from '@polkadot/util';
import Transfer from '../Accounts/modals/Transfer';
import { useTranslation } from '../translate';
import Flags from './Flags';
import RegistrarJudgement from './RegistrarJudgement';

View File

@ -6,44 +6,56 @@ import { Hash, Proposal, ProposalIndex } from '@polkadot/types/interfaces';
import React, { useState } from 'react';
import { Button, InputAddress, Modal, ProposedAction, TxButton } from '@polkadot/react-components';
import { useAccounts, useToggle } from '@polkadot/react-hooks';
import { useToggle } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
interface Props {
hash: Hash;
idNumber: ProposalIndex;
isDisabled: boolean;
members: string[];
proposal: Proposal;
}
function Close ({ hash, idNumber, proposal }: Props): React.ReactElement<Props> | null {
function Close ({ hash, idNumber, isDisabled, members, proposal }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const { hasAccounts } = useAccounts();
const [isOpen, toggleOpen] = useToggle();
const [accountId, setAccountId] = useState<string | null>(null);
if (!hasAccounts) {
return null;
}
return (
<>
{isOpen && (
<Modal
header={t('Close proposal')}
size='small'
size='large'
>
<Modal.Content>
<ProposedAction
idNumber={idNumber}
proposal={proposal}
/>
<InputAddress
help={t('Select the account you wish close the proposal with.')}
label={t('sending account')}
onChange={setAccountId}
type='account'
/>
<Modal.Columns>
<Modal.Column>
<ProposedAction
idNumber={idNumber}
proposal={proposal}
/>
</Modal.Column>
<Modal.Column>
<p>{t('The proposal that will be affected. Once closed for the current voting round, it would need to be re-submitted to council for a subsequent voting round.')}</p>
</Modal.Column>
</Modal.Columns>
<Modal.Columns>
<Modal.Column>
<InputAddress
filter={members}
help={t('Select the account you wish close the proposal with.')}
label={t('sending account')}
onChange={setAccountId}
type='account'
/>
</Modal.Column>
<Modal.Column>
<p>{t('The council account that will apply the close for the current round.')}</p>
</Modal.Column>
</Modal.Columns>
</Modal.Content>
<Modal.Actions onCancel={toggleOpen}>
<TxButton
@ -57,6 +69,7 @@ function Close ({ hash, idNumber, proposal }: Props): React.ReactElement<Props>
)}
<Button
icon='cancel'
isDisabled={isDisabled}
label={t('Close')}
onClick={toggleOpen}
/>

View File

@ -71,6 +71,8 @@ function Motion ({ className, isMember, members, motion: { hash, proposal, votes
<Close
hash={hash}
idNumber={index}
isDisabled={!isMember}
members={members}
proposal={proposal}
/>
)

View File

@ -73,10 +73,17 @@ function Voting ({ proposal, referendumId }: Props): React.ReactElement<Props> |
size='large'
>
<Modal.Content>
<ProposedAction
idNumber={referendumId}
proposal={proposal}
/>
<Modal.Columns>
<Modal.Column>
<ProposedAction
idNumber={referendumId}
proposal={proposal}
/>
</Modal.Column>
<Modal.Column>
<p>{t('The referendum info that is voted upon. If passed, the changed will be applied via dispatch and the deposit returned.')}</p>
</Modal.Column>
</Modal.Columns>
<Modal.Columns>
<Modal.Column>
<VoteAccount onChange={setAccountId} />

View File

@ -15,12 +15,13 @@ import Voting from './Voting';
interface Props {
className?: string;
isMember: boolean;
proposal: DeriveTreasuryProposal;
members: string[];
onRespond: () => void;
proposal: DeriveTreasuryProposal;
withSend: boolean;
}
function ProposalDisplay ({ className, isMember, proposal: { council, id, proposal }, withSend }: Props): React.ReactElement<Props> | null {
function ProposalDisplay ({ className, isMember, members, proposal: { council, id, proposal }, withSend }: Props): React.ReactElement<Props> | null {
return (
<tr className={className}>
<td className='number'>
@ -45,10 +46,12 @@ function ProposalDisplay ({ className, isMember, proposal: { council, id, propos
councilProposals={council}
id={id}
isDisabled={!isMember}
members={members}
/>
<Voting
councilProposals={council}
isDisabled={!isMember}
members={members}
/>
</>
)}

View File

@ -15,10 +15,11 @@ interface Props {
className?: string;
isApprovals?: boolean;
isMember: boolean;
members: string[];
proposals?: DeriveTreasuryProposal[];
}
function ProposalsBase ({ className, isApprovals, isMember, proposals }: Props): React.ReactElement<Props> {
function ProposalsBase ({ className, isApprovals, isMember, members, proposals }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const history = useHistory();
@ -47,6 +48,7 @@ function ProposalsBase ({ className, isApprovals, isMember, proposals }: Props):
<Proposal
isMember={isMember}
key={proposal.id.toString()}
members={members}
onRespond={_onRespond}
proposal={proposal}
withSend={!isApprovals}

View File

@ -15,9 +15,10 @@ interface Props {
councilProposals: DeriveCollectiveProposal[];
id: ProposalIndex;
isDisabled: boolean;
members: string[];
}
function Submission ({ councilProposals, id, isDisabled }: Props): React.ReactElement<Props> | null {
function Submission ({ councilProposals, id, isDisabled, members }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const { api } = useApi();
const councilThreshold = useCall<number>((api.query.electionsPhragmen || api.query.elections).members, [], {
@ -50,24 +51,39 @@ function Submission ({ councilProposals, id, isDisabled }: Props): React.ReactEl
<>
{isOpen && (
<Modal
header={t('To council')}
size='small'
header={t('Send to council')}
size='large'
>
<Modal.Content>
<InputAddress
help={t('Select the council account you wish to use to make the proposal.')}
label={t('submit with council account')}
onChange={setAccountId}
type='account'
withLabel
/>
<Dropdown
help={t('The type of council proposal to submit.')}
label={t('council proposal type')}
onChange={setCouncilType}
options={councilTypeOpt}
value={councilType}
/>
<Modal.Columns>
<Modal.Column>
<InputAddress
filter={members}
help={t('Select the council account you wish to use to make the proposal.')}
label={t('submit with council account')}
onChange={setAccountId}
type='account'
withLabel
/>
</Modal.Column>
<Modal.Column>
<p>{t('The council member that is proposing this, submission equates to an "aye" vote.')}</p>
</Modal.Column>
</Modal.Columns>
<Modal.Columns>
<Modal.Column>
<Dropdown
help={t('The type of council proposal to submit.')}
label={t('council proposal type')}
onChange={setCouncilType}
options={councilTypeOpt}
value={councilType}
/>
</Modal.Column>
<Modal.Column>
<p>{t('Proposal can either be to approve or reject this spend. One approved, the change is applied by either removing the proposal or scheduling payout.')}</p>
</Modal.Column>
</Modal.Columns>
</Modal.Content>
<Modal.Actions onCancel={toggleOpen}>
<TxButton
@ -89,9 +105,9 @@ function Submission ({ councilProposals, id, isDisabled }: Props): React.ReactEl
</Modal>
)}
<Button
icon='check'
icon='sort'
isDisabled={isDisabled}
label={t('Send to council')}
label={t('To council')}
onClick={toggleOpen}
/>
</>

View File

@ -33,23 +33,37 @@ function TipEndorse ({ hash, isMember, members }: Props): React.ReactElement<Pro
{isOpen && (
<Modal
header={t('Submit tip endorsement')}
size='small'
size='large'
>
<Modal.Content>
<InputAddress
filter={members}
help={t('Select the account you wish to submit the tip from.')}
label={t('submit with account')}
onChange={setAccountId}
type='account'
withLabel
/>
<InputBalance
help={t('The tip amount that should be allocated')}
isError={!hasValue}
label={t('value')}
onChange={setValue}
/>
<Modal.Columns>
<Modal.Column>
<InputAddress
filter={members}
help={t('Select the account you wish to submit the tip from.')}
label={t('submit with account')}
onChange={setAccountId}
type='account'
withLabel
/>
</Modal.Column>
<Modal.Column>
<p>{t('Your endorsement will be applied for this account.')}</p>
</Modal.Column>
</Modal.Columns>
<Modal.Columns>
<Modal.Column>
<InputBalance
help={t('The tip amount that should be allocated')}
isError={!hasValue}
label={t('value')}
onChange={setValue}
/>
</Modal.Column>
<Modal.Column>
<p>{t('Allocate a suggested tip amount. With enough endorsements, the suggested values are averaged and sent to the beneficiary.')}</p>
</Modal.Column>
</Modal.Columns>
</Modal.Content>
<Modal.Actions onCancel={toggleOpen}>
<TxButton

View File

@ -15,6 +15,7 @@ import { useTranslation } from '../translate';
interface Props {
councilProposals: DeriveCollectiveProposal[];
isDisabled?: boolean;
members: string[];
}
interface Option {
@ -22,7 +23,7 @@ interface Option {
value: number;
}
function Voting ({ councilProposals, isDisabled }: Props): React.ReactElement<Props> | null {
function Voting ({ councilProposals, isDisabled, members }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const { hasAccounts } = useAccounts();
const { api } = useApi();
@ -82,24 +83,42 @@ function Voting ({ councilProposals, isDisabled }: Props): React.ReactElement<Pr
size='small'
>
<Modal.Content>
<VoteAccount onChange={setAccountId} />
<Dropdown
help={t('The council proposal to make the vote on')}
label={t('council proposal')}
onChange={_onChangeProposal}
options={councilOpts}
value={councilOptId}
/>
<Input
help={t('The hash for the proposal this vote applies to')}
isDisabled
label={t('proposal hash')}
value={councilHash}
/>
<VoteToggle
onChange={_onChangeVote}
value={voteValue}
/>
<Modal.Columns>
<Modal.Column>
<VoteAccount
filter={members}
onChange={setAccountId}
/>
</Modal.Column>
</Modal.Columns>
<Modal.Columns>
<Modal.Column>
<Dropdown
help={t('The council proposal to make the vote on')}
label={t('council proposal')}
onChange={_onChangeProposal}
options={councilOpts}
value={councilOptId}
/>
<Input
help={t('The hash for the proposal this vote applies to')}
isDisabled
label={t('proposal hash')}
value={councilHash}
/>
</Modal.Column>
<Modal.Column>
<p>{t('Multiple council proposals could exist, both approval and rejection. Apply your vote to the correct council proposal (also available on council motions page)')}</p>
</Modal.Column>
</Modal.Columns>
<Modal.Columns>
<Modal.Column>
<VoteToggle
onChange={_onChangeVote}
value={voteValue}
/>
</Modal.Column>
</Modal.Columns>
</Modal.Content>
<VoteActions
accountId={accountId}

View File

@ -49,11 +49,13 @@ function Overview ({ className }: Props): React.ReactElement<Props> {
</Button.Group>
<Proposals
isMember={isMember}
members={members}
proposals={info?.proposals}
/>
<Proposals
isApprovals
isMember={isMember}
members={members}
proposals={info?.approvals}
/>
<Tips

View File

@ -27,7 +27,7 @@ interface Props {
}
export const styles = `
margin-bottom: 1rem;
margin-left: 2rem;
.ui--ProposedAction-extrinsic {
.ui--Params-Content {

View File

@ -1,128 +0,0 @@
// Copyright 2017-2020 @polkadot/app-accounts 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 { SubmittableExtrinsic } from '@polkadot/api/promise/types';
import BN from 'bn.js';
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { useApi } from '@polkadot/react-hooks';
import { Available } from '@polkadot/react-query';
import InputAddress from './InputAddress';
import InputBalance from './InputBalance';
import Modal from './Modal';
import TxButton from './TxButton';
import { useTranslation } from './translate';
interface Props {
className?: string;
onClose: () => void;
recipientId?: string;
senderId?: string;
}
const ZERO = new BN(0);
function Transfer ({ className, onClose, recipientId: propRecipientId, senderId: propSenderId }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [amount, setAmount] = useState<BN | undefined>(new BN(0));
const [extrinsic, setExtrinsic] = useState<SubmittableExtrinsic | null>(null);
const [hasAvailable] = useState(true);
const [maxBalance] = useState(new BN(0));
const [recipientId, setRecipientId] = useState<string | null>(propRecipientId || null);
const [senderId, setSenderId] = useState<string | null>(propSenderId || null);
useEffect((): void => {
senderId && recipientId && setExtrinsic(
() => api.tx.balances.transfer(recipientId, amount || ZERO)
);
}, [api, amount, recipientId, senderId]);
const transferrable = <span className='label'>{t('transferrable')}</span>;
return (
<Modal
className='app--accounts-Modal'
header={t('Send funds')}
>
<Modal.Content>
<div className={className}>
<InputAddress
defaultValue={propSenderId}
help={t('The account you will send funds from.')}
isDisabled={!!propSenderId}
label={t('send from account')}
labelExtra={
<Available
label={transferrable}
params={senderId}
/>
}
onChange={setSenderId}
type='account'
/>
<InputAddress
defaultValue={propRecipientId}
help={t('Select a contact or paste the address you want to send funds to.')}
isDisabled={!!propRecipientId}
label={t('send to address')}
labelExtra={
<Available
label={transferrable}
params={recipientId}
/>
}
onChange={setRecipientId}
type='allPlus'
/>
<InputBalance
autoFocus
help={t('Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.')}
isError={!hasAvailable}
isZeroable
label={t('amount')}
maxValue={maxBalance}
onChange={setAmount}
withMax
/>
</div>
</Modal.Content>
<Modal.Actions onCancel={onClose}>
<TxButton
accountId={senderId}
extrinsic={extrinsic}
icon='send'
isDisabled={!hasAvailable}
isPrimary
label={t('Make Transfer')}
onStart={onClose}
/>
</Modal.Actions>
</Modal>
);
}
export default React.memo(styled(Transfer)`
article.padded {
box-shadow: none;
margin-left: 2rem;
}
.balance {
margin-bottom: 0.5rem;
text-align: right;
padding-right: 1rem;
.label {
opacity: 0.7;
}
}
label.with-help {
flex-basis: 10rem;
}
`);

View File

@ -84,7 +84,6 @@ export { default as Tabs } from './Tabs';
export { default as Tag } from './Tag';
export { default as Toggle } from './Toggle';
export { default as Tooltip } from './Tooltip';
export { default as Transfer } from './Transfer';
export { default as TxButton } from './TxButton';
export { default as TxComponent } from './TxComponent';
export { default as TxModal } from './TxModal';