Merge pull request #333 from dylanmtaylor/fix/updater-pending-close

fix(update): keep the close request pending while the app holds unsaved work
This commit is contained in:
Dylan M. Taylor authored and GitHub committed 2026-10-25 15:03:02 -04:00
commit db19c373c8
1 file changed
+30 -5
+30 -5
View File
@@ -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();