From c311fb27c3ef3d9b52c44b558507656a2bb0ee8c Mon Sep 17 00:00:00 2001 From: lashman Date: Fri, 27 Feb 2026 23:52:28 +0200 Subject: [PATCH] Add background update checks with configurable interval Schedule automatic update checks on startup based on elapsed time since last check. Add interval SpinRow to preferences and show 'Last checked' timestamp on the dashboard updates section. --- data/app.driftwood.Driftwood.gschema.xml | 11 +++++++++ src/ui/dashboard.rs | 28 +++++++++++++++++++++ src/ui/preferences.rs | 19 +++++++++++++++ src/window.rs | 31 ++++++++++++++++++++++++ 4 files changed, 89 insertions(+) diff --git a/data/app.driftwood.Driftwood.gschema.xml b/data/app.driftwood.Driftwood.gschema.xml index 4e35b55..9872109 100644 --- a/data/app.driftwood.Driftwood.gschema.xml +++ b/data/app.driftwood.Driftwood.gschema.xml @@ -61,6 +61,17 @@ Auto check updates Automatically check for AppImage updates periodically. + + + 24 + Update check interval + Hours between automatic update checks. + + + '' + Last update check timestamp + ISO timestamp of the last automatic update check. + false Auto integrate diff --git a/src/ui/dashboard.rs b/src/ui/dashboard.rs index 4fd0fbc..4545139 100644 --- a/src/ui/dashboard.rs +++ b/src/ui/dashboard.rs @@ -1,6 +1,8 @@ use adw::prelude::*; +use gtk::gio; use std::rc::Rc; +use crate::config::APP_ID; use crate::core::database::Database; use crate::core::duplicates; use crate::core::fuse; @@ -267,6 +269,32 @@ fn build_updates_summary_group(db: &Rc) -> adw::PreferencesGroup { updates_row.add_suffix(&updates_arrow); group.add(&updates_row); + // Last checked timestamp + let settings = gio::Settings::new(APP_ID); + let last_check = settings.string("last-update-check"); + let last_label = if last_check.is_empty() { + "Never".to_string() + } else if let Ok(ts) = chrono::NaiveDateTime::parse_from_str(last_check.as_str(), "%Y-%m-%d %H:%M:%S") { + let now = chrono::Utc::now().naive_utc(); + let elapsed = now.signed_duration_since(ts); + if elapsed.num_minutes() < 1 { + "Just now".to_string() + } else if elapsed.num_hours() < 1 { + format!("{}m ago", elapsed.num_minutes()) + } else if elapsed.num_hours() < 24 { + format!("{}h ago", elapsed.num_hours()) + } else { + format!("{}d ago", elapsed.num_days()) + } + } else { + "Unknown".to_string() + }; + let last_row = adw::ActionRow::builder() + .title("Last checked") + .subtitle(&last_label) + .build(); + group.add(&last_row); + group } diff --git a/src/ui/preferences.rs b/src/ui/preferences.rs index 300536d..64babc5 100644 --- a/src/ui/preferences.rs +++ b/src/ui/preferences.rs @@ -235,6 +235,25 @@ fn build_behavior_page(settings: &gio::Settings) -> adw::PreferencesPage { }); automation_group.add(&auto_update_row); + let interval_row = adw::SpinRow::builder() + .title(&i18n("Update check interval")) + .subtitle(&i18n("Hours between automatic update checks")) + .build(); + let interval_adj = gtk::Adjustment::new( + settings.int("update-check-interval-hours") as f64, + 1.0, + 168.0, + 1.0, + 6.0, + 0.0, + ); + interval_row.set_adjustment(Some(&interval_adj)); + let settings_interval = settings.clone(); + interval_row.connect_value_notify(move |row| { + settings_interval.set_int("update-check-interval-hours", row.value() as i32).ok(); + }); + automation_group.add(&interval_row); + let auto_integrate_row = adw::SwitchRow::builder() .title(&i18n("Auto-integrate new AppImages")) .subtitle(&i18n("Automatically add newly discovered AppImages to the desktop menu")) diff --git a/src/window.rs b/src/window.rs index 7aa5cb2..f39271e 100644 --- a/src/window.rs +++ b/src/window.rs @@ -961,6 +961,37 @@ impl DriftwoodWindow { }); } + // Scheduled background update check + let settings_upd = self.settings().clone(); + if settings_upd.boolean("auto-check-updates") { + let last_check = settings_upd.string("last-update-check").to_string(); + let interval_hours = settings_upd.int("update-check-interval-hours") as i64; + let should_check = if last_check.is_empty() { + true + } else if let Ok(last) = chrono::NaiveDateTime::parse_from_str(&last_check, "%Y-%m-%d %H:%M:%S") { + let now = chrono::Utc::now().naive_utc(); + let elapsed = now.signed_duration_since(last); + elapsed.num_hours() >= interval_hours + } else { + true + }; + if should_check { + let settings_save = settings_upd.clone(); + glib::spawn_future_local(async move { + let result = gio::spawn_blocking(move || { + let bg_db = Database::open().expect("DB open failed"); + update_dialog::batch_check_updates(&bg_db) + }) + .await; + if let Ok(count) = result { + log::info!("Background update check: {} updates available", count); + } + let now = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(); + settings_save.set_string("last-update-check", &now).ok(); + }); + } + } + // Check for orphaned desktop entries in the background let toast_overlay = self.imp().toast_overlay.get().unwrap().clone(); glib::spawn_future_local(async move {