From 38418afbca8e01205f92ad0cdb532be8b732329a Mon Sep 17 00:00:00 2001 From: Mudit200408 Date: Mon, 25 May 2026 00:09:33 +0530 Subject: [PATCH] fix(notification): resolve duplicate cards, focus-mode auto-dismissal, and local hide behavior - Update addNotification to check for existing nids and update the notification array rather than appending duplicates blindly. - Query UNUserNotificationCenter authorization settings in syncWithSystemNotifications to skip manual dismissal checks if notification permission is not granted (prevents auto-dismissal in Focus/DND mode or when notifications are disabled). - Modify hideNotification to only dismiss the notification locally on Mac without sending the dismissal action back to Android. --- airsync-mac/Core/AppState.swift | 38 ++++++++++++------- .../Core/Util/NotificationDelegate.swift | 10 ----- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/airsync-mac/Core/AppState.swift b/airsync-mac/Core/AppState.swift index b785d8e7..098a6986 100644 --- a/airsync-mac/Core/AppState.swift +++ b/airsync-mac/Core/AppState.swift @@ -943,7 +943,7 @@ class AppState: ObservableObject { withAnimation { self.notifications.removeAll { $0.id == notif.id } } - self.removeNotification(notif) + UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [notif.nid]) } } @@ -1058,12 +1058,19 @@ class AppState: ObservableObject { func addNotification(_ notif: Notification) { DispatchQueue.main.async { + var contentChanged = true withAnimation { - self.notifications.insert(notif, at: 0) + if let idx = self.notifications.firstIndex(where: { $0.nid == notif.nid }) { + let old = self.notifications[idx] + contentChanged = (old.title != notif.title || old.body != notif.body || old.actions != notif.actions) + self.notifications[idx] = notif + } else { + self.notifications.insert(notif, at: 0) + } } - // Trigger native macOS notification if not silent + // Trigger native macOS notification if not silent and content actually changed/new // Default to alerting if priority is missing (backwards compatibility) - if notif.priority != "silent" { + if notif.priority != "silent" && contentChanged { var appIcon: NSImage? = nil if let iconPath = self.androidApps[notif.package]?.iconUrl { appIcon = NSImage(contentsOfFile: iconPath) @@ -1196,17 +1203,22 @@ class AppState: ObservableObject { } func syncWithSystemNotifications() { - UNUserNotificationCenter.current().getDeliveredNotifications { systemNotifs in - let systemNIDs = Set(systemNotifs.map { $0.request.identifier }) + UNUserNotificationCenter.current().getNotificationSettings { settings in + guard settings.authorizationStatus == .authorized else { + return + } + UNUserNotificationCenter.current().getDeliveredNotifications { systemNotifs in + let systemNIDs = Set(systemNotifs.map { $0.request.identifier }) - DispatchQueue.main.async { - // Only sync notifications that were actually posted to system (non-silent) - let currentSystemNIDs = Set(self.notifications.filter { $0.priority != "silent" }.map { $0.nid }) - let removedNIDs = currentSystemNIDs.subtracting(systemNIDs) + DispatchQueue.main.async { + // Only sync notifications that were actually posted to system (non-silent) + let currentSystemNIDs = Set(self.notifications.filter { $0.priority != "silent" }.map { $0.nid }) + let removedNIDs = currentSystemNIDs.subtracting(systemNIDs) - for nid in removedNIDs { - print("[state] (notification) System notification \(nid) was dismissed manually.") - self.removeNotificationById(nid) + for nid in removedNIDs { + print("[state] (notification) System notification \(nid) was dismissed manually.") + self.removeNotificationById(nid) + } } } } diff --git a/airsync-mac/Core/Util/NotificationDelegate.swift b/airsync-mac/Core/Util/NotificationDelegate.swift index b2a51ccb..f26ab625 100644 --- a/airsync-mac/Core/Util/NotificationDelegate.swift +++ b/airsync-mac/Core/Util/NotificationDelegate.swift @@ -10,16 +10,6 @@ import UserNotifications @MainActor class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate { - func userNotificationCenter(_ center: UNUserNotificationCenter, - didRemoveDeliveredNotifications identifiers: [String]) { - for nid in identifiers { - print("[notification-delegate] User dismissed system notification with nid: \(nid)") - DispatchQueue.main.async { - AppState.shared.removeNotificationById(nid) - } - } - } - func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {