mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-22 08:56:44 +00:00
fix(properties): harden lifecycle and session fencing
- Fence Properties actions and Torrent moves by current caller sessions. - Preserve move progress and authoritative destinations across stale events and recovery. - Enforce immutable identity fields and transactional queued-edit rejection. - Restore subtle theme surfaces and expand regression coverage.
This commit is contained in:
@@ -72,7 +72,7 @@ describe('download action policy', () => {
|
||||
{ status: 'completed' },
|
||||
]);
|
||||
|
||||
expect(counts).toEqual({ pause: 3, resume: 4 });
|
||||
expect(counts).toEqual({ pause: 3, resume: 3 });
|
||||
});
|
||||
|
||||
it('keeps large action badges compact without changing the accessible count', () => {
|
||||
|
||||
@@ -45,7 +45,10 @@ export const countDownloadActions = (
|
||||
downloads: ReadonlyArray<{ status: DownloadStatus }>
|
||||
): DownloadActionCounts => downloads.reduce<DownloadActionCounts>((counts, download) => {
|
||||
if (canPauseDownload(download.status)) counts.pause += 1;
|
||||
if (canStartDownload(download.status)) counts.resume += 1;
|
||||
if (download.status === 'paused'
|
||||
|| (canStartDownload(download.status) && !canPauseDownload(download.status))) {
|
||||
counts.resume += 1;
|
||||
}
|
||||
return counts;
|
||||
}, { pause: 0, resume: 0 });
|
||||
|
||||
|
||||
@@ -1,7 +1,33 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { getPropertiesConnectionPresentation } from './propertiesPresentation';
|
||||
import { getPropertiesConnectionPresentation, getPropertiesProgress } from './propertiesPresentation';
|
||||
|
||||
describe('Properties connection presentation', () => {
|
||||
it('uses move progress instead of the completed download fraction during relocation', () => {
|
||||
expect(getPropertiesProgress({
|
||||
status: 'moving',
|
||||
moveProgress: 0.42,
|
||||
fraction: 1,
|
||||
downloadedBytes: 100,
|
||||
totalBytes: 100,
|
||||
totalIsEstimate: false,
|
||||
isMedia: false,
|
||||
size: '100 B',
|
||||
})).toBe(0.42);
|
||||
expect(getPropertiesProgress({
|
||||
status: 'moving',
|
||||
moveProgress: 4,
|
||||
fraction: 0,
|
||||
isMedia: false,
|
||||
})).toBe(1);
|
||||
expect(getPropertiesProgress({
|
||||
status: 'moving',
|
||||
fraction: 1,
|
||||
downloadedBytes: 100,
|
||||
totalBytes: 100,
|
||||
isMedia: false,
|
||||
})).toBe(0);
|
||||
});
|
||||
|
||||
it('keeps media concurrency out of the live header metrics', () => {
|
||||
expect(getPropertiesConnectionPresentation({
|
||||
isMedia: true,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { PropertiesSnapshot } from '../propertiesBridge';
|
||||
import { resolveDownloadFraction } from './downloadProgress';
|
||||
|
||||
export type PropertiesConnectionKind = 'media' | 'torrent' | 'aria2';
|
||||
export type PropertiesConnectionLabelKey = 'fragmentConcurrency' | 'torrentConnectedPeers' | 'connections';
|
||||
@@ -12,6 +13,12 @@ export type PropertiesConnectionPresentation = {
|
||||
|
||||
const displayCount = (value: number | undefined): string => value == null ? '—' : String(value);
|
||||
|
||||
export const getPropertiesProgress = (
|
||||
snapshot: Pick<PropertiesSnapshot, 'status' | 'moveProgress' | 'fraction' | 'downloadedBytes' | 'totalBytes' | 'totalIsEstimate' | 'isMedia' | 'size'>,
|
||||
): number => snapshot.status === 'moving'
|
||||
? Math.max(0, Math.min(1, snapshot.moveProgress ?? 0))
|
||||
: resolveDownloadFraction(snapshot);
|
||||
|
||||
export const getPropertiesConnectionPresentation = (
|
||||
snapshot: Pick<PropertiesSnapshot, 'isMedia' | 'isTorrent' | 'connections' | 'activeConnections' | 'requestedConnections' | 'connectedPeers'>,
|
||||
): PropertiesConnectionPresentation => {
|
||||
|
||||
Reference in New Issue
Block a user