Files
electric-cherry/src/lib/ui/game-file-transfer.ts
T

58 lines
2.5 KiB
TypeScript

import type { HostLibraryCache } from '../game-files.js';
export const TRANSFER_REPORT_FRESH_MS = 30_000;
const nonnegative = (value: number | null | undefined): number | null =>
typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : null;
/** Keep byte progress factual; only the elapsed clock advances between host reports. */
export function gameFileTransferProgress(cache: HostLibraryCache, connected: boolean, now: number) {
const active = cache.status === 'downloading' || cache.status === 'verifying';
const complete = cache.status === 'ready' || cache.status === 'outdated';
const phase = active
? (cache.status as 'downloading' | 'verifying')
: (cache.transfer?.phase ?? null);
const total = nonnegative(cache.bytesTotal) ?? 0;
const downloaded = Math.min(total, nonnegative(cache.bytesDownloaded) ?? 0);
const verified = complete ? total : nonnegative(cache.bytesVerified);
const phaseBytes =
phase === 'verifying' ? (verified === null ? null : Math.min(total, verified)) : downloaded;
const transfer = cache.transfer;
const samePhase = transfer?.phase === phase;
const reportedAt = Date.parse(cache.updatedAt);
const reportAge =
Number.isFinite(reportedAt) && Number.isFinite(now) ? Math.max(0, now - reportedAt) : null;
const stale =
active && !!transfer && (reportAge === null || reportAge > TRANSFER_REPORT_FRESH_MS);
const live = active && connected && samePhase && !stale && reportAge !== null;
const elapsed = nonnegative(transfer?.elapsedSeconds);
// Stop extrapolating when reports stop. A disconnected or finished host uses its saved duration.
const elapsedSeconds =
elapsed === null
? null
: elapsed +
(active && connected && samePhase && reportAge !== null
? Math.min(reportAge, TRANSFER_REPORT_FRESH_MS) / 1000
: 0);
const rate = nonnegative(transfer?.bytesPerSecond);
const estimate = nonnegative(transfer?.etaSeconds);
const hasProgress = phaseBytes !== null && phaseBytes > 0 && phaseBytes < total;
const bytesPerSecond = live && hasProgress && rate !== null && rate > 0 ? rate : null;
const etaSeconds =
bytesPerSecond !== null && estimate !== null && estimate > 0 ? estimate : null;
return {
active,
complete,
phase,
total,
downloaded,
verified,
phaseBytes,
elapsedSeconds,
bytesPerSecond,
etaSeconds,
stale,
live
};
}