From 9b5cb850c297582f9dea4b2f8899a1f0517f8e5b Mon Sep 17 00:00:00 2001 From: Pavlenex <36959754+pavlenex@users.noreply.github.com> Date: Mon, 23 Feb 2026 10:38:03 +0400 Subject: [PATCH 1/3] Fix total hashrate and hashrate chart --- src/hooks/useHashrateHistory.ts | 61 ++++++++++++++++----------------- src/pages/UnifiedDashboard.tsx | 20 +++++++---- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/src/hooks/useHashrateHistory.ts b/src/hooks/useHashrateHistory.ts index a44060b..5bd6a91 100644 --- a/src/hooks/useHashrateHistory.ts +++ b/src/hooks/useHashrateHistory.ts @@ -18,41 +18,40 @@ const SAMPLE_INTERVAL_MS = 5000; // Sample every 5 seconds */ export function useHashrateHistory(currentHashrate: number | undefined): HashrateDataPoint[] { const [history, setHistory] = useState([]); - const lastSampleTime = useRef(0); + const hashrateRef = useRef(currentHashrate); + // Keep ref in sync with latest value without triggering the interval effect useEffect(() => { - if (currentHashrate === undefined || currentHashrate === null) return; + hashrateRef.current = currentHashrate; + }, [currentHashrate]); - const now = Date.now(); - - // Only add a new sample if enough time has passed - if (now - lastSampleTime.current < SAMPLE_INTERVAL_MS) return; - - lastSampleTime.current = now; - - const timeStr = new Date(now).toLocaleTimeString('en-US', { - hour: '2-digit', - minute: '2-digit', - hour12: false, - }); + // Sample on a fixed interval regardless of whether the value changed. + // Previously this used useEffect([currentHashrate]) which only fired on + // value changes — meaning a constant hashrate (including 0) would never + // accumulate more than one data point and the chart would stay blank. + useEffect(() => { + function sample() { + const value = hashrateRef.current; + if (value === undefined || value === null) return; - setHistory(prev => { - const newPoint: HashrateDataPoint = { - time: timeStr, - timestamp: now, - hashrate: currentHashrate, - }; - - const updated = [...prev, newPoint]; - - // Keep only the last MAX_HISTORY_POINTS - if (updated.length > MAX_HISTORY_POINTS) { - return updated.slice(-MAX_HISTORY_POINTS); - } - - return updated; - }); - }, [currentHashrate]); + const now = Date.now(); + const timeStr = new Date(now).toLocaleTimeString('en-US', { + hour: '2-digit', + minute: '2-digit', + hour12: false, + }); + + setHistory(prev => { + const newPoint: HashrateDataPoint = { time: timeStr, timestamp: now, hashrate: value }; + const updated = [...prev, newPoint]; + return updated.length > MAX_HISTORY_POINTS ? updated.slice(-MAX_HISTORY_POINTS) : updated; + }); + } + + sample(); // Capture initial value immediately + const id = setInterval(sample, SAMPLE_INTERVAL_MS); + return () => clearInterval(id); + }, []); // Run once on mount return history; } diff --git a/src/pages/UnifiedDashboard.tsx b/src/pages/UnifiedDashboard.tsx index c2cf8dd..28703f2 100644 --- a/src/pages/UnifiedDashboard.tsx +++ b/src/pages/UnifiedDashboard.tsx @@ -86,12 +86,20 @@ export function UnifiedDashboard() { return allClients.reduce((sum, c) => sum + (c.hashrate || 0), 0); }, [allClients]); - // Total hashrate: - // - JD mode: from SV2 client channels (poolGlobal.sv2_clients.total_hashrate) - // - Translator-only mode: from SV1 clients (poolGlobal.sv1_clients.total_hashrate or calculated) - const totalHashrate = isJdMode - ? (poolGlobal?.sv2_clients?.total_hashrate || 0) - : (poolGlobal?.sv1_clients?.total_hashrate || sv1TotalHashrate); + // Sum nominal hashrates from downstream client channels (JD mode fallback) + const clientChannelHashrate = useMemo(() => { + if (!clientChannels) return 0; + const ext = clientChannels.extended_channels.reduce((sum, ch) => sum + (ch.nominal_hashrate || 0), 0); + const std = clientChannels.standard_channels.reduce((sum, ch) => sum + (ch.nominal_hashrate || 0), 0); + return ext + std; + }, [clientChannels]); + + // Total hashrate, with multiple fallback sources: + // - JD mode: sv2_clients summary → client channel nominal hashrates + // - Translator-only mode: sv1_clients summary → individual client sum → server reported total + const totalHashrate = isJdMode + ? (poolGlobal?.sv2_clients?.total_hashrate || clientChannelHashrate) + : (poolGlobal?.sv1_clients?.total_hashrate || sv1TotalHashrate || poolGlobal?.server?.total_hashrate || 0); const totalClientChannels = isJdMode ? (poolGlobal?.sv2_clients?.total_channels || 0) From eecdec5bfd627790c7b4117a45806ec4ba91992a Mon Sep 17 00:00:00 2001 From: Pavlenex <36959754+pavlenex@users.noreply.github.com> Date: Mon, 23 Feb 2026 10:56:02 +0400 Subject: [PATCH 2/3] Remove refresh button from the codebase --- src/pages/Settings.tsx | 5 ----- src/pages/UnifiedDashboard.tsx | 12 +----------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index 8374562..237328c 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -83,11 +83,6 @@ export function Settings({ appMode = 'translator' }: SettingsProps) { View connection status, endpoints, and API documentation.

