Browse Source

Linter manual fixes (+ some cleanup)

Leszek Wiesner 4 years ago
parent
commit
75ab8ab507
39 changed files with 202 additions and 274 deletions
  1. 14 16
      pioneer/packages/joy-proposals/src/Proposal/Body.tsx
  2. 2 2
      pioneer/packages/joy-proposals/src/Proposal/ChooseProposalType.tsx
  3. 11 11
      pioneer/packages/joy-proposals/src/Proposal/ProposalDetails.tsx
  4. 3 1
      pioneer/packages/joy-proposals/src/Proposal/ProposalFromId.tsx
  5. 1 1
      pioneer/packages/joy-proposals/src/Proposal/ProposalPreview.tsx
  6. 9 9
      pioneer/packages/joy-proposals/src/Proposal/ProposalTypePreview.tsx
  7. 2 2
      pioneer/packages/joy-proposals/src/Proposal/Votes.tsx
  8. 3 3
      pioneer/packages/joy-proposals/src/Proposal/VotingSection.tsx
  9. 1 1
      pioneer/packages/joy-proposals/src/Proposal/getVoteStyles.tsx
  10. 8 14
      pioneer/packages/joy-proposals/src/forms/AddWorkingGroupOpeningForm.tsx
  11. 5 10
      pioneer/packages/joy-proposals/src/forms/BeginReviewLeaderApplicationsForm.tsx
  12. 5 10
      pioneer/packages/joy-proposals/src/forms/DecreaseWorkingGroupLeadStakeForm.tsx
  13. 4 4
      pioneer/packages/joy-proposals/src/forms/FileDropdown.tsx
  14. 11 18
      pioneer/packages/joy-proposals/src/forms/FillWorkingGroupLeaderOpeningForm.tsx
  15. 4 4
      pioneer/packages/joy-proposals/src/forms/FormContainer.tsx
  16. 7 7
      pioneer/packages/joy-proposals/src/forms/FormFields.tsx
  17. 20 26
      pioneer/packages/joy-proposals/src/forms/GenericProposalForm.tsx
  18. 3 4
      pioneer/packages/joy-proposals/src/forms/MintCapacityForm.tsx
  19. 4 5
      pioneer/packages/joy-proposals/src/forms/RuntimeUpgradeForm.tsx
  20. 6 11
      pioneer/packages/joy-proposals/src/forms/SetContentWorkingGroupLeadForm.tsx
  21. 6 5
      pioneer/packages/joy-proposals/src/forms/SetCouncilParamsForm.tsx
  22. 5 5
      pioneer/packages/joy-proposals/src/forms/SetMaxValidatorCountForm.tsx
  23. 5 10
      pioneer/packages/joy-proposals/src/forms/SetWorkingGroupLeadRewardForm.tsx
  24. 5 10
      pioneer/packages/joy-proposals/src/forms/SetWorkingGroupMintCapacityForm.tsx
  25. 4 5
      pioneer/packages/joy-proposals/src/forms/SignalForm.tsx
  26. 5 10
      pioneer/packages/joy-proposals/src/forms/SlashWorkingGroupLeadStakeForm.tsx
  27. 4 9
      pioneer/packages/joy-proposals/src/forms/SpendingProposalForm.tsx
  28. 6 13
      pioneer/packages/joy-proposals/src/forms/TerminateWorkingGroupLeaderForm.tsx
  29. 2 2
      pioneer/packages/joy-proposals/src/index.tsx
  30. 6 6
      pioneer/packages/joy-proposals/src/stories/data/ProposalDetails.mock.ts
  31. 1 1
      pioneer/packages/joy-proposals/src/stories/withMock.tsx
  32. 10 10
      pioneer/packages/joy-proposals/src/validationSchema.ts
  33. 6 3
      pioneer/packages/joy-utils/src/react/hooks/proposals/useProposalSubscription.tsx
  34. 0 1
      pioneer/packages/joy-utils/src/transport/chain.ts
  35. 2 1
      pioneer/packages/joy-utils/src/transport/council.ts
  36. 7 10
      pioneer/packages/joy-utils/src/transport/proposals.ts
  37. 0 1
      pioneer/packages/joy-utils/src/transport/validators.ts
  38. 1 1
      pioneer/packages/joy-utils/src/transport/workingGroups.ts
  39. 4 12
      pioneer/packages/joy-utils/src/types/proposals.ts

+ 14 - 16
pioneer/packages/joy-proposals/src/Proposal/Body.tsx

@@ -50,16 +50,11 @@ function ProposedAddress (props: { accountId?: AccountId }) {
   );
 }
 
