Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ security-framework = "3.7"
[target.'cfg(target_os = "windows")'.dependencies]
webview2-com = "0.38"
windows = "0.61"
windows-sys = { version = "0.61", features = ["Win32_Foundation", "Win32_Security_Credentials", "Win32_UI_WindowsAndMessaging"] }
windows-sys = { version = "0.61", features = ["Win32_Foundation", "Win32_Globalization", "Win32_Security_Credentials", "Win32_UI_WindowsAndMessaging"] }

[target.'cfg(target_os = "linux")'.dependencies]
secret-service = { version = "5.1", default-features = false, features = ["rt-async-io-crypto-rust"] }
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/src/commands/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ mod tests {
short_name: "P".into(),
fallback_enabled: true,
local_usage_source_note: None,
local_usage_source_key: None,
pi_usage_source_key: None,
links: vec![ProviderLink::new("Status", "https://status.example.com/")],
metrics: vec![MetricDefinition::new(
"provider.session",
Expand Down
20 changes: 14 additions & 6 deletions src-tauri/src/commands/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ pub async fn save_app_settings(
}
}
crate::app_debug!("config", "application settings persisted");
if previous.language != updated.language {
crate::update_tray_menu(&app, &updated.language);
}
tray_presentation::update(
&app,
&service.state(),
Expand Down Expand Up @@ -222,19 +225,22 @@ pub fn request_notification_permission(
settings: State<'_, Arc<SettingsService>>,
) -> SettingsViewState {
crate::app_info!("notifications", "notification permission requested");
let language = settings.get().language;
let labels = crate::native_i18n::Labels::for_preference(&language);
let error = app
.notification()
.request_permission()
.err()
.map(|_| "Notification permission could not be requested.".to_owned());
.map(|_| labels.notification_permission_failed.to_owned());
if error.is_some() {
crate::app_error!("notifications", "notification permission request failed");
}
settings.view_state(
notification_permission(&app),
error,
app.state::<DesktopIntegration>().tray_available(),
app.state::<DesktopIntegration>().platform_summary(),
app.state::<DesktopIntegration>()
.platform_summary(&language),
)
}

Expand Down Expand Up @@ -294,6 +300,7 @@ pub fn open_log_folder(app: AppHandle) -> Result<(), String> {

pub(crate) fn settings_view_state(app: &AppHandle, service: &SettingsService) -> SettingsViewState {
let mut settings = service.get();
let labels = crate::native_i18n::Labels::for_preference(&settings.language);
let mut integration_error = match autostart_is_enabled(app) {
Ok(enabled) => {
if settings.launch_at_login != enabled {
Expand All @@ -302,19 +309,20 @@ pub(crate) fn settings_view_state(app: &AppHandle, service: &SettingsService) ->
}
None
}
Err(_) => Some("Launch at login status could not be read.".to_owned()),
Err(_) => Some(labels.launch_status_failed.to_owned()),
};
if let Some(shortcut) = service.get().global_shortcut {
if !app.global_shortcut().is_registered(shortcut.as_str()) {
integration_error =
Some("The saved global shortcut is currently unavailable.".to_owned());
integration_error = Some(labels.shortcut_unavailable.to_owned());
}
}
let language = service.get().language;
service.view_state(
notification_permission(app),
integration_error,
app.state::<DesktopIntegration>().tray_available(),
app.state::<DesktopIntegration>().platform_summary(),
app.state::<DesktopIntegration>()
.platform_summary(&language),
)
}

Expand Down
79 changes: 59 additions & 20 deletions src-tauri/src/desktop_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,16 @@ pub enum LinuxDesktop {
pub struct DesktopIntegration {
tray_available: bool,
floating_window: Arc<AtomicBool>,
platform_summary: Option<String>,
#[cfg(any(target_os = "linux", test))]
platform_summary: Option<PlatformSummary>,
}

#[cfg(any(target_os = "linux", test))]
#[derive(Debug, Clone, Copy)]
struct PlatformSummary {
desktop: LinuxDesktop,
session: LinuxSessionType,
tray_available: bool,
}

impl DesktopIntegration {
Expand All @@ -42,6 +51,7 @@ impl DesktopIntegration {
Self {
tray_available: true,
floating_window: Arc::new(AtomicBool::new(false)),
#[cfg(test)]
platform_summary: None,
}
}
Expand Down Expand Up @@ -69,8 +79,44 @@ impl DesktopIntegration {
self.floating_window.store(floating, Ordering::SeqCst);
}

pub fn platform_summary(&self) -> Option<String> {
self.platform_summary.clone()
pub fn platform_summary(&self, language: &str) -> Option<String> {
#[cfg(not(any(target_os = "linux", test)))]
{
let _ = language;
None
}
#[cfg(any(target_os = "linux", test))]
{
let summary = self.platform_summary?;
let locale = crate::native_i18n::Locale::for_preference(language);
let desktop = match summary.desktop {
LinuxDesktop::Gnome => "GNOME",
LinuxDesktop::Kde => "KDE Plasma",
LinuxDesktop::Other => match locale {
crate::native_i18n::Locale::En => "Linux desktop",
crate::native_i18n::Locale::ZhCn => "Linux 桌面",
crate::native_i18n::Locale::ZhTw => "Linux 桌面",
},
};
let session = match summary.session {
LinuxSessionType::X11 => "X11",
LinuxSessionType::Wayland => "Wayland",
LinuxSessionType::Unknown => match locale {
crate::native_i18n::Locale::En => "unknown session",
crate::native_i18n::Locale::ZhCn => "未知会话",
crate::native_i18n::Locale::ZhTw => "未知工作階段",
},
};
let mode = match (locale, summary.tray_available) {
(crate::native_i18n::Locale::En, true) => "StatusNotifier tray",
(crate::native_i18n::Locale::En, false) => "standalone window",
(crate::native_i18n::Locale::ZhCn, true) => "StatusNotifier 托盘",
(crate::native_i18n::Locale::ZhCn, false) => "独立窗口",
(crate::native_i18n::Locale::ZhTw, true) => "StatusNotifier 狀態列",
(crate::native_i18n::Locale::ZhTw, false) => "獨立視窗",
};
Some(format!("{desktop} · {session} · {mode}"))
}
}
}

Expand All @@ -80,25 +126,14 @@ fn linux_integration(
desktop: LinuxDesktop,
tray_available: bool,
) -> DesktopIntegration {
let desktop = match desktop {
LinuxDesktop::Gnome => "GNOME",
LinuxDesktop::Kde => "KDE Plasma",
LinuxDesktop::Other => "Linux desktop",
};
let session = match session {
LinuxSessionType::X11 => "X11",
LinuxSessionType::Wayland => "Wayland",
LinuxSessionType::Unknown => "unknown session",
};
let mode = if tray_available {
"StatusNotifier tray"
} else {
"standalone window"
};
DesktopIntegration {
tray_available,
floating_window: Arc::new(AtomicBool::new(!tray_available)),
platform_summary: Some(format!("{desktop} · {session} · {mode}")),
platform_summary: Some(PlatformSummary {
desktop,
session,
tray_available,
}),
}
}

Expand Down Expand Up @@ -180,9 +215,13 @@ mod tests {
let integration =
super::linux_integration(LinuxSessionType::Wayland, LinuxDesktop::Gnome, false);
assert_eq!(
integration.platform_summary().as_deref(),
integration.platform_summary("en").as_deref(),
Some("GNOME · Wayland · standalone window")
);
assert_eq!(
integration.platform_summary("zh-CN").as_deref(),
Some("GNOME · Wayland · 独立窗口")
);
}

#[test]
Expand Down
70 changes: 43 additions & 27 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod logging;
#[cfg(any(target_os = "macos", test))]
mod menu_bar;
mod models;
mod native_i18n;
mod notifications;
mod pacing;
mod policy;
Expand Down Expand Up @@ -60,6 +61,47 @@ use crate::{
},
};

fn tray_menu(app: &AppHandle, language: &str) -> tauri::Result<Menu<tauri::Wry>> {
let labels = native_i18n::Labels::for_preference(language);
#[cfg(target_os = "macos")]
{
let settings =
MenuItem::with_id(app, "settings", labels.settings, true, Some("CmdOrCtrl+,"))?;
let separator = PredefinedMenuItem::separator(app)?;
let quit = MenuItem::with_id(app, "quit", labels.quit, true, Some("CmdOrCtrl+Q"))?;
Menu::with_items(app, &[&settings, &separator, &quit])
}
#[cfg(not(target_os = "macos"))]
{
let open = MenuItem::with_id(app, "open", labels.open, true, None::<&str>)?;
let customize = MenuItem::with_id(app, "customize", labels.customize, true, None::<&str>)?;
let settings = MenuItem::with_id(
app,
"settings",
labels.settings_with_ellipsis,
true,
None::<&str>,
)?;
let separator = PredefinedMenuItem::separator(app)?;
let quit = MenuItem::with_id(app, "quit", labels.quit, true, None::<&str>)?;
Menu::with_items(app, &[&open, &customize, &settings, &separator, &quit])
}
}

pub(crate) fn update_tray_menu(app: &AppHandle, language: &str) {
let Some(tray) = app.tray_by_id("openquota-tray") else {
return;
};
match tray_menu(app, language) {
Ok(menu) => {
if tray.set_menu(Some(menu)).is_err() {
app_warn!("tray", "tray menu language update failed");
}
}
Err(_) => app_warn!("tray", "localized tray menu could not be built"),
}
}

fn spawn_startup_credential_detection(
app: AppHandle,
registry: Arc<ProviderRegistry>,
Expand Down Expand Up @@ -282,33 +324,7 @@ pub fn run() {
}

if desktop_integration.tray_available() {
#[cfg(target_os = "macos")]
let menu = {
let settings_item =
MenuItem::with_id(app, "settings", "Settings", true, Some("CmdOrCtrl+,"))?;
let separator = PredefinedMenuItem::separator(app)?;
let quit = MenuItem::with_id(
app,
"quit",
"Quit OpenQuota",
true,
Some("CmdOrCtrl+Q"),
)?;
Menu::with_items(app, &[&settings_item, &separator, &quit])?
};
#[cfg(not(target_os = "macos"))]
let menu = {
let open =
MenuItem::with_id(app, "open", "Open OpenQuota", true, None::<&str>)?;
let customize =
MenuItem::with_id(app, "customize", "Customize…", true, None::<&str>)?;
let settings_item =
MenuItem::with_id(app, "settings", "Settings…", true, None::<&str>)?;
let separator = PredefinedMenuItem::separator(app)?;
let quit =
MenuItem::with_id(app, "quit", "Quit OpenQuota", true, None::<&str>)?;
Menu::with_items(app, &[&open, &customize, &settings_item, &separator, &quit])?
};
let menu = tray_menu(app.handle(), &settings.get().language)?;

let tray = TrayIconBuilder::with_id("openquota-tray")
.icon(
Expand Down
Loading