1
0
mirror of git://jb55.com/damus synced 2024-09-29 00:10:43 +00:00
damus/DamusNotificationService/NotificationFormatter.swift

137 lines
6.7 KiB
Swift
Raw Normal View History

//
// NotificationFormatter.swift
// DamusNotificationService
//
// Created by Daniel DAquino on 2023-11-13.
//
import Foundation
import UserNotifications
struct NotificationFormatter {
static var shared = NotificationFormatter()
// MARK: - Formatting with NdbNote
func format_message(event: NdbNote) -> UNMutableNotificationContent? {
let content = UNMutableNotificationContent()
if let event_json_data = try? JSONEncoder().encode(event), // Must be encoded, as the notification completion handler requires this object to conform to `NSSecureCoding`
let event_json_string = String(data: event_json_data, encoding: .utf8) {
content.userInfo = [
NDB_NOTE_JSON_USER_INFO_KEY: event_json_string
]
}
switch event.known_kind {
case .text:
content.title = NSLocalizedString("Someone posted a note", comment: "Title label for push notification where someone posted a note")
content.body = event.content
break
case .dm:
content.title = NSLocalizedString("New message", comment: "Title label for push notifications where a direct message was sent to the user")
content.body = NSLocalizedString("(Contents are encrypted)", comment: "Label on push notification indicating that the contents of the message are encrypted")
break
case .like:
guard let reactionEmoji = to_reaction_emoji(ev: event) else {
content.title = NSLocalizedString("Someone reacted to your note", comment: "Generic title label for push notifications where someone reacted to the user's post")
break
}
content.title = NSLocalizedString("New note reaction", comment: "Title label for push notifications where someone reacted to the user's post with a specific emoji")
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)
break
case .zap:
content.title = NSLocalizedString("Someone zapped you ⚡️", comment: "Title label for a push notification where someone zapped the user")
break
default:
return nil
}
return content
}
// MARK: - Formatting with LocalNotification
Implement zap notification support for push notifications The code paths for generating zap notifications were very different from the paths used by most other notifications. In this commit, I include the logic and data structures necessary for formatting zap notifications in the same fashion as local notifications. A good amount of refactoring and moving functions/structures around was necessary to reuse zap local notification logic. I also attempted to make the notification generation process more consistent between zaps and other notifications, without changing too much of existing logic to avoid even more regression risk. General push notifications + local notifications test ----------------------------------------------------- PASS Device: iPhone 15 Pro simulator iOS: 17.0.1 Damus: This commit Setup: - Two phones running Damus on different accounts - Local relay with strfry-push-notify test setup - Apple push notification test tool Coverage: 1. Mention notifications 2. DM notifications 3. Reaction notifications 4. Repost notifications Steps for each notification type: 1. Trigger a notification (local and then push) 2. Ensure that the notification is received on the other device 3. Ensure that the notification is formatted correctly 4. Ensure that DMs are decrypted correctly 5. Ensure that profile names are unfurled correctly 6. Click on the notification and ensure that the app opens to the correct screen Result: PASS (all notifications received and formatted correctly) Notes: - For some reason my relay is not receiving zap events, so I could not test zap notifications yet. - Reply notifications do not seem to be implemented yet - These apply to the tests below as well Changelog-Added: Zap notification support for push notifications Signed-off-by: Daniel D’Aquino <daniel@daquino.me> Signed-off-by: William Casarin <jb55@jb55.com>
2023-12-01 21:26:27 +00:00
func format_message(displayName: String, notify: LocalNotification) -> (content: UNMutableNotificationContent, identifier: String)? {
let content = UNMutableNotificationContent()
var title = ""
var identifier = ""
switch notify.type {
case .mention:
title = String(format: NSLocalizedString("Mentioned by %@", comment: "Mentioned by heading in local notification"), displayName)
identifier = "myMentionNotification"
case .repost:
title = String(format: NSLocalizedString("Reposted by %@", comment: "Reposted by heading in local notification"), displayName)
identifier = "myBoostNotification"
case .like:
title = String(format: NSLocalizedString("%@ reacted with %@", comment: "Reacted by heading in local notification"), displayName, to_reaction_emoji(ev: notify.event) ?? "")
identifier = "myLikeNotification"
case .dm:
title = displayName
identifier = "myDMNotification"
case .zap, .profile_zap:
Implement zap notification support for push notifications The code paths for generating zap notifications were very different from the paths used by most other notifications. In this commit, I include the logic and data structures necessary for formatting zap notifications in the same fashion as local notifications. A good amount of refactoring and moving functions/structures around was necessary to reuse zap local notification logic. I also attempted to make the notification generation process more consistent between zaps and other notifications, without changing too much of existing logic to avoid even more regression risk. General push notifications + local notifications test ----------------------------------------------------- PASS Device: iPhone 15 Pro simulator iOS: 17.0.1 Damus: This commit Setup: - Two phones running Damus on different accounts - Local relay with strfry-push-notify test setup - Apple push notification test tool Coverage: 1. Mention notifications 2. DM notifications 3. Reaction notifications 4. Repost notifications Steps for each notification type: 1. Trigger a notification (local and then push) 2. Ensure that the notification is received on the other device 3. Ensure that the notification is formatted correctly 4. Ensure that DMs are decrypted correctly 5. Ensure that profile names are unfurled correctly 6. Click on the notification and ensure that the app opens to the correct screen Result: PASS (all notifications received and formatted correctly) Notes: - For some reason my relay is not receiving zap events, so I could not test zap notifications yet. - Reply notifications do not seem to be implemented yet - These apply to the tests below as well Changelog-Added: Zap notification support for push notifications Signed-off-by: Daniel D’Aquino <daniel@daquino.me> Signed-off-by: William Casarin <jb55@jb55.com>
2023-12-01 21:26:27 +00:00
// not handled here. Try `format_message(displayName: String, notify: LocalNotification, state: HeadlessDamusState) async -> (content: UNMutableNotificationContent, identifier: String)?`
return nil
}
content.title = title
content.body = notify.content
content.sound = UNNotificationSound.default
content.userInfo = notify.to_lossy().to_user_info()
return (content, identifier)
}
Implement zap notification support for push notifications The code paths for generating zap notifications were very different from the paths used by most other notifications. In this commit, I include the logic and data structures necessary for formatting zap notifications in the same fashion as local notifications. A good amount of refactoring and moving functions/structures around was necessary to reuse zap local notification logic. I also attempted to make the notification generation process more consistent between zaps and other notifications, without changing too much of existing logic to avoid even more regression risk. General push notifications + local notifications test ----------------------------------------------------- PASS Device: iPhone 15 Pro simulator iOS: 17.0.1 Damus: This commit Setup: - Two phones running Damus on different accounts - Local relay with strfry-push-notify test setup - Apple push notification test tool Coverage: 1. Mention notifications 2. DM notifications 3. Reaction notifications 4. Repost notifications Steps for each notification type: 1. Trigger a notification (local and then push) 2. Ensure that the notification is received on the other device 3. Ensure that the notification is formatted correctly 4. Ensure that DMs are decrypted correctly 5. Ensure that profile names are unfurled correctly 6. Click on the notification and ensure that the app opens to the correct screen Result: PASS (all notifications received and formatted correctly) Notes: - For some reason my relay is not receiving zap events, so I could not test zap notifications yet. - Reply notifications do not seem to be implemented yet - These apply to the tests below as well Changelog-Added: Zap notification support for push notifications Signed-off-by: Daniel D’Aquino <daniel@daquino.me> Signed-off-by: William Casarin <jb55@jb55.com>
2023-12-01 21:26:27 +00:00
func format_message(displayName: String, notify: LocalNotification, state: HeadlessDamusState) async -> (content: UNMutableNotificationContent, identifier: String)? {
// Try sync method first and return if it works
if let sync_formatted_message = self.format_message(displayName: displayName, notify: notify) {
return sync_formatted_message
}
// If it does not work, try async formatting methods
let content = UNMutableNotificationContent()
switch notify.type {
case .zap, .profile_zap:
guard let zap = await get_zap(from: notify.event, state: state) else {
return nil
}
content.title = Self.zap_notification_title(zap)
content.body = Self.zap_notification_body(profiles: state.profiles, zap: zap)
content.sound = UNNotificationSound.default
content.userInfo = LossyLocalNotification(type: .zap, mention: .note(notify.event.id)).to_user_info()
return (content, "myZapNotification")
default:
// The sync method should have taken care of this.
return nil
}
}
// MARK: - Formatting zap utility notifications
static func zap_notification_title(_ zap: Zap) -> String {
if zap.private_request != nil {
return NSLocalizedString("Private Zap", comment: "Title of notification when a private zap is received.")
} else {
return NSLocalizedString("Zap", comment: "Title of notification when a non-private zap is received.")
}
}
static func zap_notification_body(profiles: Profiles, zap: Zap, locale: Locale = Locale.current) -> String {
let src = zap.request.ev
let pk = zap.is_anon ? ANON_PUBKEY : src.pubkey
let profile_txn = profiles.lookup(id: pk)
let profile = profile_txn?.unsafeUnownedValue
let name = Profile.displayName(profile: profile, pubkey: pk).displayName.truncate(maxLength: 50)
Implement zap notification support for push notifications The code paths for generating zap notifications were very different from the paths used by most other notifications. In this commit, I include the logic and data structures necessary for formatting zap notifications in the same fashion as local notifications. A good amount of refactoring and moving functions/structures around was necessary to reuse zap local notification logic. I also attempted to make the notification generation process more consistent between zaps and other notifications, without changing too much of existing logic to avoid even more regression risk. General push notifications + local notifications test ----------------------------------------------------- PASS Device: iPhone 15 Pro simulator iOS: 17.0.1 Damus: This commit Setup: - Two phones running Damus on different accounts - Local relay with strfry-push-notify test setup - Apple push notification test tool Coverage: 1. Mention notifications 2. DM notifications 3. Reaction notifications 4. Repost notifications Steps for each notification type: 1. Trigger a notification (local and then push) 2. Ensure that the notification is received on the other device 3. Ensure that the notification is formatted correctly 4. Ensure that DMs are decrypted correctly 5. Ensure that profile names are unfurled correctly 6. Click on the notification and ensure that the app opens to the correct screen Result: PASS (all notifications received and formatted correctly) Notes: - For some reason my relay is not receiving zap events, so I could not test zap notifications yet. - Reply notifications do not seem to be implemented yet - These apply to the tests below as well Changelog-Added: Zap notification support for push notifications Signed-off-by: Daniel D’Aquino <daniel@daquino.me> Signed-off-by: William Casarin <jb55@jb55.com>
2023-12-01 21:26:27 +00:00
let sats = NSNumber(value: (Double(zap.invoice.amount) / 1000.0))
let formattedSats = format_msats_abbrev(zap.invoice.amount)
if src.content.isEmpty {
let format = localizedStringFormat(key: "zap_notification_no_message", locale: locale)
return String(format: format, locale: locale, sats.decimalValue as NSDecimalNumber, formattedSats, name)
} else {
let format = localizedStringFormat(key: "zap_notification_with_message", locale: locale)
return String(format: format, locale: locale, sats.decimalValue as NSDecimalNumber, formattedSats, name, src.content)
}
}
}