Remove delivered notifications if their event gets redacted. (#3191)

This commit is contained in:
Doug 2024-08-22 13:49:00 +01:00 committed by GitHub
parent a1cb6bd376
commit d54e08e815
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 54 additions and 10 deletions

View File

@ -44,6 +44,10 @@ extension UNNotificationContent {
@objc var roomID: String? { @objc var roomID: String? {
userInfo[NotificationConstants.UserInfoKey.roomIdentifier] as? String userInfo[NotificationConstants.UserInfoKey.roomIdentifier] as? String
} }
@objc var eventID: String? {
userInfo[NotificationConstants.UserInfoKey.eventIdentifier] as? String
}
} }
extension UNMutableNotificationContent { extension UNMutableNotificationContent {
@ -65,6 +69,15 @@ extension UNMutableNotificationContent {
} }
} }
override var eventID: String? {
get {
userInfo[NotificationConstants.UserInfoKey.eventIdentifier] as? String
}
set {
userInfo[NotificationConstants.UserInfoKey.eventIdentifier] = newValue
}
}
func addMediaAttachment(using mediaProvider: MediaProviderProtocol?, func addMediaAttachment(using mediaProvider: MediaProviderProtocol?,
mediaSource: MediaSourceProxy) async -> UNMutableNotificationContent { mediaSource: MediaSourceProxy) async -> UNMutableNotificationContent {
guard let mediaProvider else { guard let mediaProvider else {

View File

@ -63,6 +63,10 @@ struct NotificationContentBuilder {
let notification = UNMutableNotificationContent() let notification = UNMutableNotificationContent()
notification.receiverID = notificationItem.receiverID notification.receiverID = notificationItem.receiverID
notification.roomID = notificationItem.roomID notification.roomID = notificationItem.roomID
notification.eventID = switch notificationItem.event {
case .timeline(let event): event.eventId()
case .invite, .none: nil
}
notification.sound = notificationItem.isNoisy ? UNNotificationSound(named: UNNotificationSoundName(rawValue: "message.caf")) : nil notification.sound = notificationItem.isNoisy ? UNNotificationSound(named: UNNotificationSoundName(rawValue: "message.caf")) : nil
// So that the UI groups notification that are received for the same room but also for the same user // So that the UI groups notification that are received for the same room but also for the same user
// Removing the @ fixes an iOS bug where the notification crashes if the mute button is tapped // Removing the @ fixes an iOS bug where the notification crashes if the mute button is tapped

View File

@ -59,8 +59,8 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
override func didReceive(_ request: UNNotificationRequest, override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
guard !DataProtectionManager.isDeviceLockedAfterReboot(containerURL: URL.appGroupContainerDirectory), guard !DataProtectionManager.isDeviceLockedAfterReboot(containerURL: URL.appGroupContainerDirectory),
let roomId = request.roomId, let roomID = request.roomID,
let eventId = request.eventId, let eventID = request.eventID,
let clientID = request.pusherNotificationClientIdentifier, let clientID = request.pusherNotificationClientIdentifier,
let credentials = keychainController.restorationTokens().first(where: { $0.restorationToken.pusherNotificationClientIdentifier == clientID }) else { let credentials = keychainController.restorationTokens().first(where: { $0.restorationToken.pusherNotificationClientIdentifier == clientID }) else {
// We cannot process this notification, it might be due to one of these: // We cannot process this notification, it might be due to one of these:
@ -103,8 +103,8 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
Task { Task {
await run(with: credentials, await run(with: credentials,
roomId: roomId, roomID: roomID,
eventId: eventId, eventID: eventID,
unreadCount: request.unreadCount) unreadCount: request.unreadCount)
} }
} }
@ -117,10 +117,10 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
} }
private func run(with credentials: KeychainCredentials, private func run(with credentials: KeychainCredentials,
roomId: String, roomID: String,
eventId: String, eventID: String,
unreadCount: Int?) async { unreadCount: Int?) async {
MXLog.info("\(tag) run with roomId: \(roomId), eventId: \(eventId)") MXLog.info("\(tag) run with roomId: \(roomID), eventId: \(eventID)")
guard let userSession = Self.userSession else { guard let userSession = Self.userSession else {
MXLog.error("Invalid NSE User Session, discarding.") MXLog.error("Invalid NSE User Session, discarding.")
@ -128,7 +128,7 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
} }
do { do {
guard let itemProxy = await userSession.notificationItemProxy(roomID: roomId, eventID: eventId) else { guard let itemProxy = await userSession.notificationItemProxy(roomID: roomID, eventID: eventID) else {
MXLog.info("\(tag) no notification for the event, discard") MXLog.info("\(tag) no notification for the event, discard")
return discard(unreadCount: unreadCount) return discard(unreadCount: unreadCount)
} }
@ -137,6 +137,10 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
return discard(unreadCount: unreadCount) return discard(unreadCount: unreadCount)
} }
if await handleRedactionNotification(itemProxy) {
return discard(unreadCount: unreadCount)
}
// After the first processing, update the modified content // After the first processing, update the modified content
modifiedContent = try await notificationContentBuilder.content(for: itemProxy, mediaProvider: nil) modifiedContent = try await notificationContentBuilder.content(for: itemProxy, mediaProvider: nil)
@ -246,6 +250,29 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
return false return false
} }
/// Handles a notification for an `m.room.redaction` event.
/// - Returns: A boolean indicating whether the notification was handled.
private func handleRedactionNotification(_ itemProxy: NotificationItemProxyProtocol) async -> Bool {
guard case let .timeline(event) = itemProxy.event,
case let .messageLike(content) = try? event.eventType(),
case let .roomRedaction(redactedEventID, _) = content else {
return false
}
guard let redactedEventID else {
MXLog.error("Unable to redact notification due to missing event ID.")
return true // Return true as there's no point showing this notification.
}
let deliveredNotifications = await UNUserNotificationCenter.current().deliveredNotifications()
if let targetNotification = deliveredNotifications.first(where: { $0.request.content.eventID == redactedEventID }) {
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [targetNotification.request.identifier])
}
return true
}
} }
// https://stackoverflow.com/a/77300959/730924 // https://stackoverflow.com/a/77300959/730924

View File

@ -18,11 +18,11 @@ import Foundation
import UserNotifications import UserNotifications
extension UNNotificationRequest { extension UNNotificationRequest {
var roomId: String? { var roomID: String? {
content.userInfo[NotificationConstants.UserInfoKey.roomIdentifier] as? String content.userInfo[NotificationConstants.UserInfoKey.roomIdentifier] as? String
} }
var eventId: String? { var eventID: String? {
content.userInfo[NotificationConstants.UserInfoKey.eventIdentifier] as? String content.userInfo[NotificationConstants.UserInfoKey.eventIdentifier] as? String
} }