From b74755bbaad9d817d0b538354778d32eb2f26085 Mon Sep 17 00:00:00 2001 From: kwingram25 Date: Tue, 25 Jun 2019 15:20:53 +0100 Subject: [PATCH] Treasury app (#1324) * treasury app * fixes * more fixes * treasury app * fixes * more fixes * fixes + collections * force travis --- packages/app-council/src/Overview/Members.tsx | 26 +-- .../app-democracy/src/Overview/Proposals.tsx | 13 +- .../src/Overview/Referendums.tsx | 13 +- .../app-explorer/src/BlockInfo/Events.tsx | 2 +- .../app-explorer/src/BlockInfo/Extrinsics.tsx | 15 +- packages/app-explorer/src/BlockInfo/Logs.tsx | 2 +- packages/app-explorer/src/Main.tsx | 4 +- .../app-staking/src/Overview/CurrentList.tsx | 18 +- packages/app-treasury/LICENSE | 201 +++++++++++++++++ packages/app-treasury/README.md | 1 + packages/app-treasury/package.json | 18 ++ .../app-treasury/src/Overview/Approve.tsx | 71 ++++++ packages/app-treasury/src/Overview/Item.tsx | 61 +++++ .../app-treasury/src/Overview/Proposal.tsx | 155 +++++++++++++ .../app-treasury/src/Overview/Proposals.tsx | 120 ++++++++++ .../app-treasury/src/Overview/Propose.tsx | 94 ++++++++ .../app-treasury/src/Overview/Summary.tsx | 59 +++++ packages/app-treasury/src/Overview/index.tsx | 35 +++ packages/app-treasury/src/Settings.tsx | 210 ++++++++++++++++++ packages/app-treasury/src/index.tsx | 67 ++++++ packages/app-treasury/src/md/basic.md | 3 + packages/app-treasury/src/translate.ts | 7 + packages/apps-routing/src/index.ts | 2 + packages/apps-routing/src/treasury.ts | 24 ++ packages/ui-app/src/CardGrid.tsx | 71 +++--- packages/ui-app/src/Collection.tsx | 135 +++++++++++ packages/ui-app/src/Column.tsx | 84 ++++--- packages/ui-app/src/InputAddress.tsx | 2 +- packages/ui-app/src/TxModal.tsx | 184 +++++++++++++++ packages/ui-app/src/index.tsx | 1 + tsconfig.json | 2 + 31 files changed, 1582 insertions(+), 118 deletions(-) create mode 100644 packages/app-treasury/LICENSE create mode 100644 packages/app-treasury/README.md create mode 100644 packages/app-treasury/package.json create mode 100644 packages/app-treasury/src/Overview/Approve.tsx create mode 100644 packages/app-treasury/src/Overview/Item.tsx create mode 100644 packages/app-treasury/src/Overview/Proposal.tsx create mode 100644 packages/app-treasury/src/Overview/Proposals.tsx create mode 100644 packages/app-treasury/src/Overview/Propose.tsx create mode 100644 packages/app-treasury/src/Overview/Summary.tsx create mode 100644 packages/app-treasury/src/Overview/index.tsx create mode 100644 packages/app-treasury/src/Settings.tsx create mode 100644 packages/app-treasury/src/index.tsx create mode 100644 packages/app-treasury/src/md/basic.md create mode 100644 packages/app-treasury/src/translate.ts create mode 100644 packages/apps-routing/src/treasury.ts create mode 100644 packages/ui-app/src/Collection.tsx create mode 100644 packages/ui-app/src/TxModal.tsx diff --git a/packages/app-council/src/Overview/Members.tsx b/packages/app-council/src/Overview/Members.tsx index 08452bea93..f77ac03772 100644 --- a/packages/app-council/src/Overview/Members.tsx +++ b/packages/app-council/src/Overview/Members.tsx @@ -24,34 +24,32 @@ class Members extends React.PureComponent { return ( - - {this.renderNone(council_activeCouncil.map(([address, block]) => ( + + {council_activeCouncil.map(([address, block]) => ( - )))} + ))} - - {this.renderNone(council_candidates.map((address) => ( + + {council_candidates.map((address) => ( - )))} + ))} ); } - - private renderNone (items: Array) { - const { t } = this.props; - - return items.length - ? items - :
{t('No addresses found')}
; - } } export default translate( diff --git a/packages/app-democracy/src/Overview/Proposals.tsx b/packages/app-democracy/src/Overview/Proposals.tsx index a6f9549e70..3e5737d9df 100644 --- a/packages/app-democracy/src/Overview/Proposals.tsx +++ b/packages/app-democracy/src/Overview/Proposals.tsx @@ -30,20 +30,17 @@ class Proposals extends React.PureComponent { const { t } = this.props; return ( - + {this.renderProposals()} ); } private renderProposals () { - const { democracy_publicProps, t } = this.props; - - if (!democracy_publicProps || !democracy_publicProps.length) { - return ( -
{t('no available proposals')}
- ); - } + const { democracy_publicProps = [] } = this.props; return democracy_publicProps.map(([idNumber, proposal]) => ( { const { t } = this.props; return ( - + {this.renderReferendums()} ); } private renderReferendums () { - const { democracy_referendums = [], t } = this.props; + const { democracy_referendums = [] } = this.props; const referendums = democracy_referendums .filter((opt) => opt.isSome) .map((opt) => opt.unwrap()); - if (!referendums.length) { - return ( -
{t('no available referendums')}
- ); - } - return referendums.map((referendum) => ( { })); return ( - + { return ( {this.renderContent()} @@ -32,17 +33,9 @@ class Extrinsics extends React.PureComponent { } private renderContent () { - const { t, value } = this.props; + const { value = [] } = this.props; - if (!value || !value.length) { - return ( -
- {t('no pending extrinsics are in the queue')} -
- ); - } - - return value.map(this.renderExtrinsic); + return (value || []).map(this.renderExtrinsic); } // FIXME This is _very_ similar to what we have in democracy/Item diff --git a/packages/app-explorer/src/BlockInfo/Logs.tsx b/packages/app-explorer/src/BlockInfo/Logs.tsx index aa0e79adbe..68e983af11 100644 --- a/packages/app-explorer/src/BlockInfo/Logs.tsx +++ b/packages/app-explorer/src/BlockInfo/Logs.tsx @@ -24,7 +24,7 @@ class Logs extends React.PureComponent { } return ( - + {value.map(this.renderItem)} ); diff --git a/packages/app-explorer/src/Main.tsx b/packages/app-explorer/src/Main.tsx index 3fb859b357..59b29ee00b 100644 --- a/packages/app-explorer/src/Main.tsx +++ b/packages/app-explorer/src/Main.tsx @@ -27,10 +27,10 @@ class Main extends React.PureComponent { - + - + diff --git a/packages/app-staking/src/Overview/CurrentList.tsx b/packages/app-staking/src/Overview/CurrentList.tsx index fa7cf47b84..373fd28e10 100644 --- a/packages/app-staking/src/Overview/CurrentList.tsx +++ b/packages/app-staking/src/Overview/CurrentList.tsx @@ -62,10 +62,16 @@ class CurrentList extends React.PureComponent { /> - + {this.renderColumn(current, t('validator (stash)'))} - + {this.renderColumn(next, t('intention (stash)'))} @@ -74,15 +80,9 @@ class CurrentList extends React.PureComponent { } private renderColumn (addresses: Array, defaultName: string) { - const { balances, lastAuthor, lastBlock, recentlyOffline, t } = this.props; + const { balances, lastAuthor, lastBlock, recentlyOffline } = this.props; const { filter } = this.state; - if (addresses.length === 0) { - return ( -
{t('no addresses found')}
- ); - } - return addresses.map((address) => (
", + "maintainers": [ + "Keith Ingram ", + "Jaco Greeff " + ], + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.4.5", + "@polkadot/ui-app": "^0.34.0-beta.21", + "@polkadot/ui-reactive": "^0.34.0-beta.21" + } +} diff --git a/packages/app-treasury/src/Overview/Approve.tsx b/packages/app-treasury/src/Overview/Approve.tsx new file mode 100644 index 0000000000..a96f1618bb --- /dev/null +++ b/packages/app-treasury/src/Overview/Approve.tsx @@ -0,0 +1,71 @@ +// Copyright 2017-2019 @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 { I18nProps } from '@polkadot/ui-app/types'; + +import React from 'react'; +import { Dropdown } from '@polkadot/ui-app'; +import TxModal, { TxModalProps, TxModalState } from '@polkadot/ui-app/TxModal'; + +import translate from '../translate'; + +type Props = I18nProps & TxModalProps & { + isApproved?: boolean, + proposalInfo?: React.ReactNode, + proposalId: string +}; + +type State = TxModalState & { + isApproving: boolean +}; + +class Approve extends TxModal { + state: State = { + ...this.defaultState, + isApproving: false + }; + + private approveOptions = () => [ + { text: this.props.t('Yay, I approve'), value: true }, + { text: this.props.t('Nay, I do not approve'), value: false } + ] + + headerText = () => this.props.t('Approve or reject proposal'); + + txMethod = () => this.state.isApproving ? 'treasury.approveProposal' : 'treasury.rejectProposal'; + txParams = () => [this.props.proposalId]; + + renderPreContent = () => { + const { proposalInfo = null } = this.props; + + return ( + <> + {proposalInfo} +
+
+ + ); + } + + renderContent = () => { + const { t } = this.props; + const { isApproving } = this.state; + + return ( + + ); + } + + private onChangeApproving = (isApproving: boolean) => { + this.setState({ isApproving }); + } +} + +export default translate(Approve); diff --git a/packages/app-treasury/src/Overview/Item.tsx b/packages/app-treasury/src/Overview/Item.tsx new file mode 100644 index 0000000000..0b739b66f3 --- /dev/null +++ b/packages/app-treasury/src/Overview/Item.tsx @@ -0,0 +1,61 @@ +// Copyright 2017-2019 @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 { I18nProps } from '@polkadot/ui-app/types'; + +import React from 'react'; +import styled from 'styled-components'; +import { TreasuryProposal } from '@polkadot/types'; +import { Card } from '@polkadot/ui-app'; +import { styles as rowStyles } from '@polkadot/ui-app/Row'; + +import translate from '../translate'; + +type Props = I18nProps & { + children?: React.ReactNode, + isApproved?: boolean, + proposal: TreasuryProposal, + proposalExtra?: React.ReactNode, + proposalId: string +}; + +class Item extends React.PureComponent { + render () { + const { children, className, proposalExtra, proposalId } = this.props; + + return ( + +
+
+
+

+ #{proposalId} +

+
+ {children} +
+ {proposalExtra} +
+
+ ); + } +} + +export default translate(styled(Item as React.ComponentClass)` + ${rowStyles} + + .treasury--Item-header { + margin: -0.5rem -0.5rem 0 0; + } + + .treasury--Approve-approved { + color: green; + margin: 0; + } + + .treasury--Item-header-id { + font-size: 2rem; + line-height: 2rem; + } +`); diff --git a/packages/app-treasury/src/Overview/Proposal.tsx b/packages/app-treasury/src/Overview/Proposal.tsx new file mode 100644 index 0000000000..bf9e6bf2db --- /dev/null +++ b/packages/app-treasury/src/Overview/Proposal.tsx @@ -0,0 +1,155 @@ +// Copyright 2017-2019 @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 { I18nProps } from '@polkadot/ui-app/types'; +import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; + +import React from 'react'; +import { Option, TreasuryProposal } from '@polkadot/types'; +import { Button, Icon, InputAddress, Labelled, Static } from '@polkadot/ui-app'; +import { withCalls, withMulti, withObservable } from '@polkadot/ui-api'; +import keyring from '@polkadot/ui-keyring'; +import { formatBalance } from '@polkadot/util'; + +import translate from '../translate'; +import Item from './Item'; +import Approve from './Approve'; + +type Props = I18nProps & { + allAccounts?: SubjectInfo, + isApproved: boolean, + proposal?: TreasuryProposal | null, + proposalId: string +}; + +type State = { + isApproveOpen: boolean +}; + +class ProposalDisplay extends React.PureComponent { + state: State = { + isApproveOpen: false + }; + + render () { + const { isApproved, proposal, proposalId } = this.props; + + if (!proposal) { + return null; + } + + return ( + + {this.renderAccessory()} + + ); + } + + private renderAccessory () { + const { allAccounts, isApproved, proposalId, t } = this.props; + const { isApproveOpen } = this.state; + + if (isApproved) { + return ( +

