import { CSSProperties, MouseEvent } from 'react'; import { Game } from '../lib/types'; import { downloadProgressPercent, formatDownloadBytes, formatDownloadEta, formatDownloadSpeed, formatDownloadSpeedShort, } from '../lib/gameState'; import { Icon } from './Icon'; interface Props { game: Game; size?: 'md' | 'lg'; full?: boolean; onCancel?: (game: Game) => void; } const progressStats = (game: Game) => { const progress = game.download_progress; const downloaded = progress?.downloaded_bytes ?? 0; const total = progress?.total_bytes ?? game.size; const speed = progress?.bytes_per_second ?? 0; const remaining = Math.max(0, total - downloaded); const etaSeconds = speed > 0 ? remaining / speed : Number.POSITIVE_INFINITY; return { pct: Math.min(99, Math.round(downloadProgressPercent(game))), downloaded, total, speed, eta: etaSeconds, activePeerCount: progress?.active_peer_count ?? 0, }; }; export const DownloadProgress = ({ game, size = 'md', full = false, onCancel }: Props) => { const stats = progressStats(game); const progressStyle = { '--download-progress': `${stats.pct}%`, } as CSSProperties; const className = [ 'dl', size === 'lg' ? 'dl-lg' : 'dl-md', full ? 'dl-full' : '', ].filter(Boolean).join(' '); const handleCancel = (event: MouseEvent) => { event.stopPropagation(); onCancel?.(game); }; if (size === 'lg') { const peerUnit = stats.activePeerCount === 1 ? 'peer' : 'peers'; return (
Downloading
{formatDownloadBytes(stats.downloaded)} / {formatDownloadBytes(stats.total)} · {formatDownloadSpeed(stats.speed)} {stats.activePeerCount > 0 && ( <> · from {stats.activePeerCount} {peerUnit} )} · {formatDownloadEta(stats.eta)} left
{stats.pct} %
{onCancel && ( )}
); } return (
{stats.pct} % {formatDownloadSpeedShort(stats.speed)}
); };