Switch from an always returning the notification content approach at every step to passing it in as mutable.

This commit is contained in:
Stefan Ceriu
2025-04-17 13:53:07 +03:00
committed by Stefan Ceriu
parent c33a164354
commit f71e5facb8
2 changed files with 50 additions and 63 deletions
+47 -60
View File
@@ -22,9 +22,9 @@ struct NotificationContentBuilder {
/// - notificationItem: The notification item /// - notificationItem: The notification item
/// - mediaProvider: Media provider to process also media. May be passed nil to ignore media operations. /// - mediaProvider: Media provider to process also media. May be passed nil to ignore media operations.
/// - Returns: A notification content object if the notification should be displayed. Otherwise nil. /// - Returns: A notification content object if the notification should be displayed. Otherwise nil.
func process(notificationContent: UNMutableNotificationContent, func process(notificationContent: inout UNMutableNotificationContent,
notificationItem: NotificationItemProxyProtocol, notificationItem: NotificationItemProxyProtocol,
mediaProvider: MediaProviderProtocol) async -> UNMutableNotificationContent { mediaProvider: MediaProviderProtocol) async {
notificationContent.receiverID = notificationItem.receiverID notificationContent.receiverID = notificationItem.receiverID
notificationContent.roomID = notificationItem.roomID notificationContent.roomID = notificationItem.roomID
@@ -44,55 +44,51 @@ struct NotificationContentBuilder {
switch notificationItem.event { switch notificationItem.event {
case .none: case .none:
return processEmpty(notificationContent) processEmpty(&notificationContent)
case .invite: case .invite:
return await processInvited(notificationContent: notificationContent, await processInvited(notificationContent: &notificationContent,
notificationItem: notificationItem, notificationItem: notificationItem,
mediaProvider: mediaProvider) mediaProvider: mediaProvider)
case .timeline(let event): case .timeline(let event):
guard let eventType = try? event.eventType(), guard let eventType = try? event.eventType(),
case let .messageLike(content) = eventType else { case let .messageLike(content) = eventType else {
return processEmpty(notificationContent) processEmpty(&notificationContent)
return
} }
let notificationContent = await processMessageLike(notificationContent: notificationContent, await processMessageLike(notificationContent: &notificationContent,
notificationItem: notificationItem, notificationItem: notificationItem,
mediaProvider: mediaProvider) mediaProvider: mediaProvider)
switch content { switch content {
case .roomMessage(let messageType, _): case .roomMessage(let messageType, _):
return await processRoomMessage(notificationContent: notificationContent, await processRoomMessage(notificationContent: &notificationContent,
notificationItem: notificationItem, notificationItem: notificationItem,
messageType: messageType, messageType: messageType,
mediaProvider: mediaProvider) mediaProvider: mediaProvider)
case .poll(let question): case .poll(let question):
notificationContent.body = L10n.commonPollSummary(question) notificationContent.body = L10n.commonPollSummary(question)
return notificationContent
case .callInvite: case .callInvite:
notificationContent.body = L10n.commonUnsupportedCall notificationContent.body = L10n.commonUnsupportedCall
return notificationContent
case .callNotify: case .callNotify:
notificationContent.body = L10n.notificationIncomingCall notificationContent.body = L10n.notificationIncomingCall
return notificationContent
default: default:
return processEmpty(notificationContent) processEmpty(&notificationContent)
} }
} }
} }
// MARK: - Private // MARK: - Private
private func processEmpty(_ notificationContent: UNMutableNotificationContent) -> UNMutableNotificationContent { private func processEmpty(_ notificationContent: inout UNMutableNotificationContent) {
notificationContent.title = InfoPlistReader(bundle: .app).bundleDisplayName notificationContent.title = InfoPlistReader(bundle: .app).bundleDisplayName
notificationContent.body = L10n.notification notificationContent.body = L10n.notification
notificationContent.categoryIdentifier = NotificationConstants.Category.message notificationContent.categoryIdentifier = NotificationConstants.Category.message
return notificationContent
} }
private func processInvited(notificationContent: UNMutableNotificationContent, private func processInvited(notificationContent: inout UNMutableNotificationContent,
notificationItem: NotificationItemProxyProtocol, notificationItem: NotificationItemProxyProtocol,
mediaProvider: MediaProviderProtocol) async -> UNMutableNotificationContent { mediaProvider: MediaProviderProtocol) async {
notificationContent.categoryIdentifier = NotificationConstants.Category.invite notificationContent.categoryIdentifier = NotificationConstants.Category.invite
let body: String let body: String
@@ -104,21 +100,17 @@ struct NotificationContentBuilder {
notificationContent.body = body notificationContent.body = body
do { await addSenderIcon(notificationContent: &notificationContent,
return try await addSenderIcon(notificationContent: notificationContent, senderID: notificationItem.senderID,
senderID: notificationItem.senderID, senderName: notificationItem.senderDisplayName ?? notificationItem.roomDisplayName,
senderName: notificationItem.senderDisplayName ?? notificationItem.roomDisplayName, icon: icon(for: notificationItem),
icon: icon(for: notificationItem), forcePlaceholder: settings.hideInviteAvatars,
forcePlaceholder: settings.hideInviteAvatars, mediaProvider: mediaProvider)
mediaProvider: mediaProvider)
} catch {
return notificationContent
}
} }
private func processMessageLike(notificationContent: UNMutableNotificationContent, private func processMessageLike(notificationContent: inout UNMutableNotificationContent,
notificationItem: NotificationItemProxyProtocol, notificationItem: NotificationItemProxyProtocol,
mediaProvider: MediaProviderProtocol) async -> UNMutableNotificationContent { mediaProvider: MediaProviderProtocol) async {
notificationContent.title = notificationItem.senderDisplayName ?? notificationItem.roomDisplayName notificationContent.title = notificationItem.senderDisplayName ?? notificationItem.roomDisplayName
if notificationContent.title != notificationItem.roomDisplayName { if notificationContent.title != notificationItem.roomDisplayName {
notificationContent.subtitle = notificationItem.roomDisplayName notificationContent.subtitle = notificationItem.roomDisplayName
@@ -131,15 +123,11 @@ struct NotificationContentBuilder {
notificationItem.roomDisplayName notificationItem.roomDisplayName
} }
do { await addSenderIcon(notificationContent: &notificationContent,
return try await addSenderIcon(notificationContent: notificationContent, senderID: notificationItem.senderID,
senderID: notificationItem.senderID, senderName: senderName,
senderName: senderName, icon: icon(for: notificationItem),
icon: icon(for: notificationItem), mediaProvider: mediaProvider)
mediaProvider: mediaProvider)
} catch {
return notificationContent
}
} }
private func icon(for notificationItem: NotificationItemProxyProtocol) -> NotificationIcon { private func icon(for notificationItem: NotificationItemProxyProtocol) -> NotificationIcon {
@@ -151,43 +139,41 @@ struct NotificationContentBuilder {
} }
} }
private func processRoomMessage(notificationContent: UNMutableNotificationContent, private func processRoomMessage(notificationContent: inout UNMutableNotificationContent,
notificationItem: NotificationItemProxyProtocol, notificationItem: NotificationItemProxyProtocol,
messageType: MessageType, messageType: MessageType,
mediaProvider: MediaProviderProtocol) async -> UNMutableNotificationContent { mediaProvider: MediaProviderProtocol) async {
let displayName = notificationItem.senderDisplayName ?? notificationItem.roomDisplayName let displayName = notificationItem.senderDisplayName ?? notificationItem.roomDisplayName
notificationContent.body = String(messageEventStringBuilder.buildAttributedString(for: messageType, senderDisplayName: displayName, isOutgoing: false).characters) notificationContent.body = String(messageEventStringBuilder.buildAttributedString(for: messageType, senderDisplayName: displayName, isOutgoing: false).characters)
guard settings.timelineMediaVisibility == .always || guard settings.timelineMediaVisibility == .always ||
(settings.timelineMediaVisibility == .privateOnly && notificationItem.isRoomPrivate) (settings.timelineMediaVisibility == .privateOnly && notificationItem.isRoomPrivate)
else { else {
return notificationContent return
} }
switch messageType { switch messageType {
case .image(content: let content): case .image(content: let content):
await addMediaAttachment(notificationContent: notificationContent, await addMediaAttachment(notificationContent: &notificationContent,
using: mediaProvider, using: mediaProvider,
mediaSource: .init(source: content.source, mediaSource: .init(source: content.source,
mimeType: content.info?.mimetype)) mimeType: content.info?.mimetype))
case .audio(content: let content): case .audio(content: let content):
await addMediaAttachment(notificationContent: notificationContent, await addMediaAttachment(notificationContent: &notificationContent,
using: mediaProvider, using: mediaProvider,
mediaSource: .init(source: content.source, mediaSource: .init(source: content.source,
mimeType: content.info?.mimetype)) mimeType: content.info?.mimetype))
case .video(content: let content): case .video(content: let content):
await addMediaAttachment(notificationContent: notificationContent, await addMediaAttachment(notificationContent: &notificationContent,
using: mediaProvider, using: mediaProvider,
mediaSource: .init(source: content.source, mediaSource: .init(source: content.source,
mimeType: content.info?.mimetype)) mimeType: content.info?.mimetype))
default: default:
break break
} }
return notificationContent
} }
private func addMediaAttachment(notificationContent: UNMutableNotificationContent, private func addMediaAttachment(notificationContent: inout UNMutableNotificationContent,
using mediaProvider: MediaProviderProtocol, using mediaProvider: MediaProviderProtocol,
mediaSource: MediaSourceProxy) async { mediaSource: MediaSourceProxy) async {
switch await mediaProvider.loadFileFromSource(mediaSource) { switch await mediaProvider.loadFileFromSource(mediaSource) {
@@ -214,12 +200,12 @@ struct NotificationContentBuilder {
} }
} }
private func addSenderIcon(notificationContent: UNMutableNotificationContent, private func addSenderIcon(notificationContent: inout UNMutableNotificationContent,
senderID: String, senderID: String,
senderName: String, senderName: String,
icon: NotificationIcon, icon: NotificationIcon,
forcePlaceholder: Bool = false, forcePlaceholder: Bool = false,
mediaProvider: MediaProviderProtocol) async throws -> UNMutableNotificationContent { mediaProvider: MediaProviderProtocol) async {
var fetchedImage: INImage? var fetchedImage: INImage?
let image: INImage let image: INImage
if !forcePlaceholder, if !forcePlaceholder,
@@ -279,14 +265,15 @@ struct NotificationContentBuilder {
interaction.direction = .incoming interaction.direction = .incoming
// Donate the interaction before updating notification content. // Donate the interaction before updating notification content.
try await interaction.donate() try? await interaction.donate()
// Update notification content before displaying the // Update notification content before displaying the
// communication notification. // communication notification.
let updatedContent = try notificationContent.updating(from: intent) if let updatedContent = try? notificationContent.updating(from: intent) {
// swiftlint:disable:next force_cast
// swiftlint:disable:next force_cast let content = updatedContent.mutableCopy() as! UNMutableNotificationContent
return updatedContent.mutableCopy() as! UNMutableNotificationContent notificationContent = content
}
} }
@MainActor @MainActor
@@ -125,9 +125,9 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
case .processedShouldDiscard, .unsupportedShouldDiscard: case .processedShouldDiscard, .unsupportedShouldDiscard:
discardNotification() discardNotification()
case .shouldDisplay: case .shouldDisplay:
notificationContent = await notificationContentBuilder.process(notificationContent: notificationContent, await notificationContentBuilder.process(notificationContent: &notificationContent,
notificationItem: notificationItemProxy, notificationItem: notificationItemProxy,
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
deliverNotification() deliverNotification()
} }