diff --git a/crates/update/src/updater.rs b/crates/update/src/updater.rs index 7519ccc4..836bf441 100644 --- a/crates/update/src/updater.rs +++ b/crates/update/src/updater.rs @@ -194,6 +194,11 @@ impl Updater { Ok(()) => { self.installed = true; self.restart = true; + if let Some(prompt) = &mut self.prompt { + prompt.is_installing = false; + prompt.status_message = + Some(format!("The update is installed. Close {} to finish; it restarts by itself.", self.app.name)); + } } Err(e) => { log::warn!("{} self-update failed: {e}", self.app.name); @@ -222,11 +227,10 @@ impl Updater { /// `false` while it holds unsaved work: the restart helper is scheduled before the app closes, /// and a close that the user then cancels would leave it waiting for an exit that never comes. pub fn show_if(&mut self, ctx: &egui::Context, can_start: bool) -> Action { - if std::mem::take(&mut self.restart) { - if let Some(prompt) = &mut self.prompt { - prompt.is_installing = false; - prompt.status_message = Some(format!("The update is installed. Close {} to finish; it restarts by itself.", self.app.name)); - } + // The close waits for the app to hold no unsaved work: the user may have edited while the + // update installed, and a close they cancel is asked again once the work is saved. + if self.restart && can_start { + self.restart = false; return Action::Close; } let Some(prompt) = &mut self.prompt else { return Action::None }; @@ -447,6 +451,27 @@ mod tests { assert!(u.prompt.as_ref().is_some_and(|p| !p.is_installing && p.status_message.is_some())); } + /// One frame with the updater's prompt drawn. + fn frame(u: &mut Updater, ctx: &egui::Context, can_start: bool) -> Action { + let mut action = Action::None; + ctx.run_ui(egui::RawInput::default(), |ui| action = u.show_if(ui.ctx(), can_start)).textures_delta.clear(); + action + } + + #[test] + fn a_finished_install_keeps_asking_to_close_until_the_app_can() { + let ctx = egui::Context::default(); + let mut u = updater(Ok(None)); + u.offer(newer()); + let (tx, rx) = channel(); + u.install = Some((rx, newer())); + assert!(tx.send(Ok(())).is_ok()); + u.tick(&ctx); + assert_eq!(frame(&mut u, &ctx, false), Action::None, "unsaved work holds the close back"); + assert_eq!(frame(&mut u, &ctx, true), Action::Close); + assert_eq!(frame(&mut u, &ctx, true), Action::None); + } + #[test] fn a_finished_install_asks_the_app_to_close_once() { let ctx = egui::Context::default();