Skip to content

fix: 修复在多线程下载器中点击取消按钮只能回到首页的问题#524

Open
xtr-hub wants to merge 8 commits intoSeaLantern-Studio:devfrom
xtr-hub:dev
Open

fix: 修复在多线程下载器中点击取消按钮只能回到首页的问题#524
xtr-hub wants to merge 8 commits intoSeaLantern-Studio:devfrom
xtr-hub:dev

Conversation

@xtr-hub
Copy link

@xtr-hub xtr-hub commented Feb 27, 2026

提交前检查清单

  • 已阅读 提交前测试必读!!!.md 并完成要求测试
  • 本地/CI 测试通过
  • 代码审查 (Self-review) 完成

变更分类(必选其一)

  • feat 新功能
  • fix Bug 修复
  • docs 文档/模板
  • style 代码格式(不影响功能)
  • refactor 重构(既不修复 bug 也不添加功能)
  • perf 性能优化
  • test 测试相关
  • chore 构建/CI/依赖/工具链
  • revert 回滚
  • security 安全修复

影响范围(可多选)

  • 前端 Frontend

    • UI 样式/布局
    • 组件/状态/路由逻辑
    • 依赖变更 (package.json)
  • 后端 Backend

    • API 接口变更
    • 业务逻辑
    • 依赖升级
  • 基础设施 Infrastructure

    • CI/CD 配置
    • 部署脚本
    • 数据库迁移

导入规范检查: 使用别名导入,避免相对路径 ../

变更详情

摘要

修复多线程下载器在下载途中点击取消按钮会直接回到首页而实际下载进程并未停止的bug,修复后在下载途中点击取消按钮会自动完成任务清除、页面更新、进程取消、资源回收,真正实现了下载任务的取消逻辑。

动机/背景

issue #507 考虑解决取消按钮点击后只能回到首页的问题

具体改动

  • 添加 JoinHandle 管理:在 DownloadTaskState 中增加 join_handle 字段来跟踪后台任务

  • 存储任务句柄:在 create_task 中将 tokio::spawn 返回的 JoinHandle 保存起来

  • 完善取消流程:在 cancel_task 中:

  • 调用 status_handle.cancel() 触发取消令牌

  • 调用 handle.abort() 终止后台任务

  • 删除临时下载文件

  • 从任务管理器中移除任务

  • 修正网络层取消:更新 _worker 函数中的 tokio::select! 确保网络请求响应取消信号

界面变动(如适用)

  • 无显示页面变动

关联 Issue

示例:
- Close #123
  关闭 Issue #123
常见关键词 示例
close / closes / closed Close #123
fix / fixes / fixed Fixes #123
resolve / resolves / resolved Resolve #123
其他前缀 用途 示例
ref / references / refs 引用关联,不关闭 Ref #123
related / relates to 表明相关 Related to #123
part of 表明是其中一部分 Part of #123
see / see also 参考其他 Issue See #123
re 关于/回复某个 Issue Re #123
addresses 涉及但未完全解决 Addresses #123
implements 实现某个功能请求 Implements #123
merges 合并相关 Merges #123

自动化审查说明

sourcery-ai 及其他 code review 工具请务必进行中英双语审查与交流。

Note: Please ensure sourcery-ai and other tools perform bilingual (Chinese & English) review.

Summary by Sourcery

在前端和后端中为下载任务实现完善的取消机制。

Bug 修复:

  • 让下载取消按钮停止当前正在运行的下载任务,而不是跳转到其他页面。
  • 确保被取消的下载会终止后台任务、移除其状态并删除部分下载的文件,同时不报告伪错误。

增强功能:

  • 添加任务的 JoinHandle 和取消令牌(cancellation token)的串联,用于干净地追踪并中止下载工作任务。
  • 更新启动候选项的排序逻辑,使用较新的不可变 toSorted API。

构建:

  • 添加 tokio-util 作为依赖,用于取消相关的工具功能。
Original summary in English

Summary by Sourcery

Implement proper cancellation for download tasks across frontend and backend.