-function ProposedMember (props: { memberId?: MemberId | number | null }) {
-  if (props.memberId === null || props.memberId === undefined) {
-    return <>NONE</>;
-  }
-
-  const memberId: MemberId | number = props.memberId;
-
+function ProposedMember (props: { memberId: MemberId | number }) {
   const transport = useTransport();
+
   const [member, error, loading] = usePromise<Membership | null>(
-    () => transport.members.membershipById(memberId),
+    () => transport.members.membershipById(props.memberId),
     null
   );
 
@@ -112,15 +107,15 @@ const paramParsers: { [k in ProposalType]: (params: SpecificProposalDetails<k>)
   ],
   RuntimeUpgrade: ([hash, filesize]) => [
     new ParsedParam('Blake2b256 hash of WASM code', hash, true),
-    new ParsedParam('File size', filesize + ' bytes')
+    new ParsedParam('File size', `${filesize} bytes`)
   ],
   SetElectionParameters: (params) => [
-    new ParsedParam('Announcing period', params.announcing_period.toString() + ' blocks'),
-    new ParsedParam('Voting period', params.voting_period.toString() + ' blocks'),
-    new ParsedParam('Revealing period', params.revealing_period.toString() + ' blocks'),
-    new ParsedParam('Council size', params.council_size.toString() + ' members'),
-    new ParsedParam('Candidacy limit', params.candidacy_limit.toString() + ' members'),
-    new ParsedParam('New term duration', params.new_term_duration.toString() + ' blocks'),
+    new ParsedParam('Announcing period', `${params.announcing_period.toString()} blocks`),
+    new ParsedParam('Voting period', `${params.voting_period.toString()} blocks`),
+    new ParsedParam('Revealing period', `${params.revealing_period.toString()} blocks`),
+    new ParsedParam('Council size', `${params.council_size.toString()} members`),
+    new ParsedParam('Candidacy limit', `${params.candidacy_limit.toString()} members`),
+    new ParsedParam('New term duration', `${params.new_term_duration.toString()} blocks`),
     new ParsedParam('Min. council stake', formatBalance(params.min_council_stake)),
     new ParsedParam('Min. voting stake', formatBalance(params.min_voting_stake))
   ],
@@ -129,7 +124,10 @@ const paramParsers: { [k in ProposalType]: (params: SpecificProposalDetails<k>)
     new ParsedParam('Account', <ProposedAddress accountId={account as AccountId} />)
   ],
   SetLead: (params) => [
-    new ParsedParam('Member', <ProposedMember memberId={params.unwrapOr([])[0] as MemberId | undefined} />),
+    new ParsedParam(
+      'Member',
+      params.isSome ? <ProposedMember memberId={params.unwrap()[0] as MemberId} /> : 'NONE'
+    ),
     new ParsedParam('Account id', <ProposedAddress accountId={params.unwrapOr([])[1] as AccountId | undefined} />)
   ],
   SetContentWorkingGroupMintCapacity: (capacity) => [

+ 2 - 2
pioneer/packages/joy-proposals/src/Proposal/ChooseProposalType.tsx

@@ -30,8 +30,8 @@ export default function ChooseProposalType (props: RouteComponentProps) {
         <Item.Group>
           {proposalTypes
             .filter((typeInfo) => (!category || typeInfo.category === category) && !typeInfo.outdated)
-            .map((typeInfo, idx) => (
-              <ProposalTypePreview key={`${typeInfo} - ${idx}`} typeInfo={typeInfo} history={props.history} />
+            .map((typeInfo) => (
+              <ProposalTypePreview key={typeInfo.type} typeInfo={typeInfo} history={props.history} />
             ))}
         </Item.Group>
       </PromiseComponent>

+ 11 - 11
pioneer/packages/joy-proposals/src/Proposal/ProposalDetails.tsx

@@ -8,7 +8,7 @@ import { MyAccountProps, withMyAccount } from '@polkadot/joy-utils/react/hocs/ac
 import { ParsedProposal } from '@polkadot/joy-utils/types/proposals';
 import { withCalls } from '@polkadot/react-api';
 import { withMulti } from '@polkadot/react-api/hoc';
-import { ProposalId, ProposalDecisionStatuses, ApprovedProposalStatuses, ExecutionFailedStatus } from '@joystream/types/proposals';
+import { ProposalId, ProposalDecisionStatuses, ApprovedProposalStatuses } from '@joystream/types/proposals';
 import { BlockNumber } from '@polkadot/types/interfaces';
 import { MemberId } from '@joystream/types/members';
 import { Seat } from '@joystream/types/council';
@@ -57,7 +57,7 @@ export type ExtendedProposalStatus = {
 }
 
 export function getExtendedStatus (proposal: ParsedProposal, bestNumber: BlockNumber | undefined): ExtendedProposalStatus {
-  const basicStatus = Object.keys(proposal.status)[0] as BasicProposalStatus;
+  const basicStatus: BasicProposalStatus = proposal.status.type;
   let expiresIn: number | null = null;
 
   let displayStatus: ProposalDisplayStatus = basicStatus;
@@ -73,31 +73,31 @@ export function getExtendedStatus (proposal: ParsedProposal, bestNumber: BlockNu
 
   if (basicStatus === 'Active') {
     periodStatus = 'Voting period';
-    expiresIn = Math.max(votingPeriod - blockAge, 0) || null;
+    expiresIn = Math.max(votingPeriod.toNumber() - blockAge, 0) || null;
   }
 
   if (basicStatus === 'Finalized') {
-    const { finalizedAt, proposalStatus } = proposal.status.Finalized;
-    const decisionStatus: ProposalDecisionStatuses = Object.keys(proposalStatus)[0] as ProposalDecisionStatuses;
+    const { finalizedAt, proposalStatus } = proposal.status.asType('Finalized');
+    const decisionStatus: ProposalDecisionStatuses = proposalStatus.type;
 
     displayStatus = decisionStatus;
-    finalizedAtBlock = finalizedAt as number;
+    finalizedAtBlock = finalizedAt.toNumber();
 
     if (decisionStatus === 'Approved') {
-      const approvedStatus: ApprovedProposalStatuses = Object.keys(proposalStatus.Approved)[0] as ApprovedProposalStatuses;
+      const approvedStatus: ApprovedProposalStatuses = proposalStatus.asType('Approved').type;
 
       if (approvedStatus === 'PendingExecution') {
-        const finalizedAge = best - finalizedAt;
+        const finalizedAge = best - finalizedAt.toNumber();
 
         periodStatus = 'Grace period';
-        expiresIn = Math.max(gracePeriod - finalizedAge, 0) || null;
+        expiresIn = Math.max(gracePeriod.toNumber() - finalizedAge, 0) || null;
       } else {
         // Executed / ExecutionFailed
         displayStatus = approvedStatus;
-        executedAtBlock = finalizedAtBlock + gracePeriod;
+        executedAtBlock = finalizedAtBlock + gracePeriod.toNumber();
 
         if (approvedStatus === 'ExecutionFailed') {
-          const executionFailedStatus = proposalStatus.Approved.ExecutionFailed as ExecutionFailedStatus;
+          const executionFailedStatus = proposalStatus.asType('Approved').asType('ExecutionFailed');
 
           executionFailReason = Buffer.from(executionFailedStatus.error.toString().replace('0x', ''), 'hex').toString();
         }

+ 3 - 1
pioneer/packages/joy-proposals/src/Proposal/ProposalFromId.tsx

@@ -5,7 +5,9 @@ import { useProposalSubscription } from '@polkadot/joy-utils/react/hooks';
 import PromiseComponent from '@polkadot/joy-utils/react/components/PromiseComponent';
 import { useApi } from '@polkadot/react-hooks';
 
-export default function ProposalFromId (props: RouteComponentProps<any>) {
+type RouteParams = { id?: string | undefined };
+
+export default function ProposalFromId (props: RouteComponentProps<RouteParams>) {
   const {
     match: {
       params: { id }

+ 1 - 1
pioneer/packages/joy-proposals/src/Proposal/ProposalPreview.tsx

@@ -33,7 +33,7 @@ export default function ProposalPreview ({ proposal, bestNumber }: ProposalPrevi
   return (
     <Card
       fluid
-      href={`#/proposals/${proposal.id}`}>
+      href={`#/proposals/${proposal.id.toString()}`}>
       <ProposalIdBox>{ `#${proposal.id.toString()}` }</ProposalIdBox>
       <Card.Content>
         <Card.Header>

+ 9 - 9
pioneer/packages/joy-proposals/src/Proposal/ProposalTypePreview.tsx

@@ -6,7 +6,7 @@ import { Item, Icon, Button, Label } from 'semantic-ui-react';
 import { ProposalType, Category } from '@polkadot/joy-utils/types/proposals';
 import _ from 'lodash';
 import styled from 'styled-components';
-import useVoteStyles from './useVoteStyles';
+import getVoteStyles from './getVoteStyles';
 import { formatBalance } from '@polkadot/util';
 
 import './ProposalType.css';
@@ -114,26 +114,26 @@ export default function ProposalTypePreview (props: ProposalTypePreviewProps) {
         </div>
         <QuorumsAndThresholds>
           { approvalQuorum && (
-            <QuorumThresholdLabel color={ useVoteStyles('Approve').color }>
-              <Icon name={ useVoteStyles('Approve').icon } />
+            <QuorumThresholdLabel color={ getVoteStyles('Approve').color }>
+              <Icon name={ getVoteStyles('Approve').icon } />
               Approval Quorum: <b>{ approvalQuorum }%</b>
             </QuorumThresholdLabel>
           ) }
           { approvalThreshold && (
-            <QuorumThresholdLabel color={ useVoteStyles('Approve').color }>
-              <Icon name={ useVoteStyles('Approve').icon } />
+            <QuorumThresholdLabel color={ getVoteStyles('Approve').color }>
+              <Icon name={ getVoteStyles('Approve').icon } />
               Approval Threshold: <b>{ approvalThreshold }%</b>
             </QuorumThresholdLabel>
           ) }
           { slashingQuorum && (
-            <QuorumThresholdLabel color={ useVoteStyles('Slash').color }>
-              <Icon name={ useVoteStyles('Slash').icon } />
+            <QuorumThresholdLabel color={ getVoteStyles('Slash').color }>
+              <Icon name={ getVoteStyles('Slash').icon } />
               Slashing Quorum: <b>{ slashingQuorum }%</b>
             </QuorumThresholdLabel>
           ) }
           { slashingThreshold && (
-            <QuorumThresholdLabel color={ useVoteStyles('Slash').color }>
-              <Icon name={ useVoteStyles('Slash').icon } />
+            <QuorumThresholdLabel color={ getVoteStyles('Slash').color }>
+              <Icon name={ getVoteStyles('Slash').icon } />
               Slashing Threshold: <b>{ slashingThreshold }%</b>
             </QuorumThresholdLabel>
           ) }

+ 2 - 2
pioneer/packages/joy-proposals/src/Proposal/Votes.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import { Header, Divider, Table, Icon } from 'semantic-ui-react';
-import useVoteStyles from './useVoteStyles';
+import getVoteStyles from './getVoteStyles';
 import { VoteKind } from '@joystream/types/proposals';
 import { VoteKindStr } from './VotingSection';
 import ProfilePreview from '@polkadot/joy-utils/react/components/MemberProfilePreview';
@@ -37,7 +37,7 @@ export default function Votes ({ proposal: { id, votingResults } }: VotesProps)
                 {votes.votes.map((proposalVote, idx) => {
                   const { vote, member } = proposalVote;
                   const voteStr = (vote as VoteKind).type.toString() as VoteKindStr;
-                  const { icon, textColor } = useVoteStyles(voteStr);
+                  const { icon, textColor } = getVoteStyles(voteStr);
 
                   return (
                     <Table.Row key={`${member.handle}-${idx}`}>

+ 3 - 3
pioneer/packages/joy-proposals/src/Proposal/VotingSection.tsx

@@ -1,7 +1,7 @@
 import React, { useState } from 'react';
 
 import { Icon, Message, Divider, Header } from 'semantic-ui-react';
-import useVoteStyles from './useVoteStyles';
+import getVoteStyles from './getVoteStyles';
 import { SemanticTxButton } from '@polkadot/joy-utils/react/components/TxButton';
 import { MemberId } from '@joystream/types/members';
 import { ProposalId, VoteKind, VoteKinds } from '@joystream/types/proposals';
@@ -34,7 +34,7 @@ type VoteButtonProps = {
 }
 
 function VoteButton ({ voteKind, proposalId, memberId, onSuccess }: VoteButtonProps) {
-  const { icon, color } = useVoteStyles(voteKind);
+  const { icon, color } = getVoteStyles(voteKind);
 
   return (
     <SemanticTxButton
@@ -83,7 +83,7 @@ export default function VotingSection ({
   const voteStr: VoteKindStr | null = voted || (vote && vote.type.toString() as VoteKindStr);
 
   if (voteStr) {
-    const { icon, color } = useVoteStyles(voteStr);
+    const { icon, color } = getVoteStyles(voteStr);
 
     return (
       <Message icon color={color}>

+ 1 - 1
pioneer/packages/joy-proposals/src/Proposal/useVoteStyles.tsx → pioneer/packages/joy-proposals/src/Proposal/getVoteStyles.tsx

@@ -1,6 +1,6 @@
 import { SemanticCOLORS, SemanticICONS } from 'semantic-ui-react';
 
-export default function useVoteStyles (
+export default function getVoteStyles (
   value: 'Approve' | 'Abstain' | 'Reject' | 'Slash'
 ): { textColor: string; icon: SemanticICONS; color: SemanticCOLORS } {
   let textColor;

+ 8 - 14
pioneer/packages/joy-proposals/src/forms/AddWorkingGroupOpeningForm.tsx

@@ -4,8 +4,7 @@ import * as Yup from 'yup';
 import { withProposalFormData,
   ProposalFormExportProps,
   ProposalFormContainerProps,
-  ProposalFormInnerProps,
-  genericFormDefaultOptions } from './GenericProposalForm';
+  ProposalFormInnerProps } from './GenericProposalForm';
 import { GenericWorkingGroupProposalForm,
   FormValues as WGFormValues,
   defaultValues as wgFromDefaultValues } from './GenericWorkingGroupProposalForm';
@@ -92,7 +91,7 @@ const HRTDefault: (memberHandle: string, group: WorkingGroupKey) => GenericJoySt
     }
   });
 
-type FormAdditionalProps = {}; // Aditional props coming all the way from export component into the inner form.
+type FormAdditionalProps = Record<any, never>; // Aditional props coming all the way from export component into the inner form.
 type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
 type FormContainerProps = ProposalFormContainerProps<ExportComponentProps> & {
   currentBlock?: BlockNumber;
@@ -193,7 +192,7 @@ const valuesToAddOpeningParams = (values: FormValues): SimplifiedTypeInterface<I
 };
 
 const AddWorkingGroupOpeningForm: React.FunctionComponent<FormInnerProps> = (props) => {
-  const { handleChange, errors, touched, values, setFieldValue, myMemberId, myMembership } = props;
+  const { handleChange, errors, touched, values, setFieldValue, myMembership } = props;
 
   useEffect(() => {
     if (myMembership && !touched.humanReadableText) {
@@ -202,7 +201,8 @@ const AddWorkingGroupOpeningForm: React.FunctionComponent<FormInnerProps> = (pro
         JSON.stringify(HRTDefault(myMembership.handle.toString(), values.workingGroup), undefined, 4)
       );
     }
-  }, [values.workingGroup, myMembership]);
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [values.workingGroup, myMembership, touched.humanReadableText]);
   const errorLabelsProps = getFormErrorLabelsProps<FormValues>(errors, touched);
 
   return (
@@ -210,13 +210,7 @@ const AddWorkingGroupOpeningForm: React.FunctionComponent<FormInnerProps> = (pro
       {...props}
       txMethod='createAddWorkingGroupLeaderOpeningProposal'
       proposalType='AddWorkingGroupLeaderOpening'
-      submitParams={[
-        myMemberId,
-        values.title,
-        values.rationale,
-        '{STAKE}',
-        valuesToAddOpeningParams(values)
-      ]}
+      submitParams={[valuesToAddOpeningParams(values)]}
     >
       <Grid columns='4' doubling stackable verticalAlign='bottom'>
         <Grid.Row>
@@ -350,13 +344,13 @@ const FormContainer = withFormContainer<FormContainerProps, FormValues>({
     ...(props.initialData || {})
   }),
   validationSchema: (props: FormContainerProps) => Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All(),
     ...Validation.AddWorkingGroupLeaderOpening(
       props.currentBlock?.toNumber() || 0,
       props.HRTConstraint
     )
   }),
-  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  handleSubmit: () => null,
   displayName: 'AddWorkingGroupOpeningForm'
 })(AddWorkingGroupOpeningForm);
 

+ 5 - 10
pioneer/packages/joy-proposals/src/forms/BeginReviewLeaderApplicationsForm.tsx

@@ -3,8 +3,7 @@ import * as Yup from 'yup';
 import { withProposalFormData,
   ProposalFormExportProps,
   ProposalFormContainerProps,
-  ProposalFormInnerProps,
-  genericFormDefaultOptions } from './GenericProposalForm';
+  ProposalFormInnerProps } from './GenericProposalForm';
 import { GenericWorkingGroupProposalForm,
   FormValues as WGFormValues,
   defaultValues as wgFromDefaultValues } from './GenericWorkingGroupProposalForm';
@@ -27,13 +26,13 @@ const defaultValues: FormValues = {
   openingId: ''
 };
 
-type FormAdditionalProps = {}; // Aditional props coming all the way from export component into the inner form.
+type FormAdditionalProps = Record<any, never>; // Aditional props coming all the way from export component into the inner form.
 type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
 type FormContainerProps = ProposalFormContainerProps<ExportComponentProps>;
 type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;
 
 const BeginReviewLeadeApplicationsForm: React.FunctionComponent<FormInnerProps> = (props) => {
-  const { handleChange, values, myMemberId, errors, touched } = props;
+  const { handleChange, values, errors, touched } = props;
   const errorLabelsProps = getFormErrorLabelsProps<FormValues>(errors, touched);
   const transport = useTransport();
   const [openings, openingsError, openingsLoading] = usePromise(
@@ -59,10 +58,6 @@ const BeginReviewLeadeApplicationsForm: React.FunctionComponent<FormInnerProps>
       proposalType='BeginReviewWorkingGroupLeaderApplication'
       disabled={!openingsOptions.length}
       submitParams={[
-        myMemberId,
-        values.title,
-        values.rationale,
-        '{STAKE}',
         values.openingId,
         values.workingGroup
       ]}
@@ -104,10 +99,10 @@ const FormContainer = withFormContainer<FormContainerProps, FormValues>({
     ...(props.initialData || {})
   }),
   validationSchema: Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All(),
     ...Validation.BeginReviewWorkingGroupLeaderApplication()
   }),
-  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  handleSubmit: () => null,
   displayName: 'BeginReviewLeadeApplicationsForm'
 })(BeginReviewLeadeApplicationsForm);
 

+ 5 - 10
pioneer/packages/joy-proposals/src/forms/DecreaseWorkingGroupLeadStakeForm.tsx

@@ -4,8 +4,7 @@ import * as Yup from 'yup';
 import { withProposalFormData,
   ProposalFormExportProps,
   ProposalFormContainerProps,
-  ProposalFormInnerProps,
-  genericFormDefaultOptions } from './GenericProposalForm';
+  ProposalFormInnerProps } from './GenericProposalForm';
 import { GenericWorkingGroupProposalForm,
   FormValues as WGFormValues,
   defaultValues as wgFromDefaultValues } from './GenericWorkingGroupProposalForm';
@@ -26,13 +25,13 @@ const defaultValues: FormValues = {
   amount: ''
 };
 
-type FormAdditionalProps = {}; // Aditional props coming all the way from export component into the inner form.
+type FormAdditionalProps = Record<any, never>; // Aditional props coming all the way from export component into the inner form.
 type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
 type FormContainerProps = ProposalFormContainerProps<ExportComponentProps>;
 type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;
 
 const DecreaseWorkingGroupLeadStakeForm: React.FunctionComponent<FormInnerProps> = (props) => {
-  const { handleChange, errors, touched, values, myMemberId, setFieldError } = props;
+  const { handleChange, errors, touched, values, setFieldError } = props;
   const errorLabelsProps = getFormErrorLabelsProps<FormValues>(errors, touched);
   const [lead, setLead] = useState<WorkerData | null>(null);
 
@@ -54,10 +53,6 @@ const DecreaseWorkingGroupLeadStakeForm: React.FunctionComponent<FormInnerProps>
       leadStakeRequired={true}
       onLeadChange={(lead: WorkerData | null) => setLead(lead)}
       submitParams={[
-        myMemberId,
-        values.title,
-        values.rationale,
-        '{STAKE}',
         lead?.workerId,
         values.amount,
         values.workingGroup
@@ -87,10 +82,10 @@ const FormContainer = withFormContainer<FormContainerProps, FormValues>({
     ...(props.initialData || {})
   }),
   validationSchema: Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All(),
     ...Validation.DecreaseWorkingGroupLeaderStake()
   }),
-  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  handleSubmit: () => null,
   displayName: 'DecreaseWorkingGroupLeadStakeForm'
 })(DecreaseWorkingGroupLeadStakeForm);
 

+ 4 - 4
pioneer/packages/joy-proposals/src/forms/FileDropdown.tsx

@@ -71,7 +71,7 @@ const innerSpanStyle = (): React.CSSProperties => {
 
 // Interpret the file as a UTF-8 string
 // https://developer.mozilla.org/en-US/docs/Web/API/Blob/text
-const parseFileAsUtf8 = async (file: any): Promise<string> => {
+const parseFileAsUtf8 = async (file: File): Promise<string> => {
   const text = await file.text();
 
   return text;
@@ -79,7 +79,7 @@ const parseFileAsUtf8 = async (file: any): Promise<string> => {
 
 // Interpret the file as containing binary data. This will load the entire
 // file into memory which may crash the brower with very large files.
-const parseFileAsBinary = async (file: any): Promise<ArrayBuffer> => {
+const parseFileAsBinary = async (file: File): Promise<ArrayBuffer> => {
   // return file.arrayBuffer();
   // This newer API not fully supported yet in all browsers
   // https://developer.mozilla.org/en-US/docs/Web/API/Blob/arrayBuffer
@@ -107,13 +107,13 @@ type FileDropdownProps<FormValuesT> = {
   interpretAs: 'utf-8' | 'binary';
 };
 
-export default function FileDropdown<ValuesT = {}> (props: FileDropdownProps<ValuesT>) {
+export default function FileDropdown<ValuesT = Record<string, any>> (props: FileDropdownProps<ValuesT>) {
   const [parsing, setParsing] = useState(false);
   const { error, name, setFieldValue, setFieldTouched, acceptedFormats, defaultText, interpretAs } = props;
 
   return (
     <Dropzone
-      onDropAccepted={async (acceptedFiles) => {
+      onDropAccepted={async (acceptedFiles: File[]) => {
         setParsing(true);
         let contents;
 

+ 11 - 18
pioneer/packages/joy-proposals/src/forms/FillWorkingGroupLeaderOpeningForm.tsx

@@ -3,14 +3,13 @@ import * as Yup from 'yup';
 import { withProposalFormData,
   ProposalFormExportProps,
   ProposalFormContainerProps,
-  ProposalFormInnerProps,
-  genericFormDefaultOptions } from './GenericProposalForm';
+  ProposalFormInnerProps } from './GenericProposalForm';
 import { GenericWorkingGroupProposalForm,
   FormValues as WGFormValues,
   defaultValues as wgFromDefaultValues } from './GenericWorkingGroupProposalForm';
 import { FormField, RewardPolicyFields } from './FormFields';
 import { withFormContainer } from './FormContainer';
-import { Dropdown, DropdownItemProps, Header, Checkbox, Message } from 'semantic-ui-react';
+import { Dropdown, DropdownItemProps, DropdownProps, Header, Checkbox, Message } from 'semantic-ui-react';
 import _ from 'lodash';
 import Validation from '../validationSchema';
 import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks';
@@ -45,7 +44,7 @@ const defaultValues: FormValues = {
   rewardInterval: ''
 };
 
-type FormAdditionalProps = {}; // Aditional props coming all the way from export component into the inner form.
+type FormAdditionalProps = Record<any, never>; // Aditional props coming all the way from export component into the inner form.
 type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
 type FormContainerProps = ProposalFormContainerProps<ExportComponentProps> & {
   currentBlock?: BlockNumber;
@@ -69,7 +68,7 @@ const valuesToFillOpeningParams = (values: FormValues): SimplifiedTypeInterface<
 );
 
 const FillWorkingGroupLeaderOpeningForm: React.FunctionComponent<FormInnerProps> = (props) => {
-  const { handleChange, setFieldValue, values, myMemberId, errors, touched } = props;
+  const { handleChange, setFieldValue, values, errors, touched } = props;
   const errorLabelsProps = getFormErrorLabelsProps<FormValues>(errors, touched);
   const transport = useTransport();
   const [openings, openingsError, openingsLoading] = usePromise<OpeningData[]>(
@@ -97,11 +96,11 @@ const FillWorkingGroupLeaderOpeningForm: React.FunctionComponent<FormInnerProps>
   const applicationsOptions = activeApplications
     .map((a) => {
       return {
-        text: `${a.wgApplicationId}: ${a.member.handle}`,
+        text: `${a.wgApplicationId}: ${a.member.handle.toString()}`,
         image: a.member.avatar_uri.toString() ? { avatar: true, src: a.member.avatar_uri.toString() } : undefined,
         description:
           (a.stakes.application ? `Appl. stake: ${formatBalance(a.stakes.application)}` : '') +
-          (a.stakes.role ? (a.stakes.application && ', ') + `Role stake: ${formatBalance(a.stakes.role)}` : ''),
+          (a.stakes.role ? `${(a.stakes.application && ', ')} Role stake: ${formatBalance(a.stakes.role)}` : ''),
         value: a.wgApplicationId.toString()
       };
     });
@@ -112,13 +111,7 @@ const FillWorkingGroupLeaderOpeningForm: React.FunctionComponent<FormInnerProps>
       txMethod='createFillWorkingGroupLeaderOpeningProposal'
       proposalType='FillWorkingGroupLeaderOpening'
       disabled={!openingsOptions.length || !applicationsOptions.length}
-      submitParams={[
-        myMemberId,
-        values.title,
-        values.rationale,
-        '{STAKE}',
-        valuesToFillOpeningParams(values)
-      ]}
+      submitParams={[valuesToFillOpeningParams(values)]}
     >
       <PromiseComponent error={openingsError} loading={openingsLoading} message='Fetching openings...'>
         { !openingsOptions.length
@@ -139,8 +132,8 @@ const FillWorkingGroupLeaderOpeningForm: React.FunctionComponent<FormInnerProps>
                 onChange={(...args) => {
                   setFieldValue('successfulApplicants', []);
 
-                  // "as any" assert is required due to some invalid typing of Formik's "handleChange" function (it takes 2 args, not 1)
-                  return (handleChange as any)(...args);
+                  // This assert is required due to some invalid typing of Formik's "handleChange" function (it takes 2 args, not 1)
+                  return (handleChange as Exclude<DropdownProps['onChange'], undefined>)(...args);
                 }}
                 placeholder={'Select an opening'}
                 name={'openingId'}
@@ -208,10 +201,10 @@ const FormContainer = withFormContainer<FormContainerProps, FormValues>({
     ...(props.initialData || {})
   }),
   validationSchema: (props: FormContainerProps) => Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All(),
     ...Validation.FillWorkingGroupLeaderOpening(props.currentBlock?.toNumber() || 0)
   }),
-  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  handleSubmit: () => null,
   displayName: 'FillWorkingGroupLeaderOpeningForm'
 })(FillWorkingGroupLeaderOpeningForm);
 

+ 4 - 4
pioneer/packages/joy-proposals/src/forms/FormContainer.tsx

@@ -1,17 +1,17 @@
 import React from 'react';
-import { withFormik } from 'formik';
+import { withFormik, WithFormikConfig } from 'formik';
 
-export function withFormContainer<MyFormProps, FormValues> (formikProps: any) {
+export function withFormContainer<MyFormProps, FormValues> (formikProps: WithFormikConfig<MyFormProps, FormValues>) {
   return function (InnerForm: React.ComponentType<any>) {
     return withFormik<MyFormProps, FormValues>(formikProps)(function (props) {
-      const handleBlur = (e: React.FocusEvent<HTMLInputElement>, data: any): void => {
+      const handleBlur = (e: React.FocusEvent<HTMLInputElement>, data?: { name: string, value: any }): void => {
         if (data && data.name) {
           props.setFieldValue(data.name, data.value);
           props.setFieldTouched(data.name);
         }
       };
 
-      const handleChange = (e: React.ChangeEvent<HTMLInputElement>, data: any): void => {
+      const handleChange = (e: React.ChangeEvent<HTMLInputElement>, data?: { name: string, value: any }): void => {
         if (data && data.name) {
           props.setFieldValue(data.name, data.value);
           props.setFieldTouched(data.name);

+ 7 - 7
pioneer/packages/joy-proposals/src/forms/FormFields.tsx

@@ -1,5 +1,5 @@
 import React from 'react';
-import { Form, FormInputProps, FormTextAreaProps, Label, LabelProps, Checkbox } from 'semantic-ui-react';
+import { Form, StrictFormInputProps, StrictFormTextAreaProps, Label, LabelProps, Checkbox } from 'semantic-ui-react';
 import { FormikProps } from 'formik';
 import LabelWithHelp from './LabelWithHelp';
 import { FormErrorLabelsProps } from './errorHandling';
@@ -14,13 +14,13 @@ import styled from 'styled-components';
  * and to easily switch the structure/display of a typical form field.
 */
 
-type InputFormFieldProps = Omit<FormInputProps, 'error'> & {
+type StrictInputFormFieldProps = Omit<StrictFormInputProps, 'error'> & {
   help?: string;
   unit?: string;
   error?: LabelProps;
 };
 
-export function InputFormField (props: InputFormFieldProps) {
+export function InputFormField (props: { [key: string]: any } & StrictInputFormFieldProps) {
   const { unit } = props;
   const fieldProps = { ...props, label: undefined, error: undefined };
 
@@ -36,12 +36,12 @@ export function InputFormField (props: InputFormFieldProps) {
   );
 }
 
-type TextareaFormFieldProps = Omit<FormTextAreaProps, 'error'> & {
+type StrictTextareaFormFieldProps = Omit<StrictFormTextAreaProps, 'error'> & {
   help?: string;
   error?: LabelProps;
 };
 
-export function TextareaFormField (props: TextareaFormFieldProps) {
+export function TextareaFormField (props: { [key: string]: any } & StrictTextareaFormFieldProps) {
   const fieldProps = { ...props, label: undefined, error: undefined };
 
   return (
@@ -51,7 +51,7 @@ export function TextareaFormField (props: TextareaFormFieldProps) {
   );
 }
 
-type FormFieldProps = Omit<(InputFormFieldProps | TextareaFormFieldProps), 'error'> & {
+type StrictFormFieldProps = Omit<(StrictInputFormFieldProps | StrictTextareaFormFieldProps), 'error'> & {
   error?: LabelProps;
   showErrorMsg?: boolean;
 };
@@ -62,7 +62,7 @@ const StyledFormField = styled(Form.Field)`
   }
 `;
 
-export function FormField (props: React.PropsWithChildren<FormFieldProps>) {
+export function FormField (props: React.PropsWithChildren<{ [key: string]: any } & StrictFormFieldProps>) {
   const { error, showErrorMsg = false, label, help, children } = props;
 
   return (

+ 20 - 26
pioneer/packages/joy-proposals/src/forms/GenericProposalForm.tsx

@@ -1,8 +1,7 @@
 import React, { useEffect, useState, useRef } from 'react';
-import { FormikProps, WithFormikConfig } from 'formik';
+import { FormikProps } from 'formik';
 import { Form, Icon, Button, Message } from 'semantic-ui-react';
 import { getFormErrorLabelsProps } from './errorHandling';
-import Validation from '../validationSchema';
 import { InputFormField, TextareaFormField } from './FormFields';
 import TxButton from '@polkadot/joy-utils/react/components/TxButton';
 import { SubmittableResult } from '@polkadot/api';
@@ -47,9 +46,9 @@ export type ProposalFormInnerProps<ContainerPropsT, FormValuesT> = ContainerProp
 
 // Types only used in this file
 type GenericProposalFormAdditionalProps = {
-  txMethod?: string;
+  txMethod: string;
   submitParams?: any[];
-  proposalType?: ProposalType;
+  proposalType: ProposalType;
   disabled?: boolean;
 };
 
@@ -59,21 +58,6 @@ ProposalFormExportProps<GenericProposalFormAdditionalProps, GenericFormValues>
 
 >;
 type GenericFormInnerProps = ProposalFormInnerProps<GenericFormContainerProps, GenericFormValues>;
-type GenericFormDefaultOptions = WithFormikConfig<GenericFormContainerProps, GenericFormValues>;
-
-// Default "withFormik" options that can be extended in specific forms
-export const genericFormDefaultOptions: GenericFormDefaultOptions = {
-  mapPropsToValues: (props: GenericFormContainerProps) => ({
-    ...genericFormDefaultValues,
-    ...(props.initialData || {})
-  }),
-  validationSchema: {
-    ...Validation.All()
-  },
-  handleSubmit: (values, { setSubmitting, resetForm }) => {
-    // This is handled via TxButton
-  }
-};
 
 const StyledGenericProposalForm = styled.div`
   .proposal-form {
@@ -106,6 +90,7 @@ const StyledGenericProposalForm = styled.div`
 // Other fields can be passed as children
 export const GenericProposalForm: React.FunctionComponent<GenericFormInnerProps> = (props) => {
   const {
+    myMemberId,
     handleChange,
     handleSubmit,
     errors,
@@ -121,7 +106,6 @@ export const GenericProposalForm: React.FunctionComponent<GenericFormInnerProps>
     submitParams,
     setSubmitting,
     history,
-    balances_totalIssuance,
     proposalType,
     disabled = false
   } = props;
@@ -143,6 +127,8 @@ export const GenericProposalForm: React.FunctionComponent<GenericFormInnerProps>
       setAfterSubmit(null);
       setSubmitting(false);
     }
+    // setSubmitting shouldn't change, so we don't specify it as dependency to avoid complications
+    // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [isValidating, isValid, afterSubmit]);
 
   // Focus first error field when isValidating changes to false (which happens after form is validated)
@@ -187,13 +173,13 @@ export const GenericProposalForm: React.FunctionComponent<GenericFormInnerProps>
     }
 
     setSubmitting(false);
-    history.push(`/proposals/${createdProposalId}`);
+
+    if (createdProposalId !== null) {
+      history.push(`/proposals/${createdProposalId}`);
+    }
   };
 
-  const requiredStake: number | undefined =
-    balances_totalIssuance &&
-    proposalType &&
-    proposalsConsts[proposalType].stake;
+  const requiredStake = proposalType && proposalsConsts[proposalType].stake;
 
   return (
     <StyledGenericProposalForm ref={formContainerRef}>
@@ -234,7 +220,15 @@ export const GenericProposalForm: React.FunctionComponent<GenericFormInnerProps>
               type='button' // Tx button uses custom submit handler - "onTxButtonClick"
               label='Submit proposal'
               isDisabled={disabled || isSubmitting}
-              params={(submitParams || []).map((p) => (p === '{STAKE}' ? requiredStake : p))}
+              params={[
+                myMemberId,
+                values.title,
+                values.rationale,
+                requiredStake,
+                // submitParams is any[], but it's not much of an issue (params can vary a lot)
+                // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+                ...(submitParams || [])
+              ]}
               tx={`proposalsCodex.${txMethod}`}
               txFailedCb={onTxFailed}
               txSuccessCb={onTxSuccess}

+ 3 - 4
pioneer/packages/joy-proposals/src/forms/MintCapacityForm.tsx

@@ -3,7 +3,6 @@ import * as Yup from 'yup';
 import { getFormErrorLabelsProps } from './errorHandling';
 import { GenericProposalForm,
   GenericFormValues,
-  genericFormDefaultOptions,
   genericFormDefaultValues,
   withProposalFormData,
   ProposalFormExportProps,
@@ -45,7 +44,7 @@ const MintCapacityForm: React.FunctionComponent<FormInnerProps> = (props) => {
       {...props}
       txMethod={txMethod}
       proposalType={proposalType}
-      submitParams={[props.myMemberId, values.title, values.rationale, '{STAKE}', values.capacity]}
+      submitParams={[values.capacity]}
     >
       <InputFormField
         error={errorLabelsProps.capacity}
@@ -67,10 +66,10 @@ const FormContainer = withFormContainer<FormContainerProps, FormValues>({
     ...(props.initialData || {})
   }),
   validationSchema: Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All(),
     ...Validation.SetContentWorkingGroupMintCapacity()
   }),
-  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  handleSubmit: () => null,
   displayName: 'MintCapacityForm'
 })(MintCapacityForm);
 

+ 4 - 5
pioneer/packages/joy-proposals/src/forms/RuntimeUpgradeForm.tsx

@@ -3,7 +3,6 @@ import { Form } from 'semantic-ui-react';
 import * as Yup from 'yup';
 import { GenericProposalForm,
   GenericFormValues,
-  genericFormDefaultOptions,
   genericFormDefaultValues,
   withProposalFormData,
   ProposalFormExportProps,
@@ -23,7 +22,7 @@ const defaultValues: FormValues = {
   WASM: new ArrayBuffer(0)
 };
 
-type FormAdditionalProps = {}; // Aditional props coming all the way from export comonent into the inner form.
+type FormAdditionalProps = Record<any, never>; // Aditional props coming all the way from export comonent into the inner form.
 type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
 type FormContainerProps = ProposalFormContainerProps<ExportComponentProps>;
 type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;
@@ -36,7 +35,7 @@ const RuntimeUpgradeForm: React.FunctionComponent<FormInnerProps> = (props) => {
       {...props}
       txMethod='createRuntimeUpgradeProposal'
       proposalType='RuntimeUpgrade'
-      submitParams={[props.myMemberId, values.title, values.rationale, '{STAKE}', values.WASM]}
+      submitParams={[values.WASM]}
     >
       <Form.Field>
         <FileDropdown<FormValues>
@@ -59,10 +58,10 @@ const FormContainer = withFormContainer<FormContainerProps, FormValues>({
     ...(props.initialData || {})
   }),
   validationSchema: Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All(),
     ...Validation.RuntimeUpgrade()
   }),
-  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  handleSubmit: () => null,
   displayName: 'RuntimeUpgradeForm'
 })(RuntimeUpgradeForm);
 

+ 6 - 11
pioneer/packages/joy-proposals/src/forms/SetContentWorkingGroupLeadForm.tsx

@@ -4,7 +4,6 @@ import { getFormErrorLabelsProps } from './errorHandling';
 import * as Yup from 'yup';
 import { GenericProposalForm,
   GenericFormValues,
-  genericFormDefaultOptions,
   genericFormDefaultValues,
   withProposalFormData,
   ProposalFormExportProps,
@@ -19,7 +18,7 @@ import PromiseComponent from '@polkadot/joy-utils/react/components/PromiseCompon
 import _ from 'lodash';
 
 export type FormValues = GenericFormValues & {
-  workingGroupLead: any;
+  workingGroupLead: string;
 };
 
 const defaultValues: FormValues = {
@@ -27,7 +26,7 @@ const defaultValues: FormValues = {
   workingGroupLead: ''
 };
 
-type FormAdditionalProps = {}; // Aditional props coming all the way from export comonent into the inner form.
+type FormAdditionalProps = Record<any, never>; // Aditional props coming all the way from export comonent into the inner form.
 type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
 type FormContainerProps = ProposalFormContainerProps<ExportComponentProps>;
 type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;
@@ -48,7 +47,7 @@ function membersToOptions (members: { id: number; profile: Membership }[]) {
     members
       .map(({ id, profile }) => ({
         key: profile.handle,
-        text: `${profile.handle} (id:${id})`,
+        text: `${profile.handle.toString()} (id:${id})`,
         value: memberOptionKey(id, profile),
         image: profile.avatar_uri.toString() ? { avatar: true, src: profile.avatar_uri } : null
       }))
@@ -94,7 +93,7 @@ const SetContentWorkingGroupsLeadForm: React.FunctionComponent<FormInnerProps> =
   // Filter options on search query change (we "pulled-out" this logic here to avoid lags)
   useEffect(() => {
     setFilteredOptions(filterMembers(membersOptions, membersSearchQuery));
-  }, [membersSearchQuery]);
+  }, [membersSearchQuery, membersOptions]);
 
   return (
     <PromiseComponent error={clError} loading={clLoading} message='Fetching current lead...'>
@@ -103,10 +102,6 @@ const SetContentWorkingGroupsLeadForm: React.FunctionComponent<FormInnerProps> =
         txMethod='createSetLeadProposal'
         proposalType='SetLead'
         submitParams={[
-          props.myMemberId,
-          values.title,
-          values.rationale,
-          '{STAKE}',
           values.workingGroupLead !== MEMBERS_NONE_OPTION.value ? values.workingGroupLead.split(':') : undefined
         ]}
       >
@@ -180,10 +175,10 @@ const FormContainer = withFormContainer<FormContainerProps, FormValues>({
     ...(props.initialData || {})
   }),
   validationSchema: Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All(),
     ...Validation.SetLead()
   }),
-  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  handleSubmit: () => null,
   displayName: 'SetContentWorkingGroupLeadForm'
 })(SetContentWorkingGroupsLeadForm);
 

+ 6 - 5
pioneer/packages/joy-proposals/src/forms/SetCouncilParamsForm.tsx

@@ -4,7 +4,6 @@ import { Divider, Form } from 'semantic-ui-react';
 import * as Yup from 'yup';
 import { GenericProposalForm,
   GenericFormValues,
-  genericFormDefaultOptions,
   genericFormDefaultValues,
   withProposalFormData,
   ProposalFormExportProps,
@@ -42,7 +41,7 @@ const defaultValues: FormValues = {
   councilSize: ''
 };
 
-type FormAdditionalProps = {}; // Aditional props coming all the way from export comonent into the inner form.
+type FormAdditionalProps = Record<any, never>; // Aditional props coming all the way from export comonent into the inner form.
 type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
 type FormContainerProps = ProposalFormContainerProps<ExportComponentProps>;
 type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;
@@ -90,6 +89,8 @@ const SetCouncilParamsForm: React.FunctionComponent<FormInnerProps> = (props) =>
       });
       setPlaceholders(fetchedPlaceholders);
     }
+    // We don't need dependency on "placeholders"
+    // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [councilParams]);
 
   // This logic may be moved somewhere else in the future, but it's quite easy to enforce it here:
@@ -103,7 +104,7 @@ const SetCouncilParamsForm: React.FunctionComponent<FormInnerProps> = (props) =>
         {...props}
         txMethod='createSetElectionParametersProposal'
         proposalType='SetElectionParameters'
-        submitParams={[props.myMemberId, values.title, values.rationale, '{STAKE}', createElectionParameters(values)]}
+        submitParams={[createElectionParameters(values)]}
       >
         <Divider horizontal>Voting </Divider>
         <Form.Group widths='equal' style={{ marginBottom: '8rem' }}>
@@ -202,10 +203,10 @@ const FormContainer = withFormContainer<FormContainerProps, FormValues>({
     ...(props.initialData || {})
   }),
   validationSchema: Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All(),
     ...Validation.SetElectionParameters()
   }),
-  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  handleSubmit: () => null,
   displayName: 'SetCouncilParamsForm'
 })(SetCouncilParamsForm);
 

+ 5 - 5
pioneer/packages/joy-proposals/src/forms/SetMaxValidatorCountForm.tsx

@@ -3,7 +3,6 @@ import { getFormErrorLabelsProps } from './errorHandling';
 import * as Yup from 'yup';
 import { GenericProposalForm,
   GenericFormValues,
-  genericFormDefaultOptions,
   genericFormDefaultValues,
   withProposalFormData,
   ProposalFormExportProps,
@@ -23,7 +22,7 @@ const defaultValues: FormValues = {
   maxValidatorCount: ''
 };
 
-type FormAdditionalProps = {}; // Aditional props coming all the way from export comonent into the inner form.
+type FormAdditionalProps = Record<any, never>; // Aditional props coming all the way from export comonent into the inner form.
 type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
 type FormContainerProps = ProposalFormContainerProps<ExportComponentProps>;
 type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;
@@ -38,6 +37,7 @@ const SetMaxValidatorCountForm: React.FunctionComponent<FormInnerProps> = (props
     if (validatorCount) {
       setFieldValue('maxValidatorCount', validatorCount);
     }
+    // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [validatorCount]);
 
   return (
@@ -45,7 +45,7 @@ const SetMaxValidatorCountForm: React.FunctionComponent<FormInnerProps> = (props
       {...props}
       txMethod='createSetValidatorCountProposal'
       proposalType='SetValidatorCount'
-      submitParams={[props.myMemberId, values.title, values.rationale, '{STAKE}', values.maxValidatorCount]}
+      submitParams={[values.maxValidatorCount]}
     >
       <InputFormField
         error={errorLabelsProps.maxValidatorCount}
@@ -66,10 +66,10 @@ const FormContainer = withFormContainer<FormContainerProps, FormValues>({
     ...(props.initialData || {})
   }),
   validationSchema: Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All(),
     ...Validation.SetValidatorCount()
   }),
-  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  handleSubmit: () => null,
   displayName: 'SetMaxValidatorCountForm'
 })(SetMaxValidatorCountForm);
 

+ 5 - 10
pioneer/packages/joy-proposals/src/forms/SetWorkingGroupLeadRewardForm.tsx

@@ -4,8 +4,7 @@ import * as Yup from 'yup';
 import { withProposalFormData,
   ProposalFormExportProps,
   ProposalFormContainerProps,
-  ProposalFormInnerProps,
-  genericFormDefaultOptions } from './GenericProposalForm';
+  ProposalFormInnerProps } from './GenericProposalForm';
 import { GenericWorkingGroupProposalForm,
   FormValues as WGFormValues,
   defaultValues as wgFromDefaultValues } from './GenericWorkingGroupProposalForm';
@@ -26,13 +25,13 @@ const defaultValues: FormValues = {
   amount: ''
 };
 
-type FormAdditionalProps = {}; // Aditional props coming all the way from export component into the inner form.
+type FormAdditionalProps = Record<any, never>; // Aditional props coming all the way from export component into the inner form.
 type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
 type FormContainerProps = ProposalFormContainerProps<ExportComponentProps>;
 type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;
 
 const SetWorkingGroupLeadRewardForm: React.FunctionComponent<FormInnerProps> = (props) => {
-  const { handleChange, errors, touched, values, myMemberId } = props;
+  const { handleChange, errors, touched, values } = props;
   const errorLabelsProps = getFormErrorLabelsProps<FormValues>(errors, touched);
   const [lead, setLead] = useState<WorkerData | null>(null);
 
@@ -45,10 +44,6 @@ const SetWorkingGroupLeadRewardForm: React.FunctionComponent<FormInnerProps> = (
       leadRewardRequired={true}
       onLeadChange={(lead: WorkerData | null) => setLead(lead)}
       submitParams={[
-        myMemberId,
-        values.title,
-        values.rationale,
-        '{STAKE}',
         lead?.workerId,
         values.amount,
         values.workingGroup
@@ -78,10 +73,10 @@ const FormContainer = withFormContainer<FormContainerProps, FormValues>({
     ...(props.initialData || {})
   }),
   validationSchema: Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All(),
     ...Validation.SetWorkingGroupLeaderReward()
   }),
-  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  handleSubmit: () => null,
   displayName: 'SetWorkingGroupLeadRewardForm'
 })(SetWorkingGroupLeadRewardForm);
 

+ 5 - 10
pioneer/packages/joy-proposals/src/forms/SetWorkingGroupMintCapacityForm.tsx

@@ -4,8 +4,7 @@ import * as Yup from 'yup';
 import { withProposalFormData,
   ProposalFormExportProps,
   ProposalFormContainerProps,
-  ProposalFormInnerProps,
-  genericFormDefaultOptions } from './GenericProposalForm';
+  ProposalFormInnerProps } from './GenericProposalForm';
 import { GenericWorkingGroupProposalForm,
   FormValues as WGFormValues,
   defaultValues as wgFromDefaultValues } from './GenericWorkingGroupProposalForm';
@@ -25,13 +24,13 @@ const defaultValues: FormValues = {
   capacity: ''
 };
 
-type FormAdditionalProps = {}; // Aditional props coming all the way from export component into the inner form.
+type FormAdditionalProps = Record<any, never>; // Aditional props coming all the way from export component into the inner form.
 type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
 type FormContainerProps = ProposalFormContainerProps<ExportComponentProps>;
 type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;
 
 const SetWorkingGroupMintCapacityForm: React.FunctionComponent<FormInnerProps> = (props) => {
-  const { handleChange, errors, touched, values, myMemberId } = props;
+  const { handleChange, errors, touched, values } = props;
   const errorLabelsProps = getFormErrorLabelsProps<FormValues>(errors, touched);
 
   return (
@@ -40,10 +39,6 @@ const SetWorkingGroupMintCapacityForm: React.FunctionComponent<FormInnerProps> =
       txMethod='createSetWorkingGroupMintCapacityProposal'
       proposalType='SetWorkingGroupMintCapacity'
       submitParams={[
-        myMemberId,
-        values.title,
-        values.rationale,
-        '{STAKE}',
         values.capacity,
         values.workingGroup
       ]}
@@ -71,10 +66,10 @@ const FormContainer = withFormContainer<FormContainerProps, FormValues>({
     ...(props.initialData || {})
   }),
   validationSchema: Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All(),
     ...Validation.SetWorkingGroupMintCapacity()
   }),
-  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  handleSubmit: () => null,
   displayName: 'SetWorkingGroupMintCapacityForm'
 })(SetWorkingGroupMintCapacityForm);
 

+ 4 - 5
pioneer/packages/joy-proposals/src/forms/SignalForm.tsx

@@ -3,7 +3,6 @@ import { getFormErrorLabelsProps } from './errorHandling';
 import * as Yup from 'yup';
 import { GenericProposalForm,
   GenericFormValues,
-  genericFormDefaultOptions,
   genericFormDefaultValues,
   withProposalFormData,
   ProposalFormExportProps,
@@ -22,7 +21,7 @@ const defaultValues: FormValues = {
   description: ''
 };
 
-type FormAdditionalProps = {}; // Aditional props coming all the way from export comonent into the inner form.
+type FormAdditionalProps = Record<any, never>; // Aditional props coming all the way from export comonent into the inner form.
 type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
 type FormContainerProps = ProposalFormContainerProps<ExportComponentProps>;
 type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;
@@ -36,7 +35,7 @@ const SignalForm: React.FunctionComponent<FormInnerProps> = (props) => {
       {...props}
       txMethod='createTextProposal'
       proposalType='Text'
-      submitParams={[props.myMemberId, values.title, values.rationale, '{STAKE}', values.description]}
+      submitParams={[values.description]}
     >
       <TextareaFormField
         label='Description'
@@ -57,10 +56,10 @@ const FormContainer = withFormContainer<FormContainerProps, FormValues>({
     ...(props.initialData || {})
   }),
   validationSchema: Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All(),
     ...Validation.Text()
   }),
-  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  handleSubmit: () => null,
   displayName: 'SignalForm'
 })(SignalForm);
 

+ 5 - 10
pioneer/packages/joy-proposals/src/forms/SlashWorkingGroupLeadStakeForm.tsx

@@ -4,8 +4,7 @@ import * as Yup from 'yup';
 import { withProposalFormData,
   ProposalFormExportProps,
   ProposalFormContainerProps,
-  ProposalFormInnerProps,
-  genericFormDefaultOptions } from './GenericProposalForm';
+  ProposalFormInnerProps } from './GenericProposalForm';
 import { GenericWorkingGroupProposalForm,
   FormValues as WGFormValues,
   defaultValues as wgFromDefaultValues } from './GenericWorkingGroupProposalForm';
@@ -26,13 +25,13 @@ const defaultValues: FormValues = {
   amount: ''
 };
 
-type FormAdditionalProps = {}; // Aditional props coming all the way from export component into the inner form.
+type FormAdditionalProps = Record<any, never>; // Aditional props coming all the way from export component into the inner form.
 type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
 type FormContainerProps = ProposalFormContainerProps<ExportComponentProps>;
 type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;
 
 const SlashWorkingGroupLeadStakeForm: React.FunctionComponent<FormInnerProps> = (props) => {
-  const { handleChange, errors, touched, values, myMemberId, setFieldError } = props;
+  const { handleChange, errors, touched, values, setFieldError } = props;
   const errorLabelsProps = getFormErrorLabelsProps<FormValues>(errors, touched);
   const [lead, setLead] = useState<WorkerData | null>(null);
 
@@ -54,10 +53,6 @@ const SlashWorkingGroupLeadStakeForm: React.FunctionComponent<FormInnerProps> =
       leadStakeRequired={true}
       onLeadChange={(lead: WorkerData | null) => setLead(lead)}
       submitParams={[
-        myMemberId,
-        values.title,
-        values.rationale,
-        '{STAKE}',
         lead?.workerId,
         values.amount,
         values.workingGroup
@@ -87,10 +82,10 @@ const FormContainer = withFormContainer<FormContainerProps, FormValues>({
     ...(props.initialData || {})
   }),
   validationSchema: Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All(),
     ...Validation.SlashWorkingGroupLeaderStake()
   }),
-  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  handleSubmit: () => null,
   displayName: 'SlashWorkingGroupLeadStakeForm'
 })(SlashWorkingGroupLeadStakeForm);
 

+ 4 - 9
pioneer/packages/joy-proposals/src/forms/SpendingProposalForm.tsx

@@ -4,7 +4,6 @@ import * as Yup from 'yup';
 import { Label } from 'semantic-ui-react';
 import { GenericProposalForm,
   GenericFormValues,
-  genericFormDefaultOptions,
   genericFormDefaultValues,
   withProposalFormData,
   ProposalFormExportProps,
@@ -17,7 +16,7 @@ import { InputAddress } from '@polkadot/react-components/index';
 import { formatBalance } from '@polkadot/util';
 
 export type FormValues = GenericFormValues & {
-  destinationAccount: any;
+  destinationAccount: string;
   tokens: string;
 };
 
@@ -27,7 +26,7 @@ const defaultValues: FormValues = {
   tokens: ''
 };
 
-type FormAdditionalProps = {}; // Aditional props coming all the way from export comonent into the inner form.
+type FormAdditionalProps = Record<any, never>; // Aditional props coming all the way from export comonent into the inner form.
 type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
 type FormContainerProps = ProposalFormContainerProps<ExportComponentProps>;
 type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;
@@ -42,10 +41,6 @@ const SpendingProposalForm: React.FunctionComponent<FormInnerProps> = (props) =>
       txMethod='createSpendingProposal'
       proposalType='Spending'
       submitParams={[
-        props.myMemberId,
-        values.title,
-        values.rationale,
-        '{STAKE}',
         values.tokens,
         values.destinationAccount
       ]}
@@ -83,10 +78,10 @@ const FormContainer = withFormContainer<FormContainerProps, FormValues>({
     ...(props.initialData || {})
   }),
   validationSchema: Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All(),
     ...Validation.Spending()
   }),
-  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  handleSubmit: () => null,
   displayName: 'SpendingProposalsForm'
 })(SpendingProposalForm);
 

+ 6 - 13
pioneer/packages/joy-proposals/src/forms/TerminateWorkingGroupLeaderForm.tsx

@@ -3,8 +3,7 @@ import * as Yup from 'yup';
 import { withProposalFormData,
   ProposalFormExportProps,
   ProposalFormContainerProps,
-  ProposalFormInnerProps,
-  genericFormDefaultOptions } from './GenericProposalForm';
+  ProposalFormInnerProps } from './GenericProposalForm';
 import { GenericWorkingGroupProposalForm,
   FormValues as WGFormValues,
   defaultValues as wgFromDefaultValues } from './GenericWorkingGroupProposalForm';
@@ -32,7 +31,7 @@ const defaultValues: FormValues = {
   slashStake: false
 };
 
-type FormAdditionalProps = {}; // Aditional props coming all the way from export component into the inner form.
+type FormAdditionalProps = Record<any, never>; // Aditional props coming all the way from export component into the inner form.
 type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
 type FormContainerProps = ProposalFormContainerProps<ExportComponentProps> & {
   terminationRationaleConstraint?: InputValidationLengthConstraint;
@@ -49,7 +48,7 @@ const valuesToTerminateRoleParams = (values: FormValues, lead: WorkerData): Simp
 };
 
 const TerminateWorkingGroupLeaderForm: React.FunctionComponent<FormInnerProps> = (props) => {
-  const { handleChange, errors, touched, values, myMemberId, setFieldValue } = props;
+  const { handleChange, errors, touched, values, setFieldValue } = props;
   const errorLabelsProps = getFormErrorLabelsProps<FormValues>(errors, touched);
   const [lead, setLead] = useState<WorkerData | null>(null);
 
@@ -60,13 +59,7 @@ const TerminateWorkingGroupLeaderForm: React.FunctionComponent<FormInnerProps> =
       proposalType='TerminateWorkingGroupLeaderRole'
       leadRequired={true}
       onLeadChange={(lead: WorkerData | null) => setLead(lead)}
-      submitParams={[
-        myMemberId,
-        values.title,
-        values.rationale,
-        '{STAKE}',
-        lead && valuesToTerminateRoleParams(values, lead)
-      ]}
+      submitParams={[lead && valuesToTerminateRoleParams(values, lead)]}
     >
       { lead && (<>
         <TextareaFormField
@@ -102,12 +95,12 @@ const FormContainer = withFormContainer<FormContainerProps, FormValues>({
     ...(props.initialData || {})
   }),
   validationSchema: (props: FormContainerProps) => Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All(),
     ...Validation.TerminateWorkingGroupLeaderRole(
       props.terminationRationaleConstraint
     )
   }),
-  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  handleSubmit: () => null,
   displayName: 'TerminateWorkingGroupLeaderForm'
 })(TerminateWorkingGroupLeaderForm);
 

+ 2 - 2
pioneer/packages/joy-proposals/src/index.tsx

@@ -1,5 +1,5 @@
 import React from 'react';
-import { Route, Switch } from 'react-router';
+import { Route, Switch, RouteComponentProps } from 'react-router';
 import { Link } from 'react-router-dom';
 import styled from 'styled-components';
 import { Breadcrumb } from 'semantic-ui-react';
@@ -49,7 +49,7 @@ function App (props: Props): React.ReactElement<Props> {
       <StyledHeader>
         <Breadcrumb>
           <Switch>
-            <Route path={`${basePath}/new/:type`} render={(props) => (
+            <Route path={`${basePath}/new/:type`} render={(props: RouteComponentProps<{ type?: string }>) => (
               <>
                 <Breadcrumb.Section link as={Link} to={basePath}>Proposals</Breadcrumb.Section>
                 <Breadcrumb.Divider icon='right angle' />

+ 6 - 6
pioneer/packages/joy-proposals/src/stories/data/ProposalDetails.mock.ts

@@ -8,7 +8,7 @@ const mockedProposal: ParsedProposal = {
   createdAtBlock: 36,
   type: 'Text',
   details: createMock('ProposalDetails', { Text: 'Ciao' }),
-  parameters: {
+  parameters: createMock('ProposalParameters', {
     approvalQuorumPercentage: 66,
     approvalThresholdPercentage: 80,
     gracePeriod: 0,
@@ -16,14 +16,14 @@ const mockedProposal: ParsedProposal = {
     slashingQuorumPercentage: 60,
     slashingThresholdPercentage: 80,
     votingPeriod: 7200
-  },
+  }),
   proposerId: 303,
-  status: {
+  status: createMock('ProposalStatus', {
     Active: {
       stakeId: 0,
       sourceAccountId: '5C4hrfkRjSLwQSFVtCvtbV6wctV1WFnkiexUZWLAh4Bc7jib'
     }
-  },
+  }),
   proposer: {
     about: 'Bob',
     avatar_uri: 'https://react.semantic-ui.com/images/avatar/large/steve.jpg',
@@ -38,12 +38,12 @@ const mockedProposal: ParsedProposal = {
     subscription: null,
     suspended: false
   },
-  votingResults: {
+  votingResults: createMock('VotingResults', {
     abstensions: 3,
     approvals: 0,
     rejections: 1,
     slashes: 0
-  },
+  }),
   createdAt: new Date('Mar 25, 2020 at 14:20'),
   cancellationFee: 5
 };

+ 1 - 1
pioneer/packages/joy-proposals/src/stories/withMock.tsx

@@ -4,7 +4,7 @@ import { match, RouteComponentProps } from 'react-router';
 
 const history = createMemoryHistory();
 const path = '/';
-const matchObj: match<{}> = {
+const matchObj: match<Record<string, string | undefined>> = {
   isExact: false,
   path,
   url: path,

+ 10 - 10
pioneer/packages/joy-proposals/src/validationSchema.ts

@@ -105,7 +105,7 @@ const DECREASE_LEAD_STAKE_MIN = 1;
 const SLASH_LEAD_STAKE_MIN = 1;
 // Max is validated in form component, because it depends on selected working group's leader stake
 
-function errorMessage (name: string, min?: number | string, max?: number | string, unit?: string): string {
+function errorMessage (name: string, min: number | string, max: number | string, unit?: string): string {
   return `${name} should be at least ${min} and no more than ${max}${unit ? ` ${unit}.` : '.'}`;
 }
 
@@ -119,7 +119,7 @@ Ex:
 import Validation from 'path/to/validationSchema'
 ...
   validationSchema: Yup.object().shape({
-    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.All()
     ...Validation.Text()
   }),
 
@@ -159,7 +159,7 @@ type ValidationSchemaFuncParamsByType<T extends ValidationTypeKeys> =
 
 /* eslint-enable @typescript-eslint/indent */
 
-type ValidationSchemaFunc<FieldValuesT extends {}, ParamsT extends any[] = []> = (...params: ParamsT) =>
+type ValidationSchemaFunc<FieldValuesT extends Record<string, any>, ParamsT extends any[] = []> = (...params: ParamsT) =>
 ({ [fieldK in keyof FieldValuesT]: Yup.Schema<any> });
 
 type ValidationType = {
@@ -212,9 +212,9 @@ const Validation: ValidationType = {
   }),
   RuntimeUpgrade: () => ({
     WASM: Yup.mixed()
-      .test('fileArrayBuffer', 'Unexpected data format, file cannot be processed.', (value) => typeof value.byteLength !== 'undefined')
-      .test('fileSizeMin', `Minimum file size is ${FILE_SIZE_BYTES_MIN} bytes.`, (value) => value.byteLength >= FILE_SIZE_BYTES_MIN)
-      .test('fileSizeMax', `Maximum file size is ${FILE_SIZE_BYTES_MAX} bytes.`, (value) => value.byteLength <= FILE_SIZE_BYTES_MAX)
+      .test('fileArrayBuffer', 'Unexpected data format, file cannot be processed.', (value) => value instanceof ArrayBuffer)
+      .test('fileSizeMin', `Minimum file size is ${FILE_SIZE_BYTES_MIN} bytes.`, (value: ArrayBuffer) => value.byteLength >= FILE_SIZE_BYTES_MIN)
+      .test('fileSizeMax', `Maximum file size is ${FILE_SIZE_BYTES_MAX} bytes.`, (value: ArrayBuffer) => value.byteLength <= FILE_SIZE_BYTES_MAX)
   }),
   SetElectionParameters: () => ({
     announcingPeriod: Yup.number()
@@ -368,11 +368,11 @@ const Validation: ValidationType = {
       .test(
         'schemaIsValid',
         'Schema validation failed!',
-        function (val) {
-          let schemaObj: any;
+        function (val: string) {
+          let schemaObj: Record<string, unknown>;
 
           try {
-            schemaObj = JSON.parse(val);
+            schemaObj = JSON.parse(val) as Record<string, unknown>;
           } catch (e) {
             return this.createError({ message: 'Schema validation failed: Invalid JSON' });
           }
@@ -382,7 +382,7 @@ const Validation: ValidationType = {
 
           if (!isValid) {
             return this.createError({
-              message: 'Schema validation failed: ' + errors.map((e) => `${e.message}${e.dataPath && ` (${e.dataPath})`}`).join(', ')
+              message: 'Schema validation failed: ' + errors.map((e) => `${e.message || ''}${e.dataPath && ` (${e.dataPath})`}`).join(', ')
             });
           }
 

+ 6 - 3
pioneer/packages/joy-utils/src/react/hooks/proposals/useProposalSubscription.tsx

@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
 import { useTransport } from '../';
 import { ProposalId } from '@joystream/types/proposals';
 import { ParsedProposal } from '@polkadot/joy-utils/types/proposals';
+import { normalizeError } from '@polkadot/joy-utils/functions/misc';
 
 // Take advantage of polkadot api subscriptions to re-fetch proposal data and votes
 // each time there is some runtime change in the proposal
@@ -9,7 +10,7 @@ const useProposalSubscription = (id: ProposalId) => {
   const transport = useTransport();
   // State holding current proposal data
   const [data, setData] = useState<ParsedProposal | null>(null);
-  const [error, setError] = useState<any>(null);
+  const [error, setError] = useState<string | null>(null);
   const [loading, setLoading] = useState<boolean>(true);
 
   useEffect(() => {
@@ -27,7 +28,7 @@ const useProposalSubscription = (id: ProposalId) => {
         })
         .catch((error) => {
           if (!unmounted) {
-            setError(error);
+            setError(normalizeError(error));
             setLoading(false);
           }
         });
@@ -41,7 +42,8 @@ const useProposalSubscription = (id: ProposalId) => {
         } else {
           unsubscribe(); // If already unmounted - unsubscribe immedietally!
         }
-      });
+      })
+      .catch((e) => { throw e; });
 
     return () => {
       // onUnmount...
@@ -52,6 +54,7 @@ const useProposalSubscription = (id: ProposalId) => {
         unsubscribeProposal();
       }
     };
+    // eslint-disable-next-line react-hooks/exhaustive-deps
   }, []);
 
   return { data, error, loading };

+ 0 - 1
pioneer/packages/joy-utils/src/transport/chain.ts

@@ -1,5 +1,4 @@
 import BaseTransport from './base';
-import { Moment } from '@polkadot/types/interfaces';
 
 export default class ChainTransport extends BaseTransport {
   async blockHash (height: number): Promise<string> {

+ 2 - 1
pioneer/packages/joy-utils/src/transport/council.ts

@@ -22,8 +22,9 @@ export default class CouncilTransport extends BaseTransport {
   async councilMembersLength (atBlock?: number): Promise<number> {
     if (atBlock) {
       const blockHash = await this.chainT.blockHash(atBlock);
+      const seats = await this.api.query.council.activeCouncil.at<Seats>(blockHash);
 
-      return ((await this.api.query.council.activeCouncil.at(blockHash))).length;
+      return seats.length;
     }
 
     return ((await this.council.activeCouncil()) as Seats).length;

+ 7 - 10
pioneer/packages/joy-utils/src/transport/proposals.ts

@@ -108,25 +108,22 @@ export default class ProposalsTransport extends BaseTransport {
     }
 
     const proposer = (await this.membersT.expectedMembership(rawProposal.proposerId)).toJSON() as ParsedMember;
-    const proposal = rawProposal.toJSON() as {
-      title: string;
-      description: string;
-      parameters: any;
-      votingResults: any;
-      proposerId: number;
-      status: any;
-    };
     const createdAtBlock = rawProposal.createdAt;
     const createdAt = await this.chainT.blockTimestamp(createdAtBlock.toNumber());
     const cancellationFee = this.cancellationFee();
 
     return {
       id,
-      ...proposal,
+      title: rawProposal.title.toString(),
+      description: rawProposal.description.toString(),
+      parameters: rawProposal.parameters,
+      votingResults: rawProposal.votingResults,
+      proposerId: rawProposal.proposerId.toNumber(),
+      status: rawProposal.status,
       details,
       type,
       proposer,
-      createdAtBlock: createdAtBlock.toJSON(),
+      createdAtBlock: createdAtBlock.toNumber(),
       createdAt,
       cancellationFee
     };

+ 0 - 1
pioneer/packages/joy-utils/src/transport/validators.ts

@@ -1,5 +1,4 @@
 import BaseTransport from './base';
-import { u32 } from '@polkadot/types/';
 
 export default class ValidatorsTransport extends BaseTransport {
   async maxCount (): Promise<number> {

+ 1 - 1
pioneer/packages/joy-utils/src/transport/workingGroups.ts

@@ -94,7 +94,7 @@ export default class WorkingGroupsTransport extends BaseTransport {
     const wgApplication = (await this.queryByGroup(group).applicationById(wgApplicationId)) as WGApplication;
 
     if (wgApplication.isEmpty) {
-      throw new Error(`Working group application not found (ID: ${wgApplicationId})!`);
+      throw new Error(`Working group application not found (ID: ${wgApplicationId.toString()})!`);
     }
 
     return wgApplication;

+ 4 - 12
pioneer/packages/joy-utils/src/types/proposals.ts

@@ -2,7 +2,7 @@ import { ProposalId, VoteKind } from '@joystream/types/proposals';
 import { MemberId, Membership } from '@joystream/types/members';
 import { ThreadId, PostId } from '@joystream/types/common';
 import { ParsedMember } from './members';
-import { ProposalDetails } from '@joystream/types/src/proposals';
+import { ProposalDetails, ProposalStatus, VotingResults, ProposalParameters } from '@joystream/types/src/proposals';
 
 export const ProposalTypes = [
   'Text',
@@ -46,22 +46,14 @@ export type ParsedProposal = {
   type: ProposalType;
   title: string;
   description: string;
-  status: any;
+  status: ProposalStatus;
   proposer: ParsedMember;
   proposerId: number;
   createdAtBlock: number;
   createdAt: Date;
   details: ParsedProposalDetails;
-  votingResults: any;
-  parameters: {
-    approvalQuorumPercentage: number;
-    approvalThresholdPercentage: number;
-    gracePeriod: number;
-    requiredStake: number;
-    slashingQuorumPercentage: number;
-    slashingThresholdPercentage: number;
-    votingPeriod: number;
-  };
+  votingResults: VotingResults;
+  parameters: ProposalParameters;
   cancellationFee: number;
 };