Wire backup, notification, report export, and file watcher modules into UI
- Add export button to security report page (HTML/JSON/CSV via FileDialog) - Send desktop notifications after security scans when enabled in settings - Add backup section to storage tab with create/restore/delete and toast feedback - Start file watcher on launch to auto-refresh library on AppImage changes - Fix detail view tabs requesting tallest tab height (set vhomogeneous=false) - Disable tab transitions to avoid visual glitch with variable-height tabs
This commit is contained in:
@@ -11,9 +11,11 @@ use crate::core::database::Database;
|
||||
use crate::core::discovery;
|
||||
use crate::core::integrator;
|
||||
use crate::core::launcher;
|
||||
use crate::core::notification;
|
||||
use crate::core::orphan;
|
||||
use crate::core::security;
|
||||
use crate::core::updater;
|
||||
use crate::core::watcher;
|
||||
use crate::i18n::{i18n, ni18n_f};
|
||||
use crate::ui::cleanup_wizard;
|
||||
use crate::ui::dashboard;
|
||||
@@ -37,6 +39,7 @@ mod imp {
|
||||
pub database: OnceCell<Rc<Database>>,
|
||||
pub drop_overlay: OnceCell<gtk::Box>,
|
||||
pub drop_revealer: OnceCell<gtk::Revealer>,
|
||||
pub watcher_handle: std::cell::RefCell<Option<notify::RecommendedWatcher>>,
|
||||
}
|
||||
|
||||
impl Default for DriftwoodWindow {
|
||||
@@ -49,6 +52,7 @@ mod imp {
|
||||
database: OnceCell::new(),
|
||||
drop_overlay: OnceCell::new(),
|
||||
drop_revealer: OnceCell::new(),
|
||||
watcher_handle: std::cell::RefCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -705,6 +709,19 @@ impl DriftwoodWindow {
|
||||
let msg = format!("Found {} CVE{}", total, if total == 1 { "" } else { "s" });
|
||||
toast_overlay.add_toast(adw::Toast::new(&msg));
|
||||
}
|
||||
|
||||
// Send desktop notifications for new CVE findings if enabled
|
||||
let settings = gio::Settings::new(APP_ID);
|
||||
if settings.boolean("security-notifications") {
|
||||
let threshold = settings.string("security-notification-threshold").to_string();
|
||||
glib::spawn_future_local(async move {
|
||||
let _ = gio::spawn_blocking(move || {
|
||||
let bg_db = Database::open().expect("Failed to open database");
|
||||
notification::check_and_notify(&bg_db, &threshold);
|
||||
})
|
||||
.await;
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => toast_overlay.add_toast(adw::Toast::new("Security scan failed")),
|
||||
}
|
||||
@@ -816,6 +833,9 @@ impl DriftwoodWindow {
|
||||
// Always scan on startup to discover new AppImages and complete pending analyses
|
||||
self.trigger_scan();
|
||||
|
||||
// Start watching scan directories for new AppImage files
|
||||
self.start_file_watcher();
|
||||
|
||||
// Check for orphaned desktop entries in the background
|
||||
let toast_overlay = self.imp().toast_overlay.get().unwrap().clone();
|
||||
glib::spawn_future_local(async move {
|
||||
@@ -1019,6 +1039,44 @@ impl DriftwoodWindow {
|
||||
});
|
||||
}
|
||||
|
||||
fn start_file_watcher(&self) {
|
||||
let settings = self.settings();
|
||||
let dirs: Vec<std::path::PathBuf> = settings
|
||||
.strv("scan-directories")
|
||||
.iter()
|
||||
.map(|s| discovery::expand_tilde(&s.to_string()))
|
||||
.collect();
|
||||
|
||||
if dirs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use an atomic flag to communicate across the thread boundary.
|
||||
// The watcher callback (on a background thread) sets the flag,
|
||||
// and a glib timer on the main thread polls and dispatches.
|
||||
let changed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
|
||||
let changed_watcher = changed.clone();
|
||||
let handle = watcher::start_watcher(dirs, move |_event| {
|
||||
changed_watcher.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
});
|
||||
|
||||
if let Some(h) = handle {
|
||||
self.imp().watcher_handle.replace(Some(h));
|
||||
|
||||
// Poll the flag every second from the main thread
|
||||
let window_weak = self.downgrade();
|
||||
glib::timeout_add_local(std::time::Duration::from_secs(1), move || {
|
||||
if changed.swap(false, std::sync::atomic::Ordering::Relaxed) {
|
||||
if let Some(window) = window_weak.upgrade() {
|
||||
window.trigger_scan();
|
||||
}
|
||||
}
|
||||
glib::ControlFlow::Continue
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn show_shortcuts_dialog(&self) {
|
||||
let dialog = adw::Dialog::builder()
|
||||
.title("Keyboard Shortcuts")
|
||||
|
||||
Reference in New Issue
Block a user