Bug Fixes:

  • Make the download cancel button stop the active download task instead of navigating away.
  • Ensure cancelled downloads terminate background tasks, remove their state, and delete partial files without reporting spurious errors.

Enhancements:

  • Add task JoinHandle and cancellation token wiring to track and abort download worker tasks cleanly.
  • Update startup candidate sorting to use the newer immutable toSorted API.

Build:

  • Add tokio-util as a dependency for cancellation utilities.

- 修复下载取消时网络连接未正确断开的问题
- 添加取消时的临时文件清理机制
- 实现多层级取消架构(前端、后端命令、下载管理器、网络层)
- 通过 JoinHandle 管理后台任务并支持强制终止
- 确保内存缓冲区和资源在取消时完全释放
- 解决"伪停止"问题,使取消操作真正停止所有后台活动

Fixes: 下载取消功能不完整的问题
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 27, 2026

Reviewer's Guide

通过跟踪生成的 Tokio 任务来实现一个真正可取消的下载工作流,将取消令牌在整个下载器中向下传递,把新的取消命令从 Tauri 后端串联到 Vue 前端,并更新 UI 的取消按钮,使其停止当前下载而不是导航回首页;同时现代化了一个启动工具中的排序调用,并新增了 tokio-util 依赖。

新的可取消下载流程的时序图

sequenceDiagram
  actor User
  participant DownloadFileView
  participant downloadApi
  participant TauriRuntime
  participant cancel_download_task
  participant DownloadManager
  participant DownloadTaskState
  participant DownloadStatus
  participant TokioJoinHandle
  participant MultiThreadDownloader

  User ->> DownloadFileView: click_cancel_button
  DownloadFileView ->> downloadApi: cancelDownloadTask(taskInfo.id)
  downloadApi ->> TauriRuntime: tauriInvoke cancel_download_task idStr
  TauriRuntime ->> cancel_download_task: invoke_command
  cancel_download_task ->> DownloadManager: cancel_task(id)

  DownloadManager ->> DownloadTaskState: read_state(id)
  DownloadManager ->> DownloadStatus: status_handle.cancel()
  DownloadManager ->> TokioJoinHandle: join_handle.abort()
  DownloadManager ->> DownloadManager: remove_task_from_map(id)
  DownloadManager ->> DownloadManager: remove_temp_file(file_path)
  DownloadManager -->> cancel_download_task: Ok
  cancel_download_task -->> TauriRuntime: Ok
  TauriRuntime -->> downloadApi: Ok
  downloadApi -->> DownloadFileView: resolve_promise
  DownloadFileView ->> DownloadFileView: stop_polling_and_reset_task_state

  rect rgb(230,230,250)
    DownloadStatus ->> MultiThreadDownloader: expose_cancel_token
    MultiThreadDownloader ->> MultiThreadDownloader: worker_uses_select_with_cancel_token
    MultiThreadDownloader ->> MultiThreadDownloader: check_status.cancelled_during_chunks
  end
Loading

下载管理器与下载器类型的更新类图

classDiagram
  class DownloadManager {
    - HashMap~Uuid, Arc~DownloadTaskState~~ tasks
    + create_task(url: String, path: String, thread_count: usize) Uuid
    + get_progress(id: Uuid) Option~TaskProgressResponse~
    + get_all_progress() Vec~TaskProgressResponse~
    + cancel_task(id: Uuid) Result~(), String~
  }

  class DownloadTaskState {
    - String _file_path
    - tokio::sync::Mutex~Option~Arc~DownloadStatus~~~ status_handle
    - RwLock~TaskStatus~ internal_status
    - tokio::sync::Mutex~Option~JoinHandle~()~~ join_handle
  }

  class TaskStatus {
    <<enum>>
    Pending
    Downloading
    Completed
    Error
  }

  class MultiThreadDownloader {
    + download(url: String, path: String, thread_count: usize) Result~Arc~DownloadStatus~, String~
    + worker(url: String, path: String, start: u64, end: u64, status: Arc~DownloadStatus~) Result~(), Box~dyn std::error::Error + Send + Sync~~
  }

  class DownloadStatus {
    + AtomicU64 total_size
    + AtomicU64 downloaded
    + RwLock~Option~String~~ error_message
    - CancellationToken cancel_token
    + new(total_size: u64) DownloadStatus
    + cancel()
    + cancelled() bool
    + set_error(msg: String) async
  }

  DownloadManager --> DownloadTaskState : manages
  DownloadTaskState --> TaskStatus : has
  DownloadTaskState --> DownloadStatus : holds
  DownloadTaskState --> TokioJoinHandle : holds
  MultiThreadDownloader --> DownloadStatus : updates

  class TokioJoinHandle {
    + abort()
  }

  class CancellationToken {
    + cancel()
    + is_cancelled() bool
  }

  DownloadStatus --> CancellationToken : owns
