Some more functional component conversions (#1648)
This commit is contained in:
parent
aef86d373a
commit
00048285d0
@ -21,86 +21,35 @@ interface Props extends I18nProps {
|
||||
votes: Votes | null;
|
||||
}
|
||||
|
||||
interface State {
|
||||
votedTotal: number;
|
||||
votedNay: number;
|
||||
votedAye: number;
|
||||
}
|
||||
|
||||
class Motion extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
votedTotal: 0,
|
||||
votedAye: 0,
|
||||
votedNay: 0
|
||||
};
|
||||
|
||||
public static getDerivedStateFromProps ({ votes }: Props): State | null {
|
||||
if (!votes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { ayes, nays } = votes;
|
||||
|
||||
const newState: State = {
|
||||
votedNay: nays.length,
|
||||
votedAye: ayes.length,
|
||||
votedTotal: ayes.length + nays.length
|
||||
};
|
||||
|
||||
return newState;
|
||||
function Motion ({ className, hash, proposal, t, votes }: Props): React.ReactElement<Props> | null {
|
||||
if (!proposal || !votes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { className, hash, proposal, votes } = this.props;
|
||||
const { ayes, index, nays, threshold } = votes;
|
||||
|
||||
if (!proposal || !votes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { index } = votes;
|
||||
|
||||
return (
|
||||
<ActionItem
|
||||
className={className}
|
||||
accessory={
|
||||
<Voting
|
||||
hash={hash}
|
||||
isCouncil
|
||||
idNumber={index}
|
||||
proposal={proposal}
|
||||
/>
|
||||
}
|
||||
expandNested
|
||||
idNumber={index}
|
||||
proposal={proposal}
|
||||
>
|
||||
{this.renderInfo()}
|
||||
</ActionItem>
|
||||
);
|
||||
}
|
||||
|
||||
private renderInfo (): React.ReactNode {
|
||||
const { votes, t } = this.props;
|
||||
|
||||
if (!votes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { ayes, nays, threshold } = votes;
|
||||
|
||||
return (
|
||||
return (
|
||||
<ActionItem
|
||||
className={className}
|
||||
accessory={
|
||||
<Voting
|
||||
hash={hash}
|
||||
isCouncil
|
||||
idNumber={index}
|
||||
proposal={proposal}
|
||||
/>
|
||||
}
|
||||
expandNested
|
||||
idNumber={index}
|
||||
proposal={proposal}
|
||||
>
|
||||
<div>
|
||||
<h4>
|
||||
{t(
|
||||
'ayes ({{ayes}}/{{threshold}} to approve)',
|
||||
{
|
||||
replace: {
|
||||
ayes: ayes.length,
|
||||
threshold: threshold.toString()
|
||||
}
|
||||
}
|
||||
)}
|
||||
</h4>
|
||||
<h4>{t('ayes ({{ayes}}/{{threshold}} to approve)', {
|
||||
replace: {
|
||||
ayes: ayes.length,
|
||||
threshold: threshold.toString()
|
||||
}
|
||||
})}</h4>
|
||||
{ayes.map((address, index): React.ReactNode => (
|
||||
<Labelled
|
||||
key={`${index}:${address}`}
|
||||
@ -113,16 +62,11 @@ class Motion extends React.PureComponent<Props, State> {
|
||||
/>
|
||||
</Labelled>
|
||||
))}
|
||||
<h4>
|
||||
{t(
|
||||
'nays ({{nays}})',
|
||||
{
|
||||
replace: {
|
||||
nays: nays.length
|
||||
}
|
||||
}
|
||||
)}
|
||||
</h4>
|
||||
<h4>{t('nays ({{nays}})', {
|
||||
replace: {
|
||||
nays: nays.length
|
||||
}
|
||||
})}</h4>
|
||||
{nays.map((address, index): React.ReactNode => (
|
||||
<Labelled
|
||||
key={`${index}:${address}`}
|
||||
@ -136,31 +80,25 @@ class Motion extends React.PureComponent<Props, State> {
|
||||
</Labelled>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</ActionItem>
|
||||
);
|
||||
}
|
||||
|
||||
export default withMulti(
|
||||
Motion,
|
||||
translate,
|
||||
withCalls<Props>(
|
||||
[
|
||||
'query.council.proposalOf',
|
||||
{
|
||||
paramName: 'hash',
|
||||
propName: 'proposal',
|
||||
transform: (value: Option<ProposalType>): ProposalType | null =>
|
||||
value.unwrapOr(null)
|
||||
}
|
||||
],
|
||||
[
|
||||
'query.council.voting',
|
||||
{
|
||||
paramName: 'hash',
|
||||
propName: 'votes',
|
||||
transform: (value: Option<Votes>): Votes | null =>
|
||||
value.unwrapOr(null)
|
||||
}
|
||||
]
|
||||
['query.council.proposalOf', {
|
||||
paramName: 'hash',
|
||||
propName: 'proposal',
|
||||
transform: (value: Option<ProposalType>): ProposalType | null =>
|
||||
value.unwrapOr(null)
|
||||
}],
|
||||
['query.council.voting', {
|
||||
paramName: 'hash',
|
||||
propName: 'votes',
|
||||
transform: (value: Option<Votes>): Votes | null =>
|
||||
value.unwrapOr(null)
|
||||
}]
|
||||
)
|
||||
);
|
||||
|
||||
@ -8,7 +8,7 @@ import { I18nProps } from '@polkadot/react-components/types';
|
||||
import { ReferendumInfoExtended } from '@polkadot/api-derive/type';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { ActionItem, Chart, Static, Voting } from '@polkadot/react-components';
|
||||
import { formatBalance, formatNumber } from '@polkadot/util';
|
||||
@ -37,91 +37,90 @@ interface State {
|
||||
votedTotal: BN;
|
||||
}
|
||||
|
||||
class Referendum extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
function Referendum ({ chain_bestNumber, className, democracy_enactmentPeriod, democracy_referendumVotesFor, t, value }: Props): React.ReactElement<Props> | null {
|
||||
const [{ voteCount, voteCountAye, voteCountNay, votedAye, votedNay, votedTotal }, setState] = useState<State>({
|
||||
voteCount: 0,
|
||||
voteCountAye: 0,
|
||||
voteCountNay: 0,
|
||||
votedAye: new BN(0),
|
||||
votedNay: new BN(0),
|
||||
votedTotal: new BN(0)
|
||||
};
|
||||
});
|
||||
|
||||
public static getDerivedStateFromProps ({ democracy_referendumVotesFor }: Props, prevState: State): State | null {
|
||||
if (!democracy_referendumVotesFor) {
|
||||
return null;
|
||||
}
|
||||
useEffect((): void => {
|
||||
if (democracy_referendumVotesFor) {
|
||||
const newState: State = democracy_referendumVotesFor.reduce((state, { balance, vote }): State => {
|
||||
if (vote.isAye) {
|
||||
state.voteCountAye++;
|
||||
state.votedAye = state.votedAye.add(balance);
|
||||
} else {
|
||||
state.voteCountNay++;
|
||||
state.votedNay = state.votedNay.add(balance);
|
||||
}
|
||||
|
||||
const newState: State = democracy_referendumVotesFor.reduce((state, { balance, vote }): State => {
|
||||
if (vote.isAye) {
|
||||
state.voteCountAye++;
|
||||
state.votedAye = state.votedAye.add(balance);
|
||||
} else {
|
||||
state.voteCountNay++;
|
||||
state.votedNay = state.votedNay.add(balance);
|
||||
state.voteCount++;
|
||||
state.votedTotal = state.votedTotal.add(balance);
|
||||
|
||||
return state;
|
||||
}, {
|
||||
voteCount: 0,
|
||||
voteCountAye: 0,
|
||||
voteCountNay: 0,
|
||||
votedAye: new BN(0),
|
||||
votedNay: new BN(0),
|
||||
votedTotal: new BN(0)
|
||||
});
|
||||
|
||||
if (newState.votedAye.eq(votedNay) && newState.votedNay.eq(votedNay)) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.voteCount++;
|
||||
state.votedTotal = state.votedTotal.add(balance);
|
||||
|
||||
return state;
|
||||
}, {
|
||||
voteCount: 0,
|
||||
voteCountAye: 0,
|
||||
voteCountNay: 0,
|
||||
votedAye: new BN(0),
|
||||
votedNay: new BN(0),
|
||||
votedTotal: new BN(0)
|
||||
});
|
||||
|
||||
if (newState.votedAye.eq(prevState.votedNay) && newState.votedNay.eq(prevState.votedNay)) {
|
||||
return null;
|
||||
setState(newState);
|
||||
}
|
||||
}, [democracy_referendumVotesFor]);
|
||||
|
||||
return newState;
|
||||
if (!chain_bestNumber || value.end.sub(chain_bestNumber).lten(0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { chain_bestNumber, className, value } = this.props;
|
||||
const enactBlock = (democracy_enactmentPeriod || new BN(0)).add(value.end);
|
||||
|
||||
if (!chain_bestNumber || value.end.sub(chain_bestNumber).lten(0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ActionItem
|
||||
className={className}
|
||||
idNumber={value.index}
|
||||
proposal={value.proposal}
|
||||
accessory={
|
||||
<Voting
|
||||
idNumber={value.index}
|
||||
proposal={value.proposal}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{this.renderInfo()}
|
||||
</ActionItem>
|
||||
);
|
||||
}
|
||||
|
||||
private renderInfo (): React.ReactNode {
|
||||
const { chain_bestNumber, democracy_enactmentPeriod, t, value: { end, threshold } } = this.props;
|
||||
|
||||
if (!chain_bestNumber) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const enactBlock = (democracy_enactmentPeriod || new BN(0)).add(end);
|
||||
|
||||
return (
|
||||
return (
|
||||
<ActionItem
|
||||
className={className}
|
||||
idNumber={value.index}
|
||||
proposal={value.proposal}
|
||||
accessory={
|
||||
<Voting
|
||||
idNumber={value.index}
|
||||
proposal={value.proposal}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div>
|
||||
{this.renderResults()}
|
||||
{voteCount !== 0 && votedTotal.gtn(0) && (
|
||||
<div className='democracy--Referendum-results chart'>
|
||||
<Chart.HorizBar
|
||||
values={[
|
||||
{
|
||||
colors: COLORS_AYE,
|
||||
label: `Aye, ${formatBalance(votedAye)} (${formatNumber(voteCountAye)})`,
|
||||
value: votedAye.muln(10000).div(votedTotal).toNumber() / 100
|
||||
},
|
||||
{
|
||||
colors: COLORS_NAY,
|
||||
label: `Nay, ${formatBalance(votedNay)} (${formatNumber(voteCountNay)})`,
|
||||
value: votedNay.muln(10000).div(votedTotal).toNumber() / 100
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<Static label={t('ending at')}>
|
||||
{t('block #{{blockNumber}}, {{remaining}} blocks remaining', {
|
||||
replace: {
|
||||
blockNumber: formatNumber(end),
|
||||
remaining: formatNumber(end.sub(chain_bestNumber).subn(1))
|
||||
blockNumber: formatNumber(value.end),
|
||||
remaining: formatNumber(value.end.sub(chain_bestNumber).subn(1))
|
||||
}
|
||||
})}
|
||||
</Static>
|
||||
@ -135,7 +134,7 @@ class Referendum extends React.PureComponent<Props, State> {
|
||||
<VoteThreshold
|
||||
isDisabled
|
||||
isOptional={false}
|
||||
defaultValue={{ isValid: true, value: threshold }}
|
||||
defaultValue={{ isValid: true, value: value.threshold }}
|
||||
label={t('vote threshold')}
|
||||
name='voteThreshold'
|
||||
type={{
|
||||
@ -144,39 +143,12 @@ class Referendum extends React.PureComponent<Props, State> {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private renderResults (): React.ReactNode {
|
||||
const { voteCount, voteCountAye, voteCountNay, votedAye, votedNay, votedTotal } = this.state;
|
||||
|
||||
if (voteCount === 0 || votedTotal.eqn(0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='democracy--Referendum-results chart'>
|
||||
<Chart.HorizBar
|
||||
values={[
|
||||
{
|
||||
colors: COLORS_AYE,
|
||||
label: `Aye, ${formatBalance(votedAye)} (${formatNumber(voteCountAye)})`,
|
||||
value: votedAye.muln(10000).div(votedTotal).toNumber() / 100
|
||||
},
|
||||
{
|
||||
colors: COLORS_NAY,
|
||||
label: `Nay, ${formatBalance(votedNay)} (${formatNumber(voteCountNay)})`,
|
||||
value: votedNay.muln(10000).div(votedTotal).toNumber() / 100
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</ActionItem>
|
||||
);
|
||||
}
|
||||
|
||||
export default withMulti(
|
||||
styled(Referendum as React.ComponentClass<Props>)`
|
||||
styled(Referendum)`
|
||||
.democracy--Referendum-results {
|
||||
margin-bottom: 1em;
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import { I18nProps } from '@polkadot/react-components/types';
|
||||
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { Button, InputAddress, Modal, TxButton } from '@polkadot/react-components';
|
||||
import accountObservable from '@polkadot/ui-keyring/observable/accounts';
|
||||
import { withMulti, withObservable } from '@polkadot/react-api';
|
||||
@ -20,99 +20,69 @@ interface Props extends I18nProps {
|
||||
proposalId: BN | number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
accountId?: string;
|
||||
isSecondingOpen: boolean;
|
||||
}
|
||||
function Seconding ({ allAccounts, depositors, proposalId, t }: Props): React.ReactElement<Props> | null {
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const [isSecondingOpen, setIsSecondingOpen] = useState(false);
|
||||
const hasAccounts = allAccounts && Object.keys(allAccounts).length !== 0;
|
||||
|
||||
class Seconding extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
isSecondingOpen: false
|
||||
};
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { allAccounts, t } = this.props;
|
||||
const hasAccounts = allAccounts && Object.keys(allAccounts).length !== 0;
|
||||
|
||||
if (!hasAccounts) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.renderModal()}
|
||||
<div className='ui--Row-buttons'>
|
||||
<Button
|
||||
isPrimary
|
||||
label={t('Second proposal')}
|
||||
labelIcon='toggle off'
|
||||
onClick={this.toggleSeconding}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
if (!hasAccounts) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private renderModal (): React.ReactNode {
|
||||
const { depositors, proposalId, t } = this.props;
|
||||
const { accountId, isSecondingOpen } = this.state;
|
||||
const isDepositor = depositors.some((depositor): boolean => depositor.eq(accountId));
|
||||
const _toggleSeconding = (): void => setIsSecondingOpen(!isSecondingOpen);
|
||||
|
||||
if (!isSecondingOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isDepositor = !!depositors.find((depositor): boolean => depositor.eq(accountId));
|
||||
|
||||
return (
|
||||
<Modal
|
||||
dimmer='inverted'
|
||||
open
|
||||
size='small'
|
||||
>
|
||||
<Modal.Header>{t('Second proposal')}</Modal.Header>
|
||||
<Modal.Content>
|
||||
<InputAddress
|
||||
help={t('Select the account you wish to second with. This will lock your funds until the proposal is either approved or rejected')}
|
||||
label={t('second with account')}
|
||||
onChange={this.onChangeAccount}
|
||||
type='account'
|
||||
withLabel
|
||||
/>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={this.toggleSeconding}
|
||||
label={t('Cancel')}
|
||||
labelIcon='cancel'
|
||||
return (
|
||||
<>
|
||||
{isSecondingOpen && (
|
||||
<Modal
|
||||
dimmer='inverted'
|
||||
open
|
||||
size='small'
|
||||
>
|
||||
<Modal.Header>{t('Second proposal')}</Modal.Header>
|
||||
<Modal.Content>
|
||||
<InputAddress
|
||||
help={t('Select the account you wish to second with. This will lock your funds until the proposal is either approved or rejected')}
|
||||
label={t('second with account')}
|
||||
onChange={setAccountId}
|
||||
type='account'
|
||||
withLabel
|
||||
/>
|
||||
<Button.Or />
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
isDisabled={!accountId || isDepositor}
|
||||
isPrimary
|
||||
label={t('Second')}
|
||||
labelIcon='sign-in'
|
||||
onClick={this.toggleSeconding}
|
||||
params={[proposalId]}
|
||||
tx='democracy.second'
|
||||
/>
|
||||
</Button.Group>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
private onChangeAccount = (accountId?: string): void => {
|
||||
this.setState({ accountId });
|
||||
}
|
||||
|
||||
private toggleSeconding = (): void => {
|
||||
this.setState(({ isSecondingOpen }): State => ({
|
||||
isSecondingOpen: !isSecondingOpen
|
||||
}));
|
||||
}
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={_toggleSeconding}
|
||||
label={t('Cancel')}
|
||||
labelIcon='cancel'
|
||||
/>
|
||||
<Button.Or />
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
isDisabled={!accountId || isDepositor}
|
||||
isPrimary
|
||||
label={t('Second')}
|
||||
labelIcon='sign-in'
|
||||
onClick={_toggleSeconding}
|
||||
params={[proposalId]}
|
||||
tx='democracy.second'
|
||||
/>
|
||||
</Button.Group>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
)}
|
||||
<div className='ui--Row-buttons'>
|
||||
<Button
|
||||
isPrimary
|
||||
label={t('Second proposal')}
|
||||
labelIcon='toggle off'
|
||||
onClick={_toggleSeconding}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default withMulti(
|
||||
|
||||
@ -6,7 +6,7 @@ import { I18nProps } from '@polkadot/react-components/types';
|
||||
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { Button, Dropdown, InputAddress, Modal, TxButton } from '@polkadot/react-components';
|
||||
import accountObservable from '@polkadot/ui-keyring/observable/accounts';
|
||||
import { withMulti, withObservable } from '@polkadot/react-api';
|
||||
@ -18,123 +18,79 @@ interface Props extends I18nProps {
|
||||
referendumId: BN | number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
accountId?: string;
|
||||
isVotingOpen: boolean;
|
||||
voteOptions: { text: React.ReactNode; value: boolean }[];
|
||||
voteValue: boolean;
|
||||
}
|
||||
function Voting ({ allAccounts, referendumId, t }: Props): React.ReactElement<Props> | null {
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const [isVotingOpen, setIsVotingOpen] = useState(false);
|
||||
const [voteValue, setVoteValue] = useState(true);
|
||||
const hasAccounts = allAccounts && Object.keys(allAccounts).length !== 0;
|
||||
|
||||
class Voting extends React.PureComponent<Props, State> {
|
||||
public state: State;
|
||||
|
||||
public constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
const { t } = props;
|
||||
|
||||
this.state = {
|
||||
isVotingOpen: false,
|
||||
voteOptions: [
|
||||
{ text: t('Aye, I approve'), value: true },
|
||||
{ text: t('Nay, I do not approve'), value: false }
|
||||
],
|
||||
voteValue: true
|
||||
};
|
||||
if (!hasAccounts) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { allAccounts, t } = this.props;
|
||||
const hasAccounts = allAccounts && Object.keys(allAccounts).length !== 0;
|
||||
const _toggleVoting = (): void => setIsVotingOpen(!isVotingOpen);
|
||||
|
||||
if (!hasAccounts) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.renderModal()}
|
||||
<div className='ui--Row-buttons'>
|
||||
<Button
|
||||
isPrimary
|
||||
label={t('Vote')}
|
||||
labelIcon='check'
|
||||
onClick={this.toggleVoting}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
private renderModal (): React.ReactNode {
|
||||
const { referendumId, t } = this.props;
|
||||
const { accountId, isVotingOpen, voteOptions, voteValue } = this.state;
|
||||
|
||||
if (!isVotingOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
dimmer='inverted'
|
||||
open
|
||||
size='small'
|
||||
>
|
||||
<Modal.Header>{t('Vote on proposal')}</Modal.Header>
|
||||
<Modal.Content>
|
||||
<InputAddress
|
||||
help={t('Select the account you wish to vote with. You can approve "aye" or deny "nay" the proposal.')}
|
||||
label={t('vote with account')}
|
||||
onChange={this.onChangeAccount}
|
||||
type='account'
|
||||
withLabel
|
||||
/>
|
||||
<Dropdown
|
||||
help={t('Select your vote preferences for this proposal, either to approve or disapprove')}
|
||||
label={t('record my vote as')}
|
||||
options={voteOptions}
|
||||
onChange={this.onChangeVote}
|
||||
value={voteValue}
|
||||
/>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={this.toggleVoting}
|
||||
label={t('Cancel')}
|
||||
labelIcon='cancel'
|
||||
return (
|
||||
<>
|
||||
{isVotingOpen && (
|
||||
<Modal
|
||||
dimmer='inverted'
|
||||
open
|
||||
size='small'
|
||||
>
|
||||
<Modal.Header>{t('Vote on proposal')}</Modal.Header>
|
||||
<Modal.Content>
|
||||
<InputAddress
|
||||
help={t('Select the account you wish to vote with. You can approve "aye" or deny "nay" the proposal.')}
|
||||
label={t('vote with account')}
|
||||
onChange={setAccountId}
|
||||
type='account'
|
||||
withLabel
|
||||
/>
|
||||
<Button.Or />
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
isDisabled={!accountId}
|
||||
isPrimary
|
||||
label={t('Vote')}
|
||||
labelIcon='check'
|
||||
onClick={this.toggleVoting}
|
||||
params={[referendumId, voteValue]}
|
||||
tx='democracy.vote'
|
||||
<Dropdown
|
||||
help={t('Select your vote preferences for this proposal, either to approve or disapprove')}
|
||||
label={t('record my vote as')}
|
||||
options={[
|
||||
{ text: t('Aye, I approve'), value: true },
|
||||
{ text: t('Nay, I do not approve'), value: false }
|
||||
]}
|
||||
onChange={setVoteValue}
|
||||
value={voteValue}
|
||||
/>
|
||||
</Button.Group>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
private onChangeAccount = (accountId?: string): void => {
|
||||
this.setState({ accountId });
|
||||
}
|
||||
|
||||
private onChangeVote = (voteValue: boolean): void => {
|
||||
this.setState({ voteValue });
|
||||
}
|
||||
|
||||
private toggleVoting = (): void => {
|
||||
this.setState(({ isVotingOpen }): Pick<State, never> => ({
|
||||
isVotingOpen: !isVotingOpen
|
||||
}));
|
||||
}
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={_toggleVoting}
|
||||
label={t('Cancel')}
|
||||
labelIcon='cancel'
|
||||
/>
|
||||
<Button.Or />
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
isDisabled={!accountId}
|
||||
isPrimary
|
||||
label={t('Vote')}
|
||||
labelIcon='check'
|
||||
onClick={_toggleVoting}
|
||||
params={[referendumId, voteValue]}
|
||||
tx='democracy.vote'
|
||||
/>
|
||||
</Button.Group>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
)}
|
||||
<div className='ui--Row-buttons'>
|
||||
<Button
|
||||
isPrimary
|
||||
label={t('Vote')}
|
||||
labelIcon='check'
|
||||
onClick={_toggleVoting}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default withMulti(
|
||||
|
||||
@ -61,6 +61,155 @@ interface Row {
|
||||
cols: Col[];
|
||||
}
|
||||
|
||||
// adjust the number of columns in a cell based on the children and tree depth
|
||||
function calcWidth (children: LinkArray): number {
|
||||
return Math.max(1, children.reduce((total, { hdr: { width } }): number => {
|
||||
return total + width;
|
||||
}, 0));
|
||||
}
|
||||
|
||||
// counts the height of a specific node
|
||||
function calcHeight (children: LinkArray): number {
|
||||
return children.reduce((max, { hdr, arr }): number => {
|
||||
hdr.height = hdr.isEmpty
|
||||
? 0
|
||||
: 1 + calcHeight(arr);
|
||||
|
||||
return Math.max(max, hdr.height);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// a single column in a row, it just has the details for the entry
|
||||
function createCol ({ hdr: { author, hash, isEmpty, isFinalized, parent, width } }: Link): Col {
|
||||
return { author, hash, isEmpty, isFinalized, parent, width };
|
||||
}
|
||||
|
||||
// create a simplified structure that allows for easy rendering
|
||||
function createRows (arr: LinkArray): Row[] {
|
||||
if (!arr.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return createRows(
|
||||
arr.reduce((children: LinkArray, { arr }: Link): LinkArray =>
|
||||
children.concat(...arr), [])
|
||||
).concat({
|
||||
bn: arr.reduce((result, { hdr: { bn } }): string =>
|
||||
result || bn, ''),
|
||||
cols: arr.map(createCol)
|
||||
});
|
||||
}
|
||||
|
||||
// fills in a header based on the supplied data
|
||||
function createHdr (bn: string, hash: string, parent: string, author: string | null, isEmpty = false): LinkHeader {
|
||||
return { author, bn, hash, height: 0, isEmpty, isFinalized: false, parent, width: 0 };
|
||||
}
|
||||
|
||||
// empty link helper
|
||||
function createLink (): Link {
|
||||
return {
|
||||
arr: [],
|
||||
hdr: createHdr('', ' ', ' ', null, true)
|
||||
};
|
||||
}
|
||||
|
||||
// even out the columns, i.e. add empty spacers as applicable to get tree rendering right
|
||||
function addColumnSpacers (arr: LinkArray): void {
|
||||
// check is any of the children has a non-empty set
|
||||
const hasChildren = arr.some(({ arr }): boolean => arr.length !== 0);
|
||||
|
||||
if (hasChildren) {
|
||||
// ok, non-empty found - iterate through an add at least an empty cell to all
|
||||
arr
|
||||
.filter(({ arr }): boolean => arr.length === 0)
|
||||
.forEach(({ arr }): number => arr.push(createLink()));
|
||||
|
||||
const newArr = arr.reduce((flat: LinkArray, { arr }): LinkArray => flat.concat(...arr), []);
|
||||
|
||||
// go one level deeper, ensure that the full tree has empty spacers
|
||||
addColumnSpacers(newArr);
|
||||
}
|
||||
}
|
||||
|
||||
// checks to see if a row has a single non-empty entry, i.e. it is a candidate for collapsing
|
||||
function isSingleRow (cols: Col[]): boolean {
|
||||
if (!cols[0] || cols[0].isEmpty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cols.reduce((result: boolean, col, index): boolean => {
|
||||
return index === 0
|
||||
? result
|
||||
: (!col.isEmpty ? false : result);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function renderCol ({ author, hash, isEmpty, isFinalized, parent, width }: Col, index: number): React.ReactNode {
|
||||
return (
|
||||
<td
|
||||
className={`header ${isEmpty && 'isEmpty'} ${isFinalized && 'isFinalized'}`}
|
||||
colSpan={width}
|
||||
key={`${hash}:${index}:${width}`}
|
||||
>
|
||||
{
|
||||
isEmpty
|
||||
? <div className='empty' />
|
||||
: (
|
||||
<>
|
||||
{author && (
|
||||
<IdentityIcon
|
||||
className='author'
|
||||
size={28}
|
||||
value={author}
|
||||
/>
|
||||
)}
|
||||
<div className='contents'>
|
||||
<div className='hash'>{hash}</div>
|
||||
<div className='parent'>{parent}</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
// render the rows created by createRows to React nodes
|
||||
function renderRows (rows: Row[]): React.ReactNode[] {
|
||||
const lastIndex = rows.length - 1;
|
||||
let isPrevShort = false;
|
||||
|
||||
return rows.map(({ bn, cols }, index): React.ReactNode => {
|
||||
// if not first, not last and single only, see if we can collapse
|
||||
if (index !== 0 && index !== lastIndex && isSingleRow(cols)) {
|
||||
if (isPrevShort) {
|
||||
// previous one was already a link, this one as well - skip it
|
||||
return null;
|
||||
} else if (isSingleRow(rows[index - 1].cols)) {
|
||||
isPrevShort = true;
|
||||
|
||||
return (
|
||||
<tr key={bn}>
|
||||
<td key='blockNumber' />
|
||||
<td className='header isLink' colSpan={cols[0].width}>
|
||||
<div className='link'>⋮</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
isPrevShort = false;
|
||||
|
||||
return (
|
||||
<tr key={bn}>
|
||||
<td key='blockNumber'>{`#${bn}`}</td>
|
||||
{cols.map(renderCol)}
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
class Forks extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
numBlocks: 0,
|
||||
@ -116,7 +265,7 @@ class Forks extends React.PureComponent<Props, State> {
|
||||
|
||||
// add to the header map
|
||||
// also for HeaderExtended header.author ? header.author.toString() : null
|
||||
this._headers.set(hash, this.createHdr(bn, hash, parent, null));
|
||||
this._headers.set(hash, createHdr(bn, hash, parent, null));
|
||||
|
||||
// check to see if the children already has a entry
|
||||
if (this._children.has(parent)) {
|
||||
@ -186,146 +335,13 @@ class Forks extends React.PureComponent<Props, State> {
|
||||
</SummaryBox>
|
||||
<table>
|
||||
<tbody>
|
||||
{this.renderRows(this.createRows(tree.arr))}
|
||||
{renderRows(createRows(tree.arr))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// create a simplified structure that allows for easy rendering
|
||||
private createRows (arr: LinkArray): Row[] {
|
||||
if (!arr.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this
|
||||
.createRows(
|
||||
arr.reduce((children: LinkArray, { arr }: Link): LinkArray =>
|
||||
children.concat(...arr), [])
|
||||
)
|
||||
.concat({
|
||||
bn: arr.reduce((result, { hdr: { bn } }): string =>
|
||||
result || bn, ''),
|
||||
cols: arr.map(this.createCol)
|
||||
});
|
||||
}
|
||||
|
||||
// a single column in a row, it just has the details for the entry
|
||||
private createCol = ({ hdr: { author, hash, isEmpty, isFinalized, parent, width } }: Link): Col => {
|
||||
return { author, hash, isEmpty, isFinalized, parent, width };
|
||||
}
|
||||
|
||||
// checks to see if a row has a single non-empty entry, i.e. it is a candidate for collapsing
|
||||
private isSingleRow (cols: Col[]): boolean {
|
||||
if (!cols[0] || cols[0].isEmpty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cols.reduce((result: boolean, col, index): boolean => {
|
||||
return index === 0
|
||||
? result
|
||||
: (!col.isEmpty ? false : result);
|
||||
}, true);
|
||||
}
|
||||
|
||||
// render the rows created by createRows to React nodes
|
||||
private renderRows (rows: Row[]): React.ReactNode[] {
|
||||
const lastIndex = rows.length - 1;
|
||||
let isPrevShort = false;
|
||||
|
||||
return rows.map(({ bn, cols }, index): React.ReactNode => {
|
||||
// if not first, not last and single only, see if we can collapse
|
||||
if (index !== 0 && index !== lastIndex && this.isSingleRow(cols)) {
|
||||
if (isPrevShort) {
|
||||
// previous one was already a link, this one as well - skip it
|
||||
return null;
|
||||
} else if (this.isSingleRow(rows[index - 1].cols)) {
|
||||
isPrevShort = true;
|
||||
|
||||
return (
|
||||
<tr key={bn}>
|
||||
<td key='blockNumber' />
|
||||
<td className='header isLink' colSpan={cols[0].width}>
|
||||
<div className='link'>⋮</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
isPrevShort = false;
|
||||
|
||||
return (
|
||||
<tr key={bn}>
|
||||
<td key='blockNumber'>{`#${bn}`}</td>
|
||||
{cols.map(this.renderCol)}
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private renderCol = ({ author, hash, isEmpty, isFinalized, parent, width }: Col, index: number): React.ReactNode => {
|
||||
return (
|
||||
<td
|
||||
className={`header ${isEmpty && 'isEmpty'} ${isFinalized && 'isFinalized'}`}
|
||||
colSpan={width}
|
||||
key={`${hash}:${index}:${width}`}
|
||||
>
|
||||
{
|
||||
isEmpty
|
||||
? <div className='empty' />
|
||||
: (
|
||||
<>
|
||||
{author && (
|
||||
<IdentityIcon
|
||||
className='author'
|
||||
size={28}
|
||||
value={author}
|
||||
/>
|
||||
)}
|
||||
<div className='contents'>
|
||||
<div className='hash'>{hash}</div>
|
||||
<div className='parent'>{parent}</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
// adjust the number of columns in a cell based on the children and tree depth
|
||||
private calcWidth (children: LinkArray): number {
|
||||
return Math.max(1, children.reduce((total, { hdr: { width } }): number => {
|
||||
return total + width;
|
||||
}, 0));
|
||||
}
|
||||
|
||||
// counts the height of a specific node
|
||||
private calcHeight (children: LinkArray): number {
|
||||
return children.reduce((max, { hdr, arr }): number => {
|
||||
hdr.height = hdr.isEmpty
|
||||
? 0
|
||||
: 1 + this.calcHeight(arr);
|
||||
|
||||
return Math.max(max, hdr.height);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// fills in a header based on the supplied data
|
||||
private createHdr (bn: string, hash: string, parent: string, author: string | null, isEmpty = false): LinkHeader {
|
||||
return { author, bn, hash, height: 0, isEmpty, isFinalized: false, parent, width: 0 };
|
||||
}
|
||||
|
||||
// empty link helper
|
||||
private createLink (): Link {
|
||||
return {
|
||||
arr: [],
|
||||
hdr: this.createHdr('', ' ', ' ', null, true)
|
||||
};
|
||||
}
|
||||
|
||||
// adds children for a specific header, retrieving based on matching parent
|
||||
private addChildren (base: LinkHeader, children: LinkArray): LinkArray {
|
||||
const hdrs = (this._children.get(base.hash) || [])
|
||||
@ -337,8 +353,8 @@ class Forks extends React.PureComponent<Props, State> {
|
||||
});
|
||||
|
||||
// caclulate the max height/width for this entry
|
||||
base.height = this.calcHeight(children);
|
||||
base.width = this.calcWidth(children);
|
||||
base.height = calcHeight(children);
|
||||
base.width = calcWidth(children);
|
||||
|
||||
// place the active (larger, finalized) columns first for the pyramid display
|
||||
children.sort((a, b): number => {
|
||||
@ -354,27 +370,9 @@ class Forks extends React.PureComponent<Props, State> {
|
||||
return children;
|
||||
}
|
||||
|
||||
// even out the columns, i.e. add empty spacers as applicable to get tree rendering right
|
||||
private addColumnSpacers (arr: LinkArray): void {
|
||||
// check is any of the children has a non-empty set
|
||||
const hasChildren = arr.some(({ arr }): boolean => arr.length !== 0);
|
||||
|
||||
if (hasChildren) {
|
||||
// ok, non-empty found - iterate through an add at least an empty cell to all
|
||||
arr
|
||||
.filter(({ arr }): boolean => arr.length === 0)
|
||||
.forEach(({ arr }): number => arr.push(this.createLink()));
|
||||
|
||||
const newArr = arr.reduce((flat: LinkArray, { arr }): LinkArray => flat.concat(...arr), []);
|
||||
|
||||
// go one level deeper, ensure that the full tree has empty spacers
|
||||
this.addColumnSpacers(newArr);
|
||||
}
|
||||
}
|
||||
|
||||
// create a tree list from the available headers
|
||||
private generateTree (): Link {
|
||||
const root = this.createLink();
|
||||
const root = createLink();
|
||||
|
||||
// add all the root entries first, we iterate from these
|
||||
// We add the root entry explicitly, it exists as per init
|
||||
@ -393,10 +391,10 @@ class Forks extends React.PureComponent<Props, State> {
|
||||
});
|
||||
|
||||
// align the columns with empty spacers - this aids in display
|
||||
this.addColumnSpacers(root.arr);
|
||||
addColumnSpacers(root.arr);
|
||||
|
||||
root.hdr.height = this.calcHeight(root.arr);
|
||||
root.hdr.width = this.calcWidth(root.arr);
|
||||
root.hdr.height = calcHeight(root.arr);
|
||||
root.hdr.width = calcWidth(root.arr);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { I18nProps } from '@polkadot/react-components/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { InputAddress, Labelled } from '@polkadot/react-components';
|
||||
import { Nonce } from '@polkadot/react-query';
|
||||
@ -18,84 +18,50 @@ interface Props extends I18nProps {
|
||||
onChange: (accountId: string | undefined | null, accountNonce: BN) => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
accountNonce: BN;
|
||||
accountId?: string | null;
|
||||
}
|
||||
function Account ({ className, defaultValue, isError, onChange, t }: Props): React.ReactElement<Props> {
|
||||
const [accountId, setAccountId] = useState<string | null | undefined>(defaultValue);
|
||||
const [accountNonce, setAccountNonce] = useState(new BN(0));
|
||||
|
||||
class Account extends React.PureComponent<Props, State> {
|
||||
public state: State;
|
||||
const _onChangeAccountId = (accountId: string): void => {
|
||||
setAccountId(accountId);
|
||||
onChange(accountId, accountNonce);
|
||||
};
|
||||
const _onChangeAccountNonce = (accountNonce: BN): void => {
|
||||
setAccountNonce(accountNonce);
|
||||
onChange(accountId, accountNonce);
|
||||
};
|
||||
|
||||
public constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
accountId: props.defaultValue,
|
||||
accountNonce: new BN(0)
|
||||
};
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { className, defaultValue, isError, t } = this.props;
|
||||
|
||||
return (
|
||||
<div className={`ui--row ${className}`}>
|
||||
<div className='large'>
|
||||
<InputAddress
|
||||
defaultValue={defaultValue}
|
||||
isError={isError}
|
||||
label={t('sign data from account')}
|
||||
onChange={this.onChangeAccount}
|
||||
placeholder='0x...'
|
||||
type='account'
|
||||
/>
|
||||
</div>
|
||||
{this.renderNonce()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
public renderNonce (): React.ReactNode {
|
||||
const { t } = this.props;
|
||||
const { accountId } = this.state;
|
||||
|
||||
if (!accountId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Labelled
|
||||
className='small'
|
||||
label={t('with an index of')}
|
||||
>
|
||||
<Nonce
|
||||
className='ui disabled dropdown selection'
|
||||
callOnResult={this.onChangeNonce}
|
||||
params={accountId}
|
||||
return (
|
||||
<div className={`ui--row ${className}`}>
|
||||
<div className='large'>
|
||||
<InputAddress
|
||||
defaultValue={defaultValue}
|
||||
isError={isError}
|
||||
label={t('sign data from account')}
|
||||
onChange={_onChangeAccountId}
|
||||
placeholder='0x...'
|
||||
type='account'
|
||||
/>
|
||||
</Labelled>
|
||||
);
|
||||
}
|
||||
|
||||
private onChangeAccount = (accountId: string): void => {
|
||||
const { onChange } = this.props;
|
||||
|
||||
this.setState({ accountId }, (): void =>
|
||||
onChange(accountId, this.state.accountNonce)
|
||||
);
|
||||
}
|
||||
|
||||
private onChangeNonce = (_accountNonce: BN): void => {
|
||||
const { onChange } = this.props;
|
||||
const accountNonce = _accountNonce || new BN(0);
|
||||
|
||||
this.setState({ accountNonce }, (): void =>
|
||||
onChange(this.state.accountId, accountNonce)
|
||||
);
|
||||
}
|
||||
</div>
|
||||
{accountId && (
|
||||
<Labelled
|
||||
className='small'
|
||||
label={t('with an index of')}
|
||||
>
|
||||
<Nonce
|
||||
className='ui disabled dropdown selection'
|
||||
callOnResult={_onChangeAccountNonce}
|
||||
params={accountId}
|
||||
/>
|
||||
</Labelled>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(styled(Account)`
|
||||
box-sizing: border-box;
|
||||
padding-left: 2em;
|
||||
`);
|
||||
export default translate(
|
||||
styled(Account)`
|
||||
box-sizing: border-box;
|
||||
padding-left: 2em;
|
||||
`
|
||||
);
|
||||
|
||||
@ -7,7 +7,7 @@ import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
||||
|
||||
import React from 'react';
|
||||
import { Route, Switch } from 'react-router';
|
||||
import Tabs, { TabItem } from '@polkadot/react-components/Tabs';
|
||||
import Tabs from '@polkadot/react-components/Tabs';
|
||||
import accountObservable from '@polkadot/ui-keyring/observable/accounts';
|
||||
import { withMulti, withObservable } from '@polkadot/react-api';
|
||||
|
||||
@ -21,64 +21,47 @@ interface Props extends AppProps, I18nProps {
|
||||
allAccounts?: SubjectInfo;
|
||||
}
|
||||
|
||||
interface State {
|
||||
tabs: TabItem[];
|
||||
}
|
||||
function ToolboxApp ({ allAccounts, basePath, t }: Props): React.ReactElement<Props> {
|
||||
const hasAccounts = allAccounts && Object.keys(allAccounts).length !== 0;
|
||||
const tabs = [
|
||||
{
|
||||
isRoot: true,
|
||||
name: 'rpc',
|
||||
text: t('RPC calls')
|
||||
},
|
||||
{
|
||||
name: 'hash',
|
||||
text: t('Hash data')
|
||||
},
|
||||
{
|
||||
name: 'sign',
|
||||
text: t('Sign message')
|
||||
},
|
||||
{
|
||||
name: 'verify',
|
||||
text: t('Verify signature')
|
||||
}
|
||||
];
|
||||
const filteredTabs = hasAccounts
|
||||
? tabs
|
||||
: tabs.filter(({ name }): boolean => !['sign', 'verify'].includes(name));
|
||||
|
||||
class ToolboxApp extends React.PureComponent<Props, State> {
|
||||
public constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
const { t } = this.props;
|
||||
|
||||
this.state = {
|
||||
tabs: [
|
||||
{
|
||||
isRoot: true,
|
||||
name: 'rpc',
|
||||
text: t('RPC calls')
|
||||
},
|
||||
{
|
||||
name: 'hash',
|
||||
text: t('Hash data')
|
||||
},
|
||||
{
|
||||
name: 'sign',
|
||||
text: t('Sign message')
|
||||
},
|
||||
{
|
||||
name: 'verify',
|
||||
text: t('Verify signature')
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { allAccounts, basePath } = this.props;
|
||||
const { tabs } = this.state;
|
||||
const hasAccounts = allAccounts && Object.keys(allAccounts).length !== 0;
|
||||
const filteredTabs = hasAccounts
|
||||
? tabs
|
||||
: tabs.filter(({ name }): boolean => !['sign', 'verify'].includes(name));
|
||||
|
||||
return (
|
||||
<main className='toolbox--App'>
|
||||
<header>
|
||||
<Tabs
|
||||
basePath={basePath}
|
||||
items={filteredTabs}
|
||||
/>
|
||||
</header>
|
||||
<Switch>
|
||||
<Route path={`${basePath}/hash`} component={Hash} />
|
||||
<Route path={`${basePath}/sign`} component={Sign} />
|
||||
<Route path={`${basePath}/verify`} component={Verify} />
|
||||
<Route component={Rpc} />
|
||||
</Switch>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<main className='toolbox--App'>
|
||||
<header>
|
||||
<Tabs
|
||||
basePath={basePath}
|
||||
items={filteredTabs}
|
||||
/>
|
||||
</header>
|
||||
<Switch>
|
||||
<Route path={`${basePath}/hash`} component={Hash} />
|
||||
<Route path={`${basePath}/sign`} component={Sign} />
|
||||
<Route path={`${basePath}/verify`} component={Verify} />
|
||||
<Route component={Rpc} />
|
||||
</Switch>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
export default withMulti(
|
||||
|
||||
@ -13,25 +13,15 @@ import Propose from './Propose';
|
||||
|
||||
interface Props extends AppProps, BareProps, I18nProps {}
|
||||
|
||||
interface State {
|
||||
isProposeOpen: boolean;
|
||||
}
|
||||
|
||||
export default class Overview extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
isProposeOpen: false
|
||||
};
|
||||
|
||||
public render (): React.ReactNode {
|
||||
return (
|
||||
<>
|
||||
<Summary />
|
||||
<Propose />
|
||||
<Columar>
|
||||
<Proposals />
|
||||
<Approvals />
|
||||
</Columar>
|
||||
</>
|
||||
);
|
||||
}
|
||||
export default function Overview (): React.ReactElement<Props> {
|
||||
return (
|
||||
<>
|
||||
<Summary />
|
||||
<Propose />
|
||||
<Columar>
|
||||
<Proposals />
|
||||
<Approvals />
|
||||
</Columar>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user