Implement Driftwood AppImage manager

This commit is contained in:
2026-02-26 23:04:27 +02:00
commit c3620b487d
33 changed files with 10399 additions and 0 deletions

147
src/ui/update_dialog.rs Normal file
View File

@@ -0,0 +1,147 @@
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<gtk::Widget>,
record: &AppImageRecord,
db: &Rc<Database>,
) {
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
}