damus

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

NotificationFormatter.swift (7441B)


      1 //
      2 //  NotificationFormatter.swift
      3 //  DamusNotificationService
      4 //
      5 //  Created by Daniel D’Aquino on 2023-11-13.
      6 //
      7 
      8 import Foundation
      9 import UserNotifications
     10 
     11 struct NotificationFormatter {
     12     static var shared = NotificationFormatter()
     13     
     14     // MARK: - Formatting with NdbNote
     15     
     16     func format_message(event: NdbNote) -> UNMutableNotificationContent? {
     17         let content = UNMutableNotificationContent()
     18         if let event_json_data = try? JSONEncoder().encode(event),  // Must be encoded, as the notification completion handler requires this object to conform to `NSSecureCoding`
     19            let event_json_string = String(data: event_json_data, encoding: .utf8) {
     20             content.userInfo = [
     21                 NDB_NOTE_JSON_USER_INFO_KEY: event_json_string
     22             ]
     23         }
     24         switch event.known_kind {
     25             case .text:
     26                 content.title = NSLocalizedString("Someone posted a note", comment: "Title label for push notification where someone posted a note")
     27                 content.body = event.content
     28                 break
     29             case .dm:
     30                 content.title = NSLocalizedString("New message", comment: "Title label for push notifications where a direct message was sent to the user")
     31                 content.body = NSLocalizedString("(Contents are encrypted)", comment: "Label on push notification indicating that the contents of the message are encrypted")
     32                 break
     33             case .like:
     34                 guard let reactionEmoji = to_reaction_emoji(ev: event) else {
     35                     content.title = NSLocalizedString("Someone reacted to your note", comment: "Generic title label for push notifications where someone reacted to the user's post")
     36                     break
     37                 }
     38                 content.title = NSLocalizedString("New note reaction", comment: "Title label for push notifications where someone reacted to the user's post with a specific emoji")
     39                 content.body = String(format: NSLocalizedString("Someone reacted to your note with %@", comment: "Body label for push notifications where someone reacted to the user's post with a specific emoji"), reactionEmoji)
     40                 break
     41             case .zap:
     42                 content.title = NSLocalizedString("Someone zapped you ⚡️", comment: "Title label for a push notification where someone zapped the user")
     43                 break
     44             default:
     45                 return nil
     46         }
     47         return content
     48     }
     49     
     50     // MARK: - Formatting with LocalNotification
     51     
     52     func format_message(displayName: String, notify: LocalNotification) -> (content: UNMutableNotificationContent, identifier: String)? {
     53         let content = UNMutableNotificationContent()
     54         var title = ""
     55         var identifier = ""
     56         
     57         switch notify.type {
     58             case .tagged:
     59                 title = String(format: NSLocalizedString("Tagged by %@", comment: "Tagged by heading in local notification"), displayName)
     60                 identifier = "myMentionNotification"
     61             case .mention:
     62                 title = String(format: NSLocalizedString("Mentioned by %@", comment: "Mentioned by heading in local notification"), displayName)
     63                 identifier = "myMentionNotification"
     64             case .repost:
     65                 title = String(format: NSLocalizedString("Reposted by %@", comment: "Reposted by heading in local notification"), displayName)
     66                 identifier = "myBoostNotification"
     67             case .like:
     68                 title = String(format: NSLocalizedString("%@ reacted with %@", comment: "Reacted by heading in local notification"), displayName, to_reaction_emoji(ev: notify.event) ?? "")
     69                 identifier = "myLikeNotification"
     70             case .dm:
     71                 title = displayName
     72                 identifier = "myDMNotification"
     73             case .zap, .profile_zap:
     74                 // not handled here. Try `format_message(displayName: String, notify: LocalNotification, state: HeadlessDamusState) async -> (content: UNMutableNotificationContent, identifier: String)?`
     75                 return nil
     76             case .reply:
     77                 title = String(format: NSLocalizedString("%@ replied to your note", comment: "Heading for local notification indicating a new reply"), displayName)
     78                 identifier = "myReplyNotification"
     79         }
     80         content.title = title
     81         content.body = notify.content
     82         content.sound = UNNotificationSound.default
     83         content.userInfo = notify.to_lossy().to_user_info()
     84 
     85         return (content, identifier)
     86     }
     87     
     88     func format_message(displayName: String, notify: LocalNotification, state: HeadlessDamusState) async -> (content: UNMutableNotificationContent, identifier: String)? {
     89         // Try sync method first and return if it works
     90         if let sync_formatted_message = self.format_message(displayName: displayName, notify: notify) {
     91             return sync_formatted_message
     92         }
     93         
     94         // If it does not work, try async formatting methods
     95         let content = UNMutableNotificationContent()
     96 
     97         switch notify.type {
     98             case .zap, .profile_zap:
     99                 guard let zap = await get_zap(from: notify.event, state: state) else {
    100                     Log.debug("format_message: async get_zap failed", for: .push_notifications)
    101                     return nil
    102                 }
    103                 content.title = Self.zap_notification_title(zap)
    104                 content.body = Self.zap_notification_body(profiles: state.profiles, zap: zap)
    105                 content.sound = UNNotificationSound.default
    106                 content.userInfo = LossyLocalNotification(type: .zap, mention: .note(notify.event.id)).to_user_info()
    107                 return (content, "myZapNotification")
    108             default:
    109                 // The sync method should have taken care of this.
    110                 return nil
    111         }
    112     }
    113     
    114     // MARK: - Formatting zap utility notifications
    115 
    116     static func zap_notification_title(_ zap: Zap) -> String {
    117         if zap.private_request != nil {
    118             return NSLocalizedString("Private Zap", comment: "Title of notification when a private zap is received.")
    119         } else {
    120             return NSLocalizedString("Zap", comment: "Title of notification when a non-private zap is received.")
    121         }
    122     }
    123 
    124     static func zap_notification_body(profiles: Profiles, zap: Zap, locale: Locale = Locale.current) -> String {
    125         let src = zap.request.ev
    126         let pk = zap.is_anon ? ANON_PUBKEY : src.pubkey
    127 
    128         let profile_txn = profiles.lookup(id: pk)
    129         let profile = profile_txn?.unsafeUnownedValue
    130         let name = Profile.displayName(profile: profile, pubkey: pk).displayName.truncate(maxLength: 50)
    131 
    132         let sats = NSNumber(value: (Double(zap.invoice.amount) / 1000.0))
    133         let formattedSats = format_msats_abbrev(zap.invoice.amount)
    134 
    135         if src.content.isEmpty {
    136             let format = localizedStringFormat(key: "zap_notification_no_message", locale: locale)
    137             return String(format: format, locale: locale, sats.decimalValue as NSDecimalNumber, formattedSats, name)
    138         } else {
    139             let format = localizedStringFormat(key: "zap_notification_with_message", locale: locale)
    140             return String(format: format, locale: locale, sats.decimalValue as NSDecimalNumber, formattedSats, name, src.content)
    141         }
    142     }
    143 }