feat(core,gui): tray states and menu

The tray icon follows the drive's phase (synced, syncing, paused,
offline, needs attention) and reports NeedsAttention while changes keep
failing. The menu gains an issues item that opens the Sync page, Sign In
when signed out, Hide Tray Icon (persisted as tray_hidden) and Stop
Proton Drive, which pdfs-app confirms via --confirm-stop. A second
pdfs-app launch raises the running window.
This commit is contained in:
nvrl committed 2026-09-23 03:55:51 +02:00
1 parent 4fa61a14c3
commit f6136ce344
7 files changed
+451 -100

No files matched your search

+9
View File
@@ -137,6 +137,11 @@ pub struct AppConfig {
/// Defaulted for configs predating the field.
#[serde(default)]
pub files_view: FilesView,
/// The user hid the tray icon from its own menu. The tray exits at start
/// while this is set, and the app does not spawn it; Preferences turns it
/// back on. Defaulted for configs predating the field.
#[serde(default)]
pub tray_hidden: bool,
}
/// The My Files page's view choices, remembered between runs.
@@ -208,6 +213,7 @@ impl Default for AppConfig {
prompt: None,
proton_accent: None,
files_view: FilesView::default(),
tray_hidden: false,
}
}
}
@@ -561,12 +567,14 @@ mod tests {
descending: true,
folders_first: false,
},
tray_hidden: true,
};
let json = serde_json::to_string(&config).unwrap();
let decoded: AppConfig = serde_json::from_str(&json).unwrap();
assert_eq!(decoded.app_version, "external-drive-test-client@1.0.0");
assert_eq!(decoded.user_agent, "test-agent/1.0");
assert_eq!(decoded.proton_accent, Some(true));
assert!(decoded.tray_hidden);
assert_eq!(decoded.upload_limit, Some(500_000));
assert_eq!(decoded.download_limit, None);
assert_eq!(decoded.files_view, config.files_view);
@@ -652,6 +660,7 @@ mod tests {
let json = r#"{"app_version":"v","user_agent":"u"}"#;
let decoded: AppConfig = serde_json::from_str(json).unwrap();
assert!(decoded.ignore_patterns.is_none());
assert!(!decoded.tray_hidden, "an old config keeps the tray icon");
assert_eq!(
decoded.resolved_ignore_patterns().len(),
DEFAULT_IGNORE_PATTERNS.len(),
+1
View File
@@ -21,6 +21,7 @@ pub mod service;
pub mod shell;
pub mod syncignore;
pub mod takeout;
pub mod tray;
pub use access::{Access, access_for, perm_bits};
pub use error::{CoreError, CoreResult, Error, Result};
+20
View File
@@ -0,0 +1,20 @@
//! Talking to a running `pdfs-tray`.
//!
//! The tray binds [`AppDirs::tray_socket`] to keep a single instance. The same
//! socket takes one command, [`QUIT`], so the desktop app can take the icon
//! away when the user hides it or stops Proton Drive.
use std::io::Write;
use std::os::unix::net::UnixStream;
use crate::config::AppDirs;
/// The line that asks the tray to exit.
pub const QUIT: &str = "quit";
/// Ask a running tray to exit. False when no tray answers.
pub fn quit(dirs: &AppDirs) -> bool {
UnixStream::connect(dirs.tray_socket())
.and_then(|mut stream| stream.write_all(format!("{QUIT}\n").as_bytes()))
.is_ok()
}