diff --git a/ElementX/Sources/Other/Extensions/FileManager.swift b/ElementX/Sources/Other/Extensions/FileManager.swift index a894cda77..f99e57b51 100644 --- a/ElementX/Sources/Other/Extensions/FileManager.swift +++ b/ElementX/Sources/Other/Extensions/FileManager.swift @@ -44,7 +44,8 @@ extension FileManager { return newURL } - + + @discardableResult func writeDataToTemporaryDirectory(data: Data, fileName: String) throws -> URL { let newURL = URL.temporaryDirectory.appendingPathComponent(fileName) diff --git a/ElementX/Sources/Other/Extensions/UNNotificationContent.swift b/ElementX/Sources/Other/Extensions/UNNotificationContent.swift index c27450e89..466737a5d 100644 --- a/ElementX/Sources/Other/Extensions/UNNotificationContent.swift +++ b/ElementX/Sources/Other/Extensions/UNNotificationContent.swift @@ -106,22 +106,31 @@ extension UNMutableNotificationContent { senderID: String, senderName: String, icon: NotificationIcon) async throws -> UNMutableNotificationContent { + // We display the placeholder only if... + var needsPlaceholder = false + var fetchedImage: INImage? let image: INImage if let mediaSource = icon.mediaSource { - switch await mediaProvider?.loadImageDataFromSource(mediaSource) { - case .success(let data): - fetchedImage = INImage(imageData: data) - case .failure(let error): - MXLog.error("Couldn't add sender icon: \(error)") - case .none: - break + if let mediaProvider { + switch await mediaProvider.loadImageDataFromSource(mediaSource) { + case .success(let data): + fetchedImage = INImage(imageData: data) + case .failure(let error): + MXLog.error("Couldn't add sender icon: \(error)") + // ...The provider failed to fetch + needsPlaceholder = true + } } + } else { + // ...There is no media + needsPlaceholder = true } if let fetchedImage { image = fetchedImage - } else if let data = await getPlaceholderAvatarImageData(name: icon.groupInfo?.name ?? senderName, + } else if needsPlaceholder, + let data = await getPlaceholderAvatarImageData(name: icon.groupInfo?.name ?? senderName, id: icon.groupInfo?.id ?? senderID) { image = INImage(imageData: data) } else { @@ -176,26 +185,46 @@ extension UNMutableNotificationContent { } private func getPlaceholderAvatarImageData(name: String, id: String) async -> Data? { + let fileName = "notification_placeholder_\(name)_\(id).png" + if let data = try? Data(contentsOf: URL.temporaryDirectory.appendingPathComponent(fileName)) { + MXLog.info("Found existing notification icon placeholder") + return data + } + + MXLog.info("Generating notification icon placeholder") let image = PlaceholderAvatarImage(name: name, contentID: id) .clipShape(Circle()) - .frame(width: 100, height: 100) + .frame(width: 50, height: 50) let renderer = await ImageRenderer(content: image) guard let image = await renderer.uiImage else { + MXLog.info("Generating notification icon placeholder failed") return nil } + let data: Data? // On simulator and macOS the image is rendered correctly // But on other devices is rendered upside down so we need to flip it #if targetEnvironment(simulator) - return image.pngData() + data = image.pngData() #else if ProcessInfo.processInfo.isiOSAppOnMac { - return image.pngData() + data = image.pngData() } else { - return image.flippedVertically().pngData() + data = image.flippedVertically().pngData() } #endif + + if let data { + do { + // cache image data + try FileManager.default.writeDataToTemporaryDirectory(data: data, fileName: fileName) + } catch { + MXLog.error("Could not store placeholder image") + return data + } + } + return data } } diff --git a/ElementX/Sources/Services/Notification/Proxy/NotificationItemProxy.swift b/ElementX/Sources/Services/Notification/Proxy/NotificationItemProxy.swift index fa91d1fb2..700dd15fd 100644 --- a/ElementX/Sources/Services/Notification/Proxy/NotificationItemProxy.swift +++ b/ElementX/Sources/Services/Notification/Proxy/NotificationItemProxy.swift @@ -163,8 +163,9 @@ extension NotificationItemProxyProtocol { return notification } - var requiresMediaProvider: Bool { - if senderAvatarMediaSource != nil || roomAvatarMediaSource != nil { + var hasMedia: Bool { + if (isDirect && senderAvatarMediaSource != nil) || + (!isDirect && roomAvatarMediaSource != nil) { return true } switch event.type { diff --git a/NSE/Sources/NotificationServiceExtension.swift b/NSE/Sources/NotificationServiceExtension.swift index 5b31ab784..f1b62f4c5 100644 --- a/NSE/Sources/NotificationServiceExtension.swift +++ b/NSE/Sources/NotificationServiceExtension.swift @@ -98,7 +98,7 @@ class NotificationServiceExtension: UNNotificationServiceExtension { // After the first processing, update the modified content modifiedContent = try await itemProxy.process(mediaProvider: nil) - guard itemProxy.requiresMediaProvider else { + guard itemProxy.hasMedia else { MXLog.info("\(tag) no media needed") // We've processed the item and no media operations needed, so no need to go further