Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 30 additions & 31 deletions src/hooks/useHashrateHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,41 +18,40 @@ const SAMPLE_INTERVAL_MS = 5000; // Sample every 5 seconds
*/
export function useHashrateHistory(currentHashrate: number | undefined): HashrateDataPoint[] {
const [history, setHistory] = useState<HashrateDataPoint[]>([]);
const lastSampleTime = useRef<number>(0);
const hashrateRef = useRef<number | undefined>(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;
}
Expand Down
5 changes: 0 additions & 5 deletions src/pages/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,6 @@ export function Settings({ appMode = 'translator' }: SettingsProps) {
View connection status, endpoints, and API documentation.
</p>
</div>
<div className="flex items-center space-x-2">
<Button variant="outline" onClick={() => window.location.reload()}>
Refresh Status
</Button>
</div>
</div>

<Tabs defaultValue="status" className="space-y-6">
Expand Down
38 changes: 21 additions & 17 deletions src/pages/UnifiedDashboard.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -50,7 +50,6 @@ export function UnifiedDashboard() {
const {
data: sv1Data,
isLoading: sv1Loading,
refetch: refetchSv1,
} = useSv1ClientsData(0, 1000); // Fetch all for client-side filtering

const {
Expand Down Expand Up @@ -86,12 +85,26 @@ 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 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;
}, [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
// Use nullish coalescing so an explicit 0 hashrate is preserved.
const totalHashrate = isJdMode
? (poolGlobal?.sv2_clients?.total_hashrate ?? clientChannelHashrate ?? 0)
: (
poolGlobal?.sv1_clients?.total_hashrate
?? (sv1Data ? sv1TotalHashrate : undefined)
?? poolGlobal?.server?.total_hashrate
?? 0
);
Comment on lines +96 to +107
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one needs to be reviewed


const totalClientChannels = isJdMode
? (poolGlobal?.sv2_clients?.total_channels || 0)
Expand Down Expand Up @@ -281,15 +294,6 @@ export function UnifiedDashboard() {
</div>
</div>

<div className="flex items-center gap-2 w-full sm:w-auto justify-end">
Copy link
Contributor Author

@pavlenex pavlenex Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refresh buttons aren't functional and since I believe we hot refresh data I removed them.

<button
onClick={() => refetchSv1()}
className="h-9 px-3 rounded-lg border border-border/50 bg-muted/30 hover:bg-background transition-colors flex items-center gap-2 text-sm"
>
<RefreshCw className="h-4 w-4" />
Refresh
</button>
</div>
</div>
</div>
)}
Expand Down