Explorar el Código

Reconcile in-app banners with delivered notifications

trioneer hace 1 semana
padre
commit
a57b70422f

+ 0 - 1
Trio/Sources/Modules/Settings/View/Subviews/NotificationsView.swift

@@ -161,5 +161,4 @@ extension NotificationsView {
             onOff(!notificationsDisabled)
         }
     }
-
 }

+ 34 - 0
Trio/Sources/Services/Alerts/TrioAlertManager.swift

@@ -2,6 +2,7 @@ import Combine
 import Foundation
 import LoopKit
 import Swinject
+import UIKit
 import UserNotifications
 
 protocol TrioAlertManager: AnyObject {
@@ -84,6 +85,39 @@ final class BaseTrioAlertManager: TrioAlertManager, Injectable {
         if let until = persistedSnoozeUntil, until > Date() {
             muter.mute(for: until.timeIntervalSinceNow)
         }
+
+        // iOS doesn't fire `didReceive` on swipe for critical UNs, so we
+        // can't rely on the callback for alerts that override Silence & DnD.
+        // On foreground, mirror iOS: any banner whose UN is no longer in the
+        // delivered list gets hidden.
+        Foundation.NotificationCenter.default.addObserver(
+            self,
+            selector: #selector(reconcileBannersWithDeliveredNotifications),
+            name: UIApplication.didBecomeActiveNotification,
+            object: nil
+        )
+    }
+
+    @objc private func reconcileBannersWithDeliveredNotifications() {
+        UNUserNotificationCenter.current().getDeliveredNotifications { [weak self] delivered in
+            guard let self else { return }
+            let deliveredIDs = Set(delivered.compactMap { note -> Alert.Identifier? in
+                let userInfo = note.request.content.userInfo
+                guard
+                    let managerId = userInfo[AlertUserInfoKey.managerIdentifier.rawValue] as? String,
+                    let alertId = userInfo[AlertUserInfoKey.alertIdentifier.rawValue] as? String
+                else { return nil }
+                return Alert.Identifier(managerIdentifier: managerId, alertIdentifier: alertId)
+            })
+            DispatchQueue.main.async {
+                let stale = self.modalScheduler.active
+                    .map(\.identifier)
+                    .filter { !deliveredIDs.contains($0) }
+                for identifier in stale {
+                    self.modalScheduler.unschedule(identifier: identifier)
+                }
+            }
+        }
     }
 
     /// Falls back to in-process AVAudioPlayer for `.critical` alerts on

+ 1 - 1
Trio/Sources/Views/SnoozeAlertsSheetView.swift

@@ -24,7 +24,7 @@ struct SnoozeAlertsSheetView: View {
                                 format: String(localized: "Snoozed until %@"),
                                 snoozeUntilDate.formatted(date: .omitted, time: .shortened)
                             ))
-                            .font(.headline)
+                                .font(.headline)
                         }
                     }.listRowBackground(Color.chart)
                 }