Blocks per session graph (hidden) (#1871)

This commit is contained in:
Jaco Greeff 2019-11-12 09:58:16 +01:00 committed by GitHub
parent e15b69e62c
commit 2b9dd5baa9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 230 additions and 9 deletions

View File

@ -0,0 +1,79 @@
// Copyright 2017-2019 @polkadot/app-staking authors & contributors
// 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 { SessionIndex } from '@polkadot/types/interfaces';
import BN from 'bn.js';
import React, { useEffect, useState } from 'react';
import { Chart } from '@polkadot/react-components';
import { withCalls } from '@polkadot/react-api';
import { formatNumber } from '@polkadot/util';
import translate from '../translate';
interface Props extends I18nProps {
blockCounts?: BN[];
className?: string;
currentIndex: SessionIndex;
validatorId: string;
}
interface Value {
label: string;
value: BN;
}
function getIndexRange (currentIndex: SessionIndex): BN[] {
const range: BN[] = [];
let thisIndex: BN = currentIndex;
while (thisIndex.gtn(0) && range.length < 50) {
range.push(thisIndex);
thisIndex = thisIndex.subn(1);
}
return range.reverse();
}
function Validator ({ blockCounts, className, currentIndex, t }: Props): React.ReactElement<Props> {
const [labels, setLabels] = useState<string[]>([]);
const [values, setValues] = useState<Value[]>([]);
useEffect((): void => {
setLabels(
getIndexRange(currentIndex).map((index): string => formatNumber(index))
);
}, [currentIndex]);
useEffect((): void => {
if (blockCounts) {
setValues(
blockCounts.map((value, index): Value => ({
label: labels[index],
value
}))
);
}
}, [blockCounts, labels]);
return (
<div className={className}>
<h1>{t('blocks per session')}</h1>
<Chart.Line values={values} />
</div>
);
}
export default translate(
withCalls<Props>(
['query.imOnline.authoredBlocks', {
isMulti: true,
propName: 'blockCounts',
paramPick: ({ currentIndex, validatorId }: Props): [BN, string][] =>
getIndexRange(currentIndex).map((index): [BN, string] => [index, validatorId])
}]
)(Validator)
);

View File

@ -0,0 +1,37 @@
// Copyright 2017-2019 @polkadot/app-staking authors & contributors
// 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 { ComponentProps } from '../types';
import React from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import Validator from './Validator';
interface Props extends BareProps, ComponentProps, RouteComponentProps<{}> {
match: {
isExact: boolean;
params: {
value: string;
};
path: string;
url: string;
};
}
function Query ({ stakingOverview, match: { params: { value } } }: Props): React.ReactElement<Props> {
if (!stakingOverview) {
return <div>loading</div>;
}
return (
<Validator
currentIndex={stakingOverview.currentIndex}
validatorId={value}
/>
);
}
export default withRouter(Query);

View File

@ -21,6 +21,7 @@ import accountObservable from '@polkadot/ui-keyring/observable/accounts';
import Accounts from './Actions/Accounts';
import basicMd from './md/basic.md';
import Overview from './Overview';
import Query from './Query';
import translate from './translate';
interface Props extends AppProps, ApiProps, I18nProps {
@ -80,6 +81,7 @@ function App ({ allAccounts, allStashesAndControllers: [allStashes, allControlle
</header>
<Switch>
<Route path={`${basePath}/actions`} render={_renderComponent(Accounts)} />
<Route path={`${basePath}/query/:value`} render={_renderComponent(Query)} />
<Route render={_renderComponent(Overview)} />
</Switch>
</main>

View File

@ -4,17 +4,9 @@
import { DerivedFees, DerivedBalances, DerivedHeartbeats, DerivedStakingOverview } from '@polkadot/api-derive/types';
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
import { EraPoints } from '@polkadot/types/interfaces';
import { u32 } from '@polkadot/types';
export type Nominators = Record<string, string[]>;
export interface DerivedStakingOverviewExt {
currentElected: string[];
eraPointsEarned: EraPoints;
validatorCount: u32;
}
export interface ComponentProps {
allAccounts?: SubjectInfo;
allControllers: string[];

View File

@ -0,0 +1,109 @@
// Copyright 2017-2019 @polkadot/react-components authors & contributors
// 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 '../types';
import BN from 'bn.js';
import React, { useEffect, useState } from 'react';
import ChartJs from 'chart.js';
import { Line } from 'react-chartjs-2';
import { bnToBn } from '@polkadot/util';
interface Value {
label: string;
value: number | BN;
}
interface Props extends BareProps {
aspectRatio?: number;
values: Value[];
}
interface State {
chartData?: ChartJs.ChartData;
chartOptions?: ChartJs.ChartOptions;
jsonValues?: string;
}
interface Config {
labels: string[];
datasets: {
data: number[];
fill: boolean;
backgroundColor: string;
borderColor: string;
hoverBackgroundColor: string;
}[];
}
const alphaColor = (hexColor: string): string =>
ChartJs.helpers.color(hexColor).alpha(0.65).rgbString();
function calculateOptions (aspectRatio: number, values: Value[], jsonValues: string): State {
const chartData = values.reduce((data, { label, value }): Config => {
const dataset = data.datasets[0];
dataset.data.push(bnToBn(value).toNumber());
data.labels.push(label);
return data;
}, {
labels: [] as string[],
datasets: [{
data: [] as number[],
fill: false,
backgroundColor: alphaColor('#ff8c00'),
borderColor: alphaColor('#ff8c00'),
hoverBackgroundColor: alphaColor('#ff8c00')
}]
});
return {
chartData,
chartOptions: {
// width/height by default this is "1", i.e. a square box
aspectRatio,
// no need for the legend, expect the labels contain everything
legend: {
display: false
},
scales: {
xAxes: [{
ticks: {
beginAtZero: true
}
}]
}
},
jsonValues
};
}
export default function LineChart ({ aspectRatio = 4, className, style, values }: Props): React.ReactElement<Props> | null {
const [{ chartData, chartOptions, jsonValues }, setState] = useState<State>({});
useEffect((): void => {
const newJsonValues = JSON.stringify(values);
if (newJsonValues !== jsonValues) {
setState(calculateOptions(aspectRatio, values, newJsonValues));
}
}, [values]);
if (!chartData) {
return null;
}
return (
<div
className={className}
style={style}
>
<Line
data={chartData}
options={chartOptions}
/>
</div>
);
}

View File

@ -4,8 +4,10 @@
import Doughnut from './Doughnut';
import HorizBar from './HorizBar';
import Line from './Line';
export default {
Doughnut,
HorizBar
HorizBar,
Line
};