From c37d2a9a3aeef68ed411d135b721a1b00e2c05ad Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Tue, 16 Jan 2024 09:02:13 +0200 Subject: [PATCH 1/7] Fixes #2360 - Add support for manually marking rooms as unread --- .../Sources/Application/AppSettings.swift | 4 ++ .../RoomFlowCoordinator.swift | 6 +++ .../Mocks/Generated/GeneratedMocks.swift | 38 ++++++++++++++ .../Mocks/Generated/SDKGeneratedMocks.swift | 2 +- .../Screens/HomeScreen/HomeScreenModels.swift | 11 ++++ .../HomeScreen/HomeScreenViewModel.swift | 21 ++++++++ .../HomeScreen/View/HomeScreenRoomCell.swift | 19 +++---- .../HomeScreen/View/HomeScreenRoomList.swift | 17 +++++++ .../View/InvitesScreenCell.swift | 2 + .../DeveloperOptionsScreenModels.swift | 1 + .../View/DeveloperOptionsScreen.swift | 4 ++ .../Sources/Services/Room/RoomProxy.swift | 51 ++++++++++++++++--- .../Services/Room/RoomProxyProtocol.swift | 8 ++- .../RoomSummary/MockRoomSummaryProvider.swift | 9 ++++ .../Room/RoomSummary/RoomSummaryDetails.swift | 2 + .../RoomSummary/RoomSummaryProvider.swift | 1 + 16 files changed, 174 insertions(+), 22 deletions(-) diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index dbbba52928..05cbe3a0a7 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -47,6 +47,7 @@ final class AppSettings { case userSuggestionsEnabled case mentionsBadgeEnabled case roomListFiltersEnabled + case markAsUnreadEnabled } private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier @@ -275,6 +276,9 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.roomListFiltersEnabled, defaultValue: false, storageType: .userDefaults(store)) var roomListFiltersEnabled + @UserPreference(key: UserDefaultsKeys.markAsUnreadEnabled, defaultValue: false, storageType: .userDefaults(store)) + var markAsUnreadEnabled + #endif // MARK: - Shared diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index 48cb006a13..afa5a20523 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -417,6 +417,12 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { self.roomProxy = roomProxy + Task { + // Mark the room as read on entering but don't send read receipts + // as those will be handled by the timeline + await roomProxy.markAsRead(sendReadReceipts: false, receiptType: appSettings.sendReadReceiptsEnabled ? .read : .readPrivate) + } + let userID = userSession.clientProxy.userID let timelineItemFactory = RoomTimelineItemFactory(userID: userID, diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 81ef995ed0..11df16725f 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -2222,6 +2222,44 @@ class RoomProxyMock: RoomProxyProtocol { return canUserTriggerRoomNotificationUserIDReturnValue } } + //MARK: - markAsUnread + + var markAsUnreadCallsCount = 0 + var markAsUnreadCalled: Bool { + return markAsUnreadCallsCount > 0 + } + var markAsUnreadReturnValue: Result! + var markAsUnreadClosure: (() async -> Result)? + + func markAsUnread() async -> Result { + markAsUnreadCallsCount += 1 + if let markAsUnreadClosure = markAsUnreadClosure { + return await markAsUnreadClosure() + } else { + return markAsUnreadReturnValue + } + } + //MARK: - markAsRead + + var markAsReadSendReadReceiptsReceiptTypeCallsCount = 0 + var markAsReadSendReadReceiptsReceiptTypeCalled: Bool { + return markAsReadSendReadReceiptsReceiptTypeCallsCount > 0 + } + var markAsReadSendReadReceiptsReceiptTypeReceivedArguments: (sendReadReceipts: Bool, receiptType: ReceiptType)? + var markAsReadSendReadReceiptsReceiptTypeReceivedInvocations: [(sendReadReceipts: Bool, receiptType: ReceiptType)] = [] + var markAsReadSendReadReceiptsReceiptTypeReturnValue: Result! + var markAsReadSendReadReceiptsReceiptTypeClosure: ((Bool, ReceiptType) async -> Result)? + + func markAsRead(sendReadReceipts: Bool, receiptType: ReceiptType) async -> Result { + markAsReadSendReadReceiptsReceiptTypeCallsCount += 1 + markAsReadSendReadReceiptsReceiptTypeReceivedArguments = (sendReadReceipts: sendReadReceipts, receiptType: receiptType) + markAsReadSendReadReceiptsReceiptTypeReceivedInvocations.append((sendReadReceipts: sendReadReceipts, receiptType: receiptType)) + if let markAsReadSendReadReceiptsReceiptTypeClosure = markAsReadSendReadReceiptsReceiptTypeClosure { + return await markAsReadSendReadReceiptsReceiptTypeClosure(sendReadReceipts, receiptType) + } else { + return markAsReadSendReadReceiptsReceiptTypeReturnValue + } + } //MARK: - canUserJoinCall var canUserJoinCallUserIDCallsCount = 0 diff --git a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift index b3921c992d..36e2b9fdcf 100644 --- a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift @@ -1,4 +1,4 @@ -// Generated using Sourcery 2.1.2 — https://github.com/krzysztofzablocki/Sourcery +// Generated using Sourcery 2.1.3 — https://github.com/krzysztofzablocki/Sourcery // DO NOT EDIT // swiftlint:disable all diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift index 15cb4fb315..17724e6dc0 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift @@ -51,6 +51,8 @@ enum HomeScreenViewAction { case updateVisibleItemRange(range: Range, isScrolling: Bool) case selectInvites case globalSearch + case markRoomAsUnread(roomIdentifier: String) + case markRoomAsRead(roomIdentifier: String) } enum HomeScreenRoomListMode: CustomStringConvertible { @@ -102,7 +104,9 @@ struct HomeScreenViewState: BindableState { var rooms: [HomeScreenRoom] = [] var roomListMode: HomeScreenRoomListMode = .skeletons + var shouldShowFilters = false + var markAsUnreadEnabled = false var hasPendingInvitations = false var hasUnreadPendingInvitations = false @@ -153,6 +157,8 @@ struct HomeScreenRoom: Identifiable, Equatable { var name = "" + var isMarkedUnread: Bool + var hasUnreadMessages = false var hasUnreadMentions = false @@ -171,10 +177,15 @@ struct HomeScreenRoom: Identifiable, Equatable { var isPlaceholder = false + var hasNewContent: Bool { + hasUnreadMessages || hasUnreadMentions || hasUnreadNotifications || isMarkedUnread + } + static func placeholder() -> HomeScreenRoom { HomeScreenRoom(id: UUID().uuidString, roomId: nil, name: "Placeholder room name", + isMarkedUnread: false, hasUnreadMessages: false, hasUnreadMentions: false, hasUnreadNotifications: false, diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index f945d284fe..9366c53247 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -86,6 +86,10 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol .weakAssign(to: \.state.shouldShowFilters, on: self) .store(in: &cancellables) + appSettings.$markAsUnreadEnabled + .weakAssign(to: \.state.markAsUnreadEnabled, on: self) + .store(in: &cancellables) + let isSearchFieldFocused = context.$viewState.map(\.bindings.isSearchFieldFocused) let searchQuery = context.$viewState.map(\.bindings.searchQuery) isSearchFieldFocused @@ -143,6 +147,22 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol actionsSubject.send(.presentInvitesScreen) case .globalSearch: actionsSubject.send(.presentGlobalSearch) + case .markRoomAsUnread(let roomIdentifier): + Task { + if let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) { + if case .failure(let error) = await roomProxy.markAsUnread() { + MXLog.error("Failed marking room \(roomIdentifier) as unread with error: \(error)") + } + } + } + case .markRoomAsRead(let roomIdentifier): + Task { + if let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) { + if case .failure(let error) = await roomProxy.markAsRead(sendReadReceipts: true, receiptType: appSettings.sendReadReceiptsEnabled ? .read : .readPrivate) { + MXLog.error("Failed marking room \(roomIdentifier) as read with error: \(error)") + } + } + } } } @@ -327,6 +347,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol return HomeScreenRoom(id: identifier, roomId: details.id, name: details.name, + isMarkedUnread: details.isMarkedUnread, hasUnreadMessages: details.unreadMessagesCount > 0, hasUnreadMentions: details.unreadMentionsCount > 0, hasUnreadNotifications: details.unreadNotificationsCount > 0, diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift index 3420d6af7a..f192c6143f 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift @@ -133,7 +133,7 @@ struct HomeScreenRoomCell: View { .foregroundColor(.compound.iconAccentTertiary) } - if hasNewContent { + if room.hasNewContent { Circle() .frame(width: 12, height: 12) .foregroundColor(isHighlighted ? .compound.iconAccentTertiary : .compound.iconQuaternary) @@ -141,21 +141,15 @@ struct HomeScreenRoomCell: View { } } } - - private var hasNewContent: Bool { - room.hasUnreadMessages || - room.hasUnreadMentions || - room.hasUnreadNotifications - } - + private var isHighlighted: Bool { - guard !room.isPlaceholder && - room.notificationMode != .mute else { + guard !room.isPlaceholder && room.notificationMode != .mute else { return false } - return room.hasUnreadNotifications || room.hasUnreadMentions - } + return room.hasUnreadNotifications || room.hasUnreadMentions || room.isMarkedUnread + } + private var mentionIcon: some View { CompoundIcon(\.mention, size: .custom(15), relativeTo: .compound.bodyMD) .accessibilityLabel(L10n.a11yNotificationsMentionsOnly) @@ -223,6 +217,7 @@ struct HomeScreenRoomCell_Previews: PreviewProvider, TestablePreview { return HomeScreenRoom(id: UUID().uuidString, roomId: details.id, name: details.name, + isMarkedUnread: details.isMarkedUnread, hasUnreadMessages: details.unreadMessagesCount > 0, hasUnreadMentions: details.unreadMentionsCount > 0, hasUnreadNotifications: details.unreadNotificationsCount > 0, diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift index fcbfd8be56..f4d47e2df9 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift @@ -44,8 +44,25 @@ struct HomeScreenRoomList: View { .redacted(reason: .placeholder) } else { let isSelected = context.viewState.selectedRoomID == room.id + HomeScreenRoomCell(room: room, context: context, isSelected: isSelected) .contextMenu { + if context.viewState.markAsUnreadEnabled { + if room.hasNewContent { + Button { + context.send(viewAction: .markRoomAsRead(roomIdentifier: room.id)) + } label: { + Text(L10n.screenRoomlistMarkAsRead) + } + } else { + Button { + context.send(viewAction: .markRoomAsUnread(roomIdentifier: room.id)) + } label: { + Text(L10n.screenRoomlistMarkAsUnread) + } + } + } + Button { context.send(viewAction: .showRoomDetails(roomIdentifier: room.id)) } label: { diff --git a/ElementX/Sources/Screens/InvitesScreen/View/InvitesScreenCell.swift b/ElementX/Sources/Screens/InvitesScreen/View/InvitesScreenCell.swift index cc140e7d0e..3091068c11 100644 --- a/ElementX/Sources/Screens/InvitesScreen/View/InvitesScreenCell.swift +++ b/ElementX/Sources/Screens/InvitesScreen/View/InvitesScreenCell.swift @@ -186,6 +186,7 @@ private extension InvitesScreenRoomDetails { avatarURL: nil, lastMessage: nil, lastMessageFormattedTimestamp: nil, + isMarkedUnread: false, unreadMessagesCount: 0, unreadMentionsCount: 0, unreadNotificationsCount: 0, @@ -208,6 +209,7 @@ private extension InvitesScreenRoomDetails { avatarURL: avatarURL, lastMessage: nil, lastMessageFormattedTimestamp: nil, + isMarkedUnread: false, unreadMessagesCount: 0, unreadMentionsCount: 0, unreadNotificationsCount: 0, diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index f5002bf12a..3664fed0f1 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift @@ -50,6 +50,7 @@ protocol DeveloperOptionsProtocol: AnyObject { var userSuggestionsEnabled: Bool { get set } var mentionsBadgeEnabled: Bool { get set } var roomListFiltersEnabled: Bool { get set } + var markAsUnreadEnabled: Bool { get set } var elementCallBaseURL: URL { get set } } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index 77c3fa4ffb..91b31c25ea 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -55,6 +55,10 @@ struct DeveloperOptionsScreen: View { Toggle(isOn: $context.roomListFiltersEnabled) { Text("Show filters") } + + Toggle(isOn: $context.markAsUnreadEnabled) { + Text("Mark as unread") + } } Section("Element Call") { diff --git a/ElementX/Sources/Services/Room/RoomProxy.swift b/ElementX/Sources/Services/Room/RoomProxy.swift index 12482e68d0..078e64ba63 100644 --- a/ElementX/Sources/Services/Room/RoomProxy.swift +++ b/ElementX/Sources/Services/Room/RoomProxy.swift @@ -45,18 +45,24 @@ class RoomProxy: RoomProxyProtocol { var actions: AnyPublisher { actionsSubject.eraseToAnyPublisher() } - + var ownUserID: String { room.ownUserId() } - - init(roomListItem: RoomListItemProtocol, - room: RoomProtocol, - backgroundTaskService: BackgroundTaskServiceProtocol) async { + + init?(roomListItem: RoomListItemProtocol, + room: RoomProtocol, + backgroundTaskService: BackgroundTaskServiceProtocol) async { self.roomListItem = roomListItem self.room = room self.backgroundTaskService = backgroundTaskService - timeline = await TimelineProxy(timeline: room.timeline(), backgroundTaskService: backgroundTaskService) + + do { + timeline = try await TimelineProxy(timeline: room.timeline(), backgroundTaskService: backgroundTaskService) + } catch { + MXLog.error("Failed creating timeline with error: \(error)") + return nil + } Task { await updateMembers() @@ -352,6 +358,37 @@ class RoomProxy: RoomProxyProtocol { } } + func markAsUnread() async -> Result { + MXLog.info("Marking room \(id) as unread") + + do { + try await room.markAsUnread() + return .success(()) + } catch { + MXLog.error("Failed marking room \(id) as unread with error: \(error)") + return .failure(.failedMarkingAsUnread) + } + } + + func markAsRead(sendReadReceipts: Bool, receiptType: ReceiptType) async -> Result { + MXLog.info("Marking room \(id) as read, sending read receipts: \(sendReadReceipts)") + + do { + if sendReadReceipts { + try await room.markAsReadAndSendReadReceipt(receiptType: receiptType) + } else { + try await room.markAsRead() + } + + return .success(()) + } catch { + MXLog.error("Failed marking room \(id) as read with error: \(error)") + return .failure(.failedMarkingAsRead) + } + } + + // MARK: - Element Call + func canUserJoinCall(userID: String) async -> Result { do { return try await .success(room.canUserSendState(userId: userID, stateEvent: .callMember)) @@ -361,8 +398,6 @@ class RoomProxy: RoomProxyProtocol { } } - // MARK: - Element Call - func elementCallWidgetDriver() -> ElementCallWidgetDriverProtocol { ElementCallWidgetDriver(room: room) } diff --git a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift index c91b38c918..d2be703b3a 100644 --- a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift @@ -31,6 +31,8 @@ enum RoomProxyError: Error, Equatable { case failedRemovingAvatar case failedUploadingAvatar case failedCheckingPermission + case failedMarkingAsRead + case failedMarkingAsUnread } enum RoomProxyAction { @@ -103,10 +105,14 @@ protocol RoomProxyProtocol { func canUserTriggerRoomNotification(userID: String) async -> Result - func canUserJoinCall(userID: String) async -> Result + func markAsUnread() async -> Result + + func markAsRead(sendReadReceipts: Bool, receiptType: ReceiptType) async -> Result // MARK: - Element Call + func canUserJoinCall(userID: String) async -> Result + func elementCallWidgetDriver() -> ElementCallWidgetDriverProtocol } diff --git a/ElementX/Sources/Services/Room/RoomSummary/MockRoomSummaryProvider.swift b/ElementX/Sources/Services/Room/RoomSummary/MockRoomSummaryProvider.swift index 3ab1d5ad4f..9c531efd69 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/MockRoomSummaryProvider.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/MockRoomSummaryProvider.swift @@ -83,6 +83,7 @@ extension Array where Element == RoomSummary { avatarURL: nil, lastMessage: AttributedString("I do not wish to take the trouble to understand mysticism"), lastMessageFormattedTimestamp: "14:56", + isMarkedUnread: false, unreadMessagesCount: 0, unreadMentionsCount: 0, unreadNotificationsCount: 0, @@ -96,6 +97,7 @@ extension Array where Element == RoomSummary { avatarURL: URL.picturesDirectory, lastMessage: AttributedString("How do you see the Emperor then? You think he keeps office hours?"), lastMessageFormattedTimestamp: "2:56 PM", + isMarkedUnread: false, unreadMessagesCount: 2, unreadMentionsCount: 0, unreadNotificationsCount: 2, @@ -109,6 +111,7 @@ extension Array where Element == RoomSummary { avatarURL: nil, lastMessage: try? AttributedString(markdown: "He certainly seemed no *mental genius* to me"), lastMessageFormattedTimestamp: "Some time ago", + isMarkedUnread: false, unreadMessagesCount: 3, unreadMentionsCount: 0, unreadNotificationsCount: 0, @@ -122,6 +125,7 @@ extension Array where Element == RoomSummary { avatarURL: nil, lastMessage: AttributedString("There's an archaic measure of time that's called the month"), lastMessageFormattedTimestamp: "Just now", + isMarkedUnread: false, unreadMessagesCount: 2, unreadMentionsCount: 2, unreadNotificationsCount: 2, @@ -135,6 +139,7 @@ extension Array where Element == RoomSummary { avatarURL: nil, lastMessage: AttributedString("Clearly, if Earth is powerful enough to do that, it might also be capable of adjusting minds in order to force belief in its radioactivity"), lastMessageFormattedTimestamp: "1986", + isMarkedUnread: false, unreadMessagesCount: 1, unreadMentionsCount: 1, unreadNotificationsCount: 1, @@ -148,6 +153,7 @@ extension Array where Element == RoomSummary { avatarURL: nil, lastMessage: AttributedString("Are you groping for the word 'paranoia'?"), lastMessageFormattedTimestamp: "きょうはじゅういちがつじゅういちにちです", + isMarkedUnread: false, unreadMessagesCount: 6, unreadMentionsCount: 0, unreadNotificationsCount: 0, @@ -161,6 +167,7 @@ extension Array where Element == RoomSummary { avatarURL: nil, lastMessage: nil, lastMessageFormattedTimestamp: nil, + isMarkedUnread: false, unreadMessagesCount: 0, unreadMentionsCount: 0, unreadNotificationsCount: 0, @@ -206,6 +213,7 @@ extension Array where Element == RoomSummary { avatarURL: URL.picturesDirectory, lastMessage: nil, lastMessageFormattedTimestamp: nil, + isMarkedUnread: false, unreadMessagesCount: 0, unreadMentionsCount: 0, unreadNotificationsCount: 0, @@ -219,6 +227,7 @@ extension Array where Element == RoomSummary { avatarURL: nil, lastMessage: nil, lastMessageFormattedTimestamp: nil, + isMarkedUnread: false, unreadMessagesCount: 0, unreadMentionsCount: 0, unreadNotificationsCount: 0, diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryDetails.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryDetails.swift index 3846babb23..e023a66aa2 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryDetails.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryDetails.swift @@ -24,6 +24,7 @@ struct RoomSummaryDetails { let avatarURL: URL? let lastMessage: AttributedString? let lastMessageFormattedTimestamp: String? + let isMarkedUnread: Bool let unreadMessagesCount: UInt let unreadMentionsCount: UInt let unreadNotificationsCount: UInt @@ -54,6 +55,7 @@ extension RoomSummaryDetails { avatarURL = nil lastMessage = AttributedString(string) lastMessageFormattedTimestamp = "Now" + isMarkedUnread = false unreadMessagesCount = hasUnreadMessages ? 1 : 0 unreadMentionsCount = hasUnreadMentions ? 1 : 0 unreadNotificationsCount = hasUnreadNotifications ? 1 : 0 diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift index 215b5b98c5..3f296137bd 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift @@ -248,6 +248,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { avatarURL: roomInfo.avatarUrl.flatMap(URL.init(string:)), lastMessage: attributedLastMessage, lastMessageFormattedTimestamp: lastMessageFormattedTimestamp, + isMarkedUnread: appSettings.markAsUnreadEnabled ? roomInfo.isMarkedUnread : false, unreadMessagesCount: appSettings.mentionsBadgeEnabled ? UInt(roomInfo.numUnreadMessages) : 0, unreadMentionsCount: appSettings.mentionsBadgeEnabled ? UInt(roomInfo.numUnreadMentions) : 0, unreadNotificationsCount: appSettings.mentionsBadgeEnabled ? UInt(roomInfo.numUnreadNotifications) : UInt(roomInfo.notificationCount), From c60a04fee080f9e3e32f476010f73c34872707d9 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Fri, 2 Feb 2024 13:00:51 +0200 Subject: [PATCH 2/7] Add analytics for marking rooms as (un)read --- ElementX.xcodeproj/project.pbxproj | 13 ++++++++++--- .../xcshareddata/swiftpm/Package.resolved | 7 +++---- .../Screens/HomeScreen/HomeScreenViewModel.swift | 4 ++++ .../Services/Analytics/AnalyticsService.swift | 4 ++++ project.yml | 5 +++-- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index ce5d2504a1..df87f015ad 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -1266,6 +1266,8 @@ 340179A0FC1AD4AEDA7FC134 /* CreateRoomViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomViewModelProtocol.swift; sourceTree = ""; }; 342BEBC3C5FC3F9943C41C4C /* TemplateScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModelProtocol.swift; sourceTree = ""; }; 347D708104CCEF771428C9A3 /* PollFormScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenViewModelTests.swift; sourceTree = ""; }; + 34E0FA38BD473FFA6F1AB7A5 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = be; path = be.lproj/Localizable.stringsdict; sourceTree = ""; }; + 34ED3AB7E0287552A5648AB3 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/InfoPlist.strings; sourceTree = ""; }; 351E89CE2ED9B73C5CC47955 /* TimelineReactionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReactionsView.swift; sourceTree = ""; }; 3558A15CFB934F9229301527 /* RestorationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorationToken.swift; sourceTree = ""; }; 35AFCF4C05DEED04E3DB1A16 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; @@ -1945,6 +1947,7 @@ EFF7BF82A950B91BC5469E91 /* ViewFrameReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewFrameReader.swift; sourceTree = ""; }; EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportServiceTests.swift; sourceTree = ""; }; F012CB5EE3F2B67359F6CC52 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = ""; }; + F0205C03F98BE861EDABCB0D /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/Localizable.strings; sourceTree = ""; }; F08776C48FFB47CACF64ED10 /* ServerConfirmationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenViewModelTests.swift; sourceTree = ""; }; F0B9F5BC4C80543DE7228B9D /* MapTilerStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStyle.swift; sourceTree = ""; }; F0E14FF533D25A0692F7CEB0 /* RoomPollsHistoryScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenViewModel.swift; sourceTree = ""; }; @@ -4979,6 +4982,7 @@ hasScannedForEncodings = 0; knownRegions = ( Base, + be, cs, de, en, @@ -6190,6 +6194,7 @@ 187853A7E643995EE49FAD43 /* Localizable.stringsdict */ = { isa = PBXVariantGroup; children = ( + 34E0FA38BD473FFA6F1AB7A5 /* be */, 6654859746B0BE9611459391 /* cs */, AE5DDBEBBA17973ED4638823 /* de */, 13802897C7AFA360EA74C0B0 /* en */, @@ -6209,6 +6214,7 @@ 7109E709A7738E6BCC4553E6 /* Localizable.strings */ = { isa = PBXVariantGroup; children = ( + F0205C03F98BE861EDABCB0D /* be */, 8D8169443E5AC5FF71BFB3DB /* cs */, 35AFCF4C05DEED04E3DB1A16 /* de */, CACA846B3E3E9A521D98B178 /* en */, @@ -6228,6 +6234,7 @@ 91DE43B8815918E590912DDA /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( + 34ED3AB7E0287552A5648AB3 /* be */, 8D1FA20DAB853C1156054912 /* cs */, 84311D707B09854D67F78BBF /* de */, 1215A4FC53D2319E81AE8970 /* en */, @@ -6764,7 +6771,7 @@ repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.1.36; + version = 1.1.37; }; }; 821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = { @@ -6811,8 +6818,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/matrix-org/matrix-analytics-events"; requirement = { - kind = upToNextMinorVersion; - minimumVersion = 0.9.0; + kind = revision; + revision = 28d1e65d306420b3affc32f719cb516f74a7406f; }; }; C13F55E4518415CB4C278E73 /* XCRemoteSwiftPackageReference "DTCoreText" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index b4a952c4fa..6e696e5c3f 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -121,8 +121,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-analytics-events", "state" : { - "revision" : "aa14cbcdf81af2746d20a71779ec751f971e1d7f", - "version" : "0.9.0" + "revision" : "28d1e65d306420b3affc32f719cb516f74a7406f" } }, { @@ -130,8 +129,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-rust-components-swift", "state" : { - "revision" : "e6aecad747a12e399ee718b5fb2ece45b7b6e23a", - "version" : "1.1.36" + "revision" : "4d0d75004f8361530d7424ab198e363027823718", + "version" : "1.1.37" } }, { diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index 9366c53247..d291f609f4 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -152,6 +152,8 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol if let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) { if case .failure(let error) = await roomProxy.markAsUnread() { MXLog.error("Failed marking room \(roomIdentifier) as unread with error: \(error)") + } else { + ServiceLocator.shared.analytics.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle) } } } @@ -160,6 +162,8 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol if let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) { if case .failure(let error) = await roomProxy.markAsRead(sendReadReceipts: true, receiptType: appSettings.sendReadReceiptsEnabled ? .read : .readPrivate) { MXLog.error("Failed marking room \(roomIdentifier) as read with error: \(error)") + } else { + ServiceLocator.shared.analytics.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle) } } } diff --git a/ElementX/Sources/Services/Analytics/AnalyticsService.swift b/ElementX/Sources/Services/Analytics/AnalyticsService.swift index cf83350cd8..8af872e194 100644 --- a/ElementX/Sources/Services/Analytics/AnalyticsService.swift +++ b/ElementX/Sources/Services/Analytics/AnalyticsService.swift @@ -126,6 +126,10 @@ extension AnalyticsService { let event = AnalyticsEvent.MobileScreen(durationMs: milliseconds, screenName: screen.screenName) client.screen(event) } + + func trackInteraction(index: Int? = nil, name: AnalyticsEvent.Interaction.Name) { + capture(event: AnalyticsEvent.Interaction(index: index, interactionType: .Touch, name: name)) + } /// Track the creation of a room /// - Parameter isDM: true if the created room is a direct message, false otherwise diff --git a/project.yml b/project.yml index c2889eb6bc..d244ad5f27 100644 --- a/project.yml +++ b/project.yml @@ -47,7 +47,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/matrix-org/matrix-rust-components-swift - exactVersion: 1.1.36 + exactVersion: 1.1.37 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios @@ -55,7 +55,8 @@ packages: # path: ../compound-ios AnalyticsEvents: url: https://github.com/matrix-org/matrix-analytics-events - minorVersion: 0.9.0 + # minorVersion: 0.9.0 + revision: 28d1e65d306420b3affc32f719cb516f74a7406f Emojibase: url: https://github.com/matrix-org/emojibase-bindings minorVersion: 1.0.0 From a88f737e2b25f829fb82524622acc34c8e4113b8 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Fri, 2 Feb 2024 13:12:02 +0200 Subject: [PATCH 3/7] Remove unnecessary analytics abstraction levels --- ElementX.xcodeproj/project.pbxproj | 8 - .../RoomFlowCoordinator.swift | 4 +- .../Analytics/ScreenTrackerViewModifier.swift | 5 +- .../View/PollFormScreen.swift | 2 +- .../CreateRoom/View/CreateRoomScreen.swift | 2 +- .../Screens/HomeScreen/View/HomeScreen.swift | 2 +- .../InvitesScreen/View/InvitesScreen.swift | 2 +- .../View/StaticLocationScreen.swift | 2 +- .../View/RoomDetailsEditScreen.swift | 2 +- .../View/RoomDetailsScreen.swift | 2 +- .../View/RoomMemberDetailsScreen.swift | 2 +- .../View/RoomMembersListScreen.swift | 2 +- .../View/RoomNotificationSettingsScreen.swift | 2 +- ...otificationSettingsUserDefinedScreen.swift | 2 +- .../RoomScreenInteractionHandler.swift | 2 +- .../Screens/RoomScreen/View/RoomScreen.swift | 2 +- .../View/NotificationSettingsEditScreen.swift | 2 +- .../View/NotificationSettingsScreen.swift | 2 +- .../View/StartChatScreen.swift | 2 +- .../Analytics/AnalyticsMessageType.swift | 53 ------ .../Services/Analytics/AnalyticsScreen.swift | 159 ------------------ .../Services/Analytics/AnalyticsService.swift | 8 +- 22 files changed, 25 insertions(+), 244 deletions(-) delete mode 100644 ElementX/Sources/Services/Analytics/AnalyticsMessageType.swift delete mode 100644 ElementX/Sources/Services/Analytics/AnalyticsScreen.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index df87f015ad..e43a4b8da2 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -144,7 +144,6 @@ 22882C710BC99EC34A5024A0 /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; }; 234E2C782981003971ABE96E /* PermalinkBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F754E66A8970963B15B2A41E /* PermalinkBuilder.swift */; }; 2352C541AF857241489756FF /* MockRoomSummaryProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7D42E66E939B709C1EC390 /* MockRoomSummaryProvider.swift */; }; - 2355289BB0146231DD8AFFC0 /* AnalyticsMessageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2133A5FF0C14986E60326115 /* AnalyticsMessageType.swift */; }; 23701DE32ACD6FD40AA992C3 /* MediaUploadingPreprocessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE203026B9AD3DB412439866 /* MediaUploadingPreprocessorTests.swift */; }; 237FC70AA257B935F53316BA /* SessionVerificationControllerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C55D7E514F9DE4E3D72FDCAD /* SessionVerificationControllerProxy.swift */; }; 245F7FE5961BD10C145A26E0 /* UITimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA689E792E679F5E3956F21 /* UITimelineView.swift */; }; @@ -838,7 +837,6 @@ D1E29F345F1220E1AF1BE9DF /* ReadReceiptsSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB0A77874B29D79DDFC051AC /* ReadReceiptsSummaryView.swift */; }; D1EEF0CB0F5D9C15E224E670 /* landscape_test_video.mov in Resources */ = {isa = PBXBuildFile; fileRef = 9A2AC7BE17C05CF7D2A22338 /* landscape_test_video.mov */; }; D2048FD56760BDABA3DB5FC2 /* AppLockServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EAAB54C6CE91D64B69A9F8 /* AppLockServiceProtocol.swift */; }; - D2A15D03F81342A09340BD56 /* AnalyticsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFEEE93B82937B2E86F92EB /* AnalyticsScreen.swift */; }; D2D70B5DB1A5E4AF0CD88330 /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 033DB41C51865A2E83174E87 /* target.yml */; }; D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5098DA7799946A61E34A2373 /* FileRoomTimelineItem.swift */; }; D34E328E9E65904358248FDD /* GlobalSearchScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 436A0D98D372B17EAE9AA999 /* GlobalSearchScreenModels.swift */; }; @@ -1192,7 +1190,6 @@ 1FD51B4D5173F7FC886F5360 /* NoticeRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineItemContent.swift; sourceTree = ""; }; 201305507D7DFD16E544563A /* EmojiLoaderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiLoaderProtocol.swift; sourceTree = ""; }; 20872C3887F835958CE2F1D0 /* MapTilerStaticMapProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStaticMapProtocol.swift; sourceTree = ""; }; - 2133A5FF0C14986E60326115 /* AnalyticsMessageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsMessageType.swift; sourceTree = ""; }; 2141693488CE5446BB391964 /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; }; 216F0DDC98F2A2C162D09C28 /* FileRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineItemContent.swift; sourceTree = ""; }; 218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBasedTimelineItemProtocol.swift; sourceTree = ""; }; @@ -1997,7 +1994,6 @@ FDF73F49E6B6683F7E2D26F0 /* SecureBackupScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupScreenCoordinator.swift; sourceTree = ""; }; FE87C931165F5E201CACBB87 /* MediaPlayerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayerProtocol.swift; sourceTree = ""; }; FEC2E8E1B20BB2EA07B0B61E /* WelcomeScreenScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreenScreenViewModel.swift; sourceTree = ""; }; - FEFEEE93B82937B2E86F92EB /* AnalyticsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsScreen.swift; sourceTree = ""; }; FFECCE59967018204876D0A5 /* LocationMarkerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationMarkerView.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -2935,8 +2931,6 @@ E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */, D77B3D4950F1707E66E4A45A /* AnalyticsConfiguration.swift */, 57B6B383F1FD04CC0E7B60C6 /* AnalyticsConsentState.swift */, - 2133A5FF0C14986E60326115 /* AnalyticsMessageType.swift */, - FEFEEE93B82937B2E86F92EB /* AnalyticsScreen.swift */, 5445FCE0CE15E634FDC1A2E2 /* AnalyticsService.swift */, A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */, 1715E3D7F53C0748AA50C91C /* PostHogAnalyticsClient.swift */, @@ -5392,13 +5386,11 @@ F7567DD6635434E8C563BF85 /* AnalyticsClientProtocol.swift in Sources */, 54C774874BED4A8FAD1F22FE /* AnalyticsConfiguration.swift in Sources */, 8DDC6F28C797D8685F2F8E32 /* AnalyticsConsentState.swift in Sources */, - 2355289BB0146231DD8AFFC0 /* AnalyticsMessageType.swift in Sources */, 9DF3F6318A4402305F5EB869 /* AnalyticsPromptScreen.swift in Sources */, 5F28C9146694B381BB82E18C /* AnalyticsPromptScreenCoordinator.swift in Sources */, 496CC9D59ACFAB84FD9B3B5F /* AnalyticsPromptScreenModels.swift in Sources */, 0AA0477E063E72B786A983CF /* AnalyticsPromptScreenViewModel.swift in Sources */, D4ACF3276F5D0DA28D4028C9 /* AnalyticsPromptScreenViewModelProtocol.swift in Sources */, - D2A15D03F81342A09340BD56 /* AnalyticsScreen.swift in Sources */, 3C73442084BF8A6939F0F80B /* AnalyticsService.swift in Sources */, 020F7E70167FB2833266F2F0 /* AnalyticsSettingsScreen.swift in Sources */, 95690DDD9D547D3D842ACBE3 /* AnalyticsSettingsScreenCoordinator.swift in Sources */, diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index afa5a20523..16b7f64c86 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -778,7 +778,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { self.analytics.trackComposer(inThread: false, isEditing: false, isReply: false, - messageType: isUserLocation ? .location(.user) : .location(.pin), + messageType: isUserLocation ? .LocationUser : .LocationPin, startsThread: nil) case .close: self.navigationSplitCoordinator.setSheetCoordinator(nil) @@ -839,7 +839,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { self.analytics.trackComposer(inThread: false, isEditing: false, isReply: false, - messageType: .poll, + messageType: .Poll, startsThread: nil) self.analytics.trackPollCreated(isUndisclosed: pollKind == .undisclosed, numberOfAnswers: options.count) diff --git a/ElementX/Sources/Other/Analytics/ScreenTrackerViewModifier.swift b/ElementX/Sources/Other/Analytics/ScreenTrackerViewModifier.swift index b33629abf6..73dfac54de 100644 --- a/ElementX/Sources/Other/Analytics/ScreenTrackerViewModifier.swift +++ b/ElementX/Sources/Other/Analytics/ScreenTrackerViewModifier.swift @@ -14,13 +14,14 @@ // limitations under the License. // +import AnalyticsEvents import SwiftUI /// `ScreenTrackerViewModifier` is a helper class used to track PostHog screen from SwiftUI screens. struct ScreenTrackerViewModifier: ViewModifier { @Environment(\.analyticsService) private var analyticsService - let screen: AnalyticsScreen + let screen: AnalyticsEvent.MobileScreen.ScreenName @ViewBuilder func body(content: Content) -> some View { @@ -32,7 +33,7 @@ struct ScreenTrackerViewModifier: ViewModifier { } extension View { - func track(screen: AnalyticsScreen) -> some View { + func track(screen: AnalyticsEvent.MobileScreen.ScreenName) -> some View { modifier(ScreenTrackerViewModifier(screen: screen)) } } diff --git a/ElementX/Sources/Screens/CreatePollScreen/View/PollFormScreen.swift b/ElementX/Sources/Screens/CreatePollScreen/View/PollFormScreen.swift index 4cd83e9050..7fbf887480 100644 --- a/ElementX/Sources/Screens/CreatePollScreen/View/PollFormScreen.swift +++ b/ElementX/Sources/Screens/CreatePollScreen/View/PollFormScreen.swift @@ -156,7 +156,7 @@ private extension View { case .edit: self case .new: - track(screen: .createPoll) + track(screen: .CreatePollView) } } } diff --git a/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift b/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift index 1461b091b2..e47d5389f9 100644 --- a/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift +++ b/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift @@ -33,7 +33,7 @@ struct CreateRoomScreen: View { securitySection } .compoundList() - .track(screen: .createRoom) + .track(screen: .CreateRoom) .scrollDismissesKeyboard(.immediately) .navigationTitle(L10n.screenCreateRoomTitle) .navigationBarTitleDisplayMode(.inline) diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift index 49732d6cd0..6d158db713 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift @@ -40,7 +40,7 @@ struct HomeScreen: View { .navigationTitle(L10n.screenRoomlistMainSpaceTitle) .toolbar { toolbar } .background(Color.compound.bgCanvasDefault.ignoresSafeArea()) - .track(screen: .home) + .track(screen: .Home) .introspect(.viewController, on: .supportedVersions) { controller in Task { if bloomView == nil { diff --git a/ElementX/Sources/Screens/InvitesScreen/View/InvitesScreen.swift b/ElementX/Sources/Screens/InvitesScreen/View/InvitesScreen.swift index 34279f936c..bb86961bc3 100644 --- a/ElementX/Sources/Screens/InvitesScreen/View/InvitesScreen.swift +++ b/ElementX/Sources/Screens/InvitesScreen/View/InvitesScreen.swift @@ -24,7 +24,7 @@ struct InvitesScreen: View { .background(Color.compound.bgCanvasDefault.ignoresSafeArea()) .navigationTitle(L10n.actionInvitesList) .alert(item: $context.alertInfo) - .track(screen: .invites) + .track(screen: .Invites) } // MARK: - Private diff --git a/ElementX/Sources/Screens/LocationSharing/View/StaticLocationScreen.swift b/ElementX/Sources/Screens/LocationSharing/View/StaticLocationScreen.swift index 9dd9bc3a7f..89264c2316 100644 --- a/ElementX/Sources/Screens/LocationSharing/View/StaticLocationScreen.swift +++ b/ElementX/Sources/Screens/LocationSharing/View/StaticLocationScreen.swift @@ -34,7 +34,7 @@ struct StaticLocationScreen: View { } mapView } - .track(screen: context.viewState.isLocationPickerMode ? .locationSend : .locationView) + .track(screen: context.viewState.isLocationPickerMode ? .LocationSend : .LocationView) .navigationTitle(context.viewState.navigationTitle) .navigationBarTitleDisplayMode(.inline) .toolbar { toolbar } diff --git a/ElementX/Sources/Screens/RoomDetailsEditScreen/View/RoomDetailsEditScreen.swift b/ElementX/Sources/Screens/RoomDetailsEditScreen/View/RoomDetailsEditScreen.swift index 54e0078cd9..9b23b5c296 100644 --- a/ElementX/Sources/Screens/RoomDetailsEditScreen/View/RoomDetailsEditScreen.swift +++ b/ElementX/Sources/Screens/RoomDetailsEditScreen/View/RoomDetailsEditScreen.swift @@ -37,7 +37,7 @@ struct RoomDetailsEditScreen: View { .navigationTitle(L10n.screenRoomDetailsEditRoomTitle) .navigationBarTitleDisplayMode(.inline) .toolbar { toolbar } - .track(screen: .roomSettings) + .track(screen: .RoomSettings) } // MARK: - Private diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift index b14783707e..66e8b48469 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift @@ -61,7 +61,7 @@ struct RoomDetailsScreen: View { } } } - .track(screen: .roomDetails) + .track(screen: .RoomDetails) .interactiveQuickLook(item: $context.mediaPreviewItem, shouldHideControls: true) } diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift index 0221efab54..d4ce5bd7cb 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift @@ -32,7 +32,7 @@ struct RoomMemberDetailsScreen: View { .compoundList() .alert(item: $context.ignoreUserAlert, actions: blockUserAlertActions, message: blockUserAlertMessage) .alert(item: $context.alertInfo) - .track(screen: .user) + .track(screen: .User) .interactiveQuickLook(item: $context.mediaPreviewItem, shouldHideControls: true) } diff --git a/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreen.swift b/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreen.swift index 8bc967c0f7..85c97803b8 100644 --- a/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreen.swift +++ b/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreen.swift @@ -37,7 +37,7 @@ struct RoomMembersListScreen: View { inviteButton } } - .track(screen: .roomMembers) + .track(screen: .RoomMembers) } // MARK: - Private diff --git a/ElementX/Sources/Screens/RoomNotificationSettingsScreen/View/RoomNotificationSettingsScreen.swift b/ElementX/Sources/Screens/RoomNotificationSettingsScreen/View/RoomNotificationSettingsScreen.swift index 4b493345d0..7387f603cf 100644 --- a/ElementX/Sources/Screens/RoomNotificationSettingsScreen/View/RoomNotificationSettingsScreen.swift +++ b/ElementX/Sources/Screens/RoomNotificationSettingsScreen/View/RoomNotificationSettingsScreen.swift @@ -33,7 +33,7 @@ struct RoomNotificationSettingsScreen: View { .compoundList() .navigationTitle(context.viewState.navigationTitle) .alert(item: $context.alertInfo) - .track(screen: .roomNotifications) + .track(screen: .RoomNotifications) } // MARK: - Private diff --git a/ElementX/Sources/Screens/RoomNotificationSettingsScreen/View/RoomNotificationSettingsUserDefinedScreen.swift b/ElementX/Sources/Screens/RoomNotificationSettingsScreen/View/RoomNotificationSettingsUserDefinedScreen.swift index 189d7aa80f..9c2d8e637f 100644 --- a/ElementX/Sources/Screens/RoomNotificationSettingsScreen/View/RoomNotificationSettingsUserDefinedScreen.swift +++ b/ElementX/Sources/Screens/RoomNotificationSettingsScreen/View/RoomNotificationSettingsUserDefinedScreen.swift @@ -29,7 +29,7 @@ struct RoomNotificationSettingsUserDefinedScreen: View { .compoundList() .navigationTitle(context.viewState.navigationTitle) .alert(item: $context.alertInfo) - .track(screen: .roomNotifications) + .track(screen: .RoomNotifications) } // MARK: - Private diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenInteractionHandler.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenInteractionHandler.swift index 024d05011f..e3a43a6523 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenInteractionHandler.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenInteractionHandler.swift @@ -428,7 +428,7 @@ class RoomScreenInteractionHandler { analyticsService.trackComposer(inThread: false, isEditing: false, isReply: false, - messageType: .voiceMessage, + messageType: .VoiceMessage, startsThread: nil) actionsSubject.send(.composer(action: .setMode(mode: .previewVoiceMessage(state: audioPlayerState, waveform: .url(recordingURL), isUploading: true)))) diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift index 64e15ae7df..3f5b5755d4 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift @@ -70,7 +70,7 @@ struct RoomScreen: View { .environmentObject(context) } .interactiveQuickLook(item: $context.mediaPreviewItem) - .track(screen: .room) + .track(screen: .Room) .onDrop(of: ["public.item", "public.file-url"], isTargeted: $dragOver) { providers -> Bool in guard let provider = providers.first, provider.isSupportedForPasteOrDrop else { diff --git a/ElementX/Sources/Screens/Settings/NotificationSettingsEditScreen/View/NotificationSettingsEditScreen.swift b/ElementX/Sources/Screens/Settings/NotificationSettingsEditScreen/View/NotificationSettingsEditScreen.swift index 168886a018..64425d885e 100644 --- a/ElementX/Sources/Screens/Settings/NotificationSettingsEditScreen/View/NotificationSettingsEditScreen.swift +++ b/ElementX/Sources/Screens/Settings/NotificationSettingsEditScreen/View/NotificationSettingsEditScreen.swift @@ -31,7 +31,7 @@ struct NotificationSettingsEditScreen: View { .compoundList() .navigationTitle(context.viewState.strings.navigationTitle) .alert(item: $context.alertInfo) - .track(screen: .settingsDefaultNotifications) + .track(screen: .SettingsDefaultNotifications) } // MARK: - Private diff --git a/ElementX/Sources/Screens/Settings/NotificationSettingsScreen/View/NotificationSettingsScreen.swift b/ElementX/Sources/Screens/Settings/NotificationSettingsScreen/View/NotificationSettingsScreen.swift index 16a723065a..c557494b88 100644 --- a/ElementX/Sources/Screens/Settings/NotificationSettingsScreen/View/NotificationSettingsScreen.swift +++ b/ElementX/Sources/Screens/Settings/NotificationSettingsScreen/View/NotificationSettingsScreen.swift @@ -48,7 +48,7 @@ struct NotificationSettingsScreen: View { .navigationBarTitleDisplayMode(.inline) .toolbar { toolbar } .alert(item: $context.alertInfo) - .track(screen: .settingsNotifications) + .track(screen: .SettingsNotifications) } // MARK: - Private diff --git a/ElementX/Sources/Screens/StartChatScreen/View/StartChatScreen.swift b/ElementX/Sources/Screens/StartChatScreen/View/StartChatScreen.swift index f0c9b868ca..9b0889dd00 100644 --- a/ElementX/Sources/Screens/StartChatScreen/View/StartChatScreen.swift +++ b/ElementX/Sources/Screens/StartChatScreen/View/StartChatScreen.swift @@ -29,7 +29,7 @@ struct StartChatScreen: View { } } .compoundList() - .track(screen: .startChat) + .track(screen: .StartChat) .scrollDismissesKeyboard(.immediately) .navigationTitle(L10n.actionStartChat) .navigationBarTitleDisplayMode(.inline) diff --git a/ElementX/Sources/Services/Analytics/AnalyticsMessageType.swift b/ElementX/Sources/Services/Analytics/AnalyticsMessageType.swift deleted file mode 100644 index e608075ecb..0000000000 --- a/ElementX/Sources/Services/Analytics/AnalyticsMessageType.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// Copyright 2023 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import AnalyticsEvents - -enum AnalyticsMessageType { - case location(LocationType) - case poll - case text - case voiceMessage - - enum LocationType { - case pin - case user - } -} - -extension AnalyticsEvent.Composer.MessageType { - init(_ analyticsMessageType: AnalyticsMessageType) { - switch analyticsMessageType { - case .location(let locationType): - self = .init(locationType) - case .poll: - self = .Poll - case .text: - self = .Text - case .voiceMessage: - self = .VoiceMessage - } - } - - private init(_ locationType: AnalyticsMessageType.LocationType) { - switch locationType { - case .pin: - self = .LocationPin - case .user: - self = .LocationUser - } - } -} diff --git a/ElementX/Sources/Services/Analytics/AnalyticsScreen.swift b/ElementX/Sources/Services/Analytics/AnalyticsScreen.swift deleted file mode 100644 index fd939ae43b..0000000000 --- a/ElementX/Sources/Services/Analytics/AnalyticsScreen.swift +++ /dev/null @@ -1,159 +0,0 @@ -// -// Copyright 2021 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import AnalyticsEvents -import Foundation - -enum AnalyticsScreen: Int { - case welcome - case login - case register - case forgotPassword - case sidebar - case home - case favourites - case people - case rooms - case searchRooms - case searchMessages - case searchPeople - case searchFiles - case room - case roomPreview - case roomDetails - case roomMembers - case user - case roomSearch - case roomUploads - case roomSettings - case roomNotifications - case roomDirectory - case switchDirectory - case startChat - case createRoom - case settings - case settingsSecurity - case settingsDefaultNotifications - case settingsMentionsAndKeywords - case settingsNotifications - case deactivateAccount - case inviteFriends - case threadList - case spaceMenu - case spaceMembers - case spaceExploreRooms - case dialpad - case spaceBottomSheet - case invites - case createSpace - case locationSend - case locationView - case createPoll - - /// The screen name reported to the AnalyticsEvent. - var screenName: AnalyticsEvent.MobileScreen.ScreenName { - switch self { - case .welcome: - return .Welcome - case .login: - return .Login - case .register: - return .Register - case .forgotPassword: - return .ForgotPassword - case .sidebar: - return .Sidebar - case .home: - return .Home - case .favourites: - return .Favourites - case .people: - return .People - case .rooms: - return .Rooms - case .searchRooms: - return .SearchRooms - case .searchMessages: - return .SearchMessages - case .searchPeople: - return .SearchPeople - case .searchFiles: - return .SearchFiles - case .room: - return .Room - case .roomDetails: - return .RoomDetails - case .roomMembers: - return .RoomMembers - case .user: - return .User - case .roomPreview: - return .RoomPreview - case .roomSearch: - return .RoomSearch - case .roomUploads: - return .RoomUploads - case .roomSettings: - return .RoomSettings - case .roomNotifications: - return .RoomNotifications - case .roomDirectory: - return .RoomDirectory - case .switchDirectory: - return .SwitchDirectory - case .startChat: - return .StartChat - case .createRoom: - return .CreateRoom - case .settings: - return .Settings - case .settingsSecurity: - return .SettingsSecurity - case .settingsDefaultNotifications: - return .SettingsDefaultNotifications - case .settingsMentionsAndKeywords: - return .SettingsMentionsAndKeywords - case .settingsNotifications: - return .SettingsNotifications - case .deactivateAccount: - return .DeactivateAccount - case .inviteFriends: - return .InviteFriends - case .threadList: - return .ThreadList - case .spaceMenu: - return .SpaceMenu - case .spaceMembers: - return .SpaceMembers - case .spaceExploreRooms: - return .SpaceExploreRooms - case .dialpad: - return .Dialpad - case .spaceBottomSheet: - return .SpaceBottomSheet - case .invites: - return .Invites - case .createSpace: - return .CreateSpace - case .locationSend: - return .LocationSend - case .locationView: - return .LocationView - case .createPoll: - return .CreatePollView - } - } -} diff --git a/ElementX/Sources/Services/Analytics/AnalyticsService.swift b/ElementX/Sources/Services/Analytics/AnalyticsService.swift index 8af872e194..31fc21459f 100644 --- a/ElementX/Sources/Services/Analytics/AnalyticsService.swift +++ b/ElementX/Sources/Services/Analytics/AnalyticsService.swift @@ -121,9 +121,9 @@ extension AnalyticsService { /// Track the presentation of a screen /// - Parameter screen: The screen that was shown /// - Parameter duration: An optional value representing how long the screen was shown for in milliseconds. - func track(screen: AnalyticsScreen, duration milliseconds: Int? = nil) { + func track(screen: AnalyticsEvent.MobileScreen.ScreenName, duration milliseconds: Int? = nil) { MXLog.debug("\(screen)") - let event = AnalyticsEvent.MobileScreen(durationMs: milliseconds, screenName: screen.screenName) + let event = AnalyticsEvent.MobileScreen(durationMs: milliseconds, screenName: screen) client.screen(event) } @@ -147,12 +147,12 @@ extension AnalyticsService { func trackComposer(inThread: Bool, isEditing: Bool, isReply: Bool, - messageType: AnalyticsMessageType = .text, + messageType: AnalyticsEvent.Composer.MessageType = .Text, startsThread: Bool?) { capture(event: AnalyticsEvent.Composer(inThread: inThread, isEditing: isEditing, isReply: isReply, - messageType: .init(messageType), + messageType: messageType, startsThread: startsThread)) } From d8ddf5258cae7db27ecb877a6246057df91762f2 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Mon, 5 Feb 2024 17:11:57 +0200 Subject: [PATCH 4/7] Fix breaking API changes following SDK bump --- .../Mocks/Generated/GeneratedMocks.swift | 5 ---- ElementX/Sources/Mocks/RoomProxyMock.swift | 2 -- .../Sources/Services/Client/ClientProxy.swift | 28 +++++++++---------- .../Sources/Services/Room/RoomProxy.swift | 6 +--- .../Services/Room/RoomProxyProtocol.swift | 1 - .../RoomTimelineController.swift | 3 +- 6 files changed, 16 insertions(+), 29 deletions(-) diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 11df16725f..9a83a42ba7 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -1847,11 +1847,6 @@ class RoomProxyMock: RoomProxyProtocol { } var underlyingHasOngoingCall: Bool! var canonicalAlias: String? - var hasUnreadNotifications: Bool { - get { return underlyingHasUnreadNotifications } - set(value) { underlyingHasUnreadNotifications = value } - } - var underlyingHasUnreadNotifications: Bool! var ownUserID: String { get { return underlyingOwnUserID } set(value) { underlyingOwnUserID = value } diff --git a/ElementX/Sources/Mocks/RoomProxyMock.swift b/ElementX/Sources/Mocks/RoomProxyMock.swift index b5ca2d3c79..51e99470e7 100644 --- a/ElementX/Sources/Mocks/RoomProxyMock.swift +++ b/ElementX/Sources/Mocks/RoomProxyMock.swift @@ -29,7 +29,6 @@ struct RoomProxyMockConfiguration { var isEncrypted = true var hasOngoingCall = true var canonicalAlias: String? - var hasUnreadNotifications = Bool.random() var timeline = { let mock = TimelineProxyMock() @@ -64,7 +63,6 @@ extension RoomProxyMock { isEncrypted = configuration.isEncrypted hasOngoingCall = configuration.hasOngoingCall canonicalAlias = configuration.canonicalAlias - hasUnreadNotifications = configuration.hasUnreadNotifications timeline = configuration.timeline diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 10e257e3e2..5954499dd4 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -587,20 +587,20 @@ class ClientProxy: ClientProxyProtocol { } private let eventFilters: TimelineEventTypeFilter = { - let stateEventFilters: [FilterStateEventType] = [.roomAliases, - .roomCanonicalAlias, - .roomGuestAccess, - .roomHistoryVisibility, - .roomJoinRules, - .roomPinnedEvents, - .roomPowerLevels, - .roomServerAcl, - .roomTombstone, - .spaceChild, - .spaceParent, - .policyRuleRoom, - .policyRuleServer, - .policyRuleUser] + let stateEventFilters: [StateEventType] = [.roomAliases, + .roomCanonicalAlias, + .roomGuestAccess, + .roomHistoryVisibility, + .roomJoinRules, + .roomPinnedEvents, + .roomPowerLevels, + .roomServerAcl, + .roomTombstone, + .spaceChild, + .spaceParent, + .policyRuleRoom, + .policyRuleServer, + .policyRuleUser] return .exclude(eventTypes: stateEventFilters.map { FilterTimelineEventType.state(eventType: $0) }) }() diff --git a/ElementX/Sources/Services/Room/RoomProxy.swift b/ElementX/Sources/Services/Room/RoomProxy.swift index 078e64ba63..7f20be67dc 100644 --- a/ElementX/Sources/Services/Room/RoomProxy.swift +++ b/ElementX/Sources/Services/Room/RoomProxy.swift @@ -130,11 +130,7 @@ class RoomProxy: RoomProxyProtocol { var canonicalAlias: String? { room.canonicalAlias() } - - var hasUnreadNotifications: Bool { - roomListItem.hasUnreadNotifications() - } - + var avatarURL: URL? { roomListItem.avatarUrl().flatMap(URL.init(string:)) } diff --git a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift index d2be703b3a..2558146137 100644 --- a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift @@ -49,7 +49,6 @@ protocol RoomProxyProtocol { var membership: Membership { get } var hasOngoingCall: Bool { get } var canonicalAlias: String? { get } - var hasUnreadNotifications: Bool { get } var ownUserID: String { get } var name: String? { get } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift index f470395b1d..a60f19d7be 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift @@ -92,8 +92,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } func sendReadReceipt(for itemID: TimelineItemIdentifier) async -> Result { - guard roomProxy.hasUnreadNotifications, - let eventID = itemID.eventID + guard let eventID = itemID.eventID else { return .success(()) } switch await roomProxy.timeline.sendReadReceipt(for: eventID, From 4e8dae822eb6faf1370e423c11925f47d421c092 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Mon, 5 Feb 2024 17:12:45 +0200 Subject: [PATCH 5/7] Add changelog --- changelog.d/2360.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2360.feature diff --git a/changelog.d/2360.feature b/changelog.d/2360.feature new file mode 100644 index 0000000000..b36804cb22 --- /dev/null +++ b/changelog.d/2360.feature @@ -0,0 +1 @@ +Add support for manually marking a room as read/unread \ No newline at end of file From 6e0409ec01e544bd33443d32bb6d2447289b5cb7 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Mon, 5 Feb 2024 17:34:54 +0200 Subject: [PATCH 6/7] Handle PR comments --- .../HomeScreen/HomeScreenViewModel.swift | 32 ++++++++++++------- changelog.d/pr-2408.api | 1 + 2 files changed, 21 insertions(+), 12 deletions(-) create mode 100644 changelog.d/pr-2408.api diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index d291f609f4..6deff19bcd 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -149,22 +149,30 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol actionsSubject.send(.presentGlobalSearch) case .markRoomAsUnread(let roomIdentifier): Task { - if let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) { - if case .failure(let error) = await roomProxy.markAsUnread() { - MXLog.error("Failed marking room \(roomIdentifier) as unread with error: \(error)") - } else { - ServiceLocator.shared.analytics.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle) - } + guard let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) else { + MXLog.error("Failed retrieving room for identifier: \(roomIdentifier)") + return + } + + switch await roomProxy.markAsUnread() { + case .success: + ServiceLocator.shared.analytics.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle) + case .failure(let error): + MXLog.error("Failed marking room \(roomIdentifier) as unread with error: \(error)") } } case .markRoomAsRead(let roomIdentifier): Task { - if let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) { - if case .failure(let error) = await roomProxy.markAsRead(sendReadReceipts: true, receiptType: appSettings.sendReadReceiptsEnabled ? .read : .readPrivate) { - MXLog.error("Failed marking room \(roomIdentifier) as read with error: \(error)") - } else { - ServiceLocator.shared.analytics.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle) - } + guard let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) else { + MXLog.error("Failed retrieving room for identifier: \(roomIdentifier)") + return + } + + switch await roomProxy.markAsRead(sendReadReceipts: true, receiptType: appSettings.sendReadReceiptsEnabled ? .read : .readPrivate) { + case .success: + ServiceLocator.shared.analytics.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle) + case .failure(let error): + MXLog.error("Failed marking room \(roomIdentifier) as read with error: \(error)") } } } diff --git a/changelog.d/pr-2408.api b/changelog.d/pr-2408.api new file mode 100644 index 0000000000..9983f0606c --- /dev/null +++ b/changelog.d/pr-2408.api @@ -0,0 +1 @@ +Remove unnecessary analytics abstraction levels, directly create analytics events in the analytics service \ No newline at end of file From 1f1e77473d2976ff93ca1b833d95d91449612d30 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Mon, 5 Feb 2024 18:06:02 +0200 Subject: [PATCH 7/7] Fix unit tests --- UnitTests/Sources/LoggingTests.swift | 1 + UnitTests/Sources/RoomScreenViewModelTests.swift | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/UnitTests/Sources/LoggingTests.swift b/UnitTests/Sources/LoggingTests.swift index 8fd73d5359..bb53b19e2c 100644 --- a/UnitTests/Sources/LoggingTests.swift +++ b/UnitTests/Sources/LoggingTests.swift @@ -229,6 +229,7 @@ class LoggingTests: XCTestCase { avatarURL: nil, lastMessage: AttributedString(lastMessage), lastMessageFormattedTimestamp: "Now", + isMarkedUnread: false, unreadMessagesCount: 0, unreadMentionsCount: 0, unreadNotificationsCount: 0, diff --git a/UnitTests/Sources/RoomScreenViewModelTests.swift b/UnitTests/Sources/RoomScreenViewModelTests.swift index 28965f49b8..f6e94e7792 100644 --- a/UnitTests/Sources/RoomScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomScreenViewModelTests.swift @@ -579,7 +579,6 @@ class RoomScreenViewModelTests: XCTestCase { timelineProxy.sendReadReceiptForTypeReturnValue = .success(()) - roomProxy.underlyingHasUnreadNotifications = true timelineController.timelineItems = items timelineController.roomProxy = roomProxy