fix(notifications): request OS permission at startup and fix socketService listener leak (#998)

This commit is contained in:
Mega Mind
2026-04-28 18:56:44 -07:00
committed by GitHub
parent dcbcf7cc4e
commit 23a18c9c3f
21 changed files with 893 additions and 39 deletions
+99 -1
View File
@@ -637,8 +637,13 @@ fn show_native_notification(
tag: Option<String>,
) -> Result<(), String> {
use tauri_plugin_notification::NotificationExt;
let permission_state = app
.notification()
.permission_state()
.map(|s| format!("{s:?}"))
.unwrap_or_else(|e| format!("err({e})"));
log::debug!(
"[notify] show_native_notification title_chars={} body_chars={} tag={:?}",
"[notify] show_native_notification title_chars={} body_chars={} tag={:?} permission={permission_state}",
title.len(),
body.len(),
tag
@@ -647,11 +652,102 @@ fn show_native_notification(
if !body.is_empty() {
builder = builder.body(&body);
}
#[cfg(target_os = "macos")]
{
builder = builder.sound("default");
}
builder
.show()
.map_err(|e| format!("notification show failed: {e}"))
}
#[cfg(target_os = "macos")]
fn macos_notification_permission_state_inner() -> Result<String, String> {
use std::ptr::NonNull;
use std::sync::mpsc;
use block2::RcBlock;
use objc2_user_notifications::{
UNAuthorizationStatus, UNNotificationSettings, UNUserNotificationCenter,
};
let center = UNUserNotificationCenter::currentNotificationCenter();
let (tx, rx) = mpsc::channel::<String>();
let completion = RcBlock::new(move |settings: NonNull<UNNotificationSettings>| {
let status = unsafe { settings.as_ref().authorizationStatus() };
let state = if status == UNAuthorizationStatus::Authorized {
"granted"
} else if status == UNAuthorizationStatus::Denied {
"denied"
} else if status == UNAuthorizationStatus::NotDetermined {
"not_determined"
} else if status == UNAuthorizationStatus::Provisional {
"provisional"
} else if status == UNAuthorizationStatus::Ephemeral {
"ephemeral"
} else {
"unknown"
};
let _ = tx.send(state.to_string());
});
center.getNotificationSettingsWithCompletionHandler(&completion);
rx.recv_timeout(std::time::Duration::from_secs(2))
.map_err(|_| "timed out waiting for macOS notification settings".to_string())
}
#[cfg(target_os = "macos")]
fn macos_notification_permission_request_inner() -> Result<String, String> {
use block2::RcBlock;
use objc2::runtime::Bool;
use objc2_foundation::NSError;
use objc2_user_notifications::{UNAuthorizationOptions, UNUserNotificationCenter};
use std::sync::mpsc;
let center = UNUserNotificationCenter::currentNotificationCenter();
let (tx, rx) = mpsc::channel::<bool>();
let options = UNAuthorizationOptions::Alert
| UNAuthorizationOptions::Badge
| UNAuthorizationOptions::Sound;
let completion = RcBlock::new(move |granted: Bool, _error: *mut NSError| {
let _ = tx.send(granted.as_bool());
});
center.requestAuthorizationWithOptions_completionHandler(options, &completion);
let granted = rx
.recv_timeout(std::time::Duration::from_secs(5))
.map_err(|_| "timed out waiting for macOS permission prompt result".to_string())?;
if granted {
Ok("granted".to_string())
} else {
// If the user denies or notifications are disabled for the app,
// macOS reports `false` here.
Ok("denied".to_string())
}
}
#[tauri::command]
fn notification_permission_state() -> Result<String, String> {
#[cfg(target_os = "macos")]
{
return macos_notification_permission_state_inner();
}
#[cfg(not(target_os = "macos"))]
{
Ok("granted".to_string())
}
}
#[tauri::command]
fn notification_permission_request() -> Result<String, String> {
#[cfg(target_os = "macos")]
{
return macos_notification_permission_request_inner();
}
#[cfg(not(target_os = "macos"))]
{
Ok("granted".to_string())
}
}
fn show_main_window(app: &AppHandle<AppRuntime>) -> Result<(), String> {
let window = app
.get_webview_window("main")
@@ -1383,6 +1479,8 @@ pub fn run() {
gmail::gmail_trash,
gmail::gmail_add_label,
gmail::gmail_find_linkedin_profile_url,
notification_permission_state,
notification_permission_request,
activate_main_window,
show_native_notification
])