mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-17 23:07:48 +00:00
fix(ui): ignore duplicate stale pause events during resume
This commit is contained in:
@@ -353,7 +353,7 @@ describe('useDownloadProgressStore', () => {
|
|||||||
release();
|
release();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('ignores a stale paused event during resume and accepts the new active state', async () => {
|
it('ignores duplicate stale paused events during resume and accepts the new active state', async () => {
|
||||||
const handlers: Record<string, (event: any) => void> = {};
|
const handlers: Record<string, (event: any) => void> = {};
|
||||||
vi.mocked(ipc.listenEvent).mockImplementation((event, handler) => {
|
vi.mocked(ipc.listenEvent).mockImplementation((event, handler) => {
|
||||||
handlers[event] = handler as (event: any) => void;
|
handlers[event] = handler as (event: any) => void;
|
||||||
@@ -378,6 +378,12 @@ describe('useDownloadProgressStore', () => {
|
|||||||
} });
|
} });
|
||||||
expect(useDownloadStore.getState().downloads[0].status).toBe('queued');
|
expect(useDownloadStore.getState().downloads[0].status).toBe('queued');
|
||||||
|
|
||||||
|
handlers['download-state']({ payload: {
|
||||||
|
id: 'resume-race',
|
||||||
|
status: 'paused'
|
||||||
|
} });
|
||||||
|
expect(useDownloadStore.getState().downloads[0].status).toBe('queued');
|
||||||
|
|
||||||
handlers['download-state']({ payload: {
|
handlers['download-state']({ payload: {
|
||||||
id: 'resume-race',
|
id: 'resume-race',
|
||||||
status: 'downloading'
|
status: 'downloading'
|
||||||
@@ -386,7 +392,7 @@ describe('useDownloadProgressStore', () => {
|
|||||||
release();
|
release();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows a later genuine pause after consuming the stale resume event', async () => {
|
it('accepts an explicit pause while a resume transition is in flight', async () => {
|
||||||
const handlers: Record<string, (event: any) => void> = {};
|
const handlers: Record<string, (event: any) => void> = {};
|
||||||
vi.mocked(ipc.listenEvent).mockImplementation((event, handler) => {
|
vi.mocked(ipc.listenEvent).mockImplementation((event, handler) => {
|
||||||
handlers[event] = handler as (event: any) => void;
|
handlers[event] = handler as (event: any) => void;
|
||||||
@@ -411,6 +417,9 @@ describe('useDownloadProgressStore', () => {
|
|||||||
} });
|
} });
|
||||||
expect(useDownloadStore.getState().downloads[0].status).toBe('queued');
|
expect(useDownloadStore.getState().downloads[0].status).toBe('queued');
|
||||||
|
|
||||||
|
// pauseDownload replaces the resume intent before it asks the backend to
|
||||||
|
// pause, so the next paused event is authoritative.
|
||||||
|
setDownloadControlIntent('resume-pause-race', 'pause');
|
||||||
handlers['download-state']({ payload: {
|
handlers['download-state']({ payload: {
|
||||||
id: 'resume-pause-race',
|
id: 'resume-pause-race',
|
||||||
status: 'paused'
|
status: 'paused'
|
||||||
|
|||||||
@@ -103,17 +103,18 @@ const startDownloadListeners = async () => {
|
|||||||
const status = payload.status as DownloadStatus;
|
const status = payload.status as DownloadStatus;
|
||||||
|
|
||||||
// resume_download queues the row before the backend can emit its new
|
// resume_download queues the row before the backend can emit its new
|
||||||
// active state. A paused event already emitted by the old lifecycle may
|
// active state. Paused events already emitted by the old lifecycle may
|
||||||
// arrive in that gap. Do not let it overwrite the queued transition;
|
// arrive in that gap, and more than one can be queued before the new
|
||||||
// otherwise the guard below would reject the legitimate downloading
|
// lifecycle reports its state. Do not let any of them overwrite the
|
||||||
// event and leave the row visibly paused forever.
|
// queued transition; otherwise a duplicate stale event can leave the
|
||||||
|
// row visibly paused and make dispatch reject it before IPC.
|
||||||
if (status === 'paused' &&
|
if (status === 'paused' &&
|
||||||
current.status === 'queued' &&
|
current.status === 'queued' &&
|
||||||
downloadControlIntentFor(payload.id) === 'resume') {
|
downloadControlIntentFor(payload.id) === 'resume') {
|
||||||
// Consume only the stale pause event that caused the resume race.
|
// Keep the resume intent until an active or terminal event proves
|
||||||
// A later real pause must be allowed through even if the backend has
|
// that the new lifecycle has taken over. An explicit pause replaces
|
||||||
// not emitted a new active state yet.
|
// this intent in pauseDownload, so a genuine user pause is still
|
||||||
clearDownloadControlIntent(payload.id, 'resume');
|
// applied while the transition is in flight.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (status === 'downloading' || status === 'processing' ||
|
if (status === 'downloading' || status === 'processing' ||
|
||||||
|
|||||||
Reference in New Issue
Block a user