feat: OTA 改为静默下载 + 安装需用户确认
- 下载落到数据目录 updates/ 暂存区(退出不丢失),校验后等待安装 - 启动自动检查:静默检查+下载,完成后弹「更新就绪」对话框, 由用户选择 立即安装(重启) 或 下次启动安装 - main 在进入 UI 前检测暂存区并完成自替换(下次启动安装路径) - 设置对话框:下载完成后进入「待安装」状态,提供两个安装时机按钮 - install_and_restart 拆分为 download_and_stage / load_staged / install_staged
This commit is contained in:
@@ -41,6 +41,8 @@ enum UpdateUi {
|
||||
UpToDate,
|
||||
Available(Box<UpdateInfo>),
|
||||
Downloading { downloaded: u64, total: u64 },
|
||||
/// 已下载到暂存区,等待用户选择安装时机
|
||||
ReadyToInstall(Box<update::StagedUpdate>),
|
||||
Failed(String),
|
||||
}
|
||||
|
||||
@@ -107,20 +109,12 @@ impl SettingsDialog {
|
||||
cx.notify();
|
||||
let (prog_tx, prog_rx) = smol::channel::unbounded();
|
||||
let info = info.clone();
|
||||
let rx: smol::channel::Receiver<Result<(), String>> =
|
||||
let rx: smol::channel::Receiver<Result<update::StagedUpdate, String>> =
|
||||
self.engine.run_blocking(move || {
|
||||
let result = update::download_and_verify(&info, &|d, t| {
|
||||
let _ = prog_tx.try_send((d, t));
|
||||
update::download_and_stage(&info, &|d, t| {
|
||||
let _ = prog_tx.try_send((d, t));
|
||||
})
|
||||
});
|
||||
match result {
|
||||
Ok(tmp) => match update::install_and_restart(&tmp) {
|
||||
// 新进程已拉起并接管,旧进程直接退出(设置均为即时落盘)
|
||||
Ok(()) => std::process::exit(0),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
});
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
while let Ok((downloaded, total)) = prog_rx.recv().await {
|
||||
this.update_in(cx, |s, _window, cx| {
|
||||
@@ -129,13 +123,14 @@ impl SettingsDialog {
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
// 进度通道关闭:要么安装成功(进程已退出),要么拿到最终结果
|
||||
// 进度通道关闭:拿到最终结果
|
||||
let result = rx.recv().await.unwrap_or(Err("任务丢失".into()));
|
||||
this.update_in(cx, |s, _window, cx| {
|
||||
if let Err(e) = result {
|
||||
s.update = UpdateUi::Failed(e);
|
||||
cx.notify();
|
||||
}
|
||||
s.update = match result {
|
||||
Ok(staged) => UpdateUi::ReadyToInstall(Box::new(staged)),
|
||||
Err(e) => UpdateUi::Failed(e),
|
||||
};
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
@@ -215,6 +210,59 @@ impl SettingsDialog {
|
||||
true,
|
||||
)
|
||||
}
|
||||
UpdateUi::ReadyToInstall(staged) => {
|
||||
let ver = staged.version.clone();
|
||||
let staged_now = staged.clone();
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().accent)
|
||||
.child(format!(
|
||||
"新版本 v{} 已下载并通过校验,等待安装",
|
||||
ver
|
||||
)),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1p5()
|
||||
.child(
|
||||
Button::new("upd-now")
|
||||
.label("立即安装")
|
||||
.primary()
|
||||
.xsmall()
|
||||
.on_click(move |_, window, cx| {
|
||||
if update::install_staged(&staged_now).is_ok() {
|
||||
cx.quit();
|
||||
} else {
|
||||
window.push_notification(
|
||||
Notification::error(
|
||||
"安装失败,请重试",
|
||||
),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
Button::new("upd-later")
|
||||
.label("下次启动安装")
|
||||
.outline()
|
||||
.xsmall()
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.update = UpdateUi::Idle;
|
||||
window.push_notification(
|
||||
Notification::info(
|
||||
"已暂存,下次启动时将自动安装",
|
||||
),
|
||||
cx,
|
||||
);
|
||||
cx.notify();
|
||||
})),
|
||||
),
|
||||
)
|
||||
}
|
||||
UpdateUi::Downloading { downloaded, total } => {
|
||||
let pct = if *total > 0 {
|
||||
*downloaded as f32 / *total as f32
|
||||
|
||||
Reference in New Issue
Block a user