import type { ReactNode } from 'react'; import type { StatsBucket } from './shared'; import { ArrowDownToLine, ArrowUpFromLine, HardDrive, Loader2 } from 'lucide-react'; import { formatDuration, formatRatio, formatSize, formatSpeed } from './format'; import { useFreeSpace, useSession, useSessionStats, useTorrents } from './useTransmissionData'; // Session statistics. Two columns because Transmission keeps two counters and conflating them is a classic // misreading — "this session" resets when the daemon restarts, "all time" never does. export const StatsView = () => { const { stats, isLoading } = useSessionStats(); const { session } = useSession(); const { torrents } = useTorrents(); const downloadDir = session?.['download-dir']; const { bytes: freeBytes } = useFreeSpace(downloadDir); if (isLoading && !stats) { return (
Loading statistics…
); } if (!stats) { return
No statistics.
; } const totalSize = torrents.reduce((sum, t) => sum + (t.totalSize ?? 0), 0); const onDisk = torrents.reduce((sum, t) => sum + (t.haveValid ?? 0), 0); return (
} label="Download" value={formatSpeed(stats.downloadSpeed) || '0 B/s'} /> } label="Upload" value={formatSpeed(stats.uploadSpeed) || '0 B/s'} /> } label="Free space" value={freeBytes == null ? '—' : formatSize(freeBytes)} hint={downloadDir} />

Local library

{/* haveValid, not totalSize: an unfinished or partially-wanted torrent occupies less than it claims, and the difference is the whole reason to look. */}
); }; const Bucket = ({ title, bucket }: { title: string; bucket: StatsBucket }) => (

{title}

0 ? bucket.uploadedBytes / bucket.downloadedBytes : -1)} />
); type TileProps = { label: string; value: string; hint?: string; icon?: ReactNode }; const Tile = ({ label, value, hint, icon }: TileProps) => (
{icon} {label}
{value}
{hint && (
{hint}
)}
); const Row = ({ label, value }: { label: string; value: string }) => (
{label}
{value}
);