add NSE sources to EX project to easily implement mocking and allow the code to be unit tested

This commit is contained in:
Mauro Romito
2025-11-18 14:37:32 +01:00
committed by Mauro
parent b67b4fef2f
commit 8e52ef363b
12 changed files with 457 additions and 29 deletions
+16 -3
View File
@@ -9,10 +9,19 @@
import Foundation
import MatrixRustSDK
final class NSEUserSession {
let sessionDirectories: SessionDirectories
let appSettings: CommonSettingsProtocol
// sourcery: AutoMockable
protocol NSEUserSessionProtocol {
var inviteAvatarsVisibility: InviteAvatars { get async }
var mediaPreviewVisibility: MediaPreviews { get async }
var threadsEnabled: Bool { get }
func notificationItemProxy(roomID: String, eventID: String) async -> NotificationItemProxyProtocol?
func roomForIdentifier(_ roomID: String) -> Room?
}
final class NSEUserSession: NSEUserSessionProtocol {
private let sessionDirectories: SessionDirectories
private let appSettings: CommonSettingsProtocol
private let baseClient: Client
private let notificationClient: NotificationClient
private let userID: String
@@ -42,6 +51,10 @@ final class NSEUserSession {
}
}
}
var threadsEnabled: Bool {
appSettings.threadsEnabled
}
init(credentials: KeychainCredentials,
roomID: String,
+4 -4
View File
@@ -16,7 +16,7 @@ import Version
struct NotificationContentBuilder {
let messageEventStringBuilder: RoomMessageEventStringBuilder
let userSession: NSEUserSession
let userSession: NSEUserSessionProtocol
/// Process the given notification item proxy
/// - Parameters:
@@ -38,7 +38,7 @@ struct NotificationContentBuilder {
}
// So that the UI groups notification that are received for the same room/thread but also for the same user
let threadIdentifier = if userSession.appSettings.threadsEnabled, let threadRootEventID = notificationItem.threadRootEventID {
let threadIdentifier = if userSession.threadsEnabled, let threadRootEventID = notificationItem.threadRootEventID {
// If a threaded message we group notifications also by thread root id
"\(notificationItem.receiverID)\(notificationItem.roomID)\(threadRootEventID)"
} else {
@@ -146,7 +146,7 @@ struct NotificationContentBuilder {
private func icon(for notificationItem: NotificationItemProxyProtocol) -> NotificationIcon {
if notificationItem.isDM {
if userSession.appSettings.threadsEnabled, let threadRootEventID = notificationItem.threadRootEventID {
if userSession.threadsEnabled, let threadRootEventID = notificationItem.threadRootEventID {
.init(mediaSource: notificationItem.senderAvatarMediaSource,
groupInfo: .init(avatarDisplayName: notificationItem.senderDisplayName ?? notificationItem.senderID,
displayName: L10n.commonThread,
@@ -155,7 +155,7 @@ struct NotificationContentBuilder {
.init(mediaSource: notificationItem.senderAvatarMediaSource, groupInfo: nil)
}
} else {
if userSession.appSettings.threadsEnabled, let threadRootEventID = notificationItem.threadRootEventID {
if userSession.threadsEnabled, let threadRootEventID = notificationItem.threadRootEventID {
.init(mediaSource: notificationItem.roomAvatarMediaSource,
groupInfo: .init(avatarDisplayName: notificationItem.roomDisplayName,
displayName: L10n.notificationThreadInRoom(notificationItem.roomDisplayName),
@@ -0,0 +1,118 @@
//
// Copyright 2025 Element Creations Ltd.
// Copyright 2022-2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import Foundation
import MatrixRustSDK
import UserNotifications
struct NotificationItemProxy: NotificationItemProxyProtocol {
let notificationItem: NotificationItem
let eventID: String
let receiverID: String
let roomID: String
var event: NotificationEvent? {
notificationItem.event
}
var senderDisplayName: String? {
notificationItem.senderInfo.displayName
}
var senderID: String {
switch notificationItem.event {
case .timeline(let event):
return event.senderId()
case .invite(let senderID):
return senderID
}
}
var roomDisplayName: String {
notificationItem.roomInfo.displayName
}
var isRoomDirect: Bool {
notificationItem.roomInfo.isDirect
}
var isRoomPrivate: Bool {
switch notificationItem.roomInfo.joinRule {
case .invite, .knock, .restricted, .knockRestricted:
true
default:
false
}
}
var roomJoinedMembers: Int {
Int(notificationItem.roomInfo.joinedMembersCount)
}
var isNoisy: Bool {
notificationItem.isNoisy ?? false
}
var hasMention: Bool {
notificationItem.hasMention ?? false
}
var senderAvatarMediaSource: MediaSourceProxy? {
if let senderAvatarURLString = notificationItem.senderInfo.avatarUrl,
let senderAvatarURL = URL(string: senderAvatarURLString) {
return try? MediaSourceProxy(url: senderAvatarURL, mimeType: nil)
}
return nil
}
var roomAvatarMediaSource: MediaSourceProxy? {
if let roomAvatarURLString = notificationItem.roomInfo.avatarUrl,
let roomAvatarURL = URL(string: roomAvatarURLString) {
return try? MediaSourceProxy(url: roomAvatarURL, mimeType: nil)
}
return nil
}
var threadRootEventID: String? {
notificationItem.threadId
}
}
struct EmptyNotificationItemProxy: NotificationItemProxyProtocol {
let eventID: String
var event: NotificationEvent? {
nil
}
let roomID: String
let receiverID: String
var senderID: String { "" }
var senderDisplayName: String? { nil }
var roomDisplayName: String { "" }
var isNoisy: Bool { false }
var isRoomDirect: Bool { false }
var isRoomPrivate: Bool { false }
var senderAvatarMediaSource: MediaSourceProxy? { nil }
var roomAvatarMediaSource: MediaSourceProxy? { nil }
var roomJoinedMembers: Int { 0 }
var hasMention: Bool { false }
var threadRootEventID: String? { nil }
}
@@ -0,0 +1,48 @@
//
// Copyright 2025 Element Creations Ltd.
// Copyright 2022-2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import Foundation
import MatrixRustSDK
import UserNotifications
// sourcery: AutoMockable
protocol NotificationItemProxyProtocol {
var event: NotificationEvent? { get }
var senderID: String { get }
var roomID: String { get }
var receiverID: String { get }
var senderDisplayName: String? { get }
var senderAvatarMediaSource: MediaSourceProxy? { get }
var roomDisplayName: String { get }
var roomAvatarMediaSource: MediaSourceProxy? { get }
var roomJoinedMembers: Int { get }
var isRoomDirect: Bool { get }
var isRoomPrivate: Bool { get }
var isNoisy: Bool { get }
var hasMention: Bool { get }
var threadRootEventID: String? { get }
}
extension NotificationItemProxyProtocol {
var isDM: Bool {
isRoomDirect && roomJoinedMembers <= 2
}
}