Skip to content
Closed
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"pioneer/packages/joy-utils",
"pioneer/packages/joy-members",
"pioneer/packages/joy-pages",
"pioneer/packages/joy-election",
"utils/api-examples"
],
"resolutions": {
Expand Down
1 change: 0 additions & 1 deletion pioneer/.eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
**/coverage/*
**/node_modules/*
packages/old-apps/*
packages/joy-election/*
packages/joy-forum/*
packages/joy-help/*
packages/joy-media/*
Expand Down
3 changes: 3 additions & 0 deletions pioneer/packages/apps-routing/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import transfer from './transfer';
// Joy packages
import members from './joy-members';
import { terms, privacyPolicy } from './joy-pages';
import election from './joy-election';

export default function create (t: <T = string> (key: string, text: string, options: { ns: string }) => T): Routes {
return appSettings.uiMode === 'light'
? [
members(t),
election(t),
staking(t),
null,
transfer(t),
Expand All @@ -37,6 +39,7 @@ export default function create (t: <T = string> (key: string, text: string, opti
]
: [
members(t),
election(t),
staking(t),
null,
transfer(t),
Expand Down
17 changes: 17 additions & 0 deletions pioneer/packages/apps-routing/src/joy-election.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Route } from './types';

import Election from '@polkadot/joy-election/index';
import SidebarSubtitle from '@polkadot/joy-election/SidebarSubtitle';

export default function create (t: <T = string> (key: string, text: string, options: { ns: string }) => T): Route {
return {
Component: Election,
display: {
needsApi: ['query.council.activeCouncil', 'query.councilElection.stage']
},
text: t<string>('nav.election', 'Council', { ns: 'apps-routing' }),
icon: 'university',
name: 'council',
SubtitleComponent: SidebarSubtitle
};
}
2 changes: 2 additions & 0 deletions pioneer/packages/apps-routing/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export interface Route {
name: string;
text: string;
useCounter?: () => number | string | null;
// Joystream-specific
SubtitleComponent?: React.ComponentType<any>;
}

export type Routes = (Route | null)[];
29 changes: 24 additions & 5 deletions pioneer/packages/apps/src/Content/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ import { useTranslation } from '../translate';
import NotFound from './NotFound';
import Status from './Status';

// Joystream-specific
// We must use transport provider here instead of /apps/src/index
// to avoid "Cannot create Transport: The Substrate API is not ready yet." error
import { TransportProvider } from '@polkadot/joy-utils/react/context';

interface Props {
className?: string;
}
Expand Down Expand Up @@ -60,11 +65,25 @@ function Content ({ className }: Props): React.ReactElement<Props> {
? <NotFound />
: (
<ErrorBoundary trigger={name}>
<Component
basePath={`/${name}`}
location={location}
onStatusChange={queueAction}
/>
{ needsApi
// Add transport provider for routes that need the api
// (the above condition makes sure it's aleady initialized at this point)
? (
<TransportProvider>
<Component
basePath={`/${name}`}
location={location}
onStatusChange={queueAction}
/>
</TransportProvider>
)
: (
<Component
basePath={`/${name}`}
location={location}
onStatusChange={queueAction}
/>
) }
</ErrorBoundary>
)
}
Expand Down
7 changes: 5 additions & 2 deletions pioneer/packages/apps/src/SideBar/Item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,15 @@ function Item ({ isCollapsed, onClick, route }: Props): React.ReactElement<Props
return null;
}

const { Modal, icon, name, text } = route;
const { Modal, SubtitleComponent, icon, name, text } = route;

const body = (
<>
<Icon icon={icon} />
<span className='text'>{text}</span>
<span className='text'>
{text}
{ SubtitleComponent && <SubtitleComponent/> }
</span>
{!!count && (
<Badge
color='counter'
Expand Down
Empty file.
6 changes: 3 additions & 3 deletions pioneer/packages/joy-election/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
"author": "Joystream contributors",
"maintainers": [],
"dependencies": {
"@babel/runtime": "^7.7.1",
"@polkadot/react-components": "0.37.0-beta.63",
"@polkadot/react-query": "0.37.0-beta.63",
"@babel/runtime": "^7.10.5",
"@polkadot/react-components": "0.51.1",
"@polkadot/react-query": "0.51.1",
"@polkadot/joy-utils": "^0.1.1"
}
}
15 changes: 9 additions & 6 deletions pioneer/packages/joy-election/src/Applicant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,25 @@ import { Table } from 'semantic-ui-react';

import { I18nProps } from '@polkadot/react-components/types';
import { ApiProps } from '@polkadot/react-api/types';
import { withCalls } from '@polkadot/react-api/with';
import { withCalls } from '@polkadot/react-api/hoc';
import { AccountId } from '@polkadot/types/interfaces';
import { formatBalance } from '@polkadot/util';
import CandidatePreview from './CandidatePreview';

import translate from './translate';
import { calcTotalStake } from '@polkadot/joy-utils/index';
import { calcTotalStake } from '@polkadot/joy-utils/functions/misc';
import { ElectionStake } from '@joystream/types/council';

type Props = ApiProps & I18nProps & {
index: number;
accountId: AccountId;
stake?: ElectionStake;
isVotingStage: boolean;
};

class Applicant extends React.PureComponent<Props> {
render () {
const { index, accountId, stake } = this.props;
const { index, accountId, stake, isVotingStage } = this.props;
const voteUrl = `/council/votes?applicantId=${accountId.toString()}`;

return (
Expand All @@ -33,9 +34,11 @@ class Applicant extends React.PureComponent<Props> {
<Table.Cell style={{ textAlign: 'right' }}>
{formatBalance(calcTotalStake(stake))}
</Table.Cell>
<Table.Cell>
<Link to={voteUrl} className='ui button primary inverted'>Vote</Link>
</Table.Cell>
{ isVotingStage && (
<Table.Cell>
<Link to={voteUrl} className='ui button primary inverted'>Vote</Link>
</Table.Cell>
) }
</Table.Row>
);
}
Expand Down
68 changes: 44 additions & 24 deletions pioneer/packages/joy-election/src/Applicants.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,68 @@
import React from 'react';
import { Table } from 'semantic-ui-react';
import { Table, Message } from 'semantic-ui-react';
import BN from 'bn.js';

import { I18nProps } from '@polkadot/react-components/types';
import { ApiProps } from '@polkadot/react-api/types';
import { withCalls } from '@polkadot/react-api/with';
import { withCalls } from '@polkadot/react-api/hoc';
import { AccountId } from '@polkadot/types/interfaces';
import { Option } from '@polkadot/types';
import { formatNumber } from '@polkadot/util';

import translate from './translate';
import Applicant from './Applicant';
import ApplyForm from './ApplyForm';
import Section from '@polkadot/joy-utils/Section';
import { queryToProp } from '@polkadot/joy-utils/index';
import { withMyAccount, MyAccountProps } from '@polkadot/joy-utils/MyAccount';
import Section from '@polkadot/joy-utils/react/components/Section';
import { queryToProp } from '@polkadot/joy-utils/functions/misc';
import { withMyAccount, MyAccountProps } from '@polkadot/joy-utils/react/hocs/accounts';
import { ElectionStage } from '@joystream/types/src/council';
import { RouteProps } from 'react-router-dom';

type Props = ApiProps & I18nProps & MyAccountProps & {
type Props = RouteProps & ApiProps & I18nProps & MyAccountProps & {
candidacyLimit?: BN;
applicants?: Array<AccountId>;
stage?: Option<ElectionStage>;
};

class Applicants extends React.PureComponent<Props> {
private renderTable = (applicants: Array<AccountId>) => (
<Table celled selectable compact>
<Table.Header>
<Table.Row>
<Table.HeaderCell>#</Table.HeaderCell>
<Table.HeaderCell>Applicant</Table.HeaderCell>
<Table.HeaderCell>Total stake</Table.HeaderCell>
<Table.HeaderCell style={{ width: '1%' }}>Actions</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>{applicants.map((accountId, index) => (
<Applicant key={index} index={index} accountId={accountId} />
))}</Table.Body>
</Table>
)
private renderTable = (applicants: Array<AccountId>) => {
const isVotingStage = this.props.stage?.unwrapOr(undefined)?.isOfType('Voting') || false;

return (
<Table celled selectable compact>
<Table.Header>
<Table.Row>
<Table.HeaderCell>#</Table.HeaderCell>
<Table.HeaderCell>Applicant</Table.HeaderCell>
<Table.HeaderCell>Total stake</Table.HeaderCell>
{ isVotingStage && (
<Table.HeaderCell style={{ width: '1%' }}>Actions</Table.HeaderCell>
) }
</Table.Row>
</Table.Header>
<Table.Body>{applicants.map((accountId, index) => (
<Applicant key={index} index={index} accountId={accountId} isVotingStage={isVotingStage}/>
))}</Table.Body>
</Table>
);
}

render () {
const { myAddress, applicants = [], candidacyLimit = new BN(0) } = this.props;
const { myAddress, applicants = [], candidacyLimit = new BN(0), stage } = this.props;
const title = <span>Applicants <sup>{applicants.length}/{formatNumber(candidacyLimit)}</sup></span>;

return <>
<Section title='My application'>
<ApplyForm myAddress={myAddress} />
{ stage?.unwrapOr(undefined)?.isOfType('Announcing')
? (
<ApplyForm myAddress={myAddress} />
)
: (
<Message warning>
Applying to council is only possible during <i><b>Announcing</b></i> stage.
</Message>
)
}
</Section>
<Section title={title}>
{!applicants.length
Expand All @@ -59,6 +78,7 @@ class Applicants extends React.PureComponent<Props> {
export default translate(
withCalls<Props>(
queryToProp('query.councilElection.candidacyLimit'),
queryToProp('query.councilElection.applicants')
queryToProp('query.councilElection.applicants'),
queryToProp('query.councilElection.stage')
)(withMyAccount(Applicants))
);
41 changes: 20 additions & 21 deletions pioneer/packages/joy-election/src/ApplyForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ import React from 'react';

import { I18nProps } from '@polkadot/react-components/types';
import { ApiProps } from '@polkadot/react-api/types';
import { withCalls, withMulti } from '@polkadot/react-api/with';
import { withCalls, withMulti } from '@polkadot/react-api/hoc';
import { Labelled } from '@polkadot/react-components/index';
import { Balance } from '@polkadot/types/interfaces';

import translate from './translate';
import TxButton from '@polkadot/joy-utils/TxButton';
import InputStake from '@polkadot/joy-utils/InputStake';
import TxButton from '@polkadot/joy-utils/react/components/TxButton';
import InputStake from '@polkadot/joy-utils/react/components/InputStake';
import { ElectionStake } from '@joystream/types/council';
import { calcTotalStake, ZERO } from '@polkadot/joy-utils/index';
import { MyAddressProps, withOnlyMembers } from '@polkadot/joy-utils/MyAccount';
import { calcTotalStake, ZERO } from '@polkadot/joy-utils/functions/misc';
import { MyAddressProps } from '@polkadot/joy-utils/react/hocs/accounts';
import { withOnlyMembers } from '@polkadot/joy-utils/react/hocs/guards';

type Props = ApiProps & I18nProps & MyAddressProps & {
minStake?: Balance;
alreadyStaked?: ElectionStake;
myBalance?: Balance;
};

type State = {
Expand Down Expand Up @@ -48,15 +48,17 @@ class ApplyForm extends React.PureComponent<Props, State> {
isValid={isStakeValid}
onChange={this.onChangeStake}
/>
<Labelled style={{ marginTop: '.5rem' }}>
<TxButton
size='large'
isDisabled={!isStakeValid}
label={buttonLabel}
params={[stake]}
tx='councilElection.apply'
/>
</Labelled>
<div style={{ marginTop: '.5rem' }}>
<Labelled>
<TxButton
size='large'
isDisabled={!isStakeValid}
label={buttonLabel}
params={[stake]}
tx='councilElection.apply'
/>
</Labelled>
</div>
</div>
);
}
Expand All @@ -71,10 +73,9 @@ class ApplyForm extends React.PureComponent<Props, State> {

private onChangeStake = (stake?: BN): void => {
stake = stake || ZERO;
const { myBalance = ZERO } = this.props;
const isStakeLteBalance = stake.lte(myBalance);
const isStakeGteMinStake = stake.add(this.alreadyStaked()).gte(this.minStake());
const isStakeValid = !stake.isZero() && isStakeGteMinStake && isStakeLteBalance;
const isStakeValid = !stake.isZero() && isStakeGteMinStake;

this.setState({ stake, isStakeValid });
}
}
Expand All @@ -88,8 +89,6 @@ export default withMulti(
['query.councilElection.minCouncilStake',
{ propName: 'minStake' }],
['query.councilElection.applicantStakes',
{ paramName: 'myAddress', propName: 'alreadyStaked' }],
['query.balances.freeBalance',
{ paramName: 'myAddress', propName: 'myBalance' }]
{ paramName: 'myAddress', propName: 'alreadyStaked' }]
)
);
4 changes: 2 additions & 2 deletions pioneer/packages/joy-election/src/CandidatePreview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import AddressMini from '@polkadot/react-components/AddressMiniJoy';
import MemberByAccount from '@polkadot/joy-utils/MemberByAccountPreview';
import AddressMini from '@polkadot/react-components/AddressMini';
import MemberByAccount from '@polkadot/joy-utils/react/components/MemberByAccountPreview';
import { AccountId } from '@polkadot/types/interfaces';

import styled from 'styled-components';
Expand Down
Loading