Browse Source

Merge pull request #698 from Joystream/master

Master to Nicaea
Mokhtar Naamani 4 years ago
parent
commit
f8743b9a84

+ 13 - 21
pioneer/packages/apps-routing/src/index.ts

@@ -39,46 +39,38 @@ import transfer from './transfer';
 
 let routes: Routes = ([] as Routes);
 
-if (appSettings.isFullMode) {
-  routes = routes.concat(explorer);
-}
-
 // Basic routes
 routes = routes.concat(
-  staking,
-  roles,
-  storageRoles,
-  transfer,
-  null,
   media,
+  roles,
+  proposals,
+  election,
   forum,
+  storageRoles,
   members,
+  staking,
+  null,
+  transfer,
   accounts,
   addressbook,
-  null,
-  election,
-  proposals,
-  null
+  settings,
+  pages
 );
 
 if (appSettings.isFullMode) {
   routes = routes.concat(
+    null,
+    explorer,
     storage,
     extrinsics,
     sudo,
     js,
-    toolbox,
-    null
+    toolbox
   );
 }
 
-routes = routes.concat(
-  settings,
-  pages
-);
-
 const setup: Routing = {
-  default: 'staking',
+  default: 'media',
   routes
 };
 

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

@@ -119,6 +119,9 @@ const paramParsers: { [x in ProposalType]: (params: any[]) => { [key: string]: s
   })
 };
 
+const StyledProposalDescription = styled(Card.Description)`
+  font-size: 1.15rem;
+`;
 const ProposalParams = styled.div`
   display: grid;
   font-weight: bold;
@@ -127,7 +130,7 @@ const ProposalParams = styled.div`
   border: 1px solid rgba(0,0,0,.2);
   padding: 1.5rem 1.5rem 1rem 1.25rem;
   position: relative;
-  margin-top: 1.5rem;
+  margin-top: 1.7rem;
   @media screen and (max-width: 767px) {
     grid-template-columns: 1fr;
   }
