From e691868d76d1ba9a1448d5eca3d3a9fbb306cdf5 Mon Sep 17 00:00:00 2001 From: kwingram25 Date: Wed, 10 Jul 2019 17:49:46 +0100 Subject: [PATCH] Council motions (#1358) * council motions * continue * council motions > propose * travis ci * latest api * add council votes, proposal views * linting * remove settings * new yarn.lock * base-x * Only pick text-transform from Labelled changes * collapse voting view, aye/yay * move button * lint * fixes * approve
tag * aye/nay lists --- packages/app-council/src/Motions/Motion.tsx | 171 ++++++++++++++++++ packages/app-council/src/Motions/Propose.tsx | 144 +++++++++++++++ packages/app-council/src/Motions/Summary.tsx | 0 packages/app-council/src/Motions/index.tsx | 53 ++++++ packages/app-council/src/index.tsx | 6 + .../app-democracy/src/Overview/Referendum.tsx | 8 +- .../app-democracy/src/Overview/Voting.tsx | 2 +- packages/app-democracy/src/md/basic.md | 2 +- .../app-treasury/src/Overview/Approve.tsx | 68 +++++-- packages/app-treasury/src/Overview/Item.tsx | 61 ------- .../app-treasury/src/Overview/Proposal.tsx | 123 +++++-------- .../app-treasury/src/Overview/Proposals.tsx | 60 +++--- .../app-treasury/src/Overview/Propose.tsx | 17 +- packages/app-treasury/src/Overview/index.tsx | 2 + packages/app-treasury/src/index.tsx | 12 +- packages/ui-app/src/ActionItem.tsx | 77 +++----- packages/ui-app/src/CardGrid.tsx | 6 +- packages/ui-app/src/Collection.tsx | 1 - packages/ui-app/src/Column.tsx | 7 +- packages/ui-app/src/InputNumber.tsx | 4 +- packages/ui-app/src/Inset.tsx | 153 ++++++++++++++++ packages/ui-app/src/Labelled.tsx | 4 + packages/ui-app/src/ProposedAction.tsx | 120 ++++++++++++ packages/ui-app/src/Row.tsx | 6 +- packages/ui-app/src/TreasuryProposal.tsx | 132 ++++++++++++++ packages/ui-app/src/TxModal.tsx | 81 +++++---- packages/ui-app/src/Voting.tsx | 133 ++++++++++++++ packages/ui-app/src/index.tsx | 4 + packages/ui-app/src/styles/semantic.css | 2 +- packages/ui-app/src/util/index.ts | 1 + .../ui-app/src/util/isTreasuryProposalVote.ts | 17 ++ packages/ui-params/src/Params.css | 1 + yarn.lock | 7 + 33 files changed, 1188 insertions(+), 297 deletions(-) create mode 100644 packages/app-council/src/Motions/Motion.tsx create mode 100644 packages/app-council/src/Motions/Propose.tsx create mode 100644 packages/app-council/src/Motions/Summary.tsx create mode 100644 packages/app-council/src/Motions/index.tsx delete mode 100644 packages/app-treasury/src/Overview/Item.tsx create mode 100644 packages/ui-app/src/Inset.tsx create mode 100644 packages/ui-app/src/ProposedAction.tsx create mode 100644 packages/ui-app/src/TreasuryProposal.tsx create mode 100644 packages/ui-app/src/Voting.tsx create mode 100644 packages/ui-app/src/util/isTreasuryProposalVote.ts diff --git a/packages/app-council/src/Motions/Motion.tsx b/packages/app-council/src/Motions/Motion.tsx new file mode 100644 index 0000000000..f948f51aa6 --- /dev/null +++ b/packages/app-council/src/Motions/Motion.tsx @@ -0,0 +1,171 @@ +// 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 { Option, Proposal, Votes } from '@polkadot/types'; + +import BN from 'bn.js'; +import React from 'react'; +import styled from 'styled-components'; +import { ActionItem, InputAddress, Labelled, Voting } from '@polkadot/ui-app'; +import { withCalls, withMulti } from '@polkadot/ui-api'; + +import translate from '../translate'; + +type Props = I18nProps & { + chain_bestNumber?: BN, + hash: string, + proposal: Proposal | null, + votes: Votes | null +}; + +type State = { + votedTotal: number, + votedNay: number, + votedAye: number +}; + +class Motion extends React.PureComponent { + state: State = { + votedTotal: 0, + votedAye: 0, + votedNay: 0 + }; + + static getDerivedStateFromProps ({ votes }: Props): State | null { + if (!votes) { + return null; + } + + const { ayes, nays } = votes; + + const newState: State = { + votedNay: nays.length, + votedAye: ayes.length, + votedTotal: ayes.length + nays.length + }; + + return newState; + } + + render () { + const { className, hash, proposal, votes } = this.props; + + if (!proposal || !votes) { + return null; + } + + const { index } = votes; + + return ( + + } + expandNested + idNumber={index} + proposal={proposal} + > + {this.renderInfo()} + + ); + } + + private renderInfo () { + const { votes, t } = this.props; + + if (!votes) { + return null; + } + + const { ayes, nays, threshold } = votes; + + return ( +
+

+ {t( + 'ayes ({{ayes}}/{{threshold}} to approve)', + { + replace: { + ayes: ayes.length, + threshold: threshold.toString() + } + } + )} +

+ {ayes.map((address, index) => ( + + + + ))} +

+ {t( + 'nays ({{nays}})', + { + replace: { + nays: nays.length + } + } + )} +

+ {nays.map((address, index) => ( + + + + ))} +
+ ); + } +} + +export default withMulti( + styled(Motion as React.ComponentClass)` + .democracy--Motion-results { + margin-bottom: 1em; + + &.chart { + text-align: center; + } + } + `, + translate, + withCalls( + [ + 'query.councilMotions.proposalOf', + { + paramName: 'hash', + propName: 'proposal', + transform: (value: Option) => value.unwrapOr(null) + } + ], + [ + 'query.councilMotions.voting', + { + paramName: 'hash', + propName: 'votes', + transform: (value: Option) => value.unwrapOr(null) + } + ] + ) +); diff --git a/packages/app-council/src/Motions/Propose.tsx b/packages/app-council/src/Motions/Propose.tsx new file mode 100644 index 0000000000..6ccc18c8b0 --- /dev/null +++ b/packages/app-council/src/Motions/Propose.tsx @@ -0,0 +1,144 @@ +// Copyright 2017-2019 @polkadot/ui-staking 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 { ApiProps } from '@polkadot/ui-api/types'; +import { Method, Proposal } from '@polkadot/types'; + +import BN from 'bn.js'; +import React from 'react'; + +import { Button, Extrinsic, InputNumber } from '@polkadot/ui-app'; +import TxModal, { TxModalState, TxModalProps } from '@polkadot/ui-app/TxModal'; +import { withApi, withCalls, withMulti } from '@polkadot/ui-api'; + +import translate from '../translate'; + +type Props = TxModalProps & ApiProps & { + memberCount: number +}; + +type State = TxModalState & { + method: Method | null, + threshold: BN | null +}; + +class Propose extends TxModal { + constructor (props: Props) { + super(props); + + this.defaultState = { + ...this.defaultState, + method: null, + threshold: props.memberCount ? new BN((props.memberCount / 2) + 1) : null + }; + this.state = this.defaultState; + } + + static getDerivedStateFromProps ({ memberCount }: Props, { threshold }: State) { + if (!threshold && memberCount > 0) { + const simpleMajority = new BN((memberCount / 2) + 1); + return { threshold: simpleMajority }; + } + return null; + } + + headerText = () => this.props.t('Propose a council motion'); + + txMethod = () => 'councilMotions.propose'; + txParams = () => { + const { method, threshold } = this.state; + + return [ + threshold, + ...(method ? [new Proposal(method)] : []) + ]; + } + + isDisabled = () => { + const { memberCount = 0 } = this.props; + const { accountId, method, threshold } = this.state; + + const hasThreshold = !!threshold && threshold.gtn(0) && threshold.ltn(memberCount + 1); + const hasMethod = !!method; + + return !accountId || !hasMethod || !hasThreshold; + } + + renderTrigger = () => { + const { t } = this.props; + + return ( + +