Functional components (app-*) (#1559)

* Functional components (app-*)

* refactor

* refactor

* refactor

* refactor

* refactor

* Revert "refactor"

This reverts commit 300a12ed7de5b1000f32f5269dff67f5ee1c1515.

* onRemove
This commit is contained in:
kwingram25 2019-08-29 17:45:02 +02:00 committed by GitHub
parent 8881a0e95e
commit 2752bedc6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 905 additions and 1094 deletions

View File

@ -11,19 +11,15 @@ interface Props extends BareProps {
children: React.ReactNode;
}
class Summary extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { children, className, style } = this.props;
return (
<div
className={className}
style={style}
>
{children}
</div>
);
}
function Summary ({ children, className, style }: Props): React.ReactElement<Props> {
return (
<div
className={className}
style={style}
>
{children}
</div>
);
}
export default styled(Summary)`

View File

@ -27,7 +27,7 @@ interface State {
isTransferOpen: boolean;
}
class Account extends React.PureComponent<Props> {
class Account extends React.PureComponent<Props, State> {
public state: State;
public constructor (props: Props) {

View File

@ -49,45 +49,41 @@ const browserInfo = detect();
const browserName: Browser | null = (browserInfo && (browserInfo.name as Browser)) || null;
const isSupported = browserName && Object.keys(available).includes(browserName);
class Banner extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { className, t } = this.props;
function Banner ({ className, t }: Props): React.ReactElement<Props> | null {
if (isWeb3Injected || !isSupported || !browserName) {
return null;
}
if (isWeb3Injected || !isSupported || !browserName) {
return null;
}
return (
<div className={className}>
<div className='box'>
<div className='info'>
<p>{t('It is recommended that you create/store your accounts securely and externally from the app. On {{yourBrowser}} the following browser extensions are available for use -', {
replace: {
yourBrowser: stringUpperFirst(browserName)
}
})}</p>
<ul>{available[browserName].map(({ desc, name, link }): React.ReactNode => (
<li key={name}>
<a
href={link}
rel='noopener noreferrer'
target='_blank'
>
{name}
</a> ({desc})
</li>
))
}</ul>
<p>{t('Accounts injected from any of these extensions will appear in this application and be available for use. The above list is updated as more extensions with external signing capability become available.')}&nbsp;<a
href='https://github.com/polkadot-js/extension'
rel='noopener noreferrer'
target='_blank'
>{t('Learn more...')}</a></p>
</div>
return (
<div className={className}>
<div className='box'>
<div className='info'>
<p>{t('It is recommended that you create/store your accounts securely and externally from the app. On {{yourBrowser}} the following browser extensions are available for use -', {
replace: {
yourBrowser: stringUpperFirst(browserName)
}
})}</p>
<ul>{available[browserName].map(({ desc, name, link }): React.ReactNode => (
<li key={name}>
<a
href={link}
rel='noopener noreferrer'
target='_blank'
>
{name}
</a> ({desc})
</li>
))
}</ul>
<p>{t('Accounts injected from any of these extensions will appear in this application and be available for use. The above list is updated as more extensions with external signing capability become available.')}&nbsp;<a
href='https://github.com/polkadot-js/extension'
rel='noopener noreferrer'
target='_blank'
>{t('Learn more...')}</a></p>
</div>
</div>
);
}
</div>
);
}
export default translate(styled(Banner)`

View File