Loading

前端取消按钮处理流程图

flowchart TD
  BTN["User clicks_cancel_button"] --> CHECKID{taskInfo.id_exists?}
  CHECKID -- Yes --> CALLAPI["downloadApi.cancelDownloadTask(id)"]
  CHECKID -- No --> STOPLOADING["stopLoading"]

  CALLAPI --> ONRESOLVE["on_success"]
  CALLAPI --> ONERROR["on_error_showError"]

  ONRESOLVE --> STOP["stop_polling"]
  ONRESOLVE --> RESET["resetTask_state"]
  ONRESOLVE --> STOPLOADING

  ONERROR --> STOPLOADING
Loading

文件级变更

Change Details Files
Track and manage background download tasks so they can be explicitly cancelled and cleaned up.
  • 扩展 DownloadTaskState,增加一个 join_handle 互斥量,用于存储已生成下载任务的 JoinHandle
  • create_task 中捕获 tokio::spawn 返回的 JoinHandle,并在任务生成后将其保存到任务状态中。
  • 用新的 cancel_task 方法替换之前的 remove_task 辅助函数,该方法会发送取消信号、中止任务、从管理器中移除任务并删除部分下载的文件。
  • 更新测试以使用 cancel_task 而不是 remove_task,并断言在取消之后任务已被移除。
src-tauri/src/services/download_manager.rs
Introduce a cancellation mechanism into the downloader so network I/O and worker monitoring respond to cancellation requests without leaving error state.
  • DownloadStatus 中增加 tokio_util::sync::CancellationToken,并添加 cancelcancelled 辅助方法。
  • 调整多线程下载 worker 循环,使其在子任务因取消失败时避免设置错误状态。
  • 重构 _worker 函数,将 HTTP range 下载逻辑包裹在 tokio::select! 中,在读取分片期间以及通过第二分支检查取消令牌,从而可以干净地中止进行中的请求。
  • 确保 DownloadStatus 的错误上报可以区分真实错误与取消情况,在不必要地覆盖状态的前提下向上传递取消错误。
src-tauri/src/utils/downloader.rs
src-tauri/Cargo.toml
Cargo.lock
Wire a new cancel-download command through the Tauri frontend/backend boundary and hook it to the UI cancel button.
  • src/api/downloader.ts 中添加 cancelDownloadTask API 包装器,用于调用新的 Tauri 命令。
  • 将 Tauri 命令从 remove_download_task 重命名为 cancel_download_task,并委托给 DownloadManager::cancel_task,同时更新 lib.rs 中的命令注册。
  • 修改 DownloadFileView.vue,使取消按钮调用新的 cancelDownload 处理函数而不是 router.push,并在 cancelDownload 中调用 cancelDownloadTask API、停止轮询、重置本地任务状态并处理错误。
  • 将下载视图中现在未使用的 useRouter 引入和使用全部注释掉。
src-tauri/src/commands/downloader.rs
src-tauri/src/lib.rs
src/api/downloader.ts
src/views/DownloadFileView.vue
Minor frontend utility adjustment to use the modern toSorted API for startup candidate ordering.
  • sortStartupCandidates 中用 toSorted 替换 Array.sort,以在对启动候选项排序时避免就地修改。
