use adw::prelude::*; use gtk::gio; use std::rc::Rc; use crate::core::database::{AppImageRecord, Database}; use crate::core::updater; /// Show an update check + apply dialog for a single AppImage. pub fn show_update_dialog( parent: &impl IsA, record: &AppImageRecord, db: &Rc, ) { let dialog = adw::AlertDialog::builder() .heading("Check for Updates") .body(&format!( "Checking for updates for {}...", record.app_name.as_deref().unwrap_or(&record.filename) )) .build(); dialog.add_response("close", "Close"); dialog.set_default_response(Some("close")); dialog.set_close_response("close"); let record_clone = record.clone(); let db_ref = db.clone(); let dialog_ref = dialog.clone(); // Start the update check in the background let record_id = record.id; let path = record.path.clone(); let current_version = record.app_version.clone(); glib::spawn_future_local(async move { let result = gio::spawn_blocking(move || { let appimage_path = std::path::Path::new(&path); updater::check_appimage_for_update( appimage_path, current_version.as_deref(), ) }) .await; 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, raw_info.as_deref(), type_label.as_deref(), ) .ok(); if check_result.update_available { if let Some(ref version) = check_result.latest_version { 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.", record_clone.app_version.as_deref().unwrap_or("unknown"), check_result.latest_version.as_deref().unwrap_or("unknown"), ); dialog_ref.set_heading(Some("Update Available")); dialog_ref.set_body(&body); // Future: add "Update" response to trigger download } else { dialog_ref.set_heading(Some("Up to Date")); dialog_ref.set_body(&format!( "{} is already at the latest version ({}).", record_clone.app_name.as_deref().unwrap_or(&record_clone.filename), record_clone.app_version.as_deref().unwrap_or("unknown"), )); db_ref.clear_update_available(record_id).ok(); } } Ok((type_label, raw_info, None)) => { if raw_info.is_some() { db_ref.update_update_info(record_id, raw_info.as_deref(), type_label.as_deref()).ok(); dialog_ref.set_heading(Some("Check Failed")); dialog_ref.set_body("Could not reach the update server. Try again later."); } 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.", ); } } Err(_) => { dialog_ref.set_heading(Some("Error")); dialog_ref.set_body("An error occurred while checking for updates."); } } }); dialog.present(Some(parent)); } /// 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() { Ok(r) => r, Err(e) => { log::error!("Failed to get appimages for update check: {}", e); return 0; } }; let mut updates_found = 0u32; for record in &records { let appimage_path = std::path::Path::new(&record.path); if !appimage_path.exists() { continue; } let (type_label, raw_info, check_result) = updater::check_appimage_for_update( appimage_path, record.app_version.as_deref(), ); // Store update info if raw_info.is_some() || type_label.is_some() { db.update_update_info( record.id, raw_info.as_deref(), type_label.as_deref(), ) .ok(); } if let Some(result) = check_result { if result.update_available { if let Some(ref version) = result.latest_version { db.set_update_available(record.id, Some(version), result.download_url.as_deref()).ok(); updates_found += 1; } } else { db.clear_update_available(record.id).ok(); } } } updates_found }