fix: suppress failed event for cancelled downloads
Manual download cancellation uses the same internal error path as transfer failures after the terminal-event ordering fix. That made the Tauri UI receive DownloadGameFilesFailed and show a red failure state even though the user had asked for the cancellation. Keep a clone of the cancellation token in the download task and check it after the transfer task returns an error. Cancelled downloads still refresh local state and clear active operation tracking, but they no longer emit the failed event. Real, uncancelled errors continue to send DownloadGameFilesFailed. Add unit coverage for both branches so the UI-facing event contract stays explicit. Test Plan: - just fmt - just test - just clippy - git diff --check Refs: manual cancel regression from app-state follow-up
This commit is contained in:
@@ -321,7 +321,7 @@ pub async fn handle_download_game_files_command(
|
||||
peer_whitelist,
|
||||
file_peer_map,
|
||||
tx_notify_ui_clone.clone(),
|
||||
cancel_token,
|
||||
cancel_token.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -398,8 +398,17 @@ pub async fn handle_download_game_files_command(
|
||||
}
|
||||
end_download_operation(&ctx_clone, &tx_notify_ui_clone, &download_id).await;
|
||||
download_state_guard.disarm();
|
||||
log::error!("Download failed for {download_id}: {e}");
|
||||
send_download_failed(&tx_notify_ui_clone, &download_id);
|
||||
let download_was_cancelled = cancel_token.is_cancelled();
|
||||
if download_was_cancelled {
|
||||
log::info!("Download cancelled for {download_id}: {e}");
|
||||
} else {
|
||||
log::error!("Download failed for {download_id}: {e}");
|
||||
}
|
||||
send_download_failed_unless_cancelled(
|
||||
&tx_notify_ui_clone,
|
||||
&download_id,
|
||||
download_was_cancelled,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -787,6 +796,19 @@ fn send_download_failed(tx_notify_ui: &UnboundedSender<PeerEvent>, id: &str) {
|
||||
}
|
||||
}
|
||||
|
||||
fn send_download_failed_unless_cancelled(
|
||||
tx_notify_ui: &UnboundedSender<PeerEvent>,
|
||||
id: &str,
|
||||
cancelled: bool,
|
||||
) -> bool {
|
||||
if cancelled {
|
||||
return false;
|
||||
}
|
||||
|
||||
send_download_failed(tx_notify_ui, id);
|
||||
true
|
||||
}
|
||||
|
||||
async fn end_download_operation(ctx: &Ctx, tx_notify_ui: &UnboundedSender<PeerEvent>, id: &str) {
|
||||
end_operation(ctx, tx_notify_ui, id).await;
|
||||
clear_active_download(ctx, id).await;
|
||||
@@ -1097,6 +1119,29 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancelled_download_error_does_not_emit_failed_event() {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel();
|
||||
|
||||
let emitted = send_download_failed_unless_cancelled(&tx, "game", true);
|
||||
|
||||
assert!(!emitted);
|
||||
assert!(rx.try_recv().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uncancelled_download_error_emits_failed_event() {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel();
|
||||
|
||||
let emitted = send_download_failed_unless_cancelled(&tx, "game", false);
|
||||
|
||||
assert!(emitted);
|
||||
assert!(matches!(
|
||||
rx.try_recv(),
|
||||
Ok(PeerEvent::DownloadGameFilesFailed { id }) if id == "game"
|
||||
));
|
||||
}
|
||||
|
||||
async fn recv_event(rx: &mut mpsc::UnboundedReceiver<PeerEvent>) -> PeerEvent {
|
||||
tokio::time::timeout(Duration::from_secs(1), rx.recv())
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user