@ -84,7 +84,7 @@ class Match extends React.PureComponent<Props, State> {
}
}
export default styled(Match as React.ComponentClass<Props>)`
export default styled(Match as React.ComponentClass<Props, State>)`
text-align: center;
&:hover {

View File

@ -40,7 +40,7 @@ interface State {
const ZERO = new BN(0);
class Transfer extends React.PureComponent<Props> {
class Transfer extends React.PureComponent<Props, State> {
public state: State;
public constructor (props: Props) {

View File

@ -35,7 +35,7 @@ const CodeCard = styled(Card)`
}
`;
class Contract extends React.PureComponent<Props> {
class Contract extends React.PureComponent<Props, State> {
public state: State = {
isForgetOpen: false,
isRemoveABIOpen: false

View File

@ -27,7 +27,7 @@ interface State {
isValid: boolean;
}
class ValidateCode extends React.PureComponent<Props> {
class ValidateCode extends React.PureComponent<Props, State> {
public state: State = {
isStored: false,
isValidHex: false,

View File

@ -22,7 +22,7 @@ interface State {
isUploadOpen: boolean;
}
class Codes extends React.PureComponent<Props> {
class Codes extends React.PureComponent<Props, State> {
public state: State = {
isAddOpen: false,
isUploadOpen: false

View File

@ -27,17 +27,12 @@ interface State {
isPasswordOpen: boolean;
}
class Contract extends React.PureComponent<Props> {
public state: State;
public constructor (props: Props) {
super(props);
this.state = {
isBackupOpen: false,
isForgetOpen: false,
isPasswordOpen: false
};
}
class Contract extends React.PureComponent<Props, State> {
public state: State = {
isBackupOpen: false,
isForgetOpen: false,
isPasswordOpen: false
};
public render (): React.ReactNode {
const { address, onCall } = this.props;

View File

@ -27,7 +27,7 @@ interface State {
isValid: boolean;
}
class ValidateAddr extends React.PureComponent<Props> {
class ValidateAddr extends React.PureComponent<Props, State> {
public state: State = {
isStored: false,
isValidAddr: false,

View File

@ -16,42 +16,30 @@ interface Props extends I18nProps {
onRemove: () => void;
}
class RemoveABI extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { onClose, t } = this.props;
return (
<Modal
className='app--accounts-Modal'
dimmer='inverted'
onClose={onClose}
open
>
<Modal.Header>
{t('Confirm ABI removal')}
</Modal.Header>
<Modal.Content>
{this.renderContent()}
</Modal.Content>
{this.renderButtons()}
</Modal>
);
}
private content = (): React.ReactNode => {
const { t } = this.props;
return (
<>
<p>{t('You are about to remove this code\'s ABI. Once completed, should you need to access it again, you will have to manually re-upload it.')}</p>
<p>{t('This operaion does not impact the associated on-chain code or any of its contracts.')}</p>
</>
);
}
private renderButtons (): React.ReactNode {
const { onClose, t } = this.props;
return (
function RemoveABI ({ code, onClose, onRemove, t }: Props): React.ReactElement<Props> {
const _onRemove = (): void => {
onClose && onClose();
onRemove();
};
return (
<Modal
className='app--accounts-Modal'
dimmer='inverted'
onClose={onClose}
open
>
<Modal.Header>
{t('Confirm ABI removal')}
</Modal.Header>
<Modal.Content>
<CodeRow
code={code}
isInline
>
<p>{t('You are about to remove this code\'s ABI. Once completed, should you need to access it again, you will have to manually re-upload it.')}</p>
<p>{t('This operaion does not impact the associated on-chain code or any of its contracts.')}</p>
</CodeRow>
</Modal.Content>
<Modal.Actions>
<Button.Group>
<Button
@ -62,33 +50,13 @@ class RemoveABI extends React.PureComponent<Props> {
<Button.Or />
<Button
isPrimary
onClick={this.onRemove}
onClick={_onRemove}
label={t('Remove')}
/>
</Button.Group>
</Modal.Actions>
);
}
private renderContent (): React.ReactNode {
const { code } = this.props;
return (
<CodeRow
code={code}
isInline
>
{this.content()}
</CodeRow>
);
}
private onRemove = (): void => {
const { onClose, onRemove } = this.props;
onClose && onClose();
onRemove();
}
</Modal>
);
}
export default translate(RemoveABI);

View File

@ -18,33 +18,23 @@ interface Props extends I18nProps {
council_proposals?: Hash[];
}
class Proposals extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { t } = this.props;
return (
<CardGrid
emptyText={t('No council motions')}
headerText={t('Motions')}
buttons={
<Propose />
}
>
{this.renderProposals()}
</CardGrid>
);
}
private renderProposals (): React.ReactNode {
const { council_proposals = [] } = this.props;
return council_proposals.map((hash: Hash): React.ReactNode => (
<Motion
hash={hash.toHex()}
key={hash.toHex()}
/>
));
}
function Proposals ({ council_proposals = [], t }: Props): React.ReactElement<Props> {
return (
<CardGrid
emptyText={t('No council motions')}
headerText={t('Motions')}
buttons={
<Propose />
}
>
{council_proposals.map((hash: Hash): React.ReactNode => (
<Motion
hash={hash.toHex()}
key={hash.toHex()}
/>
))}
</CardGrid>
);
}
export default translate(

View File

@ -2,24 +2,25 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { I18nProps } from '@polkadot/react-components/types';
import { AccountId } from '@polkadot/types/interfaces';
import React from 'react';
import { AddressCard } from '@polkadot/react-components';
interface Props {
import translate from '../translate';
interface Props extends I18nProps {
address: AccountId;
}
export default class Candidate extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { address } = this.props;
return (
<AddressCard
defaultName='candidate'
value={address}
/>
);
}
function Candidate ({ address, t }: Props): React.ReactElement<Props> {
return (
<AddressCard
defaultName={t('candidate')}
value={address}
/>
);
}
export default translate(Candidate);

View File

@ -16,18 +16,14 @@ interface Props extends I18nProps {
block: BlockNumber;
}
class Member extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { address, block, t } = this.props;
return (
<AddressCard
buttons={<div><label>{t('active until')}</label>#{formatNumber(block)}</div>}
defaultName='council member'
value={address}
/>
);
}
function Member ({ address, block, t }: Props): React.ReactElement<Props> {
return (
<AddressCard
buttons={<div><label>{t('active until')}</label>#{formatNumber(block)}</div>}
defaultName={t('council member')}
value={address}
/>
);
}
export default translate(Member);

View File

@ -15,39 +15,34 @@ import Member from './Member';
interface Props extends I18nProps, ComponentProps {}
class Members extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { electionsInfo, t } = this.props;
const { members, candidates } = electionsInfo;
return (
<Columar>
<Column
emptyText={t('No members found')}
headerText={t('members')}
>
{Object.entries(members).map(([address, block]): React.ReactNode => (
<Member
address={address}
block={block}
key={address}
/>
))}
</Column>
<Column
emptyText={t('No members found')}
headerText={t('candidates')}
>
{candidates.map((address): React.ReactNode => (
<Candidate
address={address}
key={address.toString()}
/>
))}
</Column>
</Columar>
);
}
function Members ({ electionsInfo: { candidates, members }, t }: Props): React.ReactElement<Props> {
return (
<Columar>
<Column
emptyText={t('No members found')}
headerText={t('members')}
>
{Object.entries(members).map(([address, block]): React.ReactNode => (
<Member
address={address}
block={block}
key={address}
/>
))}
</Column>
<Column
emptyText={t('No members found')}
headerText={t('candidates')}
>
{candidates.map((address): React.ReactNode => (
<Candidate
address={address}
key={address.toString()}
/>
))}
</Column>
</Columar>
);
}
export default translate(Members);

View File

@ -14,35 +14,30 @@ import translate from '../translate';
interface Props extends I18nProps, ComponentProps {}
class Summary extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { electionsInfo, t } = this.props;
const { members, candidateCount, desiredSeats, termDuration, voteCount } = electionsInfo;
function Summary ({ electionsInfo: { members, candidateCount, desiredSeats, termDuration, voteCount }, t }: Props): React.ReactElement<Props> {
return (
<SummaryBox>
<section>
<CardSummary label={t('seats')}>
{formatNumber(Object.keys(members).length)}/{formatNumber(desiredSeats)}
</CardSummary>
<CardSummary label={t('candidates')}>
{formatNumber(candidateCount)}
</CardSummary>
</section>
<section>
<CardSummary label={t('voting round')}>
#{formatNumber(voteCount)}
</CardSummary>
</section>
return (
<SummaryBox>
<section>
<CardSummary label={t('seats')}>
{formatNumber(Object.keys(members).length)}/{formatNumber(desiredSeats)}
</CardSummary>
<CardSummary label={t('candidates')}>
{formatNumber(candidateCount)}
</CardSummary>
</section>
<section>
<CardSummary label={t('voting round')}>
#{formatNumber(voteCount)}
</CardSummary>
</section>
<section>
<CardSummary label={t('term duration')}>
{formatNumber(termDuration)}
</CardSummary>
</section>
</SummaryBox>
);
}
<section>
<CardSummary label={t('term duration')}>
{formatNumber(termDuration)}
</CardSummary>
</section>
</SummaryBox>
);
}
export default translate(Summary);

View File

@ -27,22 +27,18 @@ const NULL_INFO: DerivedElectionsInfo = {
voterCount: new BN(0) as SetIndex
};
class Overview extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { electionsInfo = NULL_INFO } = this.props;
return (
<>
<Summary electionsInfo={electionsInfo} />
<Button.Group>
<SubmitCandidacy electionsInfo={electionsInfo} />
<Button.Or />
<Vote electionsInfo={electionsInfo} />
</Button.Group>
<Members electionsInfo={electionsInfo} />
</>
);
}
function Overview ({ electionsInfo = NULL_INFO }: Props): React.ReactElement<Props> {
return (
<>
<Summary electionsInfo={electionsInfo} />
<Button.Group>
<SubmitCandidacy electionsInfo={electionsInfo} />
<Button.Or />
<Vote electionsInfo={electionsInfo} />
</Button.Group>
<Members electionsInfo={electionsInfo} />
</>
);
}
export default withCalls<Props>(

View File

@ -16,24 +16,20 @@ interface Props extends I18nProps {
route: Route;
}
class Entry extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { className, route: { i18n, icon, name }, t } = this.props;
return (
<div className={className}>
<Link to={`/${name}`}>
<Icon
name={icon}
size='massive'
/>
<div className='name'>
{t(`entry.${name}`, i18n)}
</div>
</Link>
</div>
);
}
function Entry ({ className, route: { i18n, icon, name }, t }: Props): React.ReactElement<Props> {
return (
<div className={className}>
<Link to={`/${name}`}>
<Icon
name={icon}
size='massive'
/>
<div className='name'>
{t(`entry.${name}`, i18n)}
</div>
</Link>
</div>
);
}
export default translate(styled(Entry)`

View File

@ -3,7 +3,7 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { AccountId, Balance, Proposal } from '@polkadot/types/interfaces';
import { AccountId, Balance, Proposal as ProposalType } from '@polkadot/types/interfaces';
import { I18nProps } from '@polkadot/react-components/types';
import BN from 'bn.js';
@ -19,64 +19,60 @@ import Seconding from './Seconding';
interface Props extends I18nProps {
democracy_depositOf?: [Balance, Vec<AccountId>] | null;
idNumber: BN;
value: Proposal;
value: ProposalType;
}
class ProposalDisplay extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { className, democracy_depositOf, idNumber, value } = this.props;
const depositors = democracy_depositOf
? democracy_depositOf[1]
: [];
function renderProposal ({ democracy_depositOf, t }: Props): React.ReactNode {
if (!democracy_depositOf) {
return null;
}
return (
<ActionItem
className={className}
idNumber={idNumber}
proposal={value}
accessory={
<Seconding
depositors={depositors}
proposalId={idNumber}
const [balance, addresses] = democracy_depositOf;
return (
<div>
<Labelled label={t('depositors')}>
{addresses.map((address, index): React.ReactNode => (
<InputAddress
isDisabled
key={`${index}:${address}`}
value={address}
withLabel={false}
/>
}
>
{this.renderInfo()}
</ActionItem>
);
}
))}
</Labelled>
<Static label={t('balance')}>
{formatBalance(balance)}
</Static>
</div>
);
}
private renderInfo (): React.ReactNode {
const { democracy_depositOf, t } = this.props;
function Proposal (props: Props): React.ReactElement<Props> {
const { className, democracy_depositOf, idNumber, value } = props;
const depositors = democracy_depositOf
? democracy_depositOf[1]
: [];
if (!democracy_depositOf) {
return null;
}
const [balance, addresses] = democracy_depositOf;
return (
<div>
<Labelled label={t('depositors')}>
{addresses.map((address, index): React.ReactNode => (
<InputAddress
isDisabled
key={`${index}:${address}`}
value={address}
withLabel={false}
/>
))}
</Labelled>
<Static label={t('balance')}>
{formatBalance(balance)}
</Static>
</div>
);
}
return (
<ActionItem
className={className}
idNumber={idNumber}
proposal={value}
accessory={
<Seconding
depositors={depositors}
proposalId={idNumber}
/>
}
>
{renderProposal(props)}
</ActionItem>
);
}
export default withMulti(
ProposalDisplay,
Proposal,
translate,
withCalls<Props>(
['query.democracy.depositOf', {

View File

@ -18,39 +18,23 @@ interface Props extends I18nProps {
democracy_publicProps?: [BN, Proposal][];
}
interface State {
isProposeOpen: boolean;
}
class Proposals extends React.PureComponent<Props> {
public state: State = {
isProposeOpen: false
};
public render (): React.ReactNode {
const { t } = this.props;
return (
<Column
emptyText={t('No available proposals')}
headerText={t('proposals')}
>
{this.renderProposals()}
</Column>
);
}
private renderProposals (): React.ReactNode {
const { democracy_publicProps = [] } = this.props;
return democracy_publicProps.map(([idNumber, proposal]): React.ReactNode => (
<ProposalDisplay
idNumber={idNumber}
key={idNumber.toString()}
value={proposal}
/>
));
}
function Proposals ({ democracy_publicProps = [], t }: Props): React.ReactElement<Props> {
return (
<Column
emptyText={t('No available proposals')}
headerText={t('proposals')}
>
{
democracy_publicProps.map(([idNumber, proposal]): React.ReactNode => (
<ProposalDisplay
idNumber={idNumber}
key={idNumber.toString()}
value={proposal}
/>
))
}
</Column>
);
}
export default withMulti(

View File

@ -18,34 +18,26 @@ interface Props extends I18nProps {
democracy_referendums?: Option<ReferendumInfoExtended>[];
}
class Referendums extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { t } = this.props;
return (
<Column
emptyText={t('No available referendums')}
headerText={t('referendum')}
>
{this.renderReferendums()}
</Column>
);
}
private renderReferendums (): React.ReactNode {
const { democracy_referendums = [] } = this.props;
const referendums = democracy_referendums
.filter((opt): boolean => opt.isSome)
.map((opt): ReferendumInfoExtended => opt.unwrap());
return referendums.map((referendum): React.ReactNode => (
<Referendum
idNumber={referendum.index}
key={referendum.index.toString()}
value={referendum}
/>
));
}
function Referendums ({ democracy_referendums = [], t }: Props): React.ReactElement<Props> {
return (
<Column
emptyText={t('No available referendums')}
headerText={t('referendum')}
>
{
democracy_referendums
.filter((opt): boolean => opt.isSome)
.map((opt): ReferendumInfoExtended => opt.unwrap())
.map((referendum): React.ReactNode => (
<Referendum
idNumber={referendum.index}
key={referendum.index.toString()}
value={referendum}
/>
))
}
</Column>
);
}
export default translate(

View File

@ -21,42 +21,40 @@ interface Props extends I18nProps {
democracy_referendumCount?: BN;
}
class Summary extends React.PureComponent<Props> {
public render (): React.ReactNode {
const {
chain_bestNumber = new BN(0),
democracy_launchPeriod = new BN(1),
democracy_nextTally = new BN(0),
democracy_publicPropCount,
democracy_referendumCount = new BN(0),
t
} = this.props;
function Summary (props: Props): React.ReactElement<Props> {
const {
chain_bestNumber = new BN(0),
democracy_launchPeriod = new BN(1),
democracy_nextTally = new BN(0),
democracy_publicPropCount,
democracy_referendumCount = new BN(0),
t
} = props;
return (
<SummaryBox>
<section>
<CardSummary label={t('proposals')}>
{formatNumber(democracy_publicPropCount)}
</CardSummary>
<CardSummary label={t('referenda')}>
{formatNumber(democracy_referendumCount)}
</CardSummary>
<CardSummary label={t('active')}>
{formatNumber(democracy_referendumCount.sub(democracy_nextTally))}
</CardSummary>
</section>
<section className='ui--media-medium'>
<CardSummary
label={t('launch period')}
progress={{
value: chain_bestNumber.mod(democracy_launchPeriod).addn(1),
total: democracy_launchPeriod || new BN(1)
}}
/>
</section>
</SummaryBox>
);
}
return (
<SummaryBox>
<section>
<CardSummary label={t('proposals')}>
{formatNumber(democracy_publicPropCount)}
</CardSummary>
<CardSummary label={t('referenda')}>
{formatNumber(democracy_referendumCount)}
</CardSummary>
<CardSummary label={t('active')}>
{formatNumber(democracy_referendumCount.sub(democracy_nextTally))}
</CardSummary>
</section>
<section className='ui--media-medium'>
<CardSummary
label={t('launch period')}
progress={{
value: chain_bestNumber.mod(democracy_launchPeriod).addn(1),
total: democracy_launchPeriod || new BN(1)
}}
/>
</section>
</SummaryBox>
);
}
export default translate(

View File

@ -2,8 +2,6 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { AppProps, BareProps, I18nProps } from '@polkadot/react-components/types';
import React from 'react';
import { Columar } from '@polkadot/react-components';
@ -11,18 +9,14 @@ import Proposals from './Proposals';
import Referendums from './Referendums';
import Summary from './Summary';
type Props = AppProps & BareProps & I18nProps;
export default class Overview extends React.PureComponent<Props> {
public render (): React.ReactNode {
return (
<>
<Summary />
<Columar>
<Referendums />
<Proposals />
</Columar>
</>
);
}
export default function Overview (): React.ReactElement {
return (
<>
<Summary />
<Columar>
<Referendums />
<Proposals />
</Columar>
</>
);
}

View File

@ -14,23 +14,19 @@ interface Props extends BareProps, CallProps {
chain_subscribeNewHeads?: Header;
}
class BestHash extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { className, label = '', style, chain_subscribeNewHeads } = this.props;
return (
<div
className={className}
style={style}
>
{label}{
chain_subscribeNewHeads
? chain_subscribeNewHeads.hash.toHex()
: undefined
}
</div>
);
}
function BestHash ({ className, label = '', style, chain_subscribeNewHeads }: Props): React.ReactElement<Props> {
return (
<div
className={className}
style={style}
>
{label}{
chain_subscribeNewHeads
? chain_subscribeNewHeads.hash.toHex()
: undefined
}
</div>
);
}
export default withCalls<Props>('rpc.chain.subscribeNewHeads')(BestHash);

View File

@ -19,70 +19,68 @@ interface Props extends BareProps {
withLink?: boolean;
}
export default class BlockHeader extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { isSummary, value, withExplorer, withLink } = this.props;
const renderDetails = ({ number: blockNumber, extrinsicsRoot, parentHash, stateRoot }: HeaderExtended): React.ReactNode => {
const parentHex = parentHash.toHex();
if (!value) {
return null;
}
return (
<div className='contains'>
<div className='info'>
<label>parentHash</label>
<span className='hash'>{
blockNumber.unwrap().gtn(1)
? <Link to={`/explorer/query/${parentHex}`}>{parentHex}</Link>
: parentHex
}</span>
</div>
<div className='info'>
<label>extrinsicsRoot</label>
<span className='hash'>{extrinsicsRoot.toHex()}</span>
</div>
<div className='info'>
<label>stateRoot</label>
<span className='hash'>{stateRoot.toHex()}</span>
</div>
</div>
);
};
const hashHex = value.hash.toHex();
const textNumber = formatNumber(value.number);
return (
<article className='explorer--BlockHeader'>
<div className='header-outer'>
<div className='header'>
<div className='number'>{
withLink
? <Link to={`/explorer/query/${hashHex}`}>{textNumber}</Link>
: textNumber
}&nbsp;</div>
<div className='hash'>{hashHex}</div>
<div className='author ui--media-small'>{
value.author
? <AddressMini value={value.author} />
: undefined
}</div>
</div>
</div>
{
isSummary
? undefined
: this.renderDetails(value)
}
{
withExplorer
? <LinkPolkascan data={hashHex} type='block' />
: undefined
}
</article>
);
function BlockHeader ({ isSummary, value, withExplorer, withLink }: Props): React.ReactElement<Props> | null {
if (!value) {
return null;
}
private renderDetails ({ number: blockNumber, extrinsicsRoot, parentHash, stateRoot }: HeaderExtended): React.ReactNode {
const parentHex = parentHash.toHex();
const hashHex = value.hash.toHex();
const textNumber = formatNumber(value.number);
return (
<div className='contains'>
<div className='info'>
<label>parentHash</label>
<span className='hash'>{
blockNumber.unwrap().gtn(1)
? <Link to={`/explorer/query/${parentHex}`}>{parentHex}</Link>
: parentHex
}</span>
</div>
<div className='info'>
<label>extrinsicsRoot</label>
<span className='hash'>{extrinsicsRoot.toHex()}</span>
</div>
<div className='info'>
<label>stateRoot</label>
<span className='hash'>{stateRoot.toHex()}</span>
return (
<article className='explorer--BlockHeader'>
<div className='header-outer'>
<div className='header'>
<div className='number'>{
withLink
? <Link to={`/explorer/query/${hashHex}`}>{textNumber}</Link>
: textNumber
}&nbsp;</div>
<div className='hash'>{hashHex}</div>
<div className='author ui--media-small'>{
value.author
? <AddressMini value={value.author} />
: undefined
}</div>
</div>
</div>
);
}
{
isSummary
? undefined
: renderDetails(value)
}
{
withExplorer
? <LinkPolkascan data={hashHex} type='block' />
: undefined
}
</article>
);
}
export default BlockHeader;

View File

@ -39,19 +39,21 @@ interface Props extends CallProps {
headers?: HeaderExtended[];
}
class BlockHeaders extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { headers = [] } = this.props;
return headers.map((header, index): React.ReactNode => (
<BlockHeader
isSummary={!!index}
key={header.number.toString()}
value={header}
withLink={!header.number.isEmpty}
/>
));
}
function BlockHeaders ({ headers = [] }: Props): React.ReactElement<Props> {
return (
<>
{
headers.map((header, index): React.ReactNode => (
<BlockHeader
isSummary={!!index}
key={header.number.toString()}
value={header}
withLink={!header.number.isEmpty}
/>
))
}
</>
);
}
export default withCalls<Props>(['derive.chain.subscribeNewHeads', {

View File

@ -25,33 +25,29 @@ type Props = ApiProps & I18nProps & {
value: string;
};
class BlockByHash extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { system_events, chain_getBlock, chain_getHeader } = this.props;
if (!chain_getBlock || chain_getBlock.isEmpty || !chain_getHeader || chain_getHeader.isEmpty) {
return null;
}
return (
<>
<header>
<BlockHeader
value={chain_getHeader}
withExplorer
/>
</header>
<Columar>
<Extrinsics
blockNumber={chain_getHeader.number.unwrap()}
value={chain_getBlock.block.extrinsics}
/>
<Events value={system_events} />
<Logs value={chain_getHeader.digest.logs} />
</Columar>
</>
);
function BlockByHash ({ system_events, chain_getBlock, chain_getHeader }: Props): React.ReactElement<Props> | null {
if (!chain_getBlock || chain_getBlock.isEmpty || !chain_getHeader || chain_getHeader.isEmpty) {
return null;
}
return (
<>
<header>
<BlockHeader
value={chain_getHeader}
withExplorer
/>
</header>
<Columar>
<Extrinsics
blockNumber={chain_getHeader.number.unwrap()}
value={chain_getBlock.block.extrinsics}
/>
<Events value={system_events} />
<Logs value={chain_getHeader.digest.logs} />
</Columar>
</>
);
}
export default translate(

View File

@ -16,18 +16,14 @@ interface Props extends ApiProps {
value: string;
}
class BlockByNumber extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { chain_getBlockHash } = this.props;
if (!chain_getBlockHash) {
return null;
}
return (
<BlockByHash value={chain_getBlockHash.toHex()} />
);
function BlockByNumber ({ chain_getBlockHash }: Props): React.ReactElement<Props> | null {
if (!chain_getBlockHash) {
return null;
}
return (
<BlockByHash value={chain_getBlockHash.toHex()} />
);
}
export default withCalls<Props>(

View File

@ -15,27 +15,23 @@ interface Props extends I18nProps {
value?: EventRecord[];
}
class Events extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { t, value } = this.props;
if (!value || !value.length) {
return null;
}
const events = value.map((record, index): { key: string; record: EventRecord } => ({
key: `${index}`, record
}));
return (
<Column headerText={t('events')}>
<EventsDisplay
eventClassName='explorer--BlockByHash-block'
events={events}
/>
</Column>
);
function Events ({ value, t }: Props): React.ReactElement<Props> | null {
if (!value || !value.length) {
return null;
}
return (
<Column headerText={t('events')}>
<EventsDisplay
eventClassName='explorer--BlockByHash-block'
events={
value.map((record, index): { key: string; record: EventRecord } => ({
key: `${index}`, record
}))
}
/>
</Column>
);
}
export default translate(Events);

View File

@ -18,57 +18,62 @@ interface Props extends I18nProps {
withoutIndex?: boolean;
}
class Events extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { emptyLabel, events, t } = this.props;
if (!events || events.length === 0) {
return <article>{emptyLabel || t('no events available')}</article>;
}
return events.map(this.renderEvent);
}
private renderEvent = ({ key, record: { event, phase } }: KeyedEvent): React.ReactNode => {
const { eventClassName, withoutIndex } = this.props;
const extIndex = !withoutIndex && phase.isApplyExtrinsic
? phase.asApplyExtrinsic
: -1;
if (!event.method || !event.section) {
return null;
}
function Events ({ emptyLabel, eventClassName, events, withoutIndex, t }: Props): React.ReactElement<Props> {
if (!events || events.length === 0) {
return (
<article
className={`explorer--Container ${eventClassName}`}
key={key}
>
<div className='header'>
<h3>
{event.section}.{event.method}&nbsp;{
extIndex !== -1
? `(#${formatNumber(extIndex)})`
: ''
}
</h3>
</div>
<details>
<summary>
{
event.meta && event.meta.documentation
? event.meta.documentation.join(' ')
: 'Details'
}
</summary>
<EventDisplay
className='details'
value={event}
/>
</details>
<article>
{emptyLabel || t('no events available')}
</article>
);
}
return (
<>
{
events.map(
({ key, record: { event, phase } }: KeyedEvent): React.ReactNode => {
const extIndex = !withoutIndex && phase.isApplyExtrinsic
? phase.asApplyExtrinsic
: -1;
if (!event.method || !event.section) {
return null;
}
return (
<article
className={`explorer--Container ${eventClassName}`}
key={key}
>
<div className='header'>
<h3>
{event.section}.{event.method}&nbsp;{
extIndex !== -1
? `(#${formatNumber(extIndex)})`
: ''
}
</h3>
</div>
<details>
<summary>
{
event.meta && event.meta.documentation
? event.meta.documentation.join(' ')
: 'Details'
}
</summary>
<EventDisplay
className='details'
value={event}
/>
</details>
</article>
);
}
)
}
</>
);
}
export default translate(Events);

View File

@ -18,25 +18,21 @@ interface Props extends I18nProps {
events: KeyedEvent[];
}
class Main extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { events, t } = this.props;
return (
<>
<Query />
<Summary />
<Columar>
<Column headerText={t('recent blocks')}>
<BlockHeaders />
</Column>
<Column headerText={t('recent events')}>
<Events events={events} />
</Column>
</Columar>
</>
);
}
function Main ({ events, t }: Props): React.ReactElement<Props> {
return (
<>
<Query />
<Summary />
<Columar>
<Column headerText={t('recent blocks')}>
<BlockHeaders />
</Column>
<Column headerText={t('recent events')}>
<Events events={events} />
</Column>
</Columar>
</>
);
}
export default translate(Main);

View File

@ -14,63 +14,57 @@ interface Props extends I18nProps {
peers?: PeerInfo[] | null;
}
class Peers extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { t } = this.props;
const renderPeer = (peer: PeerInfo): React.ReactNode => {
const peerId = peer.peerId.toString();
return (
<tr key={peerId}>
<td className='roles'>{peer.roles.toString().toLowerCase()}</td>
<td className='peerid ui--media-medium'>{peerId}</td>
<td className='number'>{formatNumber(peer.bestNumber)}</td>
<td className='hash'>{peer.bestHash.toHex()}</td>
</tr>
);
};
const renderPeers = ({ peers, t }: Props): React.ReactNode => {
if (!peers || !peers.length) {
return (
<section className='status--Peers'>
<h1>{t('connected peers')}</h1>
{this.renderPeers()}
</section>
<div className='ui disabled'>
{t('no peers connected')}
</div>
);
}
private renderPeers (): React.ReactNode {
const { peers, t } = this.props;
return (
<article>
<table>
<thead>
<tr>
<th className='roles'>{t('role')}</th>
<th className='peerid ui--media-medium'>{t('peer id')}</th>
<th className='number'>{t('best #')}</th>
<th className='hash'>{t('best hash')}</th>
</tr>
</thead>
<tbody>
{peers
.sort((a, b): number => b.bestNumber.cmp(a.bestNumber))
.map(renderPeer)
}
</tbody>
</table>
</article>
);
};
if (!peers || !peers.length) {
return (
<div className='ui disabled'>
{t('no peers connected')}
</div>
);
}
return (
<article>
<table>
<thead>
<tr>
<th className='roles'>{t('role')}</th>
<th className='peerid ui--media-medium'>{t('peer id')}</th>
<th className='number'>{t('best #')}</th>
<th className='hash'>{t('best hash')}</th>
</tr>
</thead>
<tbody>
{peers
.sort((a, b): number => b.bestNumber.cmp(a.bestNumber))
.map(this.renderPeer)
}
</tbody>
</table>
</article>
);
}
private renderPeer = (peer: PeerInfo): React.ReactNode => {
const peerId = peer.peerId.toString();
return (
<tr key={peerId}>
<td className='roles'>{peer.roles.toString().toLowerCase()}</td>
<td className='peerid ui--media-medium'>{peerId}</td>
<td className='number'>{formatNumber(peer.bestNumber)}</td>
<td className='hash'>{peer.bestHash.toHex()}</td>
</tr>
);
}
function Peers (props: Props): React.ReactElement<Props> {
return (
<section className='status--Peers'>
<h1>{props.t('connected peers')}</h1>
{renderPeers(props)}
</section>
);
}
export default translate(Peers);

View File

@ -13,43 +13,39 @@ import translate from './translate';
type Props = I18nProps;
class Summary extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { t } = this.props;
return (
<SummaryBox>
<section>
<CardSummary label={t('last block')}>
<TimeNow />
</CardSummary>
<CardSummary
className='ui--media-small'
label={t('target')}
>
<TimePeriod />
</CardSummary>
<CardSummary
className='ui--media-small'
label={t('total issuance')}
>
<TotalIssuance />
</CardSummary>
</section>
<section className='ui--media-large'>
<SummarySession />
</section>
<section>
<CardSummary label={t('finalized')}>
<BestFinalized />
</CardSummary>
<CardSummary label={t('best')}>
<BestNumber />
</CardSummary>
</section>
</SummaryBox>
);
}
function Summary ({ t }: Props): React.ReactElement<Props> {
return (
<SummaryBox>
<section>
<CardSummary label={t('last block')}>
<TimeNow />
</CardSummary>
<CardSummary
className='ui--media-small'
label={t('target')}
>
<TimePeriod />
</CardSummary>
<CardSummary
className='ui--media-small'
label={t('total issuance')}
>
<TotalIssuance />
</CardSummary>
</section>
<section className='ui--media-large'>
<SummarySession />
</section>
<section>
<CardSummary label={t('finalized')}>
<BestFinalized />
</CardSummary>
<CardSummary label={t('best')}>
<BestNumber />
</CardSummary>
</section>
</SummaryBox>
);
}
export default translate(Summary);

View File

@ -19,69 +19,63 @@ interface Props extends I18nProps {
withSession?: boolean;
}
class SummarySession extends React.PureComponent<Props> {
public render (): React.ReactNode {
return (
<>
{this.renderSession()}
{this.renderEra()}
</>
function renderSession ({ session_info, t, withSession = true }: Props): React.ReactNode {
if (!withSession || !session_info) {
return null;
}
const label = session_info.isEpoch
? t('epoch')
: t('session');
return session_info.sessionLength.gtn(0)
? (
<CardSummary
label={label}
progress={{
total: session_info.sessionLength,
value: session_info.sessionProgress
}}
/>
)
: (
<CardSummary label={label}>
{formatNumber(session_info.currentIndex)}
</CardSummary>
);
}
function renderEra ({ session_info, t, withEra = true }: Props): React.ReactNode {
if (!withEra || !session_info) {
return null;
}
private renderEra (): React.ReactNode {
const { session_info, t, withEra = true } = this.props;
const label = t('era');
if (!withEra || !session_info) {
return null;
}
return session_info.sessionLength.gtn(0)
? (
<CardSummary
label={label}
progress={{
total: session_info && session_info.eraLength,
value: session_info && session_info.eraProgress
}}
/>
)
: (
<CardSummary label={label}>
{formatNumber(session_info.currentEra)}
</CardSummary>
);
}
const label = t('era');
return session_info.sessionLength.gtn(0)
? (
<CardSummary
label={label}
progress={{
total: session_info && session_info.eraLength,
value: session_info && session_info.eraProgress
}}
/>
)
: (
<CardSummary label={label}>
{formatNumber(session_info.currentEra)}
</CardSummary>
);
}
private renderSession (): React.ReactNode {
const { session_info, t, withSession = true } = this.props;
if (!withSession || !session_info) {
return null;
}
const label = session_info.isEpoch
? t('epoch')
: t('session');
return session_info.sessionLength.gtn(0)
? (
<CardSummary
label={label}
progress={{
total: session_info.sessionLength,
value: session_info.sessionProgress
}}
/>
)
: (
<CardSummary label={label}>
{formatNumber(session_info.currentIndex)}
</CardSummary>
);
}
function SummarySession (props: Props): React.ReactElement<Props> {
return (
<>
{renderSession(props)}
{renderEra(props)}
</>
);
}
export default translate(

View File

@ -15,20 +15,16 @@ interface Props extends BareProps, CallProps {
label?: React.ReactNode;
}
class BalanceDisplay extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { className, label, style, balances_all } = this.props;
return (
<InputBalance
className={className}
isDisabled
label={label}
style={style}
defaultValue={balances_all && balances_all.freeBalance}
/>
);
}
function BalanceDisplay ({ className, label, style, balances_all }: Props): React.ReactElement<Props> {
return (
<InputBalance
className={className}
isDisabled
label={label}
style={style}
defaultValue={balances_all && balances_all.freeBalance}
/>
);
}
export default withMulti(

View File

@ -12,26 +12,22 @@ import translate from './translate';
type Props = AppProps & I18nProps;
class ExtrinsicsApp extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { basePath, t } = this.props;
return (
<main className='extrinsics--App'>
<header>
<Tabs
basePath={basePath}
items={[{
isRoot: true,
name: 'create',
text: t('Extrinsic submission')
}]}
/>
</header>
<Selection />
</main>
);
}
function ExtrinsicsApp ({ basePath, t }: Props): React.ReactElement<Props> {
return (
<main className='extrinsics--App'>
<header>
<Tabs
basePath={basePath}
items={[{
isRoot: true,
name: 'create',
text: t('Extrinsic submission')
}]}
/>
</header>
<Selection />
</main>
);
}
export { ExtrinsicsApp };

View File

@ -35,21 +35,17 @@ const renderEntry = ({ args, type }: Log, index: number): React.ReactNode => (
</div>
);
export default class Output extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { children, logs } = this.props;
return (
<article className='container js--Output'>
<div className='logs-wrapper'>
<div className='logs-container'>
<pre className='logs-content'>
{logs.map(renderEntry)}
</pre>
</div>
export default function Output ({ children, logs }: Props): React.ReactElement<Props> {
return (
<article className='container js--Output'>
<div className='logs-wrapper'>
<div className='logs-container'>
<pre className='logs-content'>
{logs.map(renderEntry)}
</pre>
</div>
{children}
</article>
);
}
</div>
{children}
</article>
);
}

View File

@ -12,15 +12,11 @@ import Playground from './Playground';
type Props = AppProps & BareProps;
export default class AppJs extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { basePath } = this.props;
return (
<Switch>
<Route path={`${basePath}/share/:base64`} component={Playground} />
<Route component={Playground} />
</Switch>
);
}
export default function AppJs ({ basePath }: Props): React.ReactElement<Props> {
return (
<Switch>
<Route path={`${basePath}/share/:base64`} component={Playground} />
<Route component={Playground} />
</Switch>
);
}

View File

@ -23,36 +23,32 @@ interface Props extends I18nProps {
parachains_relayDispatchQueueSize?: [BN, BN];
}
class Parachain extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { className, paraId, parachains_heads, parachains_relayDispatchQueueSize, t } = this.props;
return (
<Card className={className}>
<div className='ui--Row'>
<div className='ui--Row-base'>
<div className='ui--Row-details parachains--Item-header'>
<h3>#{formatNumber(paraId)}</h3>
</div>
function Parachain ({ className, paraId, parachains_heads, parachains_relayDispatchQueueSize, t }: Props): React.ReactElement<Props> {
return (
<Card className={className}>
<div className='ui--Row'>
<div className='ui--Row-base'>
<div className='ui--Row-details parachains--Item-header'>
<h3>#{formatNumber(paraId)}</h3>
</div>
<Static
help={t('the last heads of this parachain')}
label={t('heads')}
value={parachains_heads || t('<unknown>')}
/>
<Static
help={t('the relay dispatch queue size')}
label={t('relay queue')}
value={
parachains_relayDispatchQueueSize
? formatNumber(parachains_relayDispatchQueueSize[0])
: '-'
}
/>
</div>
</Card>
);
}
<Static
help={t('the last heads of this parachain')}
label={t('heads')}
value={parachains_heads || t('<unknown>')}
/>
<Static
help={t('the relay dispatch queue size')}
label={t('relay queue')}
value={
parachains_relayDispatchQueueSize
? formatNumber(parachains_relayDispatchQueueSize[0])
: '-'
}
/>
</div>
</Card>
);
}
export default withMulti(

View File

@ -17,25 +17,21 @@ interface Props extends I18nProps {
parachains_parachains?: BN[];
}
class Parachains extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { parachains_parachains = [], t } = this.props;
return (
<Column
emptyText={t('no deployed parachains')}
headerText={t('parachains')}
>
{
parachains_parachains.length
? parachains_parachains.map((paraId): React.ReactNode =>
<Parachain key={paraId.toString()} paraId={paraId} />
)
: null
}
</Column>
);
}
function Parachains ({ parachains_parachains = [], t }: Props): React.ReactElement<Props> {
return (
<Column
emptyText={t('no deployed parachains')}
headerText={t('parachains')}
>
{
parachains_parachains.length
? parachains_parachains.map((paraId): React.ReactNode =>
<Parachain key={paraId.toString()} paraId={paraId} />
)
: null
}
</Column>
);
}
export default translate(

View File

@ -18,27 +18,23 @@ interface Props extends I18nProps {
parachains_parachains?: BN[];
}
class Summary extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { parachains_nextFreeId, parachains_parachains = [], t } = this.props;
return (
<SummaryBox>
function Summary ({ parachains_nextFreeId, parachains_parachains = [], t }: Props): React.ReactElement<Props> {
return (
<SummaryBox>
<section>
<CardSummary label={t('parachains')}>
{formatNumber(parachains_parachains.length)}
</CardSummary>
</section>
{parachains_nextFreeId && (
<section>
<CardSummary label={t('parachains')}>
{formatNumber(parachains_parachains.length)}
<CardSummary label={t('next id')}>
{formatNumber(parachains_nextFreeId)}
</CardSummary>
</section>
{parachains_nextFreeId && (
<section>
<CardSummary label={t('next id')}>
{formatNumber(parachains_nextFreeId)}
</CardSummary>
</section>
)}
</SummaryBox>
);
}
)}
</SummaryBox>
);
}
export default translate(

View File

@ -24,41 +24,39 @@ interface Props extends I18nProps {
next: string[];
}
class Summary extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { className, currentValidatorsControllersV1OrStashesV2, lastAuthor, lastBlock, next, style, staking_validatorCount, t } = this.props;
function Summary (props: Props): React.ReactElement<Props> {
const { className, currentValidatorsControllersV1OrStashesV2, lastAuthor, lastBlock, next, style, staking_validatorCount, t } = props;
return (
<SummaryBox
className={className}
style={style}
>
<section>
<CardSummary label={t('validators')}>
{currentValidatorsControllersV1OrStashesV2.length}/{staking_validatorCount ? staking_validatorCount.toString() : '-'}
</CardSummary>
<CardSummary label={t('waiting')}>
{next.length}
</CardSummary>
</section>
<section>
<CardSummary label={t('last block')}>
{lastAuthor && (
<IdentityIcon
className='validator--Account-block-icon'
size={24}
value={lastAuthor}
/>
)}
{lastBlock}
</CardSummary>
</section>
<section>
<SummarySession />
</section>
</SummaryBox>
);
}
return (
<SummaryBox
className={className}
style={style}
>
<section>
<CardSummary label={t('validators')}>
{currentValidatorsControllersV1OrStashesV2.length}/{staking_validatorCount ? staking_validatorCount.toString() : '-'}
</CardSummary>
<CardSummary label={t('waiting')}>
{next.length}
</CardSummary>
</section>
<section>
<CardSummary label={t('last block')}>
{lastAuthor && (
<IdentityIcon
className='validator--Account-block-icon'
size={24}
value={lastAuthor}
/>
)}
{lastBlock}
</CardSummary>
</section>
<section>
<SummarySession />
</section>
</SummaryBox>
);
}
export default withMulti(

View File

@ -20,50 +20,49 @@ interface Props extends ApiProps, BareProps, ComponentProps {
chain_subscribeNewHeads?: HeaderExtended;
}
class Overview extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { chain_subscribeNewHeads, allControllers, allStashes, currentValidatorsControllersV1OrStashesV2, isSubstrateV2, recentlyOnline } = this.props;
let nextSorted: string[];
// TODO: Switch to useState
function Overview (props: Props): React.ReactElement<Props> {
const { chain_subscribeNewHeads, allControllers, allStashes, currentValidatorsControllersV1OrStashesV2, isSubstrateV2, recentlyOnline } = props;
let nextSorted: string[];
if (isSubstrateV2) {
// this is a V2 node currentValidatorsControllersV1OrStashesV2 is a list of stashes
nextSorted = allStashes.filter((address): boolean =>
!currentValidatorsControllersV1OrStashesV2.includes(address)
);
} else {
// this is a V1 node currentValidatorsControllersV1OrStashesV2 is a list of controllers
nextSorted = allControllers.filter((address): boolean =>
!currentValidatorsControllersV1OrStashesV2.includes(address)
);
}
let lastBlock = '—';
let lastAuthor: string | undefined;
if (chain_subscribeNewHeads) {
lastBlock = formatNumber(chain_subscribeNewHeads.number);
lastAuthor = (chain_subscribeNewHeads.author || '').toString();
}
return (
<div className='staking--Overview'>
<Summary
allControllers={allControllers}
currentValidatorsControllersV1OrStashesV2={currentValidatorsControllersV1OrStashesV2}
lastBlock={lastBlock}
lastAuthor={lastAuthor}
next={nextSorted}
/>
<CurrentList
currentValidatorsControllersV1OrStashesV2={currentValidatorsControllersV1OrStashesV2}
lastBlock={lastBlock}
lastAuthor={lastAuthor}
next={nextSorted}
recentlyOnline={recentlyOnline}
/>
</div>
if (isSubstrateV2) {
// this is a V2 node currentValidatorsControllersV1OrStashesV2 is a list of stashes
nextSorted = allStashes.filter((address): boolean =>
!currentValidatorsControllersV1OrStashesV2.includes(address)
);
} else {
// this is a V1 node currentValidatorsControllersV1OrStashesV2 is a list of controllers
nextSorted = allControllers.filter((address): boolean =>
!currentValidatorsControllersV1OrStashesV2.includes(address)
);
}
let lastBlock = '—';
let lastAuthor: string | undefined;
if (chain_subscribeNewHeads) {
lastBlock = formatNumber(chain_subscribeNewHeads.number);
lastAuthor = (chain_subscribeNewHeads.author || '').toString();
}
return (
<div className='staking--Overview'>
<Summary
allControllers={allControllers}
currentValidatorsControllersV1OrStashesV2={currentValidatorsControllersV1OrStashesV2}
lastBlock={lastBlock}
lastAuthor={lastAuthor}
next={nextSorted}
/>
<CurrentList
currentValidatorsControllersV1OrStashesV2={currentValidatorsControllersV1OrStashesV2}
lastBlock={lastBlock}
lastAuthor={lastAuthor}
next={nextSorted}
recentlyOnline={recentlyOnline}
/>
</div>
);
}
export default withMulti(

View File

@ -14,24 +14,20 @@ interface Props extends BareProps {
value?: QueryTypes[];
}
export default class Queries extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { onRemove, value } = this.props;
if (!value || !value.length) {
return null;
}
return (
<section className='storage--Queries'>
{value.map((query): React.ReactNode =>
<Query
key={query.id}
onRemove={onRemove}
value={query}
/>
)}
</section>
);
export default function Queries ({ onRemove, value }: Props): React.ReactElement<Props> | null {
if (!value || !value.length) {
return null;
}
return (
<section className='storage--Queries'>
{value.map((query): React.ReactNode =>
<Query
key={query.id}
onRemove={onRemove}
value={query}
/>
)}
</section>
);
}

View File

@ -13,35 +13,33 @@ interface Props extends BareProps {
queue: QueueTx[];
}
export default class Results extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { queue = [] } = this.props;
function Results ({ queue = [] }: Props): React.ReactElement<Props> | null {
const filtered = queue
.filter(({ error, result }): boolean =>
!isUndefined(error) || !isUndefined(result)
)
.reverse();
const filtered = queue
.filter(({ error, result }): boolean =>
!isUndefined(error) || !isUndefined(result)
)
.reverse();
if (!filtered.length) {
return null;
}
return (
<section className='rpc--Results'>
{filtered.map(({ error, id, result, rpc: { section, method } }): React.ReactNode => (
<Output
isError={!!error}
key={id}
label={`${id}: ${section}.${method}`}
value={
error
? error.message
: <pre>{JSON.stringify(result, null, 2)}</pre>
}
/>
))}
</section>
);
if (!filtered.length) {
return null;
}
return (
<section className='rpc--Results'>
{filtered.map(({ error, id, result, rpc: { section, method } }): React.ReactNode => (
<Output
isError={!!error}
key={id}
label={`${id}: ${section}.${method}`}
value={
error
? error.message
: <pre>{JSON.stringify(result, null, 2)}</pre>
}
/>
))}
</section>
);
}
export default Results;

View File

@ -2,7 +2,6 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { BareProps } from '@polkadot/react-components/types';
import { QueueProps } from '@polkadot/react-components/Status/types';
import React from 'react';
@ -11,19 +10,15 @@ import { QueueConsumer } from '@polkadot/react-components/Status/Context';
import Results from './Results';
import Selection from './Selection';
type Props = BareProps;
export default class RpcApp extends React.PureComponent<Props> {
public render (): React.ReactNode {
return (
<QueueConsumer>
{({ txqueue, queueRpc }: QueueProps): React.ReactNode => (
<>
<Selection queueRpc={queueRpc} />
<Results queue={txqueue} />
</>
)}
</QueueConsumer>
);
}
export default function RpcApp (): React.ReactElement {
return (
<QueueConsumer>
{({ txqueue, queueRpc }: QueueProps): React.ReactNode => (
<>
<Selection queueRpc={queueRpc} />
<Results queue={txqueue} />
</>
)}
</QueueConsumer>
);
}

View File

@ -2,14 +2,12 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { AppProps } from '@polkadot/react-components/types';
import React from 'react';
type Props = AppProps;
export default class App extends React.PureComponent<Props> {
public render (): React.ReactNode {
return 'Deprecated, now operates via modal';
}
export default function App (): React.ReactElement {
return (
<>
Deprecated, now operates via modal
</>
);
}

View File

@ -28,7 +28,7 @@ interface State {
proposalIndices: BN[];
}
class ProposalsBase extends React.PureComponent<Props> {
class ProposalsBase extends React.PureComponent<Props, State> {
public state: State = {
isEmpty: true,
isProposeOpen: false,

View File

@ -19,35 +19,33 @@ interface Props extends I18nProps {
treasury_pot?: BN;
}
class Summary extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { treasury_proposalCount = new BN(0), treasury_approvals = [] as BN[], treasury_pot = new BN(0), t } = this.props;
const value = treasury_pot
? treasury_pot.toString()
: null;
function Summary (props: Props): React.ReactElement<Props> {
const { treasury_proposalCount = new BN(0), treasury_approvals = [] as BN[], treasury_pot = new BN(0), t } = props;
const value = treasury_pot
? treasury_pot.toString()
: null;
return (
<SummaryBox>
<section>
<CardSummary label={t('proposals')}>
{formatNumber(treasury_proposalCount)}
</CardSummary>
<CardSummary label={t('approved')}>
{treasury_approvals ? treasury_approvals.length : '0'}
</CardSummary>
</section>
<section>
<CardSummary label={t('pot')}>
{
value
? `${formatBalance(value, false)}${treasury_pot.gtn(0) ? formatBalance.calcSi(value).value : ''}`
: '-'
}
</CardSummary>
</section>
</SummaryBox>
);
}
return (
<SummaryBox>
<section>
<CardSummary label={t('proposals')}>
{formatNumber(treasury_proposalCount)}
</CardSummary>
<CardSummary label={t('approved')}>
{treasury_approvals ? treasury_approvals.length : '0'}
</CardSummary>
</section>
<section>
<CardSummary label={t('pot')}>
{
value
? `${formatBalance(value, false)}${treasury_pot.gtn(0) ? formatBalance.calcSi(value).value : ''}`
: '-'
}
</CardSummary>
</section>
</SummaryBox>
);
}
export default translate(