src/components/views/create/startupUtils.ts

与关联 Issue 的对照评估

Issue Objective Addressed Explanation
#507 在下载页面中,点击取消按钮应当取消当前下载任务,而不是跳转回首页。
#507 在后端实现对进行中下载任务的实际取消,以便当从 UI 触发取消时下载能真正停止。

可能关联的 Issue


Tips and commands

Interacting with Sourcery

  • Trigger a new review: 在 Pull Request 中评论 @sourcery-ai review 以触发一次新的代码审查。
  • Continue discussions: 直接回复 Sourcery 的审查评论以继续讨论。
  • Generate a GitHub issue from a review comment: 在某条审查评论下回复,请求 Sourcery 根据该评论创建一个 issue。你也可以直接回复 @sourcery-ai issue,从这条评论生成一个 issue。
  • Generate a pull request title: 在 Pull Request 标题中任意位置写上 @sourcery-ai,即可在任意时间生成标题。你也可以在 Pull Request 中评论 @sourcery-ai title 来(重新)生成标题。
  • Generate a pull request summary: 在 Pull Request 描述正文的任意位置写上 @sourcery-ai summary,即可在你想要的位置生成 PR 摘要。你也可以在 Pull Request 中评论 @sourcery-ai summary 来(重新)生成摘要。
  • Generate reviewer's guide: 在 Pull Request 中评论 @sourcery-ai guide,即可在任意时间(重新)生成 Reviewer's Guide。
  • Resolve all Sourcery comments: 在 Pull Request 中评论 @sourcery-ai resolve,以将所有 Sourcery 评论标记为已解决。如果你已经处理完所有评论且不想再看到它们,这会很有用。
  • Dismiss all Sourcery reviews: 在 Pull Request 中评论 @sourcery-ai dismiss,以忽略所有现有的 Sourcery 审查。特别适用于你想从头开始一次新的审查——别忘了再评论 @sourcery-ai review 触发新审查!

Customizing Your Experience

访问你的 dashboard 以:

  • 启用或禁用诸如 Sourcery 生成的 Pull Request 摘要、Reviewer's Guide 等审查功能。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查指令。
  • 调整其他审查设置。

Getting Help

Original review guide in English

Reviewer's Guide

Implements a proper cancellable download workflow by tracking spawned Tokio tasks, propagating a cancellation token through the downloader, wiring a new cancel command through the Tauri backend to the Vue frontend, and updating the UI cancel button to stop the download instead of navigating home; also modernizes one startup utility sort call and adds the tokio-util dependency.

Sequence diagram for the new cancellable download flow

sequenceDiagram
  actor User
  participant DownloadFileView
  participant downloadApi
  participant TauriRuntime
  participant cancel_download_task
  participant DownloadManager
  participant DownloadTaskState
  participant DownloadStatus
  participant TokioJoinHandle
  participant MultiThreadDownloader

  User ->> DownloadFileView: click_cancel_button
  DownloadFileView ->> downloadApi: cancelDownloadTask(taskInfo.id)
  downloadApi ->> TauriRuntime: tauriInvoke cancel_download_task idStr
  TauriRuntime ->> cancel_download_task: invoke_command
  cancel_download_task ->> DownloadManager: cancel_task(id)

  DownloadManager ->> DownloadTaskState: read_state(id)
  DownloadManager ->> DownloadStatus: status_handle.cancel()
  DownloadManager ->> TokioJoinHandle: join_handle.abort()
  DownloadManager ->> DownloadManager: remove_task_from_map(id)
  DownloadManager ->> DownloadManager: remove_temp_file(file_path)
  DownloadManager -->> cancel_download_task: Ok
  cancel_download_task -->> TauriRuntime: Ok
  TauriRuntime -->> downloadApi: Ok
  downloadApi -->> DownloadFileView: resolve_promise
  DownloadFileView ->> DownloadFileView: stop_polling_and_reset_task_state

  rect rgb(230,230,250)
    DownloadStatus ->> MultiThreadDownloader: expose_cancel_token
    MultiThreadDownloader ->> MultiThreadDownloader: worker_uses_select_with_cancel_token
    MultiThreadDownloader ->> MultiThreadDownloader: check_status.cancelled_during_chunks
  end
