Rewrite update dialog text to plain language for WCAG readability

This commit is contained in:
lashman
2026-02-27 10:06:30 +02:00
parent 3642fbd542
commit 1bafb135f0

View File

@@ -1,6 +1,8 @@
use adw::prelude::*;
use gtk::gio;
use std::path::PathBuf;
use std::rc::Rc;
use crate::config::APP_ID;
use crate::core::database::{AppImageRecord, Database};
use crate::core::updater;
@@ -43,7 +45,6 @@ pub fn show_update_dialog(
match result {
Ok((type_label, raw_info, Some(check_result))) => {
// Store update info in DB
let now = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string();
db_ref
.update_update_info(
record_id,
@@ -57,14 +58,48 @@ pub fn show_update_dialog(
db_ref.set_update_available(record_id, Some(version), check_result.download_url.as_deref()).ok();
}
let body = format!(
"{} -> {}\n\nA new version is available.",
let mut body = format!(
"{} -> {}",
record_clone.app_version.as_deref().unwrap_or("unknown"),
check_result.latest_version.as_deref().unwrap_or("unknown"),
);
if let Some(size) = check_result.file_size {
body.push_str(&format!(" ({})", humansize::format_size(size, humansize::BINARY)));
}
body.push_str("\n\nA new version is available.");
if let Some(ref notes) = check_result.release_notes {
if !notes.is_empty() {
// Truncate long release notes for the dialog
let truncated: String = notes.chars().take(300).collect();
let suffix = if truncated.len() < notes.len() { "..." } else { "" };
body.push_str(&format!("\n\n{}{}", truncated, suffix));
}
}
dialog_ref.set_heading(Some("Update Available"));
dialog_ref.set_body(&body);
// Future: add "Update" response to trigger download
// Add "Update Now" button if we have a download URL
if let Some(download_url) = check_result.download_url {
dialog_ref.add_response("update", "Update Now");
dialog_ref.set_response_appearance("update", adw::ResponseAppearance::Suggested);
dialog_ref.set_default_response(Some("update"));
let db_update = db_ref.clone();
let record_path = record_clone.path.clone();
let new_version = check_result.latest_version.clone();
dialog_ref.connect_response(None, move |dlg, response| {
if response == "update" {
start_update(
dlg,
&record_path,
&download_url,
record_id,
new_version.as_deref(),
&db_update,
);
}
});
}
} else {
dialog_ref.set_heading(Some("Up to Date"));
dialog_ref.set_body(&format!(
@@ -83,8 +118,8 @@ pub fn show_update_dialog(
} else {
dialog_ref.set_heading(Some("No Update Info"));
dialog_ref.set_body(
"This AppImage does not contain update information. \
Updates must be downloaded manually.",
"This app does not support automatic updates. \
Check the developer's website for newer versions.",
);
}
}
@@ -98,6 +133,114 @@ pub fn show_update_dialog(
dialog.present(Some(parent));
}
/// Start the actual update download + apply process.
fn start_update(
dialog: &adw::AlertDialog,
appimage_path: &str,
download_url: &str,
record_id: i64,
new_version: Option<&str>,
db: &Rc<Database>,
) {
dialog.set_heading(Some("Updating..."));
dialog.set_body("Downloading update. This may take a moment.");
dialog.set_response_enabled("update", false);
let path = appimage_path.to_string();
let url = download_url.to_string();
let version = new_version.map(|s| s.to_string());
let db_ref = db.clone();
let dialog_weak = dialog.downgrade();
glib::spawn_future_local(async move {
let result = gio::spawn_blocking(move || {
let p = std::path::Path::new(&path);
// Always keep old version initially - cleanup decision happens after
updater::perform_update(p, Some(&url), true, None)
})
.await;
let Some(dialog) = dialog_weak.upgrade() else { return };
match result {
Ok(Ok(applied)) => {
// Record the update in history using the actual new version
let actual_version = applied.new_version.as_deref().or(version.as_deref());
if let Some(ver) = actual_version {
db_ref.record_update(record_id, None, Some(ver), Some("download"), None, true).ok();
}
db_ref.clear_update_available(record_id).ok();
let success_body = format!(
"Updated to {}\nPath: {}",
applied.new_version.as_deref().unwrap_or("latest"),
applied.new_path.display(),
);
dialog.set_heading(Some("Update Complete"));
dialog.set_body(&success_body);
dialog.set_response_enabled("update", false);
// Handle old version cleanup
if let Some(old_path) = applied.old_path_backup {
handle_old_version_cleanup(&dialog, old_path);
}
}
Ok(Err(e)) => {
dialog.set_heading(Some("Update Failed"));
dialog.set_body(&format!("The update could not be applied: {}", e));
}
Err(_) => {
dialog.set_heading(Some("Update Failed"));
dialog.set_body("An unexpected error occurred during the update.");
}
}
});
}
/// After a successful update, handle cleanup of the old version based on user preference.
fn handle_old_version_cleanup(dialog: &adw::AlertDialog, old_path: PathBuf) {
let settings = gio::Settings::new(APP_ID);
let policy = settings.string("update-cleanup");
match policy.as_str() {
"always" => {
// Auto-remove without asking
if old_path.exists() {
if let Err(e) = std::fs::remove_file(&old_path) {
log::warn!("Failed to remove old version {}: {}", old_path.display(), e);
} else {
log::info!("Auto-removed old version: {}", old_path.display());
}
}
}
"never" => {
// Keep the backup, just inform
dialog.set_body(&format!(
"Update complete. The old version is saved at:\n{}",
old_path.display()
));
}
_ => {
// "ask" - prompt the user
dialog.set_body(&format!(
"Update complete.\n\nRemove the old version?\n{}",
old_path.display()
));
dialog.add_response("remove-old", "Remove Old Version");
dialog.set_response_appearance("remove-old", adw::ResponseAppearance::Destructive);
let path = old_path.clone();
dialog.connect_response(None, move |_dlg, response| {
if response == "remove-old" {
if path.exists() {
std::fs::remove_file(&path).ok();
}
}
});
}
}
}
/// Batch check all AppImages for updates. Returns count of updates found.
pub fn batch_check_updates(db: &Database) -> u32 {
let records = match db.get_all_appimages() {