Skip to main content

mimick/
notifications.rs

1//! Provides desktop notification helpers using the GIO notification portal.
2//!
3//! All functions are best-effort: if no running `gio::Application` is available,
4//! the call is silently ignored. No notification is fired more than once per
5//! concept per session -- callers are responsible for the guard logic.
6
7use std::sync::atomic::{AtomicBool, Ordering};
8
9/// Global kill-switch for desktop notifications, controlled by the user-facing
10/// "Enable Notifications" toggle in Settings → Behavior.
11static ENABLED: AtomicBool = AtomicBool::new(true);
12
13/// Update the notifications enabled flag (called from config load and settings save).
14pub fn set_enabled(enabled: bool) {
15    ENABLED.store(enabled, Ordering::Relaxed);
16    log::debug!("Notifications enabled: {}", enabled);
17}
18
19/// Fired once when the upload queue drains after an active sync cycle.
20///
21/// `succeeded` and `failed` are the counts for that sync cycle.
22pub fn send_sync_summary(succeeded: usize, failed: usize) {
23    if succeeded == 0 && failed == 0 {
24        return;
25    }
26    let title = "Sync complete".to_string();
27    let processed = succeeded.saturating_add(failed);
28    let body = if failed == 0 {
29        format!("All {} file(s) processed. Idle.", processed)
30    } else {
31        format!(
32            "All {} file(s) processed. Idle. {} failed and will be retried.",
33            processed, failed
34        )
35    };
36    send_gio(&title, &body, "sync-complete");
37}
38
39/// Fired once per session when consecutive uploads all fail due to connectivity issues.
40pub fn send_connectivity_lost() {
41    send_gio(
42        "Mimick: Connection lost",
43        "Could not reach the Immich server. Uploads will resume automatically when connectivity is restored.",
44        "connectivity-lost",
45    );
46}
47
48// ── Internal primitive ───────────────────────────────────────────────────────
49
50/// Send a desktop notification via `gio::Notification`.
51///
52/// This uses the XDG notification portal under Flatpak and the native
53/// desktop notification daemon on bare-metal installs. The themed icon
54/// ensures the app icon renders correctly in both scenarios.
55fn send_gio(title: &str, body: &str, notification_id: &str) {
56    if !ENABLED.load(Ordering::Relaxed) {
57        log::debug!("Notifications disabled; suppressing: {}", title);
58        return;
59    }
60
61    let title = title.to_string();
62    let body = body.to_string();
63    let id = notification_id.to_string();
64
65    glib::idle_add_once(move || {
66        use gtk::prelude::ApplicationExt;
67
68        let Some(app) = gtk::gio::Application::default() else {
69            log::debug!("No default GIO application; skipping notification.");
70            return;
71        };
72
73        let notification = gtk::gio::Notification::new(&title);
74        notification.set_body(Some(&body));
75
76        let icon = gtk::gio::ThemedIcon::new("dev.nicx.mimick");
77        notification.set_icon(&icon);
78
79        app.send_notification(Some(&id), &notification);
80        log::debug!("Notification sent via GIO: {} - {}", title, body);
81    });
82}