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
1 change: 1 addition & 0 deletions src/containers/navigation/components/MiningTiles/CPU.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default function CPUTile() {
progressDiff={rewardsRef.current?.rewardValue}
unpaidFMT={rewardsRef.current?.unpaidFMT || '-'}
minerModuleState={cpuMiningModuleState}
hashRateUnit="H"
/>
);
}
3 changes: 0 additions & 3 deletions src/containers/navigation/components/MiningTiles/GPU.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { useMiningPoolsStore } from '@app/store/useMiningPoolsStore';
import { useEffect, useRef } from 'react';
import { setupStoreSelectors } from '@app/store/selectors/setupStoreSelectors';
import { useSetupStore } from '@app/store/useSetupStore';
import { getSelectedMiner } from '@app/store/selectors/minningStoreSelectors';

export default function GPUTile() {
const gpuPoolStats = useMiningPoolsStore((s) => s.gpuPoolStats);
Expand All @@ -17,7 +16,6 @@ export default function GPUTile() {
const miningInitiated = useMiningStore((s) => s.isGpuMiningInitiated);
const gpu_mining_status = useMiningMetricsStore((s) => s.gpu_mining_status);
const isGpuPoolEnabled = useConfigPoolsStore((s) => s.gpu_pool_enabled);
const selectedMiner = useMiningStore(getSelectedMiner);
const { hash_rate, is_mining } = gpu_mining_status;

useEffect(() => useMiningPoolsStore.subscribe((s) => (statsRef.current = s.gpuPoolStats)), []);
Expand All @@ -40,7 +38,6 @@ export default function GPUTile() {
progressDiff={rewardsRef.current?.rewardValue}
unpaidFMT={rewardsRef.current?.unpaidFMT || '-'}
minerModuleState={gpuMiningModuleState}
algo={selectedMiner?.supported_algorithms?.[0]}
/>
);
}
9 changes: 4 additions & 5 deletions src/containers/navigation/components/MiningTiles/Miner.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PoolStats } from '@app/types/app-status';
import { formatHashrate, formatNumber, FormatPreset } from '@app/utils';
import { formatHashrate, formatNumber, FormatPreset, type HashrateUnit } from '@app/utils';
import { Trans, useTranslation } from 'react-i18next';
import Tile from './components/Tile/Tile';
import { AnimatePresence } from 'motion/react';
Expand All @@ -16,7 +16,6 @@ import { offset, useFloating, useHover, useInteractions } from '@floating-ui/rea
import { useState } from 'react';
import { PoolType } from '@app/store/useMiningPoolsStore.ts';
import { AppModuleState, AppModuleStatus } from '@app/store/types/setup.ts';
import { GpuMiningAlgorithm } from '@app/types/events-payloads.ts';
export interface MinerTileProps {
title: PoolType;
mainLabelKey: string;
Expand All @@ -31,7 +30,7 @@ export interface MinerTileProps {
progressDiff?: number | null;
unpaidFMT?: string;
minerModuleState: AppModuleState;
algo?: GpuMiningAlgorithm;
hashRateUnit?: HashrateUnit;
}

export default function MinerTile({
Expand All @@ -48,7 +47,7 @@ export default function MinerTile({
progressDiff,
unpaidFMT,
minerModuleState,
algo = GpuMiningAlgorithm.C29,
hashRateUnit = 'G',
}: MinerTileProps) {
const { t } = useTranslation(['mining-view', 'p2p'], { useSuspense: false });

Expand All @@ -57,7 +56,7 @@ export default function MinerTile({
const hashrateLoading = enabled && isMining && hashRate <= 0;
const isLoading = (isMiningInitiated && !isMining) || (isMining && !isMiningInitiated) || hashrateLoading;

const formattedHashRate = formatHashrate(hashRate, true, algo);
const formattedHashRate = formatHashrate(hashRate, true, hashRateUnit);
const currentUnpaid = (poolStats?.unpaid || 0) / 1_000_000;

const mainNumber = isPoolEnabled ? currentUnpaid : formattedHashRate.value;
Expand Down
23 changes: 17 additions & 6 deletions src/utils/formatters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,27 +195,27 @@ describe('formatters', () => {

it('formats hashrates >= 1000 with kG/s', () => {
const result = formatHashrate(1500);
expect(result).toEqual({ value: 1.5, unit: ' kG/s' });
expect(result).toEqual({ value: 1.5, unit: 'kG/s' });
});

it('formats hashrates >= 1000000 with MG/s', () => {
const result = formatHashrate(1_500_000);
expect(result).toEqual({ value: 1.5, unit: ' MG/s' });
expect(result).toEqual({ value: 1.5, unit: 'MG/s' });
});

it('formats hashrates >= 1000000000 with GG/s', () => {
const result = formatHashrate(1_500_000_000);
expect(result).toEqual({ value: 1.5, unit: ' GG/s' });
expect(result).toEqual({ value: 1.5, unit: 'GG/s' });
});

it('formats hashrates >= 1000000000000 with TG/s', () => {
const result = formatHashrate(1_500_000_000_000);
expect(result).toEqual({ value: 1.5, unit: ' TG/s' });
expect(result).toEqual({ value: 1.5, unit: 'TG/s' });
});

it('formats hashrates >= 1000000000000000 with PG/s', () => {
const result = formatHashrate(1_500_000_000_000_000);
expect(result).toEqual({ value: 1.5, unit: ' PG/s' });
expect(result).toEqual({ value: 1.5, unit: 'PG/s' });
});

it('returns short unit when joinUnit is false', () => {
Expand All @@ -225,14 +225,25 @@ describe('formatters', () => {

it('handles edge case at exactly 1000', () => {
const result = formatHashrate(1000);
expect(result).toEqual({ value: 1, unit: ' kG/s' });
expect(result).toEqual({ value: 1, unit: 'kG/s' });
});

it('handles zero hashrate', () => {
const result = formatHashrate(0);
expect(result).toEqual({ value: 0, unit: 'G/s' });
});

it('formats CPU RandomX hashrates with H/s units', () => {
expect(formatHashrate(500, true, 'H')).toEqual({ value: 500, unit: 'H/s' });
expect(formatHashrate(1500, true, 'H')).toEqual({ value: 1.5, unit: 'kH/s' });
expect(formatHashrate(1_500_000, true, 'H')).toEqual({ value: 1.5, unit: 'MH/s' });
});

it('respects joinUnit for hashrates under 1000', () => {
const result = formatHashrate(500, false);
expect(result).toEqual({ value: 500, unit: '' });
});

it('rounds large values to 1 decimal', () => {
const result = formatHashrate(150);
expect(result).toEqual({ value: 150, unit: 'G/s' });
Expand Down
19 changes: 10 additions & 9 deletions src/utils/formatters.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { GpuMiningAlgorithm } from '@app/types/events-payloads';
import i18n from 'i18next';
import { TimeParts } from '@app/types/mining/schedule.ts';

Expand Down Expand Up @@ -126,42 +125,44 @@ interface Hashrate {
unit: string;
}

export function formatHashrate(hashrate: number, joinUnit = true, _algo = GpuMiningAlgorithm.C29): Hashrate {
const unit = 'G';
export type HashrateUnit = 'H' | 'G';

export function formatHashrate(hashrate: number, joinUnit = true, unit: HashrateUnit = 'G'): Hashrate {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The implementation of formatHashrate (lines 132-167) contains a few issues that affect the new 'H' unit:

  1. Inconsistent Spacing: Base units (e.g., H/s) are formatted without a leading space (line 135), while prefixed units (e.g., kH/s) include one (lines 141, 147, etc.). This leads to inconsistent UI presentation (e.g., "500H/s" vs "1.5 kH/s").
  2. joinUnit Bug: The joinUnit parameter is ignored for hashrates under 1000 (lines 132-137), always returning the full unit string even when joinUnit is false.

Since this PR modifies the function signature and introduces the HashrateUnit type, it's a good opportunity to fix these implementation details for better consistency.

const fixed = (val: number, dec = 2) => Number(val.toFixed(val >= 100 ? 1 : dec));
const formatUnit = (prefix = '') => (joinUnit ? `${prefix}${unit}/s` : prefix);
if (hashrate < 1000) {
return {
value: fixed(hashrate, 1),
unit: `${unit}/s`,
unit: formatUnit(),
};
}
if (hashrate < 1000000) {
return {
value: fixed(hashrate / 1000),
unit: joinUnit ? ` k${unit}/s` : 'k',
unit: formatUnit('k'),
};
}
if (hashrate < 1000000000) {
return {
value: fixed(hashrate / 1000000),
unit: joinUnit ? ` M${unit}/s` : 'M',
unit: formatUnit('M'),
};
}
if (hashrate < 1000000000000) {
return {
value: fixed(hashrate / 1000000000),
unit: joinUnit ? ` G${unit}/s` : 'G',
unit: formatUnit('G'),
};
}
if (hashrate < 1000000000000000) {
return {
value: fixed(hashrate / 1000000000000),
unit: joinUnit ? ` T${unit}/s` : 'T',
unit: formatUnit('T'),
};
} else {
return {
value: fixed(hashrate / 1000000000000000),
unit: joinUnit ? ` P${unit}/s` : 'P',
unit: formatUnit('P'),
};
}
}
Expand Down