import React from "react"; import { Bar } from "react-chartjs-2"; import CountUp from "react-countup"; import { usePerformanceInfo, PERF_UPDATE_SEC, ClusterStatsStatus, } from "providers/stats/solanaClusterStats"; import classNames from "classnames"; import { TableCardBody } from "components/common/TableCardBody"; import { ChartOptions, ChartTooltipModel } from "chart.js"; import { PerformanceInfo } from "providers/stats/solanaPerformanceInfo"; import { StatsNotReady } from "pages/ClusterStatsPage"; export function TpsCard() { return (

Live Transaction Stats

); } function TpsCardBody() { const performanceInfo = usePerformanceInfo(); if (performanceInfo.status !== ClusterStatsStatus.Ready) { return ( ); } return ; } type Series = "short" | "medium" | "long"; const SERIES: Series[] = ["short", "medium", "long"]; const SERIES_INFO = { short: { label: (index: number) => index, interval: "30m", }, medium: { label: (index: number) => index * 4, interval: "2h", }, long: { label: (index: number) => index * 12, interval: "6h", }, }; const CUSTOM_TOOLTIP = function (this: any, tooltipModel: ChartTooltipModel) { // Tooltip Element let tooltipEl = document.getElementById("chartjs-tooltip"); // Create element on first render if (!tooltipEl) { tooltipEl = document.createElement("div"); tooltipEl.id = "chartjs-tooltip"; tooltipEl.innerHTML = `
`; document.body.appendChild(tooltipEl); } // Hide if no tooltip if (tooltipModel.opacity === 0) { tooltipEl.style.opacity = "0"; return; } // Set Text if (tooltipModel.body) { const { label, value } = tooltipModel.dataPoints[0]; const tooltipContent = tooltipEl.querySelector("div"); if (tooltipContent) { let innerHtml = `
${value} TPS
`; innerHtml += `
${label}
`; tooltipContent.innerHTML = innerHtml; } } // Enable tooltip and set position const canvas: Element = this._chart.canvas; const position = canvas.getBoundingClientRect(); tooltipEl.style.opacity = "1"; tooltipEl.style.left = position.left + window.pageXOffset + tooltipModel.caretX + "px"; tooltipEl.style.top = position.top + window.pageYOffset + tooltipModel.caretY + "px"; }; const CHART_OPTIONS = (historyMaxTps: number): ChartOptions => { return { tooltips: { intersect: false, // Show tooltip when cursor in between bars enabled: false, // Hide default tooltip custom: CUSTOM_TOOLTIP, }, legend: { display: false, }, scales: { xAxes: [ { ticks: { display: false, }, gridLines: { display: false, }, }, ], yAxes: [ { ticks: { stepSize: 100, fontSize: 10, fontColor: "#EEE", beginAtZero: true, display: true, suggestedMax: historyMaxTps, }, gridLines: { display: false, }, }, ], }, animation: { duration: 0, // general animation time }, hover: { animationDuration: 0, // duration of animations when hovering an item }, responsiveAnimationDuration: 0, // animation duration after a resize }; }; type TpsBarChartProps = { performanceInfo: PerformanceInfo }; function TpsBarChart({ performanceInfo }: TpsBarChartProps) { const { perfHistory, avgTps, historyMaxTps } = performanceInfo; const [series, setSeries] = React.useState("short"); const averageTps = Math.round(avgTps).toLocaleString("en-US"); const transactionCount = ; const seriesData = perfHistory[series]; const chartOptions = React.useMemo(() => CHART_OPTIONS(historyMaxTps), [ historyMaxTps, ]); const seriesLength = seriesData.length; const chartData: Chart.ChartData = { labels: seriesData.map((val, i) => { return `${SERIES_INFO[series].label(seriesLength - i)}min ago`; }), datasets: [ { backgroundColor: "#00D192", hoverBackgroundColor: "#00D192", borderWidth: 0, data: seriesData.map((val) => val || 0), }, ], }; return ( <> Transaction count {transactionCount} Transactions per second (TPS) {averageTps}
TPS history
{SERIES.map((key) => ( ))}
); } function AnimatedTransactionCount({ info }: { info: PerformanceInfo }) { const txCountRef = React.useRef(0); const countUpRef = React.useRef({ start: 0, period: 0, lastUpdate: 0 }); const countUp = countUpRef.current; const { transactionCount: txCount, avgTps } = info; // Track last tx count to reset count up options if (txCount !== txCountRef.current) { if (countUp.lastUpdate > 0) { // Since we overshoot below, calculate the elapsed value // and start from there. const elapsed = Date.now() - countUp.lastUpdate; const elapsedPeriods = elapsed / (PERF_UPDATE_SEC * 1000); countUp.start = countUp.start + elapsedPeriods * countUp.period; countUp.period = txCount - countUp.start; } else { // Since this is the first tx count value, estimate the previous // tx count in order to have a starting point for our animation countUp.period = PERF_UPDATE_SEC * avgTps; countUp.start = txCount - countUp.period; } countUp.lastUpdate = Date.now(); txCountRef.current = txCount; } // Overshoot the target tx count in case the next update is delayed const COUNT_PERIODS = 3; const countUpEnd = countUp.start + COUNT_PERIODS * countUp.period; return ( ); }