mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 19:44:05 +00:00
Clear notifications for fully read rooms using the event's timestamp instead of delivery date
This commit is contained in:
@@ -17,6 +17,7 @@ struct NotificationItemProxyMockConfiguration {
|
|||||||
let messageLikeContent = MessageLikeEventContent.roomMessage(messageType: messageType, inReplyToEventId: nil)
|
let messageLikeContent = MessageLikeEventContent.roomMessage(messageType: messageType, inReplyToEventId: nil)
|
||||||
let event = TimelineEventSDKMock()
|
let event = TimelineEventSDKMock()
|
||||||
event.eventIdReturnValue = UUID().uuidString
|
event.eventIdReturnValue = UUID().uuidString
|
||||||
|
event.timestampReturnValue = 0
|
||||||
event.contentReturnValue = .messageLike(content: messageLikeContent)
|
event.contentReturnValue = .messageLike(content: messageLikeContent)
|
||||||
return .timeline(event: event)
|
return .timeline(event: event)
|
||||||
}()
|
}()
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ nonisolated extension UNNotificationContent {
|
|||||||
userInfo[NotificationConstants.UserInfoKey.eventIdentifier] as? String
|
userInfo[NotificationConstants.UserInfoKey.eventIdentifier] as? String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc var eventDate: Date? {
|
||||||
|
userInfo[NotificationConstants.UserInfoKey.eventDate] as? Date
|
||||||
|
}
|
||||||
|
|
||||||
@objc var pusherNotificationClientIdentifier: String? {
|
@objc var pusherNotificationClientIdentifier: String? {
|
||||||
userInfo[NotificationConstants.UserInfoKey.pusherNotificationClientIdentifier] as? String
|
userInfo[NotificationConstants.UserInfoKey.pusherNotificationClientIdentifier] as? String
|
||||||
}
|
}
|
||||||
@@ -59,6 +63,15 @@ nonisolated extension UNMutableNotificationContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override var eventDate: Date? {
|
||||||
|
get {
|
||||||
|
userInfo[NotificationConstants.UserInfoKey.eventDate] as? Date
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
userInfo[NotificationConstants.UserInfoKey.eventDate] = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override var threadRootEventID: String? {
|
override var threadRootEventID: String? {
|
||||||
get {
|
get {
|
||||||
userInfo[NotificationConstants.UserInfoKey.threadRootEventIdentifier] as? String
|
userInfo[NotificationConstants.UserInfoKey.threadRootEventIdentifier] as? String
|
||||||
|
|||||||
@@ -151,7 +151,10 @@ final class NotificationManager: NSObject, NotificationManagerProtocol {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return notification.date <= lastMessageDate
|
// The delivery date is later than the event's so only use it for notifications without an event date.
|
||||||
|
let notificationDate = notification.request.content.eventDate ?? notification.date
|
||||||
|
// Truncated to seconds to match `RoomSummary.lastMessageDate`.
|
||||||
|
return notificationDate.timeIntervalSince1970.rounded(.down) <= lastMessageDate.timeIntervalSince1970
|
||||||
}
|
}
|
||||||
.map(\.request.identifier)
|
.map(\.request.identifier)
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ nonisolated enum NotificationConstants {
|
|||||||
enum UserInfoKey {
|
enum UserInfoKey {
|
||||||
static let roomIdentifier = "room_id"
|
static let roomIdentifier = "room_id"
|
||||||
static let eventIdentifier = "event_id"
|
static let eventIdentifier = "event_id"
|
||||||
|
static let eventDate = "event_date"
|
||||||
static let threadRootEventIdentifier = "thread_root_event_id"
|
static let threadRootEventIdentifier = "thread_root_event_id"
|
||||||
static let pusherNotificationClientIdentifier = "pusher_notification_client_identifier"
|
static let pusherNotificationClientIdentifier = "pusher_notification_client_identifier"
|
||||||
static let receiverIdentifier = "receiver_id"
|
static let receiverIdentifier = "receiver_id"
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ nonisolated struct NotificationContentBuilder {
|
|||||||
switch notificationItem.event {
|
switch notificationItem.event {
|
||||||
case .timeline(let event):
|
case .timeline(let event):
|
||||||
notificationContent.eventID = event.eventId()
|
notificationContent.eventID = event.eventId()
|
||||||
|
notificationContent.eventDate = Date(timeIntervalSince1970: TimeInterval(event.timestamp()) / 1000)
|
||||||
case .invite, .none:
|
case .invite, .none:
|
||||||
notificationContent.eventID = nil
|
notificationContent.eventID = nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,6 +226,7 @@ nonisolated struct NotificationContentBuilderTests {
|
|||||||
mutating func liveLocationStartNotification() async {
|
mutating func liveLocationStartNotification() async {
|
||||||
let event = TimelineEventSDKMock()
|
let event = TimelineEventSDKMock()
|
||||||
event.eventIdReturnValue = UUID().uuidString
|
event.eventIdReturnValue = UUID().uuidString
|
||||||
|
event.timestampReturnValue = 0
|
||||||
event.contentReturnValue = .state(content: .beaconInfo)
|
event.contentReturnValue = .state(content: .beaconInfo)
|
||||||
|
|
||||||
let notificationItem = NotificationItemProxyMock(.init(event: .timeline(event: event),
|
let notificationItem = NotificationItemProxyMock(.init(event: .timeline(event: event),
|
||||||
@@ -253,6 +254,7 @@ nonisolated struct NotificationContentBuilderTests {
|
|||||||
mutating func otherStateEventNotification() async {
|
mutating func otherStateEventNotification() async {
|
||||||
let event = TimelineEventSDKMock()
|
let event = TimelineEventSDKMock()
|
||||||
event.eventIdReturnValue = UUID().uuidString
|
event.eventIdReturnValue = UUID().uuidString
|
||||||
|
event.timestampReturnValue = 0
|
||||||
event.contentReturnValue = .state(content: .roomName)
|
event.contentReturnValue = .state(content: .roomName)
|
||||||
|
|
||||||
let notificationItem = NotificationItemProxyMock(.init(event: .timeline(event: event),
|
let notificationItem = NotificationItemProxyMock(.init(event: .timeline(event: event),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
@testable import ElementX
|
@testable import ElementX
|
||||||
|
import MatrixRustSDKMocks
|
||||||
import NotificationCenter
|
import NotificationCenter
|
||||||
import Testing
|
import Testing
|
||||||
|
|
||||||
@@ -192,6 +193,28 @@ final class NotificationManagerTests {
|
|||||||
#expect(authorizationStatusWasGranted)
|
#expect(authorizationStatusWasGranted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func removeDeliveredNotificationsForFullyReadRooms() async throws {
|
||||||
|
let room = RoomSummary(room: RoomSDKMock(), id: "!room:matrix.org", settingsMode: .allMessages,
|
||||||
|
hasUnreadMessages: false, hasUnreadMentions: false, hasUnreadNotifications: false)
|
||||||
|
let lastMessageDate = try #require(room.lastMessageDate)
|
||||||
|
|
||||||
|
// Delivered after the last message was sent, which is always the case for the last message's notification.
|
||||||
|
let readNotification = try UNNotification.with(userInfo: [NotificationConstants.UserInfoKey.roomIdentifier: room.id,
|
||||||
|
NotificationConstants.UserInfoKey.eventDate: lastMessageDate.addingTimeInterval(0.5)],
|
||||||
|
identifier: "read",
|
||||||
|
date: lastMessageDate.addingTimeInterval(5))
|
||||||
|
let newerNotification = try UNNotification.with(userInfo: [NotificationConstants.UserInfoKey.roomIdentifier: room.id,
|
||||||
|
NotificationConstants.UserInfoKey.eventDate: lastMessageDate.addingTimeInterval(1)],
|
||||||
|
identifier: "newer",
|
||||||
|
date: lastMessageDate.addingTimeInterval(5))
|
||||||
|
notificationCenter.deliveredNotificationsReturnValue = [readNotification, newerNotification]
|
||||||
|
|
||||||
|
await notificationManager.removeDeliveredNotificationsForFullyReadRooms([room])
|
||||||
|
|
||||||
|
#expect(notificationCenter.removeDeliveredNotificationsWithIdentifiersReceivedIdentifiers == ["read"])
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func whenWillPresentNotificationsDelegateNotSet_CorrectPresentationOptionsReturned() async throws {
|
func whenWillPresentNotificationsDelegateNotSet_CorrectPresentationOptionsReturned() async throws {
|
||||||
let archiver = MockCoder(requiringSecureCoding: false)
|
let archiver = MockCoder(requiringSecureCoding: false)
|
||||||
|
|||||||
@@ -11,15 +11,16 @@ import UserNotifications
|
|||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
extension UNNotification {
|
extension UNNotification {
|
||||||
static func with(userInfo: [AnyHashable: Any]) throws -> UNNotification {
|
static func with(userInfo: [AnyHashable: Any], identifier: String = "", date: Date = .now) throws -> UNNotification {
|
||||||
let content = UNMutableNotificationContent()
|
let content = UNMutableNotificationContent()
|
||||||
content.userInfo = userInfo
|
content.userInfo = userInfo
|
||||||
let request = UNNotificationRequest(identifier: "",
|
let request = UNNotificationRequest(identifier: identifier,
|
||||||
content: content,
|
content: content,
|
||||||
trigger: nil)
|
trigger: nil)
|
||||||
let archiver = MockCoder(requiringSecureCoding: false)
|
let archiver = MockCoder(requiringSecureCoding: false)
|
||||||
let notification = try XCTUnwrap(UNNotification(coder: archiver))
|
let notification = try XCTUnwrap(UNNotification(coder: archiver))
|
||||||
notification.setValue(request, forKey: "request")
|
notification.setValue(request, forKey: "request")
|
||||||
|
notification.setValue(date, forKey: "date")
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user