Loading

Updated class diagram for download manager and downloader types

classDiagram
  class DownloadManager {
    - HashMap~Uuid, Arc~DownloadTaskState~~ tasks
    + create_task(url: String, path: String, thread_count: usize) Uuid
    + get_progress(id: Uuid) Option~TaskProgressResponse~
    + get_all_progress() Vec~TaskProgressResponse~
    + cancel_task(id: Uuid) Result~(), String~
  }

  class DownloadTaskState {
    - String _file_path
    - tokio::sync::Mutex~Option~Arc~DownloadStatus~~~ status_handle
    - RwLock~TaskStatus~ internal_status
    - tokio::sync::Mutex~Option~JoinHandle~()~~ join_handle
  }

  class TaskStatus {
    <<enum>>
    Pending
    Downloading
    Completed
    Error
  }

  class MultiThreadDownloader {
    + download(url: String, path: String, thread_count: usize) Result~Arc~DownloadStatus~, String~
    + worker(url: String, path: String, start: u64, end: u64, status: Arc~DownloadStatus~) Result~(), Box~dyn std::error::Error + Send + Sync~~
  }

  class DownloadStatus {
    + AtomicU64 total_size
    + AtomicU64 downloaded
    + RwLock~Option~String~~ error_message
    - CancellationToken cancel_token
    + new(total_size: u64) DownloadStatus
    + cancel()
    + cancelled() bool
    + set_error(msg: String) async
  }

  DownloadManager --> DownloadTaskState : manages
  DownloadTaskState --> TaskStatus : has
  DownloadTaskState --> DownloadStatus : holds
  DownloadTaskState --> TokioJoinHandle : holds
  MultiThreadDownloader --> DownloadStatus : updates

  class TokioJoinHandle {
    + abort()
  }

  class CancellationToken {
    + cancel()
    + is_cancelled() bool
  }

  DownloadStatus --> CancellationToken : owns
Loading

Flow diagram for frontend cancel button handling

flowchart TD
  BTN["User clicks_cancel_button"] --> CHECKID{taskInfo.id_exists?}
  CHECKID -- Yes --> CALLAPI["downloadApi.cancelDownloadTask(id)"]
  CHECKID -- No --> STOPLOADING["stopLoading"]

  CALLAPI --> ONRESOLVE["on_success"]
  CALLAPI --> ONERROR["on_error_showError"]

  ONRESOLVE --> STOP["stop_polling"]
  ONRESOLVE --> RESET["resetTask_state"]
  ONRESOLVE --> STOPLOADING

  ONERROR --> STOPLOADING
Loading

File-Level Changes

Change Details Files
Track and manage background download tasks so they can be explicitly cancelled and cleaned up.
  • Extend DownloadTaskState with a join_handle mutex storing the JoinHandle of the spawned download task.
  • Capture the JoinHandle returned by tokio::spawn in create_task and save it into the task state after spawning.
  • Replace the previous remove_task helper with a new cancel_task method that sends a cancel signal, aborts the task, removes the task from the manager, and deletes the partially downloaded file.
  • Update tests to use cancel_task instead of remove_task and assert that the task is gone after cancellation.
src-tauri/src/services/download_manager.rs
Introduce a cancellation mechanism into the downloader so network I/O and worker monitoring respond to cancellation requests without leaving error state.
  • Augment DownloadStatus with a tokio_util::sync::CancellationToken plus cancel and cancelled helpers.
  • Adjust the multi-thread download worker loop to avoid setting error state when a child task fails due to cancellation.
  • Refactor the _worker function to wrap the HTTP range download logic in tokio::select!, checking the cancellation token both while reading chunks and via a secondary branch so that in-flight requests can be aborted cleanly.
  • Ensure DownloadStatus error reporting distinguishes between genuine errors and cancellations, propagating a cancellation error up without overwriting state unnecessarily.
