Notification Placeholder Image Caching (#1248)

* placeholder image caching

* code improvement

* code is now clearer
This commit is contained in:
Mauro
2023-07-05 14:28:45 +02:00
committed by GitHub
parent d15d0e0070
commit 29d90dd29e
4 changed files with 47 additions and 16 deletions
@@ -44,7 +44,8 @@ extension FileManager {
return newURL return newURL
} }
@discardableResult
func writeDataToTemporaryDirectory(data: Data, fileName: String) throws -> URL { func writeDataToTemporaryDirectory(data: Data, fileName: String) throws -> URL {
let newURL = URL.temporaryDirectory.appendingPathComponent(fileName) let newURL = URL.temporaryDirectory.appendingPathComponent(fileName)
@@ -106,22 +106,31 @@ extension UNMutableNotificationContent {
senderID: String, senderID: String,
senderName: String, senderName: String,
icon: NotificationIcon) async throws -> UNMutableNotificationContent { icon: NotificationIcon) async throws -> UNMutableNotificationContent {
// We display the placeholder only if...
var needsPlaceholder = false
var fetchedImage: INImage? var fetchedImage: INImage?
let image: INImage let image: INImage
if let mediaSource = icon.mediaSource { if let mediaSource = icon.mediaSource {
switch await mediaProvider?.loadImageDataFromSource(mediaSource) { if let mediaProvider {
case .success(let data): switch await mediaProvider.loadImageDataFromSource(mediaSource) {
fetchedImage = INImage(imageData: data) case .success(let data):
case .failure(let error): fetchedImage = INImage(imageData: data)
MXLog.error("Couldn't add sender icon: \(error)") case .failure(let error):
case .none: MXLog.error("Couldn't add sender icon: \(error)")
break // ...The provider failed to fetch
needsPlaceholder = true
}
} }
} else {
// ...There is no media
needsPlaceholder = true
} }
if let fetchedImage { if let fetchedImage {
image = 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) { id: icon.groupInfo?.id ?? senderID) {
image = INImage(imageData: data) image = INImage(imageData: data)
} else { } else {
@@ -176,26 +185,46 @@ extension UNMutableNotificationContent {
} }
private func getPlaceholderAvatarImageData(name: String, id: String) async -> Data? { 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, let image = PlaceholderAvatarImage(name: name,
contentID: id) contentID: id)
.clipShape(Circle()) .clipShape(Circle())
.frame(width: 100, height: 100) .frame(width: 50, height: 50)
let renderer = await ImageRenderer(content: image) let renderer = await ImageRenderer(content: image)
guard let image = await renderer.uiImage else { guard let image = await renderer.uiImage else {
MXLog.info("Generating notification icon placeholder failed")
return nil return nil
} }
let data: Data?
// On simulator and macOS the image is rendered correctly // On simulator and macOS the image is rendered correctly
// But on other devices is rendered upside down so we need to flip it // But on other devices is rendered upside down so we need to flip it
#if targetEnvironment(simulator) #if targetEnvironment(simulator)
return image.pngData() data = image.pngData()
#else #else
if ProcessInfo.processInfo.isiOSAppOnMac { if ProcessInfo.processInfo.isiOSAppOnMac {
return image.pngData() data = image.pngData()
} else { } else {
return image.flippedVertically().pngData() data = image.flippedVertically().pngData()
} }
#endif #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
} }
} }
@@ -163,8 +163,9 @@ extension NotificationItemProxyProtocol {
return notification return notification
} }
var requiresMediaProvider: Bool { var hasMedia: Bool {
if senderAvatarMediaSource != nil || roomAvatarMediaSource != nil { if (isDirect && senderAvatarMediaSource != nil) ||
(!isDirect && roomAvatarMediaSource != nil) {
return true return true
} }
switch event.type { switch event.type {
@@ -98,7 +98,7 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
// After the first processing, update the modified content // After the first processing, update the modified content
modifiedContent = try await itemProxy.process(mediaProvider: nil) modifiedContent = try await itemProxy.process(mediaProvider: nil)
guard itemProxy.requiresMediaProvider else { guard itemProxy.hasMedia else {
MXLog.info("\(tag) no media needed") MXLog.info("\(tag) no media needed")
// We've processed the item and no media operations needed, so no need to go further // We've processed the item and no media operations needed, so no need to go further