damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

NotificationService.swift (4993B)


      1 //
      2 //  NotificationService.swift
      3 //  DamusNotificationService
      4 //
      5 //  Created by Daniel D’Aquino on 2023-11-10.
      6 //
      7 
      8 import UserNotifications
      9 import Foundation
     10 
     11 class NotificationService: UNNotificationServiceExtension {
     12 
     13     var contentHandler: ((UNNotificationContent) -> Void)?
     14     var bestAttemptContent: UNMutableNotificationContent?
     15 
     16     override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
     17         self.contentHandler = contentHandler
     18         
     19         guard let nostr_event_json = request.content.userInfo["nostr_event"] as? String,
     20               let nostr_event = NdbNote.owned_from_json(json: nostr_event_json)
     21         else {
     22             // No nostr event detected. Just display the original notification
     23             contentHandler(request.content)
     24             return;
     25         }
     26         
     27         // Log that we got a push notification
     28         Log.debug("Got nostr event push notification from pubkey %s", for: .push_notifications, nostr_event.pubkey.hex())
     29         
     30         guard let state = NotificationExtensionState() else {
     31             Log.debug("Failed to open nostrdb", for: .push_notifications)
     32 
     33             // Something failed to initialize so let's go for the next best thing
     34             guard let improved_content = NotificationFormatter.shared.format_message(event: nostr_event) else {
     35                 // We cannot format this nostr event. Suppress notification.
     36                 contentHandler(UNNotificationContent())
     37                 return
     38             }
     39             contentHandler(improved_content)
     40             return
     41         }
     42 
     43         let txn = state.ndb.lookup_profile(nostr_event.pubkey)
     44         let profile = txn?.unsafeUnownedValue?.profile
     45         let name = Profile.displayName(profile: profile, pubkey: nostr_event.pubkey).displayName
     46 
     47         // Don't show notification details that match mute list.
     48         // TODO: Remove this code block once we get notification suppression entitlement from Apple. It will be covered by the `guard should_display_notification` block
     49         if state.mutelist_manager.is_event_muted(nostr_event) {
     50             // We cannot really suppress muted notifications until we have the notification supression entitlement.
     51             // The best we can do if we ever get those muted notifications (which we generally won't due to server-side processing) is to obscure the details
     52             let content = UNMutableNotificationContent()
     53             content.title = NSLocalizedString("Muted event", comment: "Title for a push notification which has been muted")
     54             content.body = NSLocalizedString("This is an event that has been muted according to your mute list rules. We cannot suppress this notification, but we obscured the details to respect your preferences", comment: "Description for a push notification which has been muted, and explanation that we cannot suppress it")
     55             content.sound = UNNotificationSound.default
     56             contentHandler(content)
     57             return
     58         }
     59         
     60         guard should_display_notification(state: state, event: nostr_event, mode: .push) else {
     61             Log.debug("should_display_notification failed", for: .push_notifications)
     62             // We should not display notification for this event. Suppress notification.
     63             // contentHandler(UNNotificationContent())
     64             // TODO: We cannot really suppress until we have the notification supression entitlement. Show the raw notification
     65             contentHandler(request.content)
     66             return
     67         }
     68         
     69         guard let notification_object = generate_local_notification_object(from: nostr_event, state: state) else {
     70             Log.debug("generate_local_notification_object failed", for: .push_notifications)
     71             // We could not process this notification. Probably an unsupported nostr event kind. Suppress.
     72             // contentHandler(UNNotificationContent())
     73             // TODO: We cannot really suppress until we have the notification supression entitlement. Show the raw notification
     74             contentHandler(request.content)
     75             return
     76         }
     77         
     78         Task {
     79             guard let (improvedContent, _) = await NotificationFormatter.shared.format_message(displayName: name, notify: notification_object, state: state) else {
     80 
     81                 Log.debug("NotificationFormatter.format_message failed", for: .push_notifications)
     82                 return
     83             }
     84 
     85             contentHandler(improvedContent)
     86         }
     87     }
     88     
     89     override func serviceExtensionTimeWillExpire() {
     90         // Called just before the extension will be terminated by the system.
     91         // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
     92         if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
     93             contentHandler(bestAttemptContent)
     94         }
     95     }
     96 
     97 }