Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions pioneer/packages/joy-proposals/src/Proposal/ProposalDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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, ProposalVotes } from '@polkadot/joy-utils/types/proposals';
import { ParsedProposal } from '@polkadot/joy-utils/types/proposals';
import { withCalls } from '@polkadot/react-api';
import { withMulti } from '@polkadot/react-api/with';

Expand All @@ -14,7 +14,6 @@ import { ProposalId, ProposalDecisionStatuses, ApprovedProposalStatuses, Executi
import { BlockNumber } from '@polkadot/types/interfaces';
import { MemberId } from '@joystream/types/members';
import { Seat } from '@joystream/types/council';
import { PromiseComponent } from '@polkadot/joy-utils/react/components';
import ProposalDiscussion from './discussion/ProposalDiscussion';

import styled from 'styled-components';
Expand Down Expand Up @@ -115,7 +114,6 @@ export function getExtendedStatus (proposal: ParsedProposal, bestNumber: BlockNu
type ProposalDetailsProps = MyAccountProps & {
proposal: ParsedProposal;
proposalId: ProposalId;
votesListState: { data: ProposalVotes | null; error: any; loading: boolean };
bestNumber?: BlockNumber;
council?: Seat[];
};
Expand All @@ -127,8 +125,7 @@ function ProposalDetails ({
myMemberId,
iAmMember,
council,
bestNumber,
votesListState
bestNumber
}: ProposalDetailsProps) {
const iAmCouncilMember = Boolean(iAmMember && council && council.some(seat => seat.member.toString() === myAddress));
const iAmProposer = Boolean(iAmMember && myMemberId !== undefined && proposal.proposerId === myMemberId.toNumber());
Expand Down Expand Up @@ -156,12 +153,7 @@ function ProposalDetails ({
memberId={ myMemberId as MemberId }
isVotingPeriod={ isVotingPeriod }/>
) }
<PromiseComponent
error={votesListState.error}
loading={votesListState.loading}
message="Fetching the votes...">
<Votes votes={votesListState.data as ProposalVotes} />
</PromiseComponent>
<Votes proposal={proposal}/>
</ProposalDetailsVoting>
</ProposalDetailsMain>
<ProposalDetailsDiscussion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ export default function ProposalFromId (props: RouteComponentProps<any>) {
}
} = props;

const { proposal: proposalState, votes: votesState } = useProposalSubscription(new ProposalId(id));
const proposalState = useProposalSubscription(new ProposalId(id));

return (
<PromiseComponent
error={proposalState.error}
loading={proposalState.loading}
message={'Fetching proposal...'}>
<ProposalDetails proposal={ proposalState.data } proposalId={ id } votesListState={ votesState }/>
<ProposalDetails proposal={ proposalState.data } proposalId={ id }/>
</PromiseComponent>
);
}
89 changes: 53 additions & 36 deletions pioneer/packages/joy-proposals/src/Proposal/Votes.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,67 @@
import React from 'react';
import { Header, Divider, Table, Icon } from 'semantic-ui-react';
import useVoteStyles from './useVoteStyles';
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';
import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks';
import { ParsedProposal, ProposalVotes } from '@polkadot/joy-utils/types/proposals';
import { PromiseComponent } from '@polkadot/joy-utils/react/components';

type VotesProps = {
votes: ProposalVotes;
proposal: ParsedProposal;
};

export default function Votes ({ votes }: VotesProps) {
if (!votes.votes.length) {
return <Header as="h4">No votes have been submitted!</Header>;
}
export default function Votes ({ proposal: { id, votingResults } }: VotesProps) {
const transport = useTransport();
const [votes, error, loading] = usePromise<ProposalVotes | null>(
() => transport.proposals.votes(id),
null,
[votingResults]
);

return (
<>
<Header as="h3">
All Votes: ({votes.votes.length}/{votes.councilMembersLength})
</Header>
<Divider />
<Table basic="very">
<Table.Body>
{votes.votes.map((proposalVote, idx) => {
const { vote, member } = proposalVote;
const voteStr = (vote as VoteKind).type.toString() as VoteKindStr;
const { icon, textColor } = useVoteStyles(voteStr);
return (
<Table.Row key={`${member.handle}-${idx}`}>
<Table.Cell className={textColor}>
<Icon name={icon} />
{voteStr}
</Table.Cell>
<Table.Cell>
<ProfilePreview
handle={member.handle}
avatar_uri={member.avatar_uri}
root_account={member.root_account}
/>
</Table.Cell>
</Table.Row>
);
})}
</Table.Body>
</Table>
</>
<PromiseComponent
error={error}
loading={loading}
message="Fetching the votes...">
{ (votes && votes.votes.length > 0)
? (
<>
<Header as="h3">
All Votes: ({votes.votes.length}/{votes.councilMembersLength})
</Header>
<Divider />
<Table basic="very">
<Table.Body>
{votes.votes.map((proposalVote, idx) => {
const { vote, member } = proposalVote;
const voteStr = (vote as VoteKind).type.toString() as VoteKindStr;
const { icon, textColor } = useVoteStyles(voteStr);
return (
<Table.Row key={`${member.handle}-${idx}`}>
<Table.Cell className={textColor}>
<Icon name={icon} />
{voteStr}
</Table.Cell>
<Table.Cell>
<ProfilePreview
handle={member.handle}
avatar_uri={member.avatar_uri}
root_account={member.root_account}
/>
</Table.Cell>
</Table.Row>
);
})}
</Table.Body>
</Table>
</>
)
: (
<Header as="h4">No votes have been submitted!</Header>
)
}
</PromiseComponent>
);
}
Original file line number Diff line number Diff line change
@@ -1,39 +1,41 @@
import { useState, useEffect } from 'react';
import { ParsedProposal, ProposalVotes } from '../../../types/proposals';
import { useTransport, usePromise } from '../';
import { useTransport } from '../';
import { ProposalId } from '@joystream/types/proposals';
import { ParsedProposal } from '@polkadot/joy-utils/types/proposals';

// Take advantage of polkadot api subscriptions to re-fetch proposal data and votes
// each time there is some runtime change in the proposal
const useProposalSubscription = (id: ProposalId) => {
const transport = useTransport();
// State holding an "unsubscribe method"
const [unsubscribeProposal, setUnsubscribeProposal] = useState<(() => void) | null>(null);

const [proposal, proposalError, proposalLoading, refreshProposal] = usePromise<ParsedProposal>(
() => transport.proposals.proposalById(id),
{} as ParsedProposal
);

const [votes, votesError, votesLoading, refreshVotes] = usePromise<ProposalVotes | null>(
() => transport.proposals.votes(id),
null
);

// Function to re-fetch the data using transport
const refreshProposalData = () => {
refreshProposal();
refreshVotes();
};
// State holding current proposal data
const [data, setData] = useState<ParsedProposal | null>(null);
const [error, setError] = useState<any>(null);
const [loading, setLoading] = useState<boolean>(true);

useEffect(() => {
// onMount...
let unmounted = false;
let unsubscribeProposal: (() => void) | undefined;
const refreshProposalData = () => {
transport.proposals.proposalById(id)
.then(newData => {
if (!unmounted) {
setData(newData);
setLoading(false);
}
})
.catch(error => {
if (!unmounted) {
setError(error);
setLoading(false);
}
});
};
// Create the subscription
transport.proposals.subscribeProposal(id, refreshProposalData)
.then(unsubscribe => {
if (!unmounted) {
setUnsubscribeProposal(() => unsubscribe);
unsubscribeProposal = unsubscribe;
} else {
unsubscribe(); // If already unmounted - unsubscribe immedietally!
}
Expand All @@ -42,14 +44,13 @@ const useProposalSubscription = (id: ProposalId) => {
// onUnmount...
// Clean the subscription
unmounted = true;
if (unsubscribeProposal !== null) unsubscribeProposal();
if (unsubscribeProposal) {
unsubscribeProposal();
}
};
}, []);

return {
proposal: { data: proposal, error: proposalError, loading: proposalLoading },
votes: { data: votes, error: votesError, loading: votesLoading }
};
return { data, error, loading };
};

export default useProposalSubscription;