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.
This commit is contained in:
@@ -61,6 +61,17 @@
|
|||||||
<summary>Auto check updates</summary>
|
<summary>Auto check updates</summary>
|
||||||
<description>Automatically check for AppImage updates periodically.</description>
|
<description>Automatically check for AppImage updates periodically.</description>
|
||||||
</key>
|
</key>
|
||||||
|
<key name="update-check-interval-hours" type="i">
|
||||||
|
<range min="1" max="168"/>
|
||||||
|
<default>24</default>
|
||||||
|
<summary>Update check interval</summary>
|
||||||
|
<description>Hours between automatic update checks.</description>
|
||||||
|
</key>
|
||||||
|
<key name="last-update-check" type="s">
|
||||||
|
<default>''</default>
|
||||||
|
<summary>Last update check timestamp</summary>
|
||||||
|
<description>ISO timestamp of the last automatic update check.</description>
|
||||||
|
</key>
|
||||||
<key name="auto-integrate" type="b">
|
<key name="auto-integrate" type="b">
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
<summary>Auto integrate</summary>
|
<summary>Auto integrate</summary>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
|
use gtk::gio;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::config::APP_ID;
|
||||||
use crate::core::database::Database;
|
use crate::core::database::Database;
|
||||||
use crate::core::duplicates;
|
use crate::core::duplicates;
|
||||||
use crate::core::fuse;
|
use crate::core::fuse;
|
||||||
@@ -267,6 +269,32 @@ fn build_updates_summary_group(db: &Rc<Database>) -> adw::PreferencesGroup {
|
|||||||
updates_row.add_suffix(&updates_arrow);
|
updates_row.add_suffix(&updates_arrow);
|
||||||
group.add(&updates_row);
|
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
|
group
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -235,6 +235,25 @@ fn build_behavior_page(settings: &gio::Settings) -> adw::PreferencesPage {
|
|||||||
});
|
});
|
||||||
automation_group.add(&auto_update_row);
|
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()
|
let auto_integrate_row = adw::SwitchRow::builder()
|
||||||
.title(&i18n("Auto-integrate new AppImages"))
|
.title(&i18n("Auto-integrate new AppImages"))
|
||||||
.subtitle(&i18n("Automatically add newly discovered AppImages to the desktop menu"))
|
.subtitle(&i18n("Automatically add newly discovered AppImages to the desktop menu"))
|
||||||
|
|||||||
@@ -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
|
// Check for orphaned desktop entries in the background
|
||||||
let toast_overlay = self.imp().toast_overlay.get().unwrap().clone();
|
let toast_overlay = self.imp().toast_overlay.get().unwrap().clone();
|
||||||
glib::spawn_future_local(async move {
|
glib::spawn_future_local(async move {
|
||||||
|
|||||||
Reference in New Issue
Block a user