Propose.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. // Copyright 2017-2019 @polkadot/ui-staking authors & contributors
  2. // This software may be modified and distributed under the terms
  3. // of the Apache-2.0 license. See the LICENSE file for details.
  4. import { ApiProps } from '@polkadot/react-api/types';
  5. import { Call, Proposal } from '@polkadot/types/interfaces';
  6. import BN from 'bn.js';
  7. import React from 'react';
  8. import { createType } from '@polkadot/types';
  9. import { Button, Extrinsic, InputNumber } from '@polkadot/react-components';
  10. import TxModal, { TxModalState, TxModalProps } from '@polkadot/react-components/TxModal';
  11. import { withCalls, withMulti } from '@polkadot/react-api';
  12. import translate from '../translate';
  13. interface Props extends TxModalProps, ApiProps {
  14. memberCount: number;
  15. }
  16. interface State extends TxModalState {
  17. method: Call | null;
  18. threshold: BN | null;
  19. }
  20. class Propose extends TxModal<Props, State> {
  21. constructor (props: Props) {
  22. super(props);
  23. this.defaultState = {
  24. ...this.defaultState,
  25. method: null,
  26. threshold: props.memberCount ? new BN((props.memberCount / 2) + 1) : null
  27. };
  28. this.state = this.defaultState;
  29. }
  30. public static getDerivedStateFromProps ({ memberCount }: Props, { threshold }: State): Pick<State, never> | null {
  31. if (!threshold && memberCount > 0) {
  32. const simpleMajority = new BN((memberCount / 2) + 1);
  33. return { threshold: simpleMajority };
  34. }
  35. return null;
  36. }
  37. protected headerText = (): string => this.props.t('Propose a council motion');
  38. protected txMethod = (): string => 'council.propose';
  39. protected txParams = (): [BN | null, ...Proposal[]] => {
  40. const { method, threshold } = this.state;
  41. return [
  42. threshold,
  43. ...(method ? [createType('Proposal', method)] : [])
  44. ];
  45. }
  46. protected isDisabled = (): boolean => {
  47. const { memberCount = 0 } = this.props;
  48. const { accountId, method, threshold } = this.state;
  49. const hasThreshold = !!threshold && threshold.gtn(0) && threshold.ltn(memberCount + 1);
  50. const hasMethod = !!method;
  51. return !accountId || !hasMethod || !hasThreshold;
  52. }
  53. protected renderTrigger = (): React.ReactNode => {
  54. const { t } = this.props;
  55. return (
  56. <Button.Group>
  57. <Button
  58. isPrimary
  59. label={t('Propose a council motion')}
  60. icon='add'
  61. onClick={this.showModal}
  62. />
  63. </Button.Group>
  64. );
  65. }
  66. protected renderContent = (): React.ReactNode => {
  67. const { apiDefaultTxSudo, memberCount = 0, t } = this.props;
  68. const { threshold } = this.state;
  69. return (
  70. <>
  71. <InputNumber
  72. className='medium'
  73. label={t('threshold')}
  74. help={t('The minimum number of council votes required to approve this motion')}
  75. isError={!threshold || threshold.eqn(0) || threshold.gtn(memberCount)}
  76. onChange={this.onChangeThreshold}
  77. onEnter={this.sendTx}
  78. placeholder={
  79. t(
  80. 'Positive number between 1 and {{memberCount}}',
  81. { replace: { memberCount } }
  82. )
  83. }
  84. value={threshold || new BN(0)}
  85. />
  86. <Extrinsic
  87. defaultValue={apiDefaultTxSudo}
  88. label={t('proposal')}
  89. onChange={this.onChangeExtrinsic}
  90. onEnter={this.sendTx}
  91. />
  92. </>
  93. );
  94. }
  95. private onChangeThreshold = (threshold: BN | null = null): void => {
  96. const { memberCount = 0 } = this.props;
  97. if (memberCount > 0 && !this.defaultState.threshold) {
  98. this.defaultState.threshold = new BN((memberCount / 2) + 1);
  99. }
  100. this.setState({ threshold });
  101. }
  102. private onChangeExtrinsic = (method?: Call): void => {
  103. if (!method) {
  104. return;
  105. }
  106. this.setState({ method });
  107. }
  108. }
  109. export default withMulti(
  110. Propose,
  111. translate,
  112. withCalls(
  113. ['query.electionsPhragmen.members', {
  114. fallbacks: ['query.elections.members'],
  115. propName: 'memberCount',
  116. transform: (value: any[]): number =>
  117. value.length
  118. }]
  119. )
  120. );