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)