mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-09 18:59:36 +00:00
fix(downloads): close audit lifecycle gaps
Prevent duplicate Add Window submissions and preserve discard confirmation. Reject duplicate backend primary-path ownership and clear stale lifecycle progress.
This commit is contained in:
@@ -113,4 +113,47 @@ describe('useDownloadProgressStore', () => {
|
||||
expect(useDownloadProgressStore.getState().progressMap).toEqual({});
|
||||
release();
|
||||
});
|
||||
|
||||
it('drops stale progress when a download returns to a queued lifecycle', async () => {
|
||||
const handlers: Record<string, (event: any) => void> = {};
|
||||
vi.mocked(ipc.listenEvent).mockImplementation((event, handler) => {
|
||||
handlers[event] = handler as (event: any) => void;
|
||||
return Promise.resolve(vi.fn());
|
||||
});
|
||||
useDownloadStore.setState({
|
||||
downloads: [{
|
||||
id: 'reused',
|
||||
url: 'https://example.com/file',
|
||||
fileName: 'file.bin',
|
||||
status: 'downloading',
|
||||
category: 'Other',
|
||||
dateAdded: ''
|
||||
}]
|
||||
});
|
||||
|
||||
const release = await initDownloadListener();
|
||||
handlers['download-progress']({ payload: {
|
||||
id: 'reused',
|
||||
fraction: 0.8,
|
||||
speed: '1 MB/s',
|
||||
eta: '2s',
|
||||
size: '8 MB',
|
||||
size_is_final: false
|
||||
} });
|
||||
handlers['download-state']({ payload: {
|
||||
id: 'reused',
|
||||
status: 'queued'
|
||||
} });
|
||||
handlers['download-progress']({ payload: {
|
||||
id: 'reused',
|
||||
fraction: 0.9,
|
||||
speed: '2 MB/s',
|
||||
eta: '1s',
|
||||
size: '9 MB',
|
||||
size_is_final: false
|
||||
} });
|
||||
|
||||
expect(useDownloadProgressStore.getState().progressMap).toEqual({});
|
||||
release();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,9 +38,10 @@ const startDownloadListeners = async () => {
|
||||
return;
|
||||
}
|
||||
// A sidecar can flush one last progress chunk after a pause, failure,
|
||||
// or completion event. Do not let that stale chunk repopulate the live
|
||||
// progress map or overwrite a later lifecycle's first frame.
|
||||
if (current && ['completed', 'failed', 'paused'].includes(current.status)) {
|
||||
// completion, or lifecycle reset. Do not let that stale chunk repopulate
|
||||
// the live progress map or overwrite a later lifecycle's first frame.
|
||||
if (!['downloading', 'processing'].includes(current.status)) {
|
||||
useDownloadProgressStore.getState().clearDownloadProgress(payload.id);
|
||||
return;
|
||||
}
|
||||
useDownloadProgressStore.getState().updateDownloadProgress(payload.id, payload);
|
||||
@@ -74,6 +75,9 @@ const startDownloadListeners = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (['queued', 'retrying', 'completed', 'failed', 'paused'].includes(status)) {
|
||||
useDownloadProgressStore.getState().clearDownloadProgress(payload.id);
|
||||
}
|
||||
const progress = useDownloadProgressStore.getState().progressMap[payload.id];
|
||||
const updates: Partial<DownloadItem> = {
|
||||
status,
|
||||
@@ -109,7 +113,7 @@ const startDownloadListeners = async () => {
|
||||
} else if (status === 'completed' || status === 'failed') {
|
||||
mainStore.unregisterBackendIds([payload.id]);
|
||||
}
|
||||
if (status === 'completed' || status === 'failed' || status === 'paused') {
|
||||
if (['queued', 'retrying', 'completed', 'failed', 'paused'].includes(status)) {
|
||||
useDownloadProgressStore.getState().clearDownloadProgress(payload.id);
|
||||
}
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user