From 8bdf9269b610de325d00f78ef4ac752e7c332c9d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 10 Jan 2025 13:27:04 +0700 Subject: [PATCH] [WIP]: Validator metrics to canvas (#2418) --- .../{EraPoints.tsx => EraPointsLine.tsx} | 87 ++++--- .../Graphs/LegacyEraPoints.tsx} | 98 +++----- .../src/library/ListItem/Labels/Metrics.tsx | 7 +- packages/app/src/library/PoolSync/Bar.tsx | 36 --- .../ValidatorList/ValidatorItem/Default.tsx | 6 +- .../src/overlay/canvas/CreatePool/index.tsx | 32 +-- .../src/overlay/canvas/JoinPool/Header.tsx | 40 ++-- .../canvas/JoinPool/Nominations/index.tsx | 7 +- .../JoinPool/Overview/AddressSection.tsx | 8 +- .../canvas/JoinPool/Overview/Addresses.tsx | 8 +- .../canvas/JoinPool/Overview/Performance.tsx | 83 +++++++ .../canvas/JoinPool/Overview/Roles.tsx | 8 +- .../canvas/JoinPool/Overview/Stats.tsx | 32 +-- .../canvas/JoinPool/Overview/index.tsx | 36 +-- .../src/overlay/canvas/JoinPool/Preloader.tsx | 67 +++--- .../src/overlay/canvas/JoinPool/Wrappers.ts | 220 ------------------ .../app/src/overlay/canvas/JoinPool/index.tsx | 43 ++-- .../canvas/ManageNominations/index.tsx | 24 +- .../overlay/canvas/NominatorSetup/index.tsx | 31 +-- .../src/overlay/canvas/PoolMembers/index.tsx | 14 +- .../ValidatorMetrics/ActiveGraph.tsx | 21 +- .../canvas/ValidatorMetrics/InactiveGraph.tsx | 14 ++ .../overlay/canvas/ValidatorMetrics/index.tsx | 162 +++++++++++++ packages/app/src/overlay/canvas/Wrappers.ts | 126 ---------- packages/app/src/overlay/index.tsx | 4 +- .../modals/ValidatorMetrics/InactiveGraph.tsx | 6 - .../overlay/modals/ValidatorMetrics/index.tsx | 154 ------------ .../src/queries/useValidatorEraPoints.tsx | 1 + packages/plugin-staking-api/src/types.ts | 1 + packages/styles/graphs/index.json | 10 + .../src/canvas/AccountTitle/index.module.scss | 43 ++++ .../ui-core/src/canvas/AccountTitle/index.tsx | 11 + .../src/canvas/Footer/index.module.scss | 9 + packages/ui-core/src/canvas/Footer/index.tsx | 11 + .../canvas/GraphContainer/index.module.scss | 25 ++ .../src/canvas/GraphContainer/index.tsx | 11 + .../src/canvas/GraphInner/index.module.scss | 24 ++ .../ui-core/src/canvas/GraphInner/index.tsx | 28 +++ .../ui-core/src/canvas/Head/index.module.scss | 8 + packages/ui-core/src/canvas/Head/index.tsx | 11 + .../src/canvas/HeadTags/index.module.scss | 30 +++ .../ui-core/src/canvas/HeadTags/index.tsx | 11 + .../src/canvas/Interface/index.module.scss | 35 +++ .../ui-core/src/canvas/Interface/index.tsx | 17 ++ .../ui-core/src/canvas/Main/index.module.scss | 13 ++ packages/ui-core/src/canvas/Main/index.tsx | 11 + .../src/canvas/Preload/index.module.scss | 41 ++++ packages/ui-core/src/canvas/Preload/index.tsx | 24 ++ .../ui-core/src/canvas/Stat/index.module.scss | 36 +++ packages/ui-core/src/canvas/Stat/index.tsx | 23 ++ .../src/canvas/Subheading/index.module.scss | 29 +++ .../ui-core/src/canvas/Subheading/index.tsx | 11 + .../src/canvas/Title/index.module.scss | 28 +++ packages/ui-core/src/canvas/Title/index.tsx | 11 + packages/ui-core/src/canvas/index.tsx | 12 + 55 files changed, 1060 insertions(+), 839 deletions(-) rename packages/app/src/library/Graphs/{EraPoints.tsx => EraPointsLine.tsx} (53%) rename packages/app/src/{overlay/canvas/JoinPool/Overview/PerformanceGraph.tsx => library/Graphs/LegacyEraPoints.tsx} (54%) delete mode 100644 packages/app/src/library/PoolSync/Bar.tsx create mode 100644 packages/app/src/overlay/canvas/JoinPool/Overview/Performance.tsx rename packages/app/src/overlay/{modals => canvas}/ValidatorMetrics/ActiveGraph.tsx (64%) create mode 100644 packages/app/src/overlay/canvas/ValidatorMetrics/InactiveGraph.tsx create mode 100644 packages/app/src/overlay/canvas/ValidatorMetrics/index.tsx delete mode 100644 packages/app/src/overlay/canvas/Wrappers.ts delete mode 100644 packages/app/src/overlay/modals/ValidatorMetrics/InactiveGraph.tsx delete mode 100644 packages/app/src/overlay/modals/ValidatorMetrics/index.tsx create mode 100644 packages/ui-core/src/canvas/AccountTitle/index.module.scss create mode 100644 packages/ui-core/src/canvas/AccountTitle/index.tsx create mode 100644 packages/ui-core/src/canvas/Footer/index.module.scss create mode 100644 packages/ui-core/src/canvas/Footer/index.tsx create mode 100644 packages/ui-core/src/canvas/GraphContainer/index.module.scss create mode 100644 packages/ui-core/src/canvas/GraphContainer/index.tsx create mode 100644 packages/ui-core/src/canvas/GraphInner/index.module.scss create mode 100644 packages/ui-core/src/canvas/GraphInner/index.tsx create mode 100644 packages/ui-core/src/canvas/Head/index.module.scss create mode 100644 packages/ui-core/src/canvas/Head/index.tsx create mode 100644 packages/ui-core/src/canvas/HeadTags/index.module.scss create mode 100644 packages/ui-core/src/canvas/HeadTags/index.tsx create mode 100644 packages/ui-core/src/canvas/Interface/index.module.scss create mode 100644 packages/ui-core/src/canvas/Interface/index.tsx create mode 100644 packages/ui-core/src/canvas/Main/index.module.scss create mode 100644 packages/ui-core/src/canvas/Main/index.tsx create mode 100644 packages/ui-core/src/canvas/Preload/index.module.scss create mode 100644 packages/ui-core/src/canvas/Preload/index.tsx create mode 100644 packages/ui-core/src/canvas/Stat/index.module.scss create mode 100644 packages/ui-core/src/canvas/Stat/index.tsx create mode 100644 packages/ui-core/src/canvas/Subheading/index.module.scss create mode 100644 packages/ui-core/src/canvas/Subheading/index.tsx create mode 100644 packages/ui-core/src/canvas/Title/index.module.scss create mode 100644 packages/ui-core/src/canvas/Title/index.tsx diff --git a/packages/app/src/library/Graphs/EraPoints.tsx b/packages/app/src/library/Graphs/EraPointsLine.tsx similarity index 53% rename from packages/app/src/library/Graphs/EraPoints.tsx rename to packages/app/src/library/Graphs/EraPointsLine.tsx index 0fea39992c..659e770259 100644 --- a/packages/app/src/library/Graphs/EraPoints.tsx +++ b/packages/app/src/library/Graphs/EraPointsLine.tsx @@ -2,7 +2,9 @@ // SPDX-License-Identifier: GPL-3.0-only import type { AnyJson } from '@w3ux/types' +import BigNumber from 'bignumber.js' import { + BarElement, CategoryScale, Chart as ChartJS, Legend, @@ -14,61 +16,80 @@ import { } from 'chart.js' import { useNetwork } from 'contexts/Network' import { useTheme } from 'contexts/Themes' +import { format, fromUnixTime } from 'date-fns' +import { DefaultLocale, locales } from 'locales' +import type { ValidatorEraPoints } from 'plugin-staking-api/types' import { Line } from 'react-chartjs-2' import { useTranslation } from 'react-i18next' import graphColors from 'styles/graphs/index.json' -import type { EraPointsProps } from './types' ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, + BarElement, Title, Tooltip, Legend ) -export const EraPoints = ({ items = [], height }: EraPointsProps) => { - const { t } = useTranslation('library') +export const EraPointsLine = ({ + entries, + syncing, + width, + height, +}: { + entries: ValidatorEraPoints[] + syncing: boolean + width: string | number + height: string | number +}) => { + const { i18n, t } = useTranslation() const { mode } = useTheme() const { colors } = useNetwork().networkData + // Format reward points as an array of strings, or an empty array if syncing + const dataset = syncing + ? [] + : entries.map((entry) => new BigNumber(entry.points).toString()) + + // Use primary color for line + const color = colors.primary[mode] + const options = { responsive: true, maintainAspectRatio: false, + barPercentage: 0.3, + maxBarThickness: 13, scales: { x: { - border: { - display: false, - }, + stacked: true, grid: { - color: 'rgba(0,0,0,0)', + display: false, }, ticks: { - display: true, - maxTicksLimit: 30, - autoSkip: true, - }, - title: { - display: true, - text: 'Era', + color: graphColors.canvas.axis[mode], font: { size: 10, }, + autoSkip: true, }, }, y: { + stacked: true, + beginAtZero: true, + ticks: { + color: graphColors.canvas.axis[mode], + font: { + size: 10, + }, + }, border: { display: false, }, grid: { - color: graphColors.grid[mode], - }, - min: 0, - ticks: { - display: true, - beginAtZero: false, + color: graphColors.canvas.grid[mode], }, }, }, @@ -78,7 +99,6 @@ export const EraPoints = ({ items = [], height }: EraPointsProps) => { }, title: { display: false, - text: t('eraPoints'), }, tooltip: { displayColors: false, @@ -90,7 +110,8 @@ export const EraPoints = ({ items = [], height }: EraPointsProps) => { }, callbacks: { title: () => [], - label: (context: AnyJson) => `${context.parsed.y}`, + label: (context: AnyJson) => + `${new BigNumber(context.parsed.y).decimalPlaces(0).toFormat()} ${t('eraPoints', { ns: 'library' })}`, }, intersect: false, interaction: { @@ -101,24 +122,30 @@ export const EraPoints = ({ items = [], height }: EraPointsProps) => { } const data = { - labels: items.map(({ era }) => era), + labels: entries.map(({ start }: { start: number }) => { + const dateObj = format(fromUnixTime(start), 'do MMM', { + locale: locales[i18n.resolvedLanguage ?? DefaultLocale].dateFormat, + }) + return `${dateObj}` + }), datasets: [ { - label: t('points'), - data: items.map(({ points }) => points), - borderColor: colors.primary[mode], - backgroundColor: colors.primary[mode], - pointStyle: undefined, + label: t('era', { ns: 'library' }), + data: dataset, + borderColor: color, + backgroundColor: color, pointRadius: 0, - borderWidth: 2, + borderRadius: 3, }, ], } return (
diff --git a/packages/app/src/overlay/canvas/JoinPool/Overview/PerformanceGraph.tsx b/packages/app/src/library/Graphs/LegacyEraPoints.tsx similarity index 54% rename from packages/app/src/overlay/canvas/JoinPool/Overview/PerformanceGraph.tsx rename to packages/app/src/library/Graphs/LegacyEraPoints.tsx index f66c7d0e9c..26c6e9d6ec 100644 --- a/packages/app/src/overlay/canvas/JoinPool/Overview/PerformanceGraph.tsx +++ b/packages/app/src/library/Graphs/LegacyEraPoints.tsx @@ -1,7 +1,6 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { useSize } from '@w3ux/hooks' import type { AnyJson } from '@w3ux/types' import BigNumber from 'bignumber.js' import { @@ -15,19 +14,12 @@ import { Title, Tooltip, } from 'chart.js' -import { useHelp } from 'contexts/Help' import { useNetwork } from 'contexts/Network' -import { usePoolPerformance } from 'contexts/Pools/PoolPerformance' import { useTheme } from 'contexts/Themes' -import { useUi } from 'contexts/UI' -import { formatSize } from 'library/Graphs/Utils' -import { useRef } from 'react' import { Line } from 'react-chartjs-2' import { useTranslation } from 'react-i18next' import graphColors from 'styles/graphs/index.json' -import { ButtonHelp } from 'ui-buttons' -import type { OverviewSectionProps } from '../types' -import { GraphWrapper, HeadingWrapper } from '../Wrappers' +import type { PointsByEra } from 'types' ChartJS.register( CategoryScale, @@ -40,56 +32,45 @@ ChartJS.register( Legend ) -export const PerformanceGraph = ({ - bondedPool, - performanceKey, - graphSyncing, -}: OverviewSectionProps) => { +export const LegacyEraPoints = ({ + pointsByEra, + syncing, + width, + height, +}: { + pointsByEra: PointsByEra + syncing: boolean + width: string | number + height: string | number +}) => { const { t } = useTranslation() const { mode } = useTheme() - const { openHelp } = useHelp() - const { containerRefs } = useUi() const { colors } = useNetwork().networkData - const { getPoolRewardPoints } = usePoolPerformance() - const poolRewardPoints = getPoolRewardPoints(performanceKey) - const rawEraRewardPoints = poolRewardPoints[bondedPool.addresses.stash] || {} - - // Ref to the graph container. - const graphInnerRef = useRef(null) - - // Get the size of the graph container. - const size = useSize(graphInnerRef, { - outerElement: containerRefs?.mainInterface, - }) - const { width, height } = formatSize(size, 150) - - // Format reward points as an array of strings, or an empty array if syncing. - const dataset = graphSyncing + // Format reward points as an array of strings, or an empty array if syncing + const dataset = syncing ? [] : Object.values( Object.fromEntries( - Object.entries(rawEraRewardPoints).map(([k, v]: AnyJson) => [ + Object.entries(pointsByEra).map(([k, v]) => [ k, new BigNumber(v).toString(), ]) ) ) - // Format labels, only displaying the first and last era. - const labels = Object.keys(rawEraRewardPoints).map(() => '') - - const firstEra = Object.keys(rawEraRewardPoints)[0] + // Format labels, only displaying the first and last era + const labels = Object.keys(pointsByEra).map(() => '') + const firstEra = Object.keys(pointsByEra)[0] labels[0] = firstEra - ? `${t('era', { ns: 'library' })} ${Object.keys(rawEraRewardPoints)[0]}` + ? `${t('era', { ns: 'library' })} ${Object.keys(pointsByEra)[0]}` : '' - - const lastEra = Object.keys(rawEraRewardPoints)[labels.length - 1] + const lastEra = Object.keys(pointsByEra)[labels.length - 1] labels[labels.length - 1] = lastEra - ? `${t('era', { ns: 'library' })} ${Object.keys(rawEraRewardPoints)[labels.length - 1]}` + ? `${t('era', { ns: 'library' })} ${Object.keys(pointsByEra)[labels.length - 1]}` : '' - // Use primary color for bars. + // Use primary color for line const color = colors.primary[mode] const options = { @@ -104,6 +85,7 @@ export const PerformanceGraph = ({ display: false, }, ticks: { + color: graphColors.canvas.axis[mode], font: { size: 10, }, @@ -114,6 +96,7 @@ export const PerformanceGraph = ({ stacked: true, beginAtZero: true, ticks: { + color: graphColors.canvas.axis[mode], font: { size: 10, }, @@ -122,7 +105,7 @@ export const PerformanceGraph = ({ display: false, }, grid: { - color: graphColors.grid[mode], + color: graphColors.canvas.grid[mode], }, }, }, @@ -169,29 +152,14 @@ export const PerformanceGraph = ({ } return ( -
- -

- {t('recentPerformance', { ns: 'library' })} - openHelp('Era Points')} - /> -

-
- - -
- -
-
+
+
) } diff --git a/packages/app/src/library/ListItem/Labels/Metrics.tsx b/packages/app/src/library/ListItem/Labels/Metrics.tsx index 3c0ce1cb69..9eb4543a03 100644 --- a/packages/app/src/library/ListItem/Labels/Metrics.tsx +++ b/packages/app/src/library/ListItem/Labels/Metrics.tsx @@ -7,19 +7,20 @@ import { useOverlay } from 'ui-overlay' import type { MetricsProps } from '../types' export const Metrics = ({ display, address }: MetricsProps) => { - const { openModal } = useOverlay().modal + const { openCanvas } = useOverlay().canvas return (