fix(updates): report self-update results to the prompt and finish through the app's close path

Keeps the worker's failure visible instead of leaving the prompt installing, repaints while a check runs, and no longer calls process::exit past the unsaved-work prompt.
This commit is contained in:
Dylan M. Taylor
2026-10-25 13:58:38 -04:00
parent adcc3b916b
commit 89a0313ab0
2 changed files with 53 additions and 55 deletions
+1 -1
View File
@@ -28,9 +28,9 @@ pub mod native_menu;
pub mod panels;
mod pointer_wrap;
pub mod prefs_live;
pub mod updates_ui;
pub mod state;
pub mod theme;
pub mod updates_ui;
pub mod widgets;
mod workspace_state;
+52 -54
View File
@@ -13,6 +13,8 @@ pub(crate) struct Runtime {
rx: Option<std::sync::mpsc::Receiver<Result<Option<craft_update::Release>, String>>>,
#[cfg(not(target_arch = "wasm32"))]
checking: bool,
#[cfg(not(target_arch = "wasm32"))]
update_rx: Option<std::sync::mpsc::Receiver<Result<(), String>>>,
}
impl Default for Runtime {
@@ -28,6 +30,8 @@ impl Default for Runtime {
rx: None,
#[cfg(not(target_arch = "wasm32"))]
checking: false,
#[cfg(not(target_arch = "wasm32"))]
update_rx: None,
}
}
}
@@ -43,7 +47,7 @@ pub(crate) fn check(app: &mut EffectcraftApp) {
#[cfg(target_arch = "wasm32")]
{
app.updates_rt.manual_window_open = true;
app.updates_rt.status_message = Some(crate::i18n::tr("Update check is not supported in the web browser."));
app.updates_rt.status_message = Some(crate::i18n::tr("Update check is not supported in the web browser.").to_string());
}
}
@@ -77,6 +81,10 @@ pub(crate) fn tick(app: &mut EffectcraftApp, ctx: &egui::Context) {
start_check(app);
}
if app.updates_rt.checking {
ctx.request_repaint_after(std::time::Duration::from_millis(100));
}
let mut completed = None;
if let Some(rx) = &app.updates_rt.rx {
if let Ok(res) = rx.try_recv() {
@@ -89,8 +97,7 @@ pub(crate) fn tick(app: &mut EffectcraftApp, ctx: &egui::Context) {
app.updates_rt.checking = false;
match res {
Ok(Some(release)) => {
let mut prompt =
craft_update::StartupPrompt::new(&craft_update::suite::EFFECTCRAFT, release, env!("CARGO_PKG_VERSION"));
let mut prompt = craft_update::StartupPrompt::new(&craft_update::suite::EFFECTCRAFT, release, env!("CARGO_PKG_VERSION"));
prompt.open = true;
app.updates_rt.prompt = Some(prompt);
ctx.request_repaint();
@@ -98,8 +105,7 @@ pub(crate) fn tick(app: &mut EffectcraftApp, ctx: &egui::Context) {
Ok(None) => {
if app.updates_rt.manual_checked {
app.updates_rt.manual_checked = false;
app.updates_rt.status_message =
Some(format!("EffectCraft {} is currently the newest version available.", env!("CARGO_PKG_VERSION")));
app.updates_rt.status_message = Some(format!("EffectCraft {} is currently the newest version available.", env!("CARGO_PKG_VERSION")));
app.updates_rt.manual_window_open = true;
ctx.request_repaint();
}
@@ -122,68 +128,60 @@ pub(crate) fn show(app: &mut EffectcraftApp, ctx: &egui::Context) {
{
let mut download_url = None;
if let Some(prompt) = &mut app.updates_rt.prompt {
if let Some(result) = app.updates_rt.update_rx.as_ref().and_then(|rx| rx.try_recv().ok()) {
app.updates_rt.update_rx = None;
match result {
// The app's close path asks about unsaved work; the restart helper relaunches once the process exits.
Ok(()) => ctx.send_viewport_cmd(egui::ViewportCommand::Close),
Err(e) => {
prompt.status_message = Some(e);
prompt.is_installing = false;
}
}
}
match prompt.show(ctx) {
craft_update::StartupPromptResponse::Pending => {}
craft_update::StartupPromptResponse::UpdateAndRestart => {
prompt.is_installing = true;
if let Some(install) = craft_update::self_update::current(&craft_update::suite::EFFECTCRAFT) {
let mut release = prompt.release.clone();
let (tx, rx) = std::sync::mpsc::channel();
app.updates_rt.update_rx = Some(rx);
let ctx = ctx.clone();
std::thread::spawn(move || {
let http = match craft_update::Http::new("EffectCraft/Update") {
Ok(h) => h,
Err(e) => {
log::warn!("could not initialize HTTP: {e}");
return;
}
};
if release.assets.is_empty() {
if let Ok(Some(full)) = craft_update::check_for_update(
&craft_update::suite::EFFECTCRAFT,
&http,
env!("CARGO_PKG_VERSION"),
) {
let mut run = || -> Result<(), String> {
let http = craft_update::Http::new("EffectCraft/Update").map_err(|e| format!("could not initialize HTTP: {e}"))?;
if release.assets.is_empty()
&& let Some(full) = craft_update::check_for_update(&craft_update::suite::EFFECTCRAFT, &http, env!("CARGO_PKG_VERSION"))
.map_err(|e| format!("could not fetch release assets: {e}"))?
{
release = full;
}
}
let paths = match craft_update::Paths::for_app(&craft_update::suite::EFFECTCRAFT) {
Ok(p) => p,
Err(e) => {
log::warn!("could not find paths: {e}");
return;
}
let paths =
craft_update::Paths::for_app(&craft_update::suite::EFFECTCRAFT).map_err(|e| format!("could not find paths: {e}"))?;
let platform = craft_update::Platform::current().ok_or("unsupported platform for self update")?;
craft_update::self_update::update(
&craft_update::suite::EFFECTCRAFT,
&install,
&release,
platform,
&http,
&paths,
&craft_update::Stop::default(),
&mut |_| {},
&|| Ok(()),
Some(env!("CARGO_PKG_VERSION")),
)
};
let stop = craft_update::Stop::default();
let platform = match craft_update::Platform::current() {
Some(p) => p,
None => {
log::warn!("unsupported platform for self update");
return;
}
};
let last = || Ok(());
match craft_update::self_update::update(
&craft_update::suite::EFFECTCRAFT,
&install,
&release,
platform,
&http,
&paths,
&stop,
&mut |_| {},
&last,
Some(env!("CARGO_PKG_VERSION")),
) {
Ok(()) => {
std::process::exit(0);
}
Err(e) => {
log::warn!("EffectCraft self-update failed: {e}");
}
let result = run();
if let Err(e) = &result {
log::warn!("self-update failed: {e}");
}
let _ = tx.send(result);
ctx.request_repaint();
});
} else {
prompt.status_message =
Some("Cannot auto-update this installation type. Please download manually.".to_string());
prompt.status_message = Some("Cannot auto-update this installation type. Please download manually.".to_string());
prompt.is_installing = false;
}
}