@@ -149,6 +152,7 @@ const ProposalParamValue = styled.div`
   color: black;
   word-wrap: break-word;
   word-break: break-word;
+  font-size: 1.15rem;
   & .TextProposalContent {
     font-weight: normal;
   }
@@ -176,9 +180,9 @@ export default function Body ({
         <Card.Header>
           <Header as="h1">{title}</Header>
         </Card.Header>
-        <Card.Description>
+        <StyledProposalDescription>
           <ReactMarkdown source={description} linkTarget='_blank' />
-        </Card.Description>
+        </StyledProposalDescription>
         <ProposalParams>
           <ParamsHeader>Parameters:</ParamsHeader>
           { Object.entries(parsedParams).map(([paramName, paramValue]) => (

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

@@ -5,7 +5,7 @@ import Body from './Body';
 import VotingSection from './VotingSection';
 import Votes from './Votes';
 import { MyAccountProps, withMyAccount } from '@polkadot/joy-utils/MyAccount';
-import { ParsedProposal, ProposalVote } from '@polkadot/joy-utils/types/proposals';
+import { ParsedProposal, ProposalVotes } from '@polkadot/joy-utils/types/proposals';
 import { withCalls } from '@polkadot/react-api';
 import { withMulti } from '@polkadot/react-api/with';
 
@@ -115,7 +115,7 @@ export function getExtendedStatus (proposal: ParsedProposal, bestNumber: BlockNu
 type ProposalDetailsProps = MyAccountProps & {
   proposal: ParsedProposal;
   proposalId: ProposalId;
-  votesListState: { data: ProposalVote[]; error: any; loading: boolean };
+  votesListState: { data: ProposalVotes | null; error: any; loading: boolean };
   bestNumber?: BlockNumber;
   council?: Seat[];
 };
@@ -160,7 +160,7 @@ function ProposalDetails ({
             error={votesListState.error}
             loading={votesListState.loading}
             message="Fetching the votes...">
-            <Votes votes={votesListState.data} />
+            <Votes votes={votesListState.data as ProposalVotes} />
           </PromiseComponent>
         </ProposalDetailsVoting>
       </ProposalDetailsMain>

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

@@ -59,7 +59,7 @@ function ProposalPreviewList ({ bestNumber }: ProposalPreviewListProps) {
   const filteredProposals = proposalsMap.get(activeFilter) as ParsedProposal[];
 
   return (
-    <Container className="Proposal">
+    <Container className="Proposal" fluid>
       <PromiseComponent error={ error } loading={ loading } message="Fetching proposals...">
         <Menu tabular className="list-menu">
           {filters.map((filter, idx) => (

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

@@ -1,31 +1,29 @@
 import React from 'react';
 import { Header, Divider, Table, Icon } from 'semantic-ui-react';
 import useVoteStyles from './useVoteStyles';
-import { ProposalVote } from '@polkadot/joy-utils/types/proposals';
+import { ProposalVotes } from '@polkadot/joy-utils/types/proposals';
 import { VoteKind } from '@joystream/types/proposals';
 import { VoteKindStr } from './VotingSection';
 import ProfilePreview from '@polkadot/joy-utils/MemberProfilePreview';
 
 type VotesProps = {
-  votes: ProposalVote[];
+  votes: ProposalVotes;
 };
 
 export default function Votes ({ votes }: VotesProps) {
-  const nonEmptyVotes = votes.filter(proposalVote => proposalVote.vote !== null);
-
-  if (!nonEmptyVotes.length) {
+  if (!votes.votes.length) {
     return <Header as="h4">No votes has been submitted!</Header>;
   }
 
   return (
     <>
       <Header as="h3">
-        All Votes: ({nonEmptyVotes.length} / {votes.length})
+        All Votes: ({votes.votes.length}/{votes.councilMembersLength})
       </Header>
       <Divider />
       <Table basic="very">
         <Table.Body>
-          {nonEmptyVotes.map((proposalVote, idx) => {
+          {votes.votes.map((proposalVote, idx) => {
             const { vote, member } = proposalVote;
             const voteStr = (vote as VoteKind).type.toString() as VoteKindStr;
             const { icon, textColor } = useVoteStyles(voteStr);

+ 0 - 2
pioneer/packages/joy-proposals/src/validationSchema.ts

@@ -1,5 +1,4 @@
 import * as Yup from 'yup';
-import { checkAddress } from '@polkadot/util-crypto';
 
 // TODO: If we really need this (currency unit) we can we make "Validation" a functiction that returns an object.
 // We could then "instantialize" it in "withFormContainer" where instead of passing
@@ -242,7 +241,6 @@ const Validation: ValidationType = {
       .required('You need to specify an amount of tokens.'),
     destinationAccount: Yup.string()
       .required('Select a destination account!')
-      .test('address-test', 'Invalid account address.', account => checkAddress(account, 5)[0])
   },
   SetLead: {
     workingGroupLead: Yup.string().required('Select a proposed lead!')

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

@@ -1,5 +1,5 @@
 import { useState, useEffect } from 'react';
-import { ParsedProposal, ProposalVote } from '../../../types/proposals';
+import { ParsedProposal, ProposalVotes } from '../../../types/proposals';
 import { useTransport, usePromise } from '../';
 import { ProposalId } from '@joystream/types/proposals';
 
@@ -15,9 +15,9 @@ const useProposalSubscription = (id: ProposalId) => {
     {} as ParsedProposal
   );
 
-  const [votes, votesError, votesLoading, refreshVotes] = usePromise<ProposalVote[]>(
+  const [votes, votesError, votesLoading, refreshVotes] = usePromise<ProposalVotes | null>(
     () => transport.proposals.votes(id),
-    []
+    null
   );
 
   // Function to re-fetch the data using transport

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

@@ -7,13 +7,25 @@ import { Balance, BlockNumber } from '@polkadot/types/interfaces';
 import { FIRST_MEMBER_ID } from '../consts/members';
 import { ApiPromise } from '@polkadot/api';
 import MembersTransport from './members';
+import ChainTransport from './chain';
 
 export default class CouncilTransport extends BaseTransport {
   private membersT: MembersTransport;
+  private chainT: ChainTransport;
 
-  constructor (api: ApiPromise, membersTransport: MembersTransport) {
+  constructor (api: ApiPromise, membersTransport: MembersTransport, chainTransport: ChainTransport) {
     super(api);
     this.membersT = membersTransport;
+    this.chainT = chainTransport;
+  }
+
+  async councilMembersLength (atBlock?: number): Promise<number> {
+    if (atBlock) {
+      const blockHash = await this.chainT.blockHash(atBlock);
+      return ((await this.council.activeCouncil.at(blockHash)) as Seats).length;
+    }
+
+    return ((await this.council.activeCouncil()) as Seats).length;
   }
 
   async councilMembers (): Promise<(ParsedMember & { memberId: MemberId })[]> {

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

@@ -24,7 +24,7 @@ export default class Transport {
     this.members = new MembersTransport(api);
     this.storageProviders = new StorageProvidersTransport(api);
     this.validators = new ValidatorsTransport(api);
-    this.council = new CouncilTransport(api, this.members);
+    this.council = new CouncilTransport(api, this.members, this.chain);
     this.contentWorkingGroup = new ContentWorkingGroupTransport(api, this.members);
     this.proposals = new ProposalsTransport(api, this.members, this.chain, this.council);
   }

+ 4 - 0
pioneer/packages/joy-utils/src/transport/members.ts

@@ -6,4 +6,8 @@ export default class MembersTransport extends BaseTransport {
   memberProfile (id: MemberId | number): Promise<Option<Profile>> {
     return this.members.memberProfile(id) as Promise<Option<Profile>>;
   }
+
+  async membersCreated (): Promise<number> {
+    return (await this.members.membersCreated() as MemberId).toNumber();
+  }
 }

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

@@ -3,6 +3,7 @@ import {
   ProposalType,
   ProposalTypes,
   ProposalVote,
+  ProposalVotes,
   ParsedPost,
   ParsedDiscussion,
   DiscussionContraints
@@ -20,6 +21,7 @@ import { BalanceOf } from '@polkadot/types/interfaces';
 import { includeKeys, bytesToString } from '../functions/misc';
 import _ from 'lodash';
 import proposalsConsts from '../consts/proposals';
+import { FIRST_MEMBER_ID } from '../consts/members';
 
 import { ApiPromise } from '@polkadot/api';
 import MembersTransport from './members';
@@ -120,17 +122,35 @@ export default class ProposalsTransport extends BaseTransport {
     return hasVoted ? vote : null;
   }
 
-  async votes (proposalId: ProposalId): Promise<ProposalVote[]> {
-    const councilMembers = await this.councilT.councilMembers();
-    return Promise.all(
-      councilMembers.map(async member => {
-        const vote = await this.voteByProposalAndMember(proposalId, member.memberId);
-        return {
-          vote,
-          member
-        };
-      })
+  async votes (proposalId: ProposalId): Promise<ProposalVotes> {
+    const voteEntries = await this.doubleMapEntries(
+      'proposalsEngine.voteExistsByProposalByVoter', // Double map of intrest
+      proposalId, // First double-map key value
+      (v) => new VoteKind(v), // Converter from hex
+      async () => (await this.membersT.membersCreated()), // A function that returns the number of iterations to go through when chekcing possible values for the second double-map key (memberId)
+      FIRST_MEMBER_ID.toNumber() // Min. possible value for second double-map key (memberId)
     );
+
+    const votesWithMembers: ProposalVote[] = [];
+    for (const voteEntry of voteEntries) {
+      const memberId = voteEntry.secondKey;
+      const vote = voteEntry.value;
+      const parsedMember = (await this.membersT.memberProfile(memberId)).toJSON() as ParsedMember;
+      votesWithMembers.push({
+        vote,
+        member: {
+          memberId: new MemberId(memberId),
+          ...parsedMember
+        }
+      });
+    }
+
+    const proposal = await this.rawProposalById(proposalId);
+
+    return {
+      councilMembersLength: await this.councilT.councilMembersLength(proposal.createdAt.toNumber()),
+      votes: votesWithMembers
+    };
   }
 
   async fetchProposalMethodsFromCodex (includeKey: string) {

+ 5 - 0
pioneer/packages/joy-utils/src/types/proposals.ts

@@ -46,6 +46,11 @@ export type ProposalVote = {
   member: ParsedMember & { memberId: MemberId };
 };
 
+export type ProposalVotes = {
+  councilMembersLength: number;
+  votes: ProposalVote[];
+};
+
 export const Categories = {
   storage: 'Storage',
   council: 'Council',