+ + {' '} + {t('Approved')} +

+ ); + } + + const hasAccounts = allAccounts && Object.keys(allAccounts).length !== 0; + if (!hasAccounts) { + return null; + } + + return ( + <> +
+ +
+ + + ); + } + + private renderExtra () { + const { isApproved, proposal, t } = this.props; + + if (!proposal) { + return null; + } + + const { bond, beneficiary, proposer, value } = proposal; + + return ( +
+ + + + + + + + {formatBalance(value)} + + + {formatBalance(bond)} + +
+ ); + } + + private showApprove = (): void => { + this.setState({ + isApproveOpen: true + }); + } + + private hideApprove = (): void => { + this.setState({ + isApproveOpen: false + }); + } +} + +export default withMulti( + ProposalDisplay, + translate, + withCalls( + ['query.treasury.proposals', { + paramName: 'proposalId', + propName: 'proposal', + transform: (value: Option) => + value.unwrapOr(null) + }] + ), + withObservable(keyring.accounts.subject, { propName: 'allAccounts' }) +); diff --git a/packages/app-treasury/src/Overview/Proposals.tsx b/packages/app-treasury/src/Overview/Proposals.tsx new file mode 100644 index 0000000000..f377c6a056 --- /dev/null +++ b/packages/app-treasury/src/Overview/Proposals.tsx @@ -0,0 +1,120 @@ +// Copyright 2017-2019 @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 { I18nProps } from '@polkadot/ui-app/types'; +import { ProposalIndex } from '@polkadot/types'; + +import BN from 'bn.js'; +import React from 'react'; +import styled from 'styled-components'; +import { withCalls, withMulti } from '@polkadot/ui-api'; +import { Button, Column } from '@polkadot/ui-app'; + +import Proposal from './Proposal'; +import Propose from './Propose'; +import translate from '../translate'; + +type Props = I18nProps & { + isApprovals?: boolean, + treasury_approvals?: Array, + treasury_proposalCount?: BN +}; + +type State = { + isProposeOpen: boolean, + proposalIndices: Array +}; + +class ProposalsBase extends React.PureComponent { + state: State = { + isProposeOpen: false, + proposalIndices: [] as Array + }; + + static getDerivedStateFromProps ({ isApprovals = false, treasury_approvals = [] as Array, treasury_proposalCount = new BN(0) }: Props) { + let proposalIndices: Array = []; + + if (isApprovals) { + proposalIndices = treasury_approvals; + } else { + for (let i = 0; i < treasury_proposalCount.toNumber(); i++) { + proposalIndices.push(new BN(i)); + } + } + return { proposalIndices }; + } + + render () { + const { isApprovals, t } = this.props; + const { isProposeOpen } = this.state; + + return ( + <> + +