NotificationContent.swift 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import Foundation
  2. import UserNotifications
  3. private let actionsCategory = "ntfyActions" // It seems ok to re-use the same category
  4. extension UNMutableNotificationContent {
  5. func modify(message: Message, baseUrl: String) {
  6. // Body and title
  7. if let body = message.message {
  8. self.body = body
  9. }
  10. // Set notification title to short URL if there is no title. The title is always set
  11. // by the server, but it may be empty.
  12. if let title = message.title, title != "" {
  13. self.title = title
  14. } else {
  15. self.title = topicShortUrl(baseUrl: baseUrl, topic: message.topic)
  16. }
  17. // Emojify title or message
  18. let emojiTags = parseEmojiTags(message.tags)
  19. if !emojiTags.isEmpty {
  20. if let title = message.title, title != "" {
  21. self.title = emojiTags.joined(separator: "") + " " + self.title
  22. } else {
  23. self.body = emojiTags.joined(separator: "") + " " + self.body
  24. }
  25. }
  26. // Add custom actions
  27. //
  28. // We re-define the categories every time here, which is weird, but it works. When tapped, the action sets the
  29. // actionIdentifier in the application(didReceive) callback. This logic is handled in the AppDelegate. This approach
  30. // is described in a comment in https://stackoverflow.com/questions/30103867/changing-action-titles-in-interactive-notifications-at-run-time#comment122812568_30107065
  31. //
  32. // We also must set the .foreground flag, which brings the notification to the foreground and avoids an error about
  33. // permissions. This is described in https://stackoverflow.com/a/44580916/1440785
  34. if let actions = message.actions, !actions.isEmpty {
  35. self.categoryIdentifier = actionsCategory
  36. let center = UNUserNotificationCenter.current()
  37. let notificationActions = actions.map { UNNotificationAction(identifier: $0.id, title: $0.label, options: [.foreground]) }
  38. let category = UNNotificationCategory(identifier: actionsCategory, actions: notificationActions, intentIdentifiers: [])
  39. center.setNotificationCategories([category])
  40. }
  41. // Play a sound, and group by topic
  42. self.sound = .default
  43. self.threadIdentifier = topicUrl(baseUrl: baseUrl, topic: message.topic)
  44. // Map priorities to interruption level (light up screen, ...) and relevance (order)
  45. if #available(iOS 15.0, *) {
  46. switch message.priority {
  47. case 1:
  48. self.interruptionLevel = .passive
  49. self.relevanceScore = 0
  50. case 2:
  51. self.interruptionLevel = .passive
  52. self.relevanceScore = 0.25
  53. case 4:
  54. self.interruptionLevel = .timeSensitive
  55. self.relevanceScore = 0.75
  56. case 5:
  57. self.interruptionLevel = .critical
  58. self.relevanceScore = 1
  59. default:
  60. self.interruptionLevel = .active
  61. self.relevanceScore = 0.5
  62. }
  63. }
  64. // Make sure the userInfo matches, so that when the notification is tapped, the AppDelegate
  65. // can properly navigate to the right topic and re-assemble the message.
  66. self.userInfo = message.toUserInfo()
  67. self.userInfo["base_url"] = baseUrl
  68. }
  69. }