src-tauri/src/utils/downloader.rs
src-tauri/Cargo.toml
Cargo.lock
Wire a new cancel-download command through the Tauri frontend/backend boundary and hook it to the UI cancel button.
  • Add a cancelDownloadTask API wrapper in src/api/downloader.ts that invokes the new Tauri command.
  • Rename the Tauri command from remove_download_task to cancel_download_task and delegate to DownloadManager::cancel_task, updating lib.rs command registration accordingly.
  • Change DownloadFileView.vue so the cancel button calls a new cancelDownload handler instead of router.push, and implement cancelDownload to call the cancelDownloadTask API, stop polling, reset local task state, and handle errors.
  • Comment out now-unused useRouter imports/usages in the download view.
src-tauri/src/commands/downloader.rs
src-tauri/src/lib.rs
src/api/downloader.ts
src/views/DownloadFileView.vue
Minor frontend utility adjustment to use the modern toSorted API for startup candidate ordering.
  • Replace Array.sort with toSorted in sortStartupCandidates to avoid in-place mutation when ordering startup candidates.
src/components/views/create/startupUtils.ts

Assessment against linked issues

Issue Objective Addressed Explanation
#507 On the download page, clicking the cancel button should cancel the current download task instead of navigating back to the home page.
#507 Implement actual cancellation of an in-progress download task in the backend so that the download is stopped when cancel is triggered from the UI.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了两个问题,并给出了一些整体性的反馈:

  • cancel_task 中,tokio::fs::remove_file(&file_path) 会将缺失文件视为错误,从而导致整个取消流程失败;建议忽略 NotFound(或者把删除操作改成尽力而为),这样当文件已经被删除时,取消操作仍然可以成功。
  • sortStartupCandidates 中从 sort 更改为 toSorted 依赖于一个相对较新的 JS API;请确认目标运行环境支持 Array.prototype.toSorted,否则最好回退到 sort 以避免兼容性问题。
供 AI 代理使用的提示
Please address the comments from this code review:

## Overall Comments
-`cancel_task` 中,`tokio::fs::remove_file(&file_path)` 会将缺失文件视为错误,从而导致整个取消流程失败;建议忽略 `NotFound`(或者把删除操作改成尽力而为),这样当文件已经被删除时,取消操作仍然可以成功。
-`sortStartupCandidates` 中从 `sort` 更改为 `toSorted` 依赖于一个相对较新的 JS API;请确认目标运行环境支持 `Array.prototype.toSorted`,否则最好回退到 `sort` 以避免兼容性问题。

## Individual Comments

### Comment 1
<location path="src-tauri/src/utils/downloader.rs" line_range="200-205" />
<code_context>
+            // 检查取消令牌
+            if status.cancelled() {
+                // 如果任务被取消,返回错误,文件将在上层被删除
+                return Err("任务已取消".into());
+            }
+
</code_context>
<issue_to_address>
**issue (bug_risk):** 字符串字面量不能直接转换为声明的装箱错误类型。

该函数返回 `Result<_, Box<dyn std::error::Error + Send + Sync>>`,但 `"任务已取消".into()` 生成的是 `String`,而不是装箱后的 `Error`,因此代码将无法通过编译。你需要返回一个实现了 `Error` 的具体错误类型,例如:

```rust
use std::io;

return Err(Box::<dyn std::error::Error + Send + Sync>::from(
    io::Error::new(io::ErrorKind::Interrupted, "任务已取消"),
));
```

或者使用你自己的自定义错误类型并进行装箱。请对该函数中另一个 `"任务已取消".into()` 做同样的修改。
</issue_to_address>