-
- -
diff --git a/src/pages/UnifiedDashboard.tsx b/src/pages/UnifiedDashboard.tsx index 28703f2..f97e0ad 100644 --- a/src/pages/UnifiedDashboard.tsx +++ b/src/pages/UnifiedDashboard.tsx @@ -1,5 +1,5 @@ import { useState, useMemo } from 'react'; -import { AlertTriangle, Search, RefreshCw } from 'lucide-react'; +import { AlertTriangle, Search } from 'lucide-react'; import { Shell } from '@/components/layout/Shell'; import { StatCard } from '@/components/data/StatCard'; import { HashrateChart } from '@/components/data/HashrateChart'; @@ -50,7 +50,6 @@ export function UnifiedDashboard() { const { data: sv1Data, isLoading: sv1Loading, - refetch: refetchSv1, } = useSv1ClientsData(0, 1000); // Fetch all for client-side filtering const { @@ -289,15 +288,6 @@ export function UnifiedDashboard() { -
- -
)} From 0f24a00d83a097931dc9774a544a85b5f73bbf7b Mon Sep 17 00:00:00 2001 From: Pavlenex <36959754+pavlenex@users.noreply.github.com> Date: Mon, 23 Feb 2026 18:15:06 +0400 Subject: [PATCH 3/3] fix(unified-dashboard): treat 0 hashrate as valid value --- src/pages/UnifiedDashboard.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/pages/UnifiedDashboard.tsx b/src/pages/UnifiedDashboard.tsx index f97e0ad..5767b69 100644 --- a/src/pages/UnifiedDashboard.tsx +++ b/src/pages/UnifiedDashboard.tsx @@ -87,7 +87,7 @@ export function UnifiedDashboard() { // Sum nominal hashrates from downstream client channels (JD mode fallback) const clientChannelHashrate = useMemo(() => { - if (!clientChannels) return 0; + if (!clientChannels) return undefined; const ext = clientChannels.extended_channels.reduce((sum, ch) => sum + (ch.nominal_hashrate || 0), 0); const std = clientChannels.standard_channels.reduce((sum, ch) => sum + (ch.nominal_hashrate || 0), 0); return ext + std; @@ -96,9 +96,15 @@ export function UnifiedDashboard() { // Total hashrate, with multiple fallback sources: // - JD mode: sv2_clients summary → client channel nominal hashrates // - Translator-only mode: sv1_clients summary → individual client sum → server reported total + // Use nullish coalescing so an explicit 0 hashrate is preserved. const totalHashrate = isJdMode - ? (poolGlobal?.sv2_clients?.total_hashrate || clientChannelHashrate) - : (poolGlobal?.sv1_clients?.total_hashrate || sv1TotalHashrate || poolGlobal?.server?.total_hashrate || 0); + ? (poolGlobal?.sv2_clients?.total_hashrate ?? clientChannelHashrate ?? 0) + : ( + poolGlobal?.sv1_clients?.total_hashrate + ?? (sv1Data ? sv1TotalHashrate : undefined) + ?? poolGlobal?.server?.total_hashrate + ?? 0 + ); const totalClientChannels = isJdMode ? (poolGlobal?.sv2_clients?.total_channels || 0)