diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 49aa9bcb..0dbdfdfb 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -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; diff --git a/crates/ui-egui/src/updates_ui.rs b/crates/ui-egui/src/updates_ui.rs index 563429f2..5d300e6e 100644 --- a/crates/ui-egui/src/updates_ui.rs +++ b/crates/ui-egui/src/updates_ui.rs @@ -13,6 +13,8 @@ pub(crate) struct Runtime { rx: Option, String>>>, #[cfg(not(target_arch = "wasm32"))] checking: bool, + #[cfg(not(target_arch = "wasm32"))] + update_rx: Option>>, } 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; } }