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:
Jaco Greeff 2019-09-19 11:55:44 +02:00 committed by GitHub
parent e90a09da52
commit 1e0c99dae3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 216 additions and 253 deletions

View File

@ -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>
);
}

View File

@ -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);

View File

@ -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);
};
}, []);

View File

@ -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;
}

View File

@ -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(

View File

@ -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;
}

View File

@ -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>
</>
);
}