### Comment 2
<location path="src/components/views/create/startupUtils.ts" line_range="48" />
<code_context>
 }

 export function sortStartupCandidates(candidates: StartupCandidate[]): StartupCandidate[] {
-  return [...candidates].sort((a, b) => {
+  return [...candidates].toSorted((a, b) => {
     if (a.recommended !== b.recommended) {
       return a.recommended - b.recommended;
</code_context>
<issue_to_address>
**issue (bug_risk):** 使用 `toSorted` 可能会在不同目标 JS 运行时之间产生兼容性问题。

`Array.prototype.toSorted` 相对较新,并非所有 JS 运行时都支持,因此较旧的 WebView 可能在运行时抛出异常。如果无法保证使用足够新的引擎(或提供 polyfill),建议保持使用:

```ts
return [...candidates].sort((a, b) => { /* ... */ });
```

或者在目标运行环境中明确确保已经为 `toSorted` 提供 polyfill。
</issue_to_address>

Sourcery 对开源项目免费使用 —— 如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请对每条评论点 👍 或 👎,我会根据你的反馈改进评审质量。
Original comment in English

Hey - I've found 2 issues, and left some high level feedback:

  • In cancel_task, tokio::fs::remove_file(&file_path) will treat a missing file as an error and fail the whole cancellation; consider ignoring NotFound (or making deletion best-effort) so that cancellation still succeeds when the file is already gone.
  • The change from sort to toSorted in sortStartupCandidates relies on a relatively new JS API; please confirm the target runtime supports Array.prototype.toSorted or fall back to sort to avoid compatibility issues.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `cancel_task`, `tokio::fs::remove_file(&file_path)` will treat a missing file as an error and fail the whole cancellation; consider ignoring `NotFound` (or making deletion best-effort) so that cancellation still succeeds when the file is already gone.
- The change from `sort` to `toSorted` in `sortStartupCandidates` relies on a relatively new JS API; please confirm the target runtime supports `Array.prototype.toSorted` or fall back to `sort` to avoid compatibility issues.

## Individual Comments

### Comment 1
<location path="src-tauri/src/utils/downloader.rs" line_range="200-205" />
<code_context>
+            // 检查取消令牌
+            if status.cancelled() {
+                // 如果任务被取消,返回错误,文件将在上层被删除
+                return Err("任务已取消".into());
+            }
+
</code_context>
<issue_to_address>
**issue (bug_risk):** String literals cannot be directly converted into the declared boxed error type.

The function returns `Result<_, Box<dyn std::error::Error + Send + Sync>>`, but `"任务已取消".into()` produces a `String`, not a boxed `Error`, so this won’t compile. You need to return a concrete error type that implements `Error`, for example:

```rust
use std::io;

return Err(Box::<dyn std::error::Error + Send + Sync>::from(
    io::Error::new(io::ErrorKind::Interrupted, "任务已取消"),
));
```

or a custom error type that you box. Apply the same fix to the other `"任务已取消".into()` in this function.
</issue_to_address>

### Comment 2
<location path="src/components/views/create/startupUtils.ts" line_range="48" />
<code_context>
 }

 export function sortStartupCandidates(candidates: StartupCandidate[]): StartupCandidate[] {
-  return [...candidates].sort((a, b) => {
+  return [...candidates].toSorted((a, b) => {
     if (a.recommended !== b.recommended) {
       return a.recommended - b.recommended;
</code_context>
<issue_to_address>
**issue (bug_risk):** Using `toSorted` may introduce compatibility issues depending on the target JS runtime.

`Array.prototype.toSorted` is relatively new and not available in all JS runtimes, so older WebViews may throw at runtime. If you can’t guarantee a sufficiently modern engine (or a polyfill), stick with:

```ts
return [...candidates].sort((a, b) => { /* ... */ });
```

or explicitly ensure `toSorted` is polyfilled in your target environments.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

- 在startuputils中使用sort防止js版本冲突
- 调整了删除临时下载文件的代码格式,提高可读性
- 将长链式调用拆分为多行,符合代码风格规范
@xtr-hub xtr-hub changed the title 修复取消按钮逻辑 修复在多线程下载器中点击取消按钮只能回到首页的问题 Mar 1, 2026
@xtr-hub xtr-hub changed the title 修复在多线程下载器中点击取消按钮只能回到首页的问题 fix: 修复在多线程下载器中点击取消按钮只能回到首页的问题 Mar 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 使用下载页下载文件时点取消不会取消下载

1 participant