ghost-portal/packages/app-council/src/Motions/Propose.tsx
Jaco Greeff e6ca2db884
Rename ui-* -> react-* (#1495)
* Rename ui-* -> react-*

* Rename actual folders

* react-app -> react-components

* CSS links
2019-08-12 13:12:05 +02:00

148 lines
3.9 KiB
TypeScript

// 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 { Call, Proposal } from '@polkadot/types/interfaces';
import { ApiProps } from '@polkadot/react-api/types';
import BN from 'bn.js';
import React from 'react';
import { createType } from '@polkadot/types';
import { Button, Extrinsic, InputNumber } from '@polkadot/react-components';
import TxModal, { TxModalState, TxModalProps } from '@polkadot/react-components/TxModal';
import { withApi, withCalls, withMulti } from '@polkadot/react-api';
import translate from '../translate';
interface Props extends TxModalProps, ApiProps {
memberCount: number;
}
interface State extends TxModalState {
method: Call | null;
threshold: BN | null;
}
class Propose extends TxModal<Props, State> {
public 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;
}
public static getDerivedStateFromProps ({ memberCount }: Props, { threshold }: State): Pick<State, never> | null {
if (!threshold && memberCount > 0) {
const simpleMajority = new BN((memberCount / 2) + 1);
return { threshold: simpleMajority };
}
return null;
}
protected headerText = (): string => this.props.t('Propose a council motion');
protected txMethod = (): string => 'council.propose';
protected txParams = (): [BN | null, ...Proposal[]] => {
const { method, threshold } = this.state;
return [
threshold,
...(method ? [createType('Proposal', method)] : [])
];
}
protected isDisabled = (): boolean => {
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;
}
protected renderTrigger = (): React.ReactNode => {
const { t } = this.props;
return (
<Button.Group>
<Button
isPrimary
label={t('Propose a council motion')}
labelIcon='add'
onClick={this.showModal}
/>
</Button.Group>
);
}
protected renderContent = (): React.ReactNode => {
const { apiDefaultTxSudo, memberCount = 0, t } = this.props;
const { threshold } = this.state;
return (
<>
<InputNumber
className='medium'
label={t('threshold')}
help={t('The minimum number of council votes required to approve this motion')}
isError={!threshold || threshold.eqn(0) || threshold.gtn(memberCount)}
onChange={this.onChangeThreshold}
onEnter={this.sendTx}
placeholder={
t(
'Positive number between 1 and {{memberCount}}',
{ replace: { memberCount } }
)
}
value={threshold || new BN(0)}
/>
<Extrinsic
defaultValue={apiDefaultTxSudo}
label={t('proposal')}
onChange={this.onChangeExtrinsic}
onEnter={this.sendTx}
/>
</>
);
}
private onChangeThreshold = (threshold: BN | null = null): void => {
const { memberCount = 0 } = this.props;
if (memberCount > 0 && !this.defaultState.threshold) {
this.defaultState.threshold = new BN((memberCount / 2) + 1);
}
this.setState({ threshold });
}
private onChangeExtrinsic = (method: Call): void => {
if (!method) {
return;
}
this.setState({ method });
}
}
export default withMulti(
Propose,
translate,
withApi,
withCalls(
['query.elections.members', {
propName: 'memberCount',
transform: (value: any[]): number =>
value.length
}]
)
);