Minor func. component updates (#1631)
* Minor func component updates * Swap peer summary to functional * Swap to setInterval * Remove unneeded catch
This commit is contained in:
parent
e90a09da52
commit
1e0c99dae3
@ -27,42 +27,37 @@ const renderPeer = (peer: PeerInfo): React.ReactNode => {
|
||||
);
|
||||
};
|
||||
|
||||
const renderPeers = ({ peers, t }: Props): React.ReactNode => {
|
||||
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(renderPeer)
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
);
|
||||
};
|
||||
|
||||
function Peers (props: Props): React.ReactElement<Props> {
|
||||
function Peers ({ peers, t }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<section className='status--Peers'>
|
||||
<h1>{props.t('connected peers')}</h1>
|
||||
{renderPeers(props)}
|
||||
<h1>{t('connected peers')}</h1>
|
||||
{peers && peers.length
|
||||
? (
|
||||
<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>
|
||||
)
|
||||
: (
|
||||
<div className='ui disabled'>
|
||||
{t('no peers connected')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import { I18nProps } from '@polkadot/react-components/types';
|
||||
import { Info } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { SummaryBox, CardSummary } from '@polkadot/react-components';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
import { BestNumber, Elapsed } from '@polkadot/react-query';
|
||||
@ -18,82 +18,76 @@ interface Props extends I18nProps {
|
||||
info: Info;
|
||||
}
|
||||
|
||||
interface State {
|
||||
peerBest?: BN;
|
||||
}
|
||||
const ZERO = new BN(0);
|
||||
const EMPTY_INFO = { extrinsics: null, health: null, peers: null };
|
||||
|
||||
class Summary extends React.PureComponent<Props, State> {
|
||||
public state: State = {};
|
||||
function Summary ({ info: { extrinsics, health, peers } = EMPTY_INFO, nextRefresh, t }: Props): React.ReactElement<Props> {
|
||||
const [peerBest, setPeerBest] = useState(ZERO);
|
||||
|
||||
public static getDerivedStateFromProps ({ info = {} }: Props): State | null {
|
||||
if (!info.peers) {
|
||||
return null;
|
||||
useEffect((): void => {
|
||||
if (!peers) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bestPeer = info.peers.sort((a, b): number => b.bestNumber.cmp(a.bestNumber))[0];
|
||||
const bestPeer = peers.sort((a, b): number => b.bestNumber.cmp(a.bestNumber))[0];
|
||||
|
||||
return {
|
||||
peerBest: bestPeer
|
||||
setPeerBest(
|
||||
bestPeer
|
||||
? bestPeer.bestNumber
|
||||
: new BN(0)
|
||||
};
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { info = {}, nextRefresh, t } = this.props;
|
||||
const { peerBest } = this.state;
|
||||
|
||||
return (
|
||||
<SummaryBox>
|
||||
<section>
|
||||
<CardSummary label={t('refresh in')}>
|
||||
<Elapsed value={nextRefresh} />
|
||||
</CardSummary>
|
||||
<CardSummary
|
||||
className='ui--media-small'
|
||||
label={t('total peers')}
|
||||
>
|
||||
{
|
||||
info.health
|
||||
? `${info.health.peers.toNumber()}`
|
||||
: '-'
|
||||
}
|
||||
</CardSummary>
|
||||
<CardSummary
|
||||
className='ui--media-small'
|
||||
label={t('syncing')}
|
||||
>
|
||||
{
|
||||
info.health
|
||||
? (
|
||||
info.health.isSyncing.valueOf()
|
||||
? t('yes')
|
||||
: t('no')
|
||||
)
|
||||
: '-'
|
||||
}
|
||||
</CardSummary>
|
||||
</section>
|
||||
<section className='ui--media-large'>
|
||||
<CardSummary label={t('queued tx')}>
|
||||
{
|
||||
info.extrinsics
|
||||
? `${info.extrinsics.length}`
|
||||
: '-'
|
||||
}
|
||||
</CardSummary>
|
||||
</section>
|
||||
<section>
|
||||
<CardSummary label={t('peer best')}>
|
||||
{formatNumber(peerBest)}
|
||||
</CardSummary>
|
||||
<CardSummary label={t('our best')}>
|
||||
<BestNumber />
|
||||
</CardSummary>
|
||||
</section>
|
||||
</SummaryBox>
|
||||
);
|
||||
}
|
||||
}, [peers]);
|
||||
|
||||
return (
|
||||
<SummaryBox>
|
||||
<section>
|
||||
<CardSummary label={t('refresh in')}>
|
||||
<Elapsed value={nextRefresh} />
|
||||
</CardSummary>
|
||||
<CardSummary
|
||||
className='ui--media-small'
|
||||
label={t('total peers')}
|
||||
>
|
||||
{
|
||||
health
|
||||
? `${health.peers.toNumber()}`
|
||||
: '-'
|
||||
}
|
||||
</CardSummary>
|
||||
<CardSummary
|
||||
className='ui--media-small'
|
||||
label={t('syncing')}
|
||||
>
|
||||
{
|
||||
health
|
||||
? (
|
||||
health.isSyncing.valueOf()
|
||||
? t('yes')
|
||||
: t('no')
|
||||
)
|
||||
: '-'
|
||||
}
|
||||
</CardSummary>
|
||||
</section>
|
||||
<section className='ui--media-large'>
|
||||
<CardSummary label={t('queued tx')}>
|
||||
{
|
||||
extrinsics
|
||||
? `${extrinsics.length}`
|
||||
: '-'
|
||||
}
|
||||
</CardSummary>
|
||||
</section>
|
||||
<section>
|
||||
<CardSummary label={t('peer best')}>
|
||||
{formatNumber(peerBest)}
|
||||
</CardSummary>
|
||||
<CardSummary label={t('our best')}>
|
||||
<BestNumber />
|
||||
</CardSummary>
|
||||
</section>
|
||||
</SummaryBox>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(Summary);
|
||||
|
||||
@ -37,26 +37,21 @@ function NodeInfo ({ t }: Props): React.ReactElement<Props> {
|
||||
const { api } = useContext(ApiContext);
|
||||
const [info, setInfo] = useState<Partial<Info>>({});
|
||||
const [nextRefresh, setNextRefresh] = useState(Date.now());
|
||||
const [timerId, setTimerId] = useState(0);
|
||||
|
||||
const executeStatus = (isFirstRun = false): void => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
getStatus(isFirstRun).catch((): void => {});
|
||||
};
|
||||
const getStatus = async (isFirstRun: boolean): Promise<void> => {
|
||||
if (isFirstRun || timerId) {
|
||||
setInfo(await retrieveInfo(api));
|
||||
setNextRefresh(Date.now() + POLL_TIMEOUT);
|
||||
setTimerId(window.setTimeout(executeStatus, POLL_TIMEOUT));
|
||||
}
|
||||
};
|
||||
|
||||
useEffect((): () => void => {
|
||||
executeStatus(true);
|
||||
const _getStatus = (): void => {
|
||||
retrieveInfo(api).then(setInfo);
|
||||
};
|
||||
|
||||
_getStatus();
|
||||
|
||||
const timerId = window.setInterval((): void => {
|
||||
setNextRefresh(Date.now() + POLL_TIMEOUT);
|
||||
_getStatus();
|
||||
}, POLL_TIMEOUT);
|
||||
|
||||
return (): void => {
|
||||
window.clearTimeout(timerId);
|
||||
setTimerId(0);
|
||||
window.clearInterval(timerId);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import { Vec } from '@polkadot/types';
|
||||
|
||||
export interface Info {
|
||||
blockNumber?: BlockNumber;
|
||||
extrinsics?: Vec<Extrinsic> | null;
|
||||
health?: Health | null;
|
||||
peers?: PeerInfo[] | null;
|
||||
extrinsics?: Vec<Extrinsic> | null;
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
import { BareProps, I18nProps } from '@polkadot/react-components/types';
|
||||
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Button as SUIB, Popup } from 'semantic-ui-react';
|
||||
import { Button, Input } from '@polkadot/react-components';
|
||||
@ -22,135 +22,114 @@ interface Props extends BareProps, I18nProps {
|
||||
stopJs: () => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
isOpen: boolean;
|
||||
shareText: string;
|
||||
snippetName: string;
|
||||
}
|
||||
function ActionButtons ({ className, generateLink, isCustomExample, isRunning, removeSnippet, runJs, saveSnippet, stopJs, t }: Props): React.ReactElement<Props> {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [shareText, setShareText] = useState(t('Generate link to share code example'));
|
||||
const [snippetName, setSnippetName] = useState('');
|
||||
|
||||
class ActionButtons extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
isOpen: false,
|
||||
shareText: this.props.t('Generate link to share code example'),
|
||||
snippetName: ''
|
||||
const _generateLink = (): void => {
|
||||
setShareText(t('Copied to clipboard'));
|
||||
generateLink();
|
||||
};
|
||||
const _onShareClose = (): void => {
|
||||
setShareText(t('Generate link to share code example'));
|
||||
};
|
||||
const _onChangeName = (snippetName: string): void => {
|
||||
setSnippetName(snippetName);
|
||||
};
|
||||
const _onPopupOpen = (): void => {
|
||||
setIsOpen(true);
|
||||
};
|
||||
const _onPopupClose = (): void => {
|
||||
setSnippetName('');
|
||||
setIsOpen(false);
|
||||
};
|
||||
const _saveSnippet = (): void => {
|
||||
saveSnippet(snippetName);
|
||||
_onPopupClose();
|
||||
};
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { className, isCustomExample, isRunning, removeSnippet, runJs, stopJs, t } = this.props;
|
||||
const { isOpen, shareText, snippetName } = this.state;
|
||||
|
||||
return (
|
||||
<div className={`${className} action-button`}>
|
||||
return (
|
||||
<div className={`${className} action-button`}>
|
||||
<Popup
|
||||
content={shareText}
|
||||
on='hover'
|
||||
onClose={_onShareClose}
|
||||
trigger={
|
||||
<SUIB
|
||||
circular
|
||||
icon='share alternate'
|
||||
onClick={_generateLink}
|
||||
/>
|
||||
}
|
||||
wide='very'
|
||||
/>
|
||||
{
|
||||
// FIXME: The <Popup /> event trigger on='hover' does not work together with the ui-app'
|
||||
// <Button /> component. That's why the original Semantic UI component is being used here.
|
||||
}
|
||||
{isCustomExample && (
|
||||
<Popup
|
||||
content={shareText}
|
||||
content={t('Delete this custom example')}
|
||||
on='hover'
|
||||
onClose={this.onShareClose}
|
||||
trigger={
|
||||
<SUIB
|
||||
circular
|
||||
icon='share alternate'
|
||||
onClick={this.generateLink}
|
||||
icon='trash alternate outline'
|
||||
negative
|
||||
onClick={removeSnippet}
|
||||
/>
|
||||
}
|
||||
wide='very'
|
||||
/>
|
||||
{
|
||||
// FIXME: The <Popup /> event trigger on='hover' does not work together with the ui-app'
|
||||
// <Button /> component. That's why the original Semantic UI component is being used here.
|
||||
}
|
||||
{isCustomExample && (
|
||||
<Popup
|
||||
content={t('Delete this custom example')}
|
||||
on='hover'
|
||||
trigger={
|
||||
<SUIB
|
||||
circular
|
||||
icon='trash alternate outline'
|
||||
negative
|
||||
onClick={removeSnippet}
|
||||
/>
|
||||
}
|
||||
)}
|
||||
{!(isCustomExample) && (
|
||||
<Popup
|
||||
className='popup-local'
|
||||
on='click'
|
||||
onClose={_onPopupClose}
|
||||
open={isOpen}
|
||||
trigger={
|
||||
<SUIB
|
||||
circular
|
||||
icon='save'
|
||||
onClick={_onPopupOpen}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Input
|
||||
autoFocus
|
||||
onChange={_onChangeName}
|
||||
onEnter={_saveSnippet}
|
||||
maxLength={50}
|
||||
min={1}
|
||||
placeholder={t('Name your example')}
|
||||
value={snippetName}
|
||||
withLabel={false}
|
||||
/>
|
||||
)}
|
||||
{!(isCustomExample) && (
|
||||
<Popup
|
||||
className='popup-local'
|
||||
on='click'
|
||||
onClose={this.onPopupClose}
|
||||
open={isOpen}
|
||||
trigger={
|
||||
<SUIB
|
||||
circular
|
||||
icon='save'
|
||||
onClick={this.onPopupOpen}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Input
|
||||
autoFocus
|
||||
onChange={this.onChangeName}
|
||||
onEnter={this.saveSnippet}
|
||||
maxLength={50}
|
||||
min={1}
|
||||
placeholder={t('Name your example')}
|
||||
value={snippetName}
|
||||
withLabel={false}
|
||||
/>
|
||||
<Button
|
||||
isDisabled={!snippetName.length}
|
||||
isPrimary
|
||||
label={t('Save snippet to local storage')}
|
||||
onClick={this.saveSnippet}
|
||||
/>
|
||||
</Popup>
|
||||
)}
|
||||
<Button
|
||||
className='play-button'
|
||||
icon='play'
|
||||
isCircular
|
||||
isPrimary
|
||||
onClick={runJs}
|
||||
/>
|
||||
<Button
|
||||
icon='close'
|
||||
isCircular
|
||||
isDisabled={!isRunning}
|
||||
isNegative
|
||||
onClick={stopJs}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private generateLink = (): void => {
|
||||
const { generateLink, t } = this.props;
|
||||
|
||||
this.setState({ shareText: t('Copied to clipboard') });
|
||||
generateLink();
|
||||
}
|
||||
|
||||
private onShareClose = (): void => {
|
||||
this.setState({ shareText: this.props.t('Generate link to share code example') });
|
||||
}
|
||||
|
||||
private onChangeName = (snippetName: string): void => {
|
||||
this.setState({ snippetName });
|
||||
}
|
||||
|
||||
private saveSnippet = (): void => {
|
||||
const { state: { snippetName }, props: { saveSnippet } } = this;
|
||||
|
||||
saveSnippet(snippetName);
|
||||
this.setState({ snippetName: '', isOpen: false });
|
||||
}
|
||||
|
||||
private onPopupOpen = (): void => {
|
||||
this.setState({ isOpen: true, snippetName: this.props.snippetName || '' });
|
||||
}
|
||||
|
||||
private onPopupClose = (): void => {
|
||||
this.setState({ snippetName: '', isOpen: false });
|
||||
}
|
||||
<Button
|
||||
isDisabled={!snippetName.length}
|
||||
isPrimary
|
||||
label={t('Save snippet to local storage')}
|
||||
onClick={_saveSnippet}
|
||||
/>
|
||||
</Popup>
|
||||
)}
|
||||
<Button
|
||||
className='play-button'
|
||||
icon='play'
|
||||
isCircular
|
||||
isPrimary
|
||||
onClick={runJs}
|
||||
/>
|
||||
<Button
|
||||
icon='close'
|
||||
isCircular
|
||||
isDisabled={!isRunning}
|
||||
isNegative
|
||||
onClick={stopJs}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export default class Chain extends React.PureComponent<{}> {
|
||||
public render (): React.ReactNode {
|
||||
return null;
|
||||
}
|
||||
type Props = {};
|
||||
|
||||
export default function Chain (): React.ReactElement<Props> | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -8,15 +8,15 @@ import { Columar } from '@polkadot/react-components';
|
||||
import Parachains from './Parachains';
|
||||
import Summary from './Summary';
|
||||
|
||||
export default class Overview extends React.PureComponent<{}> {
|
||||
public render (): React.ReactNode {
|
||||
return (
|
||||
<>
|
||||
<Summary />
|
||||
<Columar>
|
||||
<Parachains />
|
||||
</Columar>
|
||||
</>
|
||||
);
|
||||
}
|
||||
type Props = {};
|
||||
|
||||
export default function Overview (): React.ReactElement<Props> {
|
||||
return (
|
||||
<>
|
||||
<Summary />
|
||||
<Columar>
|
||||
<Parachains />
|
||||
</Columar>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user