Make AnalyticsService mockable via `AnalyticsServiceProtocol (#5599)

* Create AnalyticsServiceProtocol and conform AnalyticsService

* Replace AnalyticsService properties and arguments with AnalyticsServiceProtocol

* Inline AnalyticsServiceMock replacing old mock

* handle more complex AnalyticsServiceMock callsites

* move documentation from AnalyticsService to AnalyticsServiceProtocol
This commit is contained in:
Michael
2026-05-20 00:23:58 -05:00
committed by GitHub
parent 946cb53559
commit 5a07c98afc
118 changed files with 1218 additions and 505 deletions
+8 -4
View File
@@ -426,7 +426,6 @@
484202C5D50983442D24D061 /* AttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BD6ED18E2EB61E28C340AD /* AttributedString.swift */; }; 484202C5D50983442D24D061 /* AttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BD6ED18E2EB61E28C340AD /* AttributedString.swift */; };
4870F7F72CEBEFDAD1C973A6 /* VoiceMessageMediaEventsTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD3521DF92391551E02004D8 /* VoiceMessageMediaEventsTimelineView.swift */; }; 4870F7F72CEBEFDAD1C973A6 /* VoiceMessageMediaEventsTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD3521DF92391551E02004D8 /* VoiceMessageMediaEventsTimelineView.swift */; };
489BB6A733D3DA0FE7062650 /* IdentityConfirmationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C71B9802433F1B4252291BB /* IdentityConfirmationScreenViewModelProtocol.swift */; }; 489BB6A733D3DA0FE7062650 /* IdentityConfirmationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C71B9802433F1B4252291BB /* IdentityConfirmationScreenViewModelProtocol.swift */; };
48F2D00593F6D73434D3F893 /* AnalyticsService+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A384A5A9F8472A0DA76FDB27 /* AnalyticsService+Mock.swift */; };
491D62ACD19E6F134B1766AF /* RoomNotificationSettingsUserDefinedScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3203C6566DC17B7AECC1B7FD /* RoomNotificationSettingsUserDefinedScreen.swift */; }; 491D62ACD19E6F134B1766AF /* RoomNotificationSettingsUserDefinedScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3203C6566DC17B7AECC1B7FD /* RoomNotificationSettingsUserDefinedScreen.swift */; };
492274DA6691EE985C2FCCAA /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 21C83087604B154AA30E9A8F /* SnapshotTesting */; }; 492274DA6691EE985C2FCCAA /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 21C83087604B154AA30E9A8F /* SnapshotTesting */; };
4940B439681767BE9D78CFDB /* AppLockSetupBiometricsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F5EE5DE3B55D59299DB5BC /* AppLockSetupBiometricsScreenViewModelTests.swift */; }; 4940B439681767BE9D78CFDB /* AppLockSetupBiometricsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F5EE5DE3B55D59299DB5BC /* AppLockSetupBiometricsScreenViewModelTests.swift */; };
@@ -1005,6 +1004,7 @@
A5D551E5691749066E0E0C44 /* RoomDetailsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 837B440C4705E4B899BCB899 /* RoomDetailsScreenViewModel.swift */; }; A5D551E5691749066E0E0C44 /* RoomDetailsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 837B440C4705E4B899BCB899 /* RoomDetailsScreenViewModel.swift */; };
A5FD8284744E2FECFC842FC1 /* TraceLogPack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7149BDDE47F8AD104E644E2 /* TraceLogPack.swift */; }; A5FD8284744E2FECFC842FC1 /* TraceLogPack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7149BDDE47F8AD104E644E2 /* TraceLogPack.swift */; };
A64B52D9F73F9A6B95AF24FE /* UserDetailsEditScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CD503F5E0938FE53C7C6E7 /* UserDetailsEditScreenCoordinator.swift */; }; A64B52D9F73F9A6B95AF24FE /* UserDetailsEditScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CD503F5E0938FE53C7C6E7 /* UserDetailsEditScreenCoordinator.swift */; };
A65999909C9D478DB71982B4 /* AnalyticsServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 558AA5B376CE8C3982A9557F /* AnalyticsServiceProtocol.swift */; };
A6B83EB78F025D21B6EBA90C /* CompoundIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 044E501B8331B339874D1B96 /* CompoundIcon.swift */; }; A6B83EB78F025D21B6EBA90C /* CompoundIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 044E501B8331B339874D1B96 /* CompoundIcon.swift */; };
A6D4C5EEA85A6A0ABA1559D6 /* RoomDetailsEditScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16D09C79746BDCD9173EB3A7 /* RoomDetailsEditScreenModels.swift */; }; A6D4C5EEA85A6A0ABA1559D6 /* RoomDetailsEditScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16D09C79746BDCD9173EB3A7 /* RoomDetailsEditScreenModels.swift */; };
A6DEC1ADEC8FEEC206A0FA37 /* AttributedStringBuilderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72F37B5DA798C9AE436F2C2C /* AttributedStringBuilderProtocol.swift */; }; A6DEC1ADEC8FEEC206A0FA37 /* AttributedStringBuilderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72F37B5DA798C9AE436F2C2C /* AttributedStringBuilderProtocol.swift */; };
@@ -1441,6 +1441,7 @@
F2E580C0FBFBEFFE9D69893B /* RoomPreviewProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 739077686814E4EA339B1C83 /* RoomPreviewProxyProtocol.swift */; }; F2E580C0FBFBEFFE9D69893B /* RoomPreviewProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 739077686814E4EA339B1C83 /* RoomPreviewProxyProtocol.swift */; };
F34D06F86C29E219E7132E87 /* AuthenticationStartScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F71A54CB96DAA1E72C6541D /* AuthenticationStartScreenViewModel.swift */; }; F34D06F86C29E219E7132E87 /* AuthenticationStartScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F71A54CB96DAA1E72C6541D /* AuthenticationStartScreenViewModel.swift */; };
F35FAD1B1B289E221A07D719 /* ManageRoomMemberSheetViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB7B588A06911B455AC0B4C9 /* ManageRoomMemberSheetViewModelProtocol.swift */; }; F35FAD1B1B289E221A07D719 /* ManageRoomMemberSheetViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB7B588A06911B455AC0B4C9 /* ManageRoomMemberSheetViewModelProtocol.swift */; };
F371FB436B77F939DF9EF276 /* AnalyticsServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BEE119165C106FA83F253EF /* AnalyticsServiceMock.swift */; };
F37629BAA5E8F50AAF2A131D /* SoftLogoutScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB7BAD55A4E2B8E5828CD64C /* SoftLogoutScreenViewModel.swift */; }; F37629BAA5E8F50AAF2A131D /* SoftLogoutScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB7BAD55A4E2B8E5828CD64C /* SoftLogoutScreenViewModel.swift */; };
F382E2FE3AC3719BC1F22D9C /* MapURLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB08D1F7C27A8C24EF81073C /* MapURLs.swift */; }; F382E2FE3AC3719BC1F22D9C /* MapURLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB08D1F7C27A8C24EF81073C /* MapURLs.swift */; };
F38D32C1B0232AAFE6A0822C /* ExtensionLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A8571A8A071FB41778C016 /* ExtensionLogger.swift */; }; F38D32C1B0232AAFE6A0822C /* ExtensionLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A8571A8A071FB41778C016 /* ExtensionLogger.swift */; };
@@ -1778,6 +1779,7 @@
1BA5A62DA4B543827FF82354 /* LAContextMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LAContextMock.swift; sourceTree = "<group>"; }; 1BA5A62DA4B543827FF82354 /* LAContextMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LAContextMock.swift; sourceTree = "<group>"; };
1BA8082E26C77A2C587B34B3 /* MockTimelineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTimelineController.swift; sourceTree = "<group>"; }; 1BA8082E26C77A2C587B34B3 /* MockTimelineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTimelineController.swift; sourceTree = "<group>"; };
1BC752C2A4606C4C2D1ADB41 /* 94 */ = {isa = PBXFileReference; path = 94; sourceTree = "<group>"; }; 1BC752C2A4606C4C2D1ADB41 /* 94 */ = {isa = PBXFileReference; path = 94; sourceTree = "<group>"; };
1BEE119165C106FA83F253EF /* AnalyticsServiceMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsServiceMock.swift; sourceTree = "<group>"; };
1C21A715237F2B6D6E80998C /* SecureBackupControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupControllerProtocol.swift; sourceTree = "<group>"; }; 1C21A715237F2B6D6E80998C /* SecureBackupControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupControllerProtocol.swift; sourceTree = "<group>"; };
1C25B6EBEB414431187D73B7 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = "<group>"; }; 1C25B6EBEB414431187D73B7 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = "<group>"; };
1C78111573987B1D79ED0868 /* LinkMetadataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkMetadataProvider.swift; sourceTree = "<group>"; }; 1C78111573987B1D79ED0868 /* LinkMetadataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkMetadataProvider.swift; sourceTree = "<group>"; };
@@ -2104,6 +2106,7 @@
54AD70D6E03D2031AE1B5A52 /* TimelineReactionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReactionsView.swift; sourceTree = "<group>"; }; 54AD70D6E03D2031AE1B5A52 /* TimelineReactionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReactionsView.swift; sourceTree = "<group>"; };
54C4E7B46099462F12000C91 /* DeveloperOptionsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenViewModelProtocol.swift; sourceTree = "<group>"; }; 54C4E7B46099462F12000C91 /* DeveloperOptionsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
5557DDA438841AF5DC003D0B /* SpaceListScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceListScreenViewModelTests.swift; sourceTree = "<group>"; }; 5557DDA438841AF5DC003D0B /* SpaceListScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceListScreenViewModelTests.swift; sourceTree = "<group>"; };
558AA5B376CE8C3982A9557F /* AnalyticsServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsServiceProtocol.swift; sourceTree = "<group>"; };
55AEEF8142DF1B59DB40FB93 /* TimelineItemSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemSender.swift; sourceTree = "<group>"; }; 55AEEF8142DF1B59DB40FB93 /* TimelineItemSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemSender.swift; sourceTree = "<group>"; };
5644919DB2022397D9D5825A /* MockSoftLogoutScreenState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSoftLogoutScreenState.swift; sourceTree = "<group>"; }; 5644919DB2022397D9D5825A /* MockSoftLogoutScreenState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSoftLogoutScreenState.swift; sourceTree = "<group>"; };
565F1B2B300597C616B37888 /* FullscreenDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullscreenDialog.swift; sourceTree = "<group>"; }; 565F1B2B300597C616B37888 /* FullscreenDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullscreenDialog.swift; sourceTree = "<group>"; };
@@ -2526,7 +2529,6 @@
A232D9156D225BD9FD1D0C43 /* PhotoLibraryPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLibraryPicker.swift; sourceTree = "<group>"; }; A232D9156D225BD9FD1D0C43 /* PhotoLibraryPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLibraryPicker.swift; sourceTree = "<group>"; };
A243A6E6207297123E60DE48 /* TimelineItemMacContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemMacContextMenu.swift; sourceTree = "<group>"; }; A243A6E6207297123E60DE48 /* TimelineItemMacContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemMacContextMenu.swift; sourceTree = "<group>"; };
A2723A4AF3AB9F5E18D26E49 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/InfoPlist.strings; sourceTree = "<group>"; }; A2723A4AF3AB9F5E18D26E49 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
A384A5A9F8472A0DA76FDB27 /* AnalyticsService+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AnalyticsService+Mock.swift"; sourceTree = "<group>"; };
A3A4F56855188DAD23E59031 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = "<group>"; }; A3A4F56855188DAD23E59031 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = "<group>"; };
A3B4B58B79A6FA250B24A1EC /* HomeScreenContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenContent.swift; sourceTree = "<group>"; }; A3B4B58B79A6FA250B24A1EC /* HomeScreenContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenContent.swift; sourceTree = "<group>"; };
A3E3DE6616DCA81AD5F4791B /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/InfoPlist.strings; sourceTree = "<group>"; }; A3E3DE6616DCA81AD5F4791B /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@@ -3766,7 +3768,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */, 69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */,
A384A5A9F8472A0DA76FDB27 /* AnalyticsService+Mock.swift */, 1BEE119165C106FA83F253EF /* AnalyticsServiceMock.swift */,
3BAC027034248429A438886B /* AppMediatorMock.swift */, 3BAC027034248429A438886B /* AppMediatorMock.swift */,
0554FEA301486A8CFA475D5A /* AuthenticationClientBuilderFactoryMock.swift */, 0554FEA301486A8CFA475D5A /* AuthenticationClientBuilderFactoryMock.swift */,
9FD7E851E2BA8C5A8D284B2A /* BannedRoomProxyMock.swift */, 9FD7E851E2BA8C5A8D284B2A /* BannedRoomProxyMock.swift */,
@@ -4376,6 +4378,7 @@
children = ( children = (
E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */, E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */,
5445FCE0CE15E634FDC1A2E2 /* AnalyticsService.swift */, 5445FCE0CE15E634FDC1A2E2 /* AnalyticsService.swift */,
558AA5B376CE8C3982A9557F /* AnalyticsServiceProtocol.swift */,
A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */, A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */,
5CEEAE1BFAACD6C96B6DB731 /* PHGPostHogProtocol.swift */, 5CEEAE1BFAACD6C96B6DB731 /* PHGPostHogProtocol.swift */,
1715E3D7F53C0748AA50C91C /* PostHogAnalyticsClient.swift */, 1715E3D7F53C0748AA50C91C /* PostHogAnalyticsClient.swift */,
@@ -8079,8 +8082,9 @@
6DC8E43BA04AC2AC4EB2EB97 /* AnalyticsPromptScreenModels.swift in Sources */, 6DC8E43BA04AC2AC4EB2EB97 /* AnalyticsPromptScreenModels.swift in Sources */,
DB65401349C143DFF883E2B0 /* AnalyticsPromptScreenViewModel.swift in Sources */, DB65401349C143DFF883E2B0 /* AnalyticsPromptScreenViewModel.swift in Sources */,
05BAB510CBC2ED35C154ADD0 /* AnalyticsPromptScreenViewModelProtocol.swift in Sources */, 05BAB510CBC2ED35C154ADD0 /* AnalyticsPromptScreenViewModelProtocol.swift in Sources */,
48F2D00593F6D73434D3F893 /* AnalyticsService+Mock.swift in Sources */,
3C73442084BF8A6939F0F80B /* AnalyticsService.swift in Sources */, 3C73442084BF8A6939F0F80B /* AnalyticsService.swift in Sources */,
F371FB436B77F939DF9EF276 /* AnalyticsServiceMock.swift in Sources */,
A65999909C9D478DB71982B4 /* AnalyticsServiceProtocol.swift in Sources */,
020F7E70167FB2833266F2F0 /* AnalyticsSettingsScreen.swift in Sources */, 020F7E70167FB2833266F2F0 /* AnalyticsSettingsScreen.swift in Sources */,
95690DDD9D547D3D842ACBE3 /* AnalyticsSettingsScreenCoordinator.swift in Sources */, 95690DDD9D547D3D842ACBE3 /* AnalyticsSettingsScreenCoordinator.swift in Sources */,
7C6376192F578E0BA801BFEC /* AnalyticsSettingsScreenModels.swift in Sources */, 7C6376192F578E0BA801BFEC /* AnalyticsSettingsScreenModels.swift in Sources */,
@@ -22,7 +22,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
private let targetConfiguration: Target.ConfigurationResult private let targetConfiguration: Target.ConfigurationResult
private let appMediator: AppMediator private let appMediator: AppMediator
private let appSettings: AppSettings private let appSettings: AppSettings
private let analyticsService: AnalyticsService private let analyticsService: AnalyticsServiceProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let appDelegate: AppDelegate private let appDelegate: AppDelegate
@@ -946,7 +946,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
} }
} }
private static func setupSentry(bugReportService: BugReportServiceProtocol, appSettings: AppSettings, analytics: AnalyticsService) { private static func setupSentry(bugReportService: BugReportServiceProtocol, appSettings: AppSettings, analytics: AnalyticsServiceProtocol) {
guard let bugReportSentryURL = appSettings.bugReportSentryURL else { return } guard let bugReportSentryURL = appSettings.bugReportSentryURL else { return }
let options: Options = .init() let options: Options = .init()
@@ -36,7 +36,7 @@ struct CommonFlowParameters {
let appMediator: AppMediatorProtocol let appMediator: AppMediatorProtocol
let appSettings: AppSettings let appSettings: AppSettings
let appHooks: AppHooks let appHooks: AppHooks
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let notificationManager: NotificationManagerProtocol let notificationManager: NotificationManagerProtocol
let stateMachineFactory: StateMachineFactoryProtocol let stateMachineFactory: StateMachineFactoryProtocol
@@ -23,7 +23,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
private let appMediator: AppMediatorProtocol private let appMediator: AppMediatorProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private let appHooks: AppHooks private let appHooks: AppHooks
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
enum State: StateType { enum State: StateType {
@@ -109,7 +109,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
appMediator: AppMediatorProtocol, appMediator: AppMediatorProtocol,
appSettings: AppSettings, appSettings: AppSettings,
appHooks: AppHooks, appHooks: AppHooks,
analytics: AnalyticsService, analytics: AnalyticsServiceProtocol,
userIndicatorController: UserIndicatorControllerProtocol) { userIndicatorController: UserIndicatorControllerProtocol) {
self.authenticationService = authenticationService self.authenticationService = authenticationService
self.bugReportService = bugReportService self.bugReportService = bugReportService
@@ -19,7 +19,7 @@ enum OnboardingFlowCoordinatorAction {
class OnboardingFlowCoordinator: FlowCoordinatorProtocol { class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
private let userSession: UserSessionProtocol private let userSession: UserSessionProtocol
private let appLockService: AppLockServiceProtocol private let appLockService: AppLockServiceProtocol
private let analyticsService: AnalyticsService private let analyticsService: AnalyticsServiceProtocol
private let appMediator: AppMediatorProtocol private let appMediator: AppMediatorProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private let appHooks: AppHooks private let appHooks: AppHooks
@@ -20,7 +20,7 @@ struct RoomRolesAndPermissionsFlowCoordinatorParameters {
let mediaProvider: MediaProviderProtocol let mediaProvider: MediaProviderProtocol
let navigationStackCoordinator: NavigationStackCoordinator let navigationStackCoordinator: NavigationStackCoordinator
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
} }
class RoomRolesAndPermissionsFlowCoordinator: FlowCoordinatorProtocol { class RoomRolesAndPermissionsFlowCoordinator: FlowCoordinatorProtocol {
@@ -28,7 +28,7 @@ class RoomRolesAndPermissionsFlowCoordinator: FlowCoordinatorProtocol {
private let navigationStackCoordinator: NavigationStackCoordinator private let navigationStackCoordinator: NavigationStackCoordinator
private let mediaProvider: MediaProviderProtocol private let mediaProvider: MediaProviderProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
enum State: StateType { enum State: StateType {
/// The state machine hasn't started. /// The state machine hasn't started.
@@ -1,21 +0,0 @@
//
// Copyright 2026 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
extension AnalyticsService {
static func mock(_ clientOverride: AnalyticsClientProtocol? = nil, settings: AppSettings? = nil) -> AnalyticsService {
let client: AnalyticsClientProtocol
if let clientOverride {
client = clientOverride
} else {
let mockClient = AnalyticsClientMock()
mockClient.isRunning = false
client = mockClient
}
return .init(client: client, appSettings: settings ?? AppSettings())
}
}
@@ -0,0 +1,16 @@
//
// Copyright 2026 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
extension AnalyticsServiceMock {
static func `default`() -> AnalyticsServiceMock {
let mock = AnalyticsServiceMock()
mock.isEnabled = false
mock.shouldShowAnalyticsPrompt = false
mock.signpost = Signposter()
return mock
}
}
@@ -256,6 +256,761 @@ class AnalyticsClientMock: AnalyticsClientProtocol, @unchecked Sendable {
updateUserPropertiesClosure?(event) updateUserPropertiesClosure?(event)
} }
} }
class AnalyticsServiceMock: AnalyticsServiceProtocol, @unchecked Sendable {
var signpost: Signposter {
get { return underlyingSignpost }
set(value) { underlyingSignpost = value }
}
var underlyingSignpost: Signposter!
var shouldShowAnalyticsPrompt: Bool {
get { return underlyingShouldShowAnalyticsPrompt }
set(value) { underlyingShouldShowAnalyticsPrompt = value }
}
var underlyingShouldShowAnalyticsPrompt: Bool!
var isEnabled: Bool {
get { return underlyingIsEnabled }
set(value) { underlyingIsEnabled = value }
}
var underlyingIsEnabled: Bool!
//MARK: - optIn
var optInUnderlyingCallsCount = 0
var optInCallsCount: Int {
get {
if Thread.isMainThread {
return optInUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = optInUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
optInUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
optInUnderlyingCallsCount = newValue
}
}
}
}
var optInCalled: Bool {
return optInCallsCount > 0
}
var optInClosure: (() -> Void)?
func optIn() {
optInCallsCount += 1
optInClosure?()
}
//MARK: - optOut
var optOutUnderlyingCallsCount = 0
var optOutCallsCount: Int {
get {
if Thread.isMainThread {
return optOutUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = optOutUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
optOutUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
optOutUnderlyingCallsCount = newValue
}
}
}
}
var optOutCalled: Bool {
return optOutCallsCount > 0
}
var optOutClosure: (() -> Void)?
func optOut() {
optOutCallsCount += 1
optOutClosure?()
}
//MARK: - startIfEnabled
var startIfEnabledUnderlyingCallsCount = 0
var startIfEnabledCallsCount: Int {
get {
if Thread.isMainThread {
return startIfEnabledUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = startIfEnabledUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
startIfEnabledUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
startIfEnabledUnderlyingCallsCount = newValue
}
}
}
}
var startIfEnabledCalled: Bool {
return startIfEnabledCallsCount > 0
}
var startIfEnabledClosure: (() -> Void)?
func startIfEnabled() {
startIfEnabledCallsCount += 1
startIfEnabledClosure?()
}
//MARK: - reset
var resetUnderlyingCallsCount = 0
var resetCallsCount: Int {
get {
if Thread.isMainThread {
return resetUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = resetUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
resetUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
resetUnderlyingCallsCount = newValue
}
}
}
}
var resetCalled: Bool {
return resetCallsCount > 0
}
var resetClosure: (() -> Void)?
func reset() {
resetCallsCount += 1
resetClosure?()
}
//MARK: - resetConsentState
var resetConsentStateUnderlyingCallsCount = 0
var resetConsentStateCallsCount: Int {
get {
if Thread.isMainThread {
return resetConsentStateUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = resetConsentStateUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
resetConsentStateUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
resetConsentStateUnderlyingCallsCount = newValue
}
}
}
}
var resetConsentStateCalled: Bool {
return resetConsentStateCallsCount > 0
}
var resetConsentStateClosure: (() -> Void)?
func resetConsentState() {
resetConsentStateCallsCount += 1
resetConsentStateClosure?()
}
//MARK: - track
var trackScreenDurationUnderlyingCallsCount = 0
var trackScreenDurationCallsCount: Int {
get {
if Thread.isMainThread {
return trackScreenDurationUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = trackScreenDurationUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
trackScreenDurationUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
trackScreenDurationUnderlyingCallsCount = newValue
}
}
}
}
var trackScreenDurationCalled: Bool {
return trackScreenDurationCallsCount > 0
}
var trackScreenDurationReceivedArguments: (screen: AnalyticsEvent.MobileScreen.ScreenName, milliseconds: Int?)?
var trackScreenDurationReceivedInvocations: [(screen: AnalyticsEvent.MobileScreen.ScreenName, milliseconds: Int?)] = []
var trackScreenDurationClosure: ((AnalyticsEvent.MobileScreen.ScreenName, Int?) -> Void)?
func track(screen: AnalyticsEvent.MobileScreen.ScreenName, duration milliseconds: Int?) {
trackScreenDurationCallsCount += 1
trackScreenDurationReceivedArguments = (screen: screen, milliseconds: milliseconds)
DispatchQueue.main.async {
self.trackScreenDurationReceivedInvocations.append((screen: screen, milliseconds: milliseconds))
}
trackScreenDurationClosure?(screen, milliseconds)
}
//MARK: - trackInteraction
var trackInteractionIndexNameUnderlyingCallsCount = 0
var trackInteractionIndexNameCallsCount: Int {
get {
if Thread.isMainThread {
return trackInteractionIndexNameUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = trackInteractionIndexNameUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
trackInteractionIndexNameUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
trackInteractionIndexNameUnderlyingCallsCount = newValue
}
}
}
}
var trackInteractionIndexNameCalled: Bool {
return trackInteractionIndexNameCallsCount > 0
}
var trackInteractionIndexNameReceivedArguments: (index: Int?, name: AnalyticsEvent.Interaction.Name)?
var trackInteractionIndexNameReceivedInvocations: [(index: Int?, name: AnalyticsEvent.Interaction.Name)] = []
var trackInteractionIndexNameClosure: ((Int?, AnalyticsEvent.Interaction.Name) -> Void)?
func trackInteraction(index: Int?, name: AnalyticsEvent.Interaction.Name) {
trackInteractionIndexNameCallsCount += 1
trackInteractionIndexNameReceivedArguments = (index: index, name: name)
DispatchQueue.main.async {
self.trackInteractionIndexNameReceivedInvocations.append((index: index, name: name))
}
trackInteractionIndexNameClosure?(index, name)
}
//MARK: - trackError
var trackErrorContextDomainNameTimeToDecryptMillisEventLocalAgeMillisIsFederatedIsMatrixDotOrgUserTrustsOwnIdentityWasVisibleToUserUnderlyingCallsCount = 0
var trackErrorContextDomainNameTimeToDecryptMillisEventLocalAgeMillisIsFederatedIsMatrixDotOrgUserTrustsOwnIdentityWasVisibleToUserCallsCount: Int {
get {
if Thread.isMainThread {
return trackErrorContextDomainNameTimeToDecryptMillisEventLocalAgeMillisIsFederatedIsMatrixDotOrgUserTrustsOwnIdentityWasVisibleToUserUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = trackErrorContextDomainNameTimeToDecryptMillisEventLocalAgeMillisIsFederatedIsMatrixDotOrgUserTrustsOwnIdentityWasVisibleToUserUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
trackErrorContextDomainNameTimeToDecryptMillisEventLocalAgeMillisIsFederatedIsMatrixDotOrgUserTrustsOwnIdentityWasVisibleToUserUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
trackErrorContextDomainNameTimeToDecryptMillisEventLocalAgeMillisIsFederatedIsMatrixDotOrgUserTrustsOwnIdentityWasVisibleToUserUnderlyingCallsCount = newValue
}
}
}
}
var trackErrorContextDomainNameTimeToDecryptMillisEventLocalAgeMillisIsFederatedIsMatrixDotOrgUserTrustsOwnIdentityWasVisibleToUserCalled: Bool {
return trackErrorContextDomainNameTimeToDecryptMillisEventLocalAgeMillisIsFederatedIsMatrixDotOrgUserTrustsOwnIdentityWasVisibleToUserCallsCount > 0
}
var trackErrorContextDomainNameTimeToDecryptMillisEventLocalAgeMillisIsFederatedIsMatrixDotOrgUserTrustsOwnIdentityWasVisibleToUserReceivedArguments: (context: String?, domain: AnalyticsEvent.Error.Domain, name: AnalyticsEvent.Error.Name, timeToDecryptMillis: Int?, eventLocalAgeMillis: Int?, isFederated: Bool?, isMatrixDotOrg: Bool?, userTrustsOwnIdentity: Bool?, wasVisibleToUser: Bool?)?
var trackErrorContextDomainNameTimeToDecryptMillisEventLocalAgeMillisIsFederatedIsMatrixDotOrgUserTrustsOwnIdentityWasVisibleToUserReceivedInvocations: [(context: String?, domain: AnalyticsEvent.Error.Domain, name: AnalyticsEvent.Error.Name, timeToDecryptMillis: Int?, eventLocalAgeMillis: Int?, isFederated: Bool?, isMatrixDotOrg: Bool?, userTrustsOwnIdentity: Bool?, wasVisibleToUser: Bool?)] = []
var trackErrorContextDomainNameTimeToDecryptMillisEventLocalAgeMillisIsFederatedIsMatrixDotOrgUserTrustsOwnIdentityWasVisibleToUserClosure: ((String?, AnalyticsEvent.Error.Domain, AnalyticsEvent.Error.Name, Int?, Int?, Bool?, Bool?, Bool?, Bool?) -> Void)?
func trackError(context: String?, domain: AnalyticsEvent.Error.Domain, name: AnalyticsEvent.Error.Name, timeToDecryptMillis: Int?, eventLocalAgeMillis: Int?, isFederated: Bool?, isMatrixDotOrg: Bool?, userTrustsOwnIdentity: Bool?, wasVisibleToUser: Bool?) {
trackErrorContextDomainNameTimeToDecryptMillisEventLocalAgeMillisIsFederatedIsMatrixDotOrgUserTrustsOwnIdentityWasVisibleToUserCallsCount += 1
trackErrorContextDomainNameTimeToDecryptMillisEventLocalAgeMillisIsFederatedIsMatrixDotOrgUserTrustsOwnIdentityWasVisibleToUserReceivedArguments = (context: context, domain: domain, name: name, timeToDecryptMillis: timeToDecryptMillis, eventLocalAgeMillis: eventLocalAgeMillis, isFederated: isFederated, isMatrixDotOrg: isMatrixDotOrg, userTrustsOwnIdentity: userTrustsOwnIdentity, wasVisibleToUser: wasVisibleToUser)
DispatchQueue.main.async {
self.trackErrorContextDomainNameTimeToDecryptMillisEventLocalAgeMillisIsFederatedIsMatrixDotOrgUserTrustsOwnIdentityWasVisibleToUserReceivedInvocations.append((context: context, domain: domain, name: name, timeToDecryptMillis: timeToDecryptMillis, eventLocalAgeMillis: eventLocalAgeMillis, isFederated: isFederated, isMatrixDotOrg: isMatrixDotOrg, userTrustsOwnIdentity: userTrustsOwnIdentity, wasVisibleToUser: wasVisibleToUser))
}
trackErrorContextDomainNameTimeToDecryptMillisEventLocalAgeMillisIsFederatedIsMatrixDotOrgUserTrustsOwnIdentityWasVisibleToUserClosure?(context, domain, name, timeToDecryptMillis, eventLocalAgeMillis, isFederated, isMatrixDotOrg, userTrustsOwnIdentity, wasVisibleToUser)
}
//MARK: - trackCreatedRoom
var trackCreatedRoomIsDMUnderlyingCallsCount = 0
var trackCreatedRoomIsDMCallsCount: Int {
get {
if Thread.isMainThread {
return trackCreatedRoomIsDMUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = trackCreatedRoomIsDMUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
trackCreatedRoomIsDMUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
trackCreatedRoomIsDMUnderlyingCallsCount = newValue
}
}
}
}
var trackCreatedRoomIsDMCalled: Bool {
return trackCreatedRoomIsDMCallsCount > 0
}
var trackCreatedRoomIsDMReceivedIsDM: Bool?
var trackCreatedRoomIsDMReceivedInvocations: [Bool] = []
var trackCreatedRoomIsDMClosure: ((Bool) -> Void)?
func trackCreatedRoom(isDM: Bool) {
trackCreatedRoomIsDMCallsCount += 1
trackCreatedRoomIsDMReceivedIsDM = isDM
DispatchQueue.main.async {
self.trackCreatedRoomIsDMReceivedInvocations.append(isDM)
}
trackCreatedRoomIsDMClosure?(isDM)
}
//MARK: - trackComposer
var trackComposerInThreadIsEditingIsReplyMessageTypeStartsThreadUnderlyingCallsCount = 0
var trackComposerInThreadIsEditingIsReplyMessageTypeStartsThreadCallsCount: Int {
get {
if Thread.isMainThread {
return trackComposerInThreadIsEditingIsReplyMessageTypeStartsThreadUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = trackComposerInThreadIsEditingIsReplyMessageTypeStartsThreadUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
trackComposerInThreadIsEditingIsReplyMessageTypeStartsThreadUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
trackComposerInThreadIsEditingIsReplyMessageTypeStartsThreadUnderlyingCallsCount = newValue
}
}
}
}
var trackComposerInThreadIsEditingIsReplyMessageTypeStartsThreadCalled: Bool {
return trackComposerInThreadIsEditingIsReplyMessageTypeStartsThreadCallsCount > 0
}
var trackComposerInThreadIsEditingIsReplyMessageTypeStartsThreadReceivedArguments: (inThread: Bool, isEditing: Bool, isReply: Bool, messageType: AnalyticsEvent.Composer.MessageType, startsThread: Bool?)?
var trackComposerInThreadIsEditingIsReplyMessageTypeStartsThreadReceivedInvocations: [(inThread: Bool, isEditing: Bool, isReply: Bool, messageType: AnalyticsEvent.Composer.MessageType, startsThread: Bool?)] = []
var trackComposerInThreadIsEditingIsReplyMessageTypeStartsThreadClosure: ((Bool, Bool, Bool, AnalyticsEvent.Composer.MessageType, Bool?) -> Void)?
func trackComposer(inThread: Bool, isEditing: Bool, isReply: Bool, messageType: AnalyticsEvent.Composer.MessageType, startsThread: Bool?) {
trackComposerInThreadIsEditingIsReplyMessageTypeStartsThreadCallsCount += 1
trackComposerInThreadIsEditingIsReplyMessageTypeStartsThreadReceivedArguments = (inThread: inThread, isEditing: isEditing, isReply: isReply, messageType: messageType, startsThread: startsThread)
DispatchQueue.main.async {
self.trackComposerInThreadIsEditingIsReplyMessageTypeStartsThreadReceivedInvocations.append((inThread: inThread, isEditing: isEditing, isReply: isReply, messageType: messageType, startsThread: startsThread))
}
trackComposerInThreadIsEditingIsReplyMessageTypeStartsThreadClosure?(inThread, isEditing, isReply, messageType, startsThread)
}
//MARK: - trackViewRoom
var trackViewRoomIsDMIsSpaceUnderlyingCallsCount = 0
var trackViewRoomIsDMIsSpaceCallsCount: Int {
get {
if Thread.isMainThread {
return trackViewRoomIsDMIsSpaceUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = trackViewRoomIsDMIsSpaceUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
trackViewRoomIsDMIsSpaceUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
trackViewRoomIsDMIsSpaceUnderlyingCallsCount = newValue
}
}
}
}
var trackViewRoomIsDMIsSpaceCalled: Bool {
return trackViewRoomIsDMIsSpaceCallsCount > 0
}
var trackViewRoomIsDMIsSpaceReceivedArguments: (isDM: Bool, isSpace: Bool)?
var trackViewRoomIsDMIsSpaceReceivedInvocations: [(isDM: Bool, isSpace: Bool)] = []
var trackViewRoomIsDMIsSpaceClosure: ((Bool, Bool) -> Void)?
func trackViewRoom(isDM: Bool, isSpace: Bool) {
trackViewRoomIsDMIsSpaceCallsCount += 1
trackViewRoomIsDMIsSpaceReceivedArguments = (isDM: isDM, isSpace: isSpace)
DispatchQueue.main.async {
self.trackViewRoomIsDMIsSpaceReceivedInvocations.append((isDM: isDM, isSpace: isSpace))
}
trackViewRoomIsDMIsSpaceClosure?(isDM, isSpace)
}
//MARK: - trackJoinedRoom
var trackJoinedRoomIsDMIsSpaceActiveMemberCountUnderlyingCallsCount = 0
var trackJoinedRoomIsDMIsSpaceActiveMemberCountCallsCount: Int {
get {
if Thread.isMainThread {
return trackJoinedRoomIsDMIsSpaceActiveMemberCountUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = trackJoinedRoomIsDMIsSpaceActiveMemberCountUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
trackJoinedRoomIsDMIsSpaceActiveMemberCountUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
trackJoinedRoomIsDMIsSpaceActiveMemberCountUnderlyingCallsCount = newValue
}
}
}
}
var trackJoinedRoomIsDMIsSpaceActiveMemberCountCalled: Bool {
return trackJoinedRoomIsDMIsSpaceActiveMemberCountCallsCount > 0
}
var trackJoinedRoomIsDMIsSpaceActiveMemberCountReceivedArguments: (isDM: Bool, isSpace: Bool, activeMemberCount: UInt)?
var trackJoinedRoomIsDMIsSpaceActiveMemberCountReceivedInvocations: [(isDM: Bool, isSpace: Bool, activeMemberCount: UInt)] = []
var trackJoinedRoomIsDMIsSpaceActiveMemberCountClosure: ((Bool, Bool, UInt) -> Void)?
func trackJoinedRoom(isDM: Bool, isSpace: Bool, activeMemberCount: UInt) {
trackJoinedRoomIsDMIsSpaceActiveMemberCountCallsCount += 1
trackJoinedRoomIsDMIsSpaceActiveMemberCountReceivedArguments = (isDM: isDM, isSpace: isSpace, activeMemberCount: activeMemberCount)
DispatchQueue.main.async {
self.trackJoinedRoomIsDMIsSpaceActiveMemberCountReceivedInvocations.append((isDM: isDM, isSpace: isSpace, activeMemberCount: activeMemberCount))
}
trackJoinedRoomIsDMIsSpaceActiveMemberCountClosure?(isDM, isSpace, activeMemberCount)
}
//MARK: - trackPollCreated
var trackPollCreatedIsUndisclosedNumberOfAnswersUnderlyingCallsCount = 0
var trackPollCreatedIsUndisclosedNumberOfAnswersCallsCount: Int {
get {
if Thread.isMainThread {
return trackPollCreatedIsUndisclosedNumberOfAnswersUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = trackPollCreatedIsUndisclosedNumberOfAnswersUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
trackPollCreatedIsUndisclosedNumberOfAnswersUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
trackPollCreatedIsUndisclosedNumberOfAnswersUnderlyingCallsCount = newValue
}
}
}
}
var trackPollCreatedIsUndisclosedNumberOfAnswersCalled: Bool {
return trackPollCreatedIsUndisclosedNumberOfAnswersCallsCount > 0
}
var trackPollCreatedIsUndisclosedNumberOfAnswersReceivedArguments: (isUndisclosed: Bool, numberOfAnswers: Int)?
var trackPollCreatedIsUndisclosedNumberOfAnswersReceivedInvocations: [(isUndisclosed: Bool, numberOfAnswers: Int)] = []
var trackPollCreatedIsUndisclosedNumberOfAnswersClosure: ((Bool, Int) -> Void)?
func trackPollCreated(isUndisclosed: Bool, numberOfAnswers: Int) {
trackPollCreatedIsUndisclosedNumberOfAnswersCallsCount += 1
trackPollCreatedIsUndisclosedNumberOfAnswersReceivedArguments = (isUndisclosed: isUndisclosed, numberOfAnswers: numberOfAnswers)
DispatchQueue.main.async {
self.trackPollCreatedIsUndisclosedNumberOfAnswersReceivedInvocations.append((isUndisclosed: isUndisclosed, numberOfAnswers: numberOfAnswers))
}
trackPollCreatedIsUndisclosedNumberOfAnswersClosure?(isUndisclosed, numberOfAnswers)
}
//MARK: - trackPollVote
var trackPollVoteUnderlyingCallsCount = 0
var trackPollVoteCallsCount: Int {
get {
if Thread.isMainThread {
return trackPollVoteUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = trackPollVoteUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
trackPollVoteUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
trackPollVoteUnderlyingCallsCount = newValue
}
}
}
}
var trackPollVoteCalled: Bool {
return trackPollVoteCallsCount > 0
}
var trackPollVoteClosure: (() -> Void)?
func trackPollVote() {
trackPollVoteCallsCount += 1
trackPollVoteClosure?()
}
//MARK: - trackPollEnd
var trackPollEndUnderlyingCallsCount = 0
var trackPollEndCallsCount: Int {
get {
if Thread.isMainThread {
return trackPollEndUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = trackPollEndUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
trackPollEndUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
trackPollEndUnderlyingCallsCount = newValue
}
}
}
}
var trackPollEndCalled: Bool {
return trackPollEndCallsCount > 0
}
var trackPollEndClosure: (() -> Void)?
func trackPollEnd() {
trackPollEndCallsCount += 1
trackPollEndClosure?()
}
//MARK: - trackRoomModeration
var trackRoomModerationActionRoleUnderlyingCallsCount = 0
var trackRoomModerationActionRoleCallsCount: Int {
get {
if Thread.isMainThread {
return trackRoomModerationActionRoleUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = trackRoomModerationActionRoleUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
trackRoomModerationActionRoleUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
trackRoomModerationActionRoleUnderlyingCallsCount = newValue
}
}
}
}
var trackRoomModerationActionRoleCalled: Bool {
return trackRoomModerationActionRoleCallsCount > 0
}
var trackRoomModerationActionRoleReceivedArguments: (action: AnalyticsEvent.RoomModeration.Action, role: RoomRole?)?
var trackRoomModerationActionRoleReceivedInvocations: [(action: AnalyticsEvent.RoomModeration.Action, role: RoomRole?)] = []
var trackRoomModerationActionRoleClosure: ((AnalyticsEvent.RoomModeration.Action, RoomRole?) -> Void)?
func trackRoomModeration(action: AnalyticsEvent.RoomModeration.Action, role: RoomRole?) {
trackRoomModerationActionRoleCallsCount += 1
trackRoomModerationActionRoleReceivedArguments = (action: action, role: role)
DispatchQueue.main.async {
self.trackRoomModerationActionRoleReceivedInvocations.append((action: action, role: role))
}
trackRoomModerationActionRoleClosure?(action, role)
}
//MARK: - trackSessionSecurityState
var trackSessionSecurityStateUnderlyingCallsCount = 0
var trackSessionSecurityStateCallsCount: Int {
get {
if Thread.isMainThread {
return trackSessionSecurityStateUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = trackSessionSecurityStateUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
trackSessionSecurityStateUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
trackSessionSecurityStateUnderlyingCallsCount = newValue
}
}
}
}
var trackSessionSecurityStateCalled: Bool {
return trackSessionSecurityStateCallsCount > 0
}
var trackSessionSecurityStateReceivedState: SessionSecurityState?
var trackSessionSecurityStateReceivedInvocations: [SessionSecurityState] = []
var trackSessionSecurityStateClosure: ((SessionSecurityState) -> Void)?
func trackSessionSecurityState(_ state: SessionSecurityState) {
trackSessionSecurityStateCallsCount += 1
trackSessionSecurityStateReceivedState = state
DispatchQueue.main.async {
self.trackSessionSecurityStateReceivedInvocations.append(state)
}
trackSessionSecurityStateClosure?(state)
}
//MARK: - updateUserProperties
var updateUserPropertiesUnderlyingCallsCount = 0
var updateUserPropertiesCallsCount: Int {
get {
if Thread.isMainThread {
return updateUserPropertiesUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = updateUserPropertiesUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
updateUserPropertiesUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
updateUserPropertiesUnderlyingCallsCount = newValue
}
}
}
}
var updateUserPropertiesCalled: Bool {
return updateUserPropertiesCallsCount > 0
}
var updateUserPropertiesReceivedUserProperties: AnalyticsEvent.UserProperties?
var updateUserPropertiesReceivedInvocations: [AnalyticsEvent.UserProperties] = []
var updateUserPropertiesClosure: ((AnalyticsEvent.UserProperties) -> Void)?
func updateUserProperties(_ userProperties: AnalyticsEvent.UserProperties) {
updateUserPropertiesCallsCount += 1
updateUserPropertiesReceivedUserProperties = userProperties
DispatchQueue.main.async {
self.updateUserPropertiesReceivedInvocations.append(userProperties)
}
updateUserPropertiesClosure?(userProperties)
}
//MARK: - trackPinUnpinEvent
var trackPinUnpinEventUnderlyingCallsCount = 0
var trackPinUnpinEventCallsCount: Int {
get {
if Thread.isMainThread {
return trackPinUnpinEventUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = trackPinUnpinEventUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
trackPinUnpinEventUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
trackPinUnpinEventUnderlyingCallsCount = newValue
}
}
}
}
var trackPinUnpinEventCalled: Bool {
return trackPinUnpinEventCallsCount > 0
}
var trackPinUnpinEventReceivedEvent: AnalyticsEvent.PinUnpinAction?
var trackPinUnpinEventReceivedInvocations: [AnalyticsEvent.PinUnpinAction] = []
var trackPinUnpinEventClosure: ((AnalyticsEvent.PinUnpinAction) -> Void)?
func trackPinUnpinEvent(_ event: AnalyticsEvent.PinUnpinAction) {
trackPinUnpinEventCallsCount += 1
trackPinUnpinEventReceivedEvent = event
DispatchQueue.main.async {
self.trackPinUnpinEventReceivedInvocations.append(event)
}
trackPinUnpinEventClosure?(event)
}
}
class AppLockServiceMock: AppLockServiceProtocol, @unchecked Sendable { class AppLockServiceMock: AppLockServiceProtocol, @unchecked Sendable {
var isMandatory: Bool { var isMandatory: Bool {
get { return underlyingIsMandatory } get { return underlyingIsMandatory }
@@ -16,7 +16,7 @@ struct LoginScreenCoordinatorParameters {
let loginHint: String? let loginHint: String?
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let appSettings: AppSettings let appSettings: AppSettings
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
} }
enum LoginScreenCoordinatorAction { enum LoginScreenCoordinatorAction {
@@ -15,7 +15,7 @@ class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtoc
private let authenticationService: AuthenticationServiceProtocol private let authenticationService: AuthenticationServiceProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private var actionsSubject: PassthroughSubject<LoginScreenViewModelAction, Never> = .init() private var actionsSubject: PassthroughSubject<LoginScreenViewModelAction, Never> = .init()
var actions: AnyPublisher<LoginScreenViewModelAction, Never> { var actions: AnyPublisher<LoginScreenViewModelAction, Never> {
@@ -26,7 +26,7 @@ class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtoc
loginHint: String?, loginHint: String?,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
appSettings: AppSettings, appSettings: AppSettings,
analytics: AnalyticsService) { analytics: AnalyticsServiceProtocol) {
self.authenticationService = authenticationService self.authenticationService = authenticationService
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.appSettings = appSettings self.appSettings = appSettings
@@ -158,14 +158,11 @@ struct LoginScreen_Previews: PreviewProvider, TestablePreview {
Task { await authenticationService.configure(for: homeserverAddress, flow: .login) } Task { await authenticationService.configure(for: homeserverAddress, flow: .login) }
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
let viewModel = LoginScreenViewModel(authenticationService: authenticationService, let viewModel = LoginScreenViewModel(authenticationService: authenticationService,
loginHint: nil, loginHint: nil,
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: appSettings, appSettings: AppSettings(),
analytics: analytics) analytics: AnalyticsServiceMock.default())
if withCredentials { if withCredentials {
viewModel.context.username = "alice" viewModel.context.username = "alice"
@@ -16,7 +16,7 @@ struct CallScreenCoordinatorParameters {
let allowPictureInPicture: Bool let allowPictureInPicture: Bool
let appSettings: AppSettings let appSettings: AppSettings
let appHooks: AppHooks let appHooks: AppHooks
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
} }
enum CallScreenCoordinatorAction { enum CallScreenCoordinatorAction {
@@ -18,7 +18,7 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
private let configuration: ElementCallConfiguration private let configuration: ElementCallConfiguration
private let isPictureInPictureAllowed: Bool private let isPictureInPictureAllowed: Bool
private let appSettings: AppSettings private let appSettings: AppSettings
private let analyticsService: AnalyticsService private let analyticsService: AnalyticsServiceProtocol
private let widgetDriver: ElementCallWidgetDriverProtocol private let widgetDriver: ElementCallWidgetDriverProtocol
@@ -41,7 +41,7 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
allowPictureInPicture: Bool, allowPictureInPicture: Bool,
appHooks: AppHooks, appHooks: AppHooks,
appSettings: AppSettings, appSettings: AppSettings,
analyticsService: AnalyticsService) { analyticsService: AnalyticsServiceProtocol) {
self.elementCallService = elementCallService self.elementCallService = elementCallService
self.configuration = configuration self.configuration = configuration
self.appSettings = appSettings self.appSettings = appSettings
@@ -364,9 +364,6 @@ struct CallScreen_Previews: PreviewProvider {
roomProxy.elementCallWidgetDriverDeviceIDReturnValue = widgetDriver roomProxy.elementCallWidgetDriverDeviceIDReturnValue = widgetDriver
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return CallScreenViewModel(elementCallService: ElementCallServiceMock(.init()), return CallScreenViewModel(elementCallService: ElementCallServiceMock(.init()),
configuration: .init(roomProxy: roomProxy, configuration: .init(roomProxy: roomProxy,
clientProxy: clientProxy, clientProxy: clientProxy,
@@ -377,8 +374,8 @@ struct CallScreen_Previews: PreviewProvider {
colorScheme: .light), colorScheme: .light),
allowPictureInPicture: false, allowPictureInPicture: false,
appHooks: AppHooks(), appHooks: AppHooks(),
appSettings: appSettings, appSettings: AppSettings(),
analyticsService: analytics) analyticsService: AnalyticsServiceMock.default())
}() }()
static var previews: some View { static var previews: some View {
@@ -14,7 +14,7 @@ struct PollFormScreenCoordinatorParameters {
/// The max number of allowed options, if no value provided the default value of the view model will be used. /// The max number of allowed options, if no value provided the default value of the view model will be used.
var maxNumberOfOptions: Int? var maxNumberOfOptions: Int?
let timelineController: TimelineControllerProtocol let timelineController: TimelineControllerProtocol
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
} }
@@ -13,7 +13,7 @@ typealias PollFormScreenViewModelType = StateStoreViewModelV2<PollFormScreenView
class PollFormScreenViewModel: PollFormScreenViewModelType, PollFormScreenViewModelProtocol { class PollFormScreenViewModel: PollFormScreenViewModelType, PollFormScreenViewModelProtocol {
private let timelineController: TimelineControllerProtocol private let timelineController: TimelineControllerProtocol
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private var actionsSubject: PassthroughSubject<PollFormScreenViewModelAction, Never> = .init() private var actionsSubject: PassthroughSubject<PollFormScreenViewModelAction, Never> = .init()
@@ -24,7 +24,7 @@ class PollFormScreenViewModel: PollFormScreenViewModelType, PollFormScreenViewMo
init(mode: PollFormMode, init(mode: PollFormMode,
maxNumberOfOptions: Int? = nil, maxNumberOfOptions: Int? = nil,
timelineController: TimelineControllerProtocol, timelineController: TimelineControllerProtocol,
analytics: AnalyticsService, analytics: AnalyticsServiceProtocol,
userIndicatorController: UserIndicatorControllerProtocol) { userIndicatorController: UserIndicatorControllerProtocol) {
self.timelineController = timelineController self.timelineController = timelineController
self.analytics = analytics self.analytics = analytics
@@ -227,7 +227,7 @@ struct PollFormScreen_Previews: PreviewProvider, TestablePreview {
static func makeViewModel(mode: PollFormMode) -> PollFormScreenViewModel { static func makeViewModel(mode: PollFormMode) -> PollFormScreenViewModel {
PollFormScreenViewModel(mode: mode, PollFormScreenViewModel(mode: mode,
timelineController: MockTimelineController(), timelineController: MockTimelineController(),
analytics: .mock(), analytics: AnalyticsServiceMock.default(),
userIndicatorController: UserIndicatorControllerMock.default) userIndicatorController: UserIndicatorControllerMock.default)
} }
} }
@@ -16,7 +16,7 @@ struct CreateRoomScreenCoordinatorParameters {
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let appSettings: AppSettings let appSettings: AppSettings
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
} }
enum CreateRoomScreenCoordinatorAction { enum CreateRoomScreenCoordinatorAction {
@@ -15,7 +15,7 @@ typealias CreateRoomScreenViewModelType = StateStoreViewModel<CreateRoomScreenVi
class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreenViewModelProtocol { class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreenViewModelProtocol {
private let userSession: UserSessionProtocol private let userSession: UserSessionProtocol
private let mediaUploadingPreprocessor: MediaUploadingPreprocessor private let mediaUploadingPreprocessor: MediaUploadingPreprocessor
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private var syncNameAndAlias = true private var syncNameAndAlias = true
@@ -31,7 +31,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
spaceSelectionMode: CreateRoomScreenSpaceSelectionMode, spaceSelectionMode: CreateRoomScreenSpaceSelectionMode,
shouldShowCancelButton: Bool, shouldShowCancelButton: Bool,
userSession: UserSessionProtocol, userSession: UserSessionProtocol,
analytics: AnalyticsService, analytics: AnalyticsServiceProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
appSettings: AppSettings) { appSettings: AppSettings) {
self.userSession = userSession self.userSession = userSession
@@ -461,7 +461,7 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
spaceSelectionMode: selectionMode, spaceSelectionMode: selectionMode,
shouldShowCancelButton: isSpace, shouldShowCancelButton: isSpace,
userSession: userSession, userSession: userSession,
analytics: .mock(settings: appSettings), analytics: AnalyticsServiceMock.default(),
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
appSettings: appSettings) appSettings: appSettings)
} }
@@ -68,13 +68,11 @@ struct CreateRoomSpaceSelectionSheet_Previews: PreviewProvider, TestablePreview
let userSession = UserSessionMock(.init(clientProxy: clientProxy)) let userSession = UserSessionMock(.init(clientProxy: clientProxy))
let appSettings = AppSettings() let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return CreateRoomScreenViewModel(isSpace: false, return CreateRoomScreenViewModel(isSpace: false,
spaceSelectionMode: .editableSpacesList(preSelectedSpace: nil), spaceSelectionMode: .editableSpacesList(preSelectedSpace: nil),
shouldShowCancelButton: false, shouldShowCancelButton: false,
userSession: userSession, userSession: userSession,
analytics: analytics, analytics: AnalyticsServiceMock.default(),
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
appSettings: appSettings) appSettings: appSettings)
}() }()
@@ -14,7 +14,7 @@ struct HomeScreenCoordinatorParameters {
let bugReportService: BugReportServiceProtocol let bugReportService: BugReportServiceProtocol
let selectedRoomPublisher: CurrentValuePublisher<String?, Never> let selectedRoomPublisher: CurrentValuePublisher<String?, Never>
let appSettings: AppSettings let appSettings: AppSettings
let analyticsService: AnalyticsService let analyticsService: AnalyticsServiceProtocol
let notificationManager: NotificationManagerProtocol let notificationManager: NotificationManagerProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
} }
@@ -16,7 +16,7 @@ typealias HomeScreenViewModelType = StateStoreViewModel<HomeScreenViewState, Hom
class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol { class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol {
private let userSession: UserSessionProtocol private let userSession: UserSessionProtocol
private let spaceFilterSubject: CurrentValueSubject<SpaceServiceFilter?, Never> private let spaceFilterSubject: CurrentValueSubject<SpaceServiceFilter?, Never>
private let analyticsService: AnalyticsService private let analyticsService: AnalyticsServiceProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private let notificationManager: NotificationManagerProtocol private let notificationManager: NotificationManagerProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
@@ -32,7 +32,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
init(userSession: UserSessionProtocol, init(userSession: UserSessionProtocol,
selectedRoomPublisher: CurrentValuePublisher<String?, Never>, selectedRoomPublisher: CurrentValuePublisher<String?, Never>,
appSettings: AppSettings, appSettings: AppSettings,
analyticsService: AnalyticsService, analyticsService: AnalyticsServiceProtocol,
notificationManager: NotificationManagerProtocol, notificationManager: NotificationManagerProtocol,
userIndicatorController: UserIndicatorControllerProtocol) { userIndicatorController: UserIndicatorControllerProtocol) {
self.userSession = userSession self.userSession = userSession
@@ -222,13 +222,10 @@ struct HomeScreen_Previews: PreviewProvider, TestablePreview {
let userSession = UserSessionMock(.init(clientProxy: clientProxy)) let userSession = UserSessionMock(.init(clientProxy: clientProxy))
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
appSettings: appSettings, appSettings: AppSettings(),
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
notificationManager: NotificationManagerMock(), notificationManager: NotificationManagerMock(),
userIndicatorController: UserIndicatorControllerMock.default) userIndicatorController: UserIndicatorControllerMock.default)
} }
@@ -143,13 +143,10 @@ struct HomeScreenEmptyStateView_Previews: PreviewProvider, TestablePreview {
static let viewModel = { static let viewModel = {
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@user:example.com", let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@user:example.com",
roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded([]))))))) roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded([])))))))
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
appSettings: appSettings, appSettings: AppSettings(),
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
notificationManager: NotificationManagerMock(), notificationManager: NotificationManagerMock(),
userIndicatorController: UserIndicatorControllerMock.default) userIndicatorController: UserIndicatorControllerMock.default)
}() }()
@@ -200,13 +200,10 @@ struct HomeScreenInviteCell_Previews: PreviewProvider, TestablePreview {
let userSession = UserSessionMock(.init(clientProxy: clientProxy)) let userSession = UserSessionMock(.init(clientProxy: clientProxy))
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
appSettings: appSettings, appSettings: AppSettings(),
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
notificationManager: NotificationManagerMock(), notificationManager: NotificationManagerMock(),
userIndicatorController: UserIndicatorControllerMock.default) userIndicatorController: UserIndicatorControllerMock.default)
} }
@@ -132,13 +132,10 @@ struct HomeScreenKnockedCell_Previews: PreviewProvider, TestablePreview {
let userSession = UserSessionMock(.init(clientProxy: clientProxy)) let userSession = UserSessionMock(.init(clientProxy: clientProxy))
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
appSettings: appSettings, appSettings: AppSettings(),
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
notificationManager: NotificationManagerMock(), notificationManager: NotificationManagerMock(),
userIndicatorController: UserIndicatorControllerMock.default) userIndicatorController: UserIndicatorControllerMock.default)
} }
@@ -121,13 +121,10 @@ struct HomeScreenRecoveryKeyConfirmationBanner_Previews: PreviewProvider, Testab
let userSession = UserSessionMock(.init(clientProxy: clientProxy)) let userSession = UserSessionMock(.init(clientProxy: clientProxy))
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
appSettings: appSettings, appSettings: AppSettings(),
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
notificationManager: NotificationManagerMock(), notificationManager: NotificationManagerMock(),
userIndicatorController: UserIndicatorControllerMock.default) userIndicatorController: UserIndicatorControllerMock.default)
} }
@@ -253,13 +253,10 @@ struct HomeScreenRoomCell_Previews: PreviewProvider, TestablePreview {
static func makeViewModel(roomSummaryProvider: RoomSummaryProviderProtocol) -> HomeScreenViewModel { static func makeViewModel(roomSummaryProvider: RoomSummaryProviderProtocol) -> HomeScreenViewModel {
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "John Doe", roomSummaryProvider: roomSummaryProvider)))) let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "John Doe", roomSummaryProvider: roomSummaryProvider))))
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
appSettings: appSettings, appSettings: AppSettings(),
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
notificationManager: NotificationManagerMock(), notificationManager: NotificationManagerMock(),
userIndicatorController: UserIndicatorControllerMock.default) userIndicatorController: UserIndicatorControllerMock.default)
} }
@@ -16,7 +16,7 @@ struct LocationSharingScreenCoordinatorParameters {
let timelineController: TimelineControllerProtocol let timelineController: TimelineControllerProtocol
let liveLocationManager: LiveLocationManagerProtocol let liveLocationManager: LiveLocationManagerProtocol
let appMediator: AppMediatorProtocol let appMediator: AppMediatorProtocol
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let mediaProvider: MediaProviderProtocol let mediaProvider: MediaProviderProtocol
} }
@@ -17,7 +17,7 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let timelineController: TimelineControllerProtocol private let timelineController: TimelineControllerProtocol
private let liveLocationManager: LiveLocationManagerProtocol private let liveLocationManager: LiveLocationManagerProtocol
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let notificationCenter: NotificationCenter private let notificationCenter: NotificationCenter
@@ -36,7 +36,7 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati
roomProxy: JoinedRoomProxyProtocol, roomProxy: JoinedRoomProxyProtocol,
timelineController: TimelineControllerProtocol, timelineController: TimelineControllerProtocol,
liveLocationManager: LiveLocationManagerProtocol, liveLocationManager: LiveLocationManagerProtocol,
analytics: AnalyticsService, analytics: AnalyticsServiceProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
mediaProvider: MediaProviderProtocol, mediaProvider: MediaProviderProtocol,
notificationCenter: NotificationCenter = .default) { notificationCenter: NotificationCenter = .default) {
@@ -389,15 +389,12 @@ extension LocationSharingScreenViewModel {
let roomProxy = JoinedRoomProxyMock(.init(members: .allMembers, ownUserID: RoomMemberProxyMock.mockMe.userID)) let roomProxy = JoinedRoomProxyMock(.init(members: .allMembers, ownUserID: RoomMemberProxyMock.mockMe.userID))
roomProxy.makeLiveLocationServiceReturnValue = liveLocationServiceMock roomProxy.makeLiveLocationServiceReturnValue = liveLocationServiceMock
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return LocationSharingScreenViewModel(interactionMode: interactionMode, return LocationSharingScreenViewModel(interactionMode: interactionMode,
mapURLBuilder: appSettings.mapTilerConfiguration, mapURLBuilder: AppSettings().mapTilerConfiguration,
roomProxy: roomProxy, roomProxy: roomProxy,
timelineController: MockTimelineController(), timelineController: MockTimelineController(),
liveLocationManager: LiveLocationManagerMock(), liveLocationManager: LiveLocationManagerMock(),
analytics: analytics, analytics: AnalyticsServiceMock.default(),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
mediaProvider: MediaProviderMock(configuration: .init())) mediaProvider: MediaProviderMock(configuration: .init()))
} }
@@ -15,7 +15,7 @@ typealias ManageRoomMemberSheetViewModelType = StateStoreViewModelV2<ManageRoomM
class ManageRoomMemberSheetViewModel: ManageRoomMemberSheetViewModelType, ManageRoomMemberSheetViewModelProtocol { class ManageRoomMemberSheetViewModel: ManageRoomMemberSheetViewModelType, ManageRoomMemberSheetViewModelProtocol {
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let analyticsService: AnalyticsService private let analyticsService: AnalyticsServiceProtocol
private var actionsSubject: PassthroughSubject<ManageRoomMemberSheetViewModelAction, Never> = .init() private var actionsSubject: PassthroughSubject<ManageRoomMemberSheetViewModelAction, Never> = .init()
@@ -27,7 +27,7 @@ class ManageRoomMemberSheetViewModel: ManageRoomMemberSheetViewModelType, Manage
permissions: ManageRoomMemberPermissions, permissions: ManageRoomMemberPermissions,
roomProxy: JoinedRoomProxyProtocol, roomProxy: JoinedRoomProxyProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
analyticsService: AnalyticsService, analyticsService: AnalyticsServiceProtocol,
mediaProvider: MediaProviderProtocol) { mediaProvider: MediaProviderProtocol) {
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.roomProxy = roomProxy self.roomProxy = roomProxy
@@ -120,7 +120,7 @@ private extension ManageRoomMemberSheetViewModel {
ownPowerLevel: powerLevel), ownPowerLevel: powerLevel),
roomProxy: JoinedRoomProxyMock(.init()), roomProxy: JoinedRoomProxyMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
analyticsService: .mock(), analyticsService: AnalyticsServiceMock.default(),
mediaProvider: MediaProviderMock(configuration: .init())) mediaProvider: MediaProviderMock(configuration: .init()))
} }
} }
@@ -17,7 +17,7 @@ struct MediaEventsTimelineScreenCoordinatorParameters {
let mediaPlayerProvider: MediaPlayerProviderProtocol let mediaPlayerProvider: MediaPlayerProviderProtocol
let appMediator: AppMediatorProtocol let appMediator: AppMediatorProtocol
let appSettings: AppSettings let appSettings: AppSettings
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
let emojiProvider: EmojiProviderProtocol let emojiProvider: EmojiProviderProtocol
let linkMetadataProvider: LinkMetadataProviderProtocol let linkMetadataProvider: LinkMetadataProviderProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
@@ -311,8 +311,6 @@ struct MediaEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
} }
let appSettings = AppSettings() let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")), return TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")),
timelineController: timelineController, timelineController: timelineController,
userSession: UserSessionMock(.init()), userSession: UserSessionMock(.init()),
@@ -320,7 +318,7 @@ struct MediaEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: appSettings, appSettings: appSettings,
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -14,7 +14,7 @@ enum AnalyticsPromptScreenCoordinatorAction {
} }
final class AnalyticsPromptScreenCoordinator: CoordinatorProtocol { final class AnalyticsPromptScreenCoordinator: CoordinatorProtocol {
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private var viewModel: AnalyticsPromptScreenViewModelProtocol private var viewModel: AnalyticsPromptScreenViewModelProtocol
private let actionsSubject: PassthroughSubject<AnalyticsPromptScreenCoordinatorAction, Never> = .init() private let actionsSubject: PassthroughSubject<AnalyticsPromptScreenCoordinatorAction, Never> = .init()
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
@@ -23,7 +23,7 @@ final class AnalyticsPromptScreenCoordinator: CoordinatorProtocol {
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(analytics: AnalyticsService, termsURL: URL?) { init(analytics: AnalyticsServiceProtocol, termsURL: URL?) {
self.analytics = analytics self.analytics = analytics
viewModel = AnalyticsPromptScreenViewModel(termsURL: termsURL) viewModel = AnalyticsPromptScreenViewModel(termsURL: termsURL)
} }
@@ -16,7 +16,7 @@ struct PinnedEventsTimelineScreenCoordinatorParameters {
let mediaPlayerProvider: MediaPlayerProviderProtocol let mediaPlayerProvider: MediaPlayerProviderProtocol
let appMediator: AppMediatorProtocol let appMediator: AppMediatorProtocol
let appSettings: AppSettings let appSettings: AppSettings
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
let emojiProvider: EmojiProviderProtocol let emojiProvider: EmojiProviderProtocol
let linkMetadataProvider: LinkMetadataProviderProtocol let linkMetadataProvider: LinkMetadataProviderProtocol
let timelineControllerFactory: TimelineControllerFactoryProtocol let timelineControllerFactory: TimelineControllerFactoryProtocol
@@ -15,7 +15,7 @@ class PinnedEventsTimelineScreenViewModel: PinnedEventsTimelineScreenViewModelTy
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private let analyticsService: AnalyticsService private let analyticsService: AnalyticsServiceProtocol
private let actionsSubject: PassthroughSubject<PinnedEventsTimelineScreenViewModelAction, Never> = .init() private let actionsSubject: PassthroughSubject<PinnedEventsTimelineScreenViewModelAction, Never> = .init()
var actionsPublisher: AnyPublisher<PinnedEventsTimelineScreenViewModelAction, Never> { var actionsPublisher: AnyPublisher<PinnedEventsTimelineScreenViewModelAction, Never> {
@@ -25,7 +25,7 @@ class PinnedEventsTimelineScreenViewModel: PinnedEventsTimelineScreenViewModelTy
init(roomProxy: JoinedRoomProxyProtocol, init(roomProxy: JoinedRoomProxyProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
appSettings: AppSettings, appSettings: AppSettings,
analyticsService: AnalyticsService) { analyticsService: AnalyticsServiceProtocol) {
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.appSettings = appSettings self.appSettings = appSettings
@@ -69,15 +69,13 @@ struct PinnedEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = PinnedEventsTimelineScreenViewModel(roomProxy: JoinedRoomProxyMock(.init()), static let viewModel = PinnedEventsTimelineScreenViewModel(roomProxy: JoinedRoomProxyMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: AppSettings(), appSettings: AppSettings(),
analyticsService: .mock()) analyticsService: AnalyticsServiceMock.default())
static let emptyTimelineViewModel: TimelineViewModel = { static let emptyTimelineViewModel: TimelineViewModel = {
let timelineController = MockTimelineController(timelineKind: .pinned) let timelineController = MockTimelineController(timelineKind: .pinned)
timelineController.timelineItems = [] timelineController.timelineItems = []
let appSettings = AppSettings() let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")), return TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")),
timelineController: timelineController, timelineController: timelineController,
userSession: UserSessionMock(.init()), userSession: UserSessionMock(.init()),
@@ -85,7 +83,7 @@ struct PinnedEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: appSettings, appSettings: appSettings,
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -14,7 +14,7 @@ struct RoomChangePermissionsScreenCoordinatorParameters {
let permissions: RoomPermissions let permissions: RoomPermissions
let roomProxy: JoinedRoomProxyProtocol let roomProxy: JoinedRoomProxyProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
} }
enum RoomChangePermissionsScreenCoordinatorAction { enum RoomChangePermissionsScreenCoordinatorAction {
@@ -15,7 +15,7 @@ typealias RoomChangePermissionsScreenViewModelType = StateStoreViewModelV2<RoomC
class RoomChangePermissionsScreenViewModel: RoomChangePermissionsScreenViewModelType, RoomChangePermissionsScreenViewModelProtocol { class RoomChangePermissionsScreenViewModel: RoomChangePermissionsScreenViewModelType, RoomChangePermissionsScreenViewModelProtocol {
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private var actionsSubject: PassthroughSubject<RoomChangePermissionsScreenViewModelAction, Never> = .init() private var actionsSubject: PassthroughSubject<RoomChangePermissionsScreenViewModelAction, Never> = .init()
var actionsPublisher: AnyPublisher<RoomChangePermissionsScreenViewModelAction, Never> { var actionsPublisher: AnyPublisher<RoomChangePermissionsScreenViewModelAction, Never> {
@@ -26,7 +26,7 @@ class RoomChangePermissionsScreenViewModel: RoomChangePermissionsScreenViewModel
ownPowerLevel: RoomPowerLevel, ownPowerLevel: RoomPowerLevel,
roomProxy: JoinedRoomProxyProtocol, roomProxy: JoinedRoomProxyProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
analytics: AnalyticsService) { analytics: AnalyticsServiceProtocol) {
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.analytics = analytics self.analytics = analytics
@@ -91,6 +91,6 @@ struct RoomChangePermissionsScreen_Previews: PreviewProvider, TestablePreview {
ownPowerLevel: ownPowerLevel, ownPowerLevel: ownPowerLevel,
roomProxy: JoinedRoomProxyMock(.init(isSpace: isSpace)), roomProxy: JoinedRoomProxyMock(.init(isSpace: isSpace)),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
analytics: .mock()) analytics: AnalyticsServiceMock.default())
} }
} }
@@ -14,7 +14,7 @@ struct RoomChangeRolesScreenCoordinatorParameters {
let roomProxy: JoinedRoomProxyProtocol let roomProxy: JoinedRoomProxyProtocol
let mediaProvider: MediaProviderProtocol let mediaProvider: MediaProviderProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
} }
enum RoomChangeRolesScreenCoordinatorAction { enum RoomChangeRolesScreenCoordinatorAction {
@@ -14,7 +14,7 @@ typealias RoomChangeRolesScreenViewModelType = StateStoreViewModelV2<RoomChangeR
class RoomChangeRolesScreenViewModel: RoomChangeRolesScreenViewModelType, RoomChangeRolesScreenViewModelProtocol { class RoomChangeRolesScreenViewModel: RoomChangeRolesScreenViewModelType, RoomChangeRolesScreenViewModelProtocol {
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private let actionsSubject: PassthroughSubject<RoomChangeRolesScreenViewModelAction, Never> = .init() private let actionsSubject: PassthroughSubject<RoomChangeRolesScreenViewModelAction, Never> = .init()
var actionsPublisher: AnyPublisher<RoomChangeRolesScreenViewModelAction, Never> { var actionsPublisher: AnyPublisher<RoomChangeRolesScreenViewModelAction, Never> {
@@ -25,7 +25,7 @@ class RoomChangeRolesScreenViewModel: RoomChangeRolesScreenViewModelType, RoomCh
roomProxy: JoinedRoomProxyProtocol, roomProxy: JoinedRoomProxyProtocol,
mediaProvider: MediaProviderProtocol, mediaProvider: MediaProviderProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
analytics: AnalyticsService) { analytics: AnalyticsServiceProtocol) {
guard mode != .user || mode != .creator else { fatalError("Invalid screen configuration: \(mode)") } guard mode != .user || mode != .creator else { fatalError("Invalid screen configuration: \(mode)") }
self.roomProxy = roomProxy self.roomProxy = roomProxy
@@ -151,6 +151,6 @@ struct RoomChangeRolesScreen_Previews: PreviewProvider, TestablePreview {
roomProxy: JoinedRoomProxyMock(.init(members: members)), roomProxy: JoinedRoomProxyMock(.init(members: members)),
mediaProvider: MediaProviderMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
analytics: .mock()) analytics: AnalyticsServiceMock.default())
} }
} }
@@ -12,7 +12,7 @@ import SwiftUI
struct RoomDetailsScreenCoordinatorParameters { struct RoomDetailsScreenCoordinatorParameters {
let roomProxy: JoinedRoomProxyProtocol let roomProxy: JoinedRoomProxyProtocol
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let analyticsService: AnalyticsService let analyticsService: AnalyticsServiceProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let notificationSettings: NotificationSettingsProxyProtocol let notificationSettings: NotificationSettingsProxyProtocol
let attributedStringBuilder: AttributedStringBuilderProtocol let attributedStringBuilder: AttributedStringBuilderProtocol
@@ -14,7 +14,7 @@ typealias RoomDetailsScreenViewModelType = StateStoreViewModelV2<RoomDetailsScre
class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScreenViewModelProtocol { class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScreenViewModelProtocol {
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let userSession: UserSessionProtocol private let userSession: UserSessionProtocol
private let analyticsService: AnalyticsService private let analyticsService: AnalyticsServiceProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let notificationSettingsProxy: NotificationSettingsProxyProtocol private let notificationSettingsProxy: NotificationSettingsProxyProtocol
private let attributedStringBuilder: AttributedStringBuilderProtocol private let attributedStringBuilder: AttributedStringBuilderProtocol
@@ -46,7 +46,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
init(roomProxy: JoinedRoomProxyProtocol, init(roomProxy: JoinedRoomProxyProtocol,
userSession: UserSessionProtocol, userSession: UserSessionProtocol,
analyticsService: AnalyticsService, analyticsService: AnalyticsServiceProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
notificationSettingsProxy: NotificationSettingsProxyProtocol, notificationSettingsProxy: NotificationSettingsProxyProtocol,
attributedStringBuilder: AttributedStringBuilderProtocol, attributedStringBuilder: AttributedStringBuilderProtocol,
@@ -418,16 +418,13 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
let notificationSettingsProxy = NotificationSettingsProxyMock(with: notificationSettingsProxyMockConfiguration) let notificationSettingsProxy = NotificationSettingsProxyMock(with: notificationSettingsProxyMockConfiguration)
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return .init(roomProxy: roomProxy, return .init(roomProxy: roomProxy,
userSession: UserSessionMock(.init()), userSession: UserSessionMock(.init()),
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
notificationSettingsProxy: notificationSettingsProxy, notificationSettingsProxy: notificationSettingsProxy,
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
appSettings: appSettings) appSettings: AppSettings())
} }
private static func makeSimpleRoomViewModel() -> RoomDetailsScreenViewModel { private static func makeSimpleRoomViewModel() -> RoomDetailsScreenViewModel {
@@ -449,16 +446,13 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init()) let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return .init(roomProxy: roomProxy, return .init(roomProxy: roomProxy,
userSession: UserSessionMock(.init()), userSession: UserSessionMock(.init()),
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
notificationSettingsProxy: notificationSettingsProxy, notificationSettingsProxy: notificationSettingsProxy,
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
appSettings: appSettings) appSettings: AppSettings())
} }
private static func makeDMViewModel(verificationState: UserIdentityVerificationState) -> RoomDetailsScreenViewModel { private static func makeDMViewModel(verificationState: UserIdentityVerificationState) -> RoomDetailsScreenViewModel {
@@ -490,15 +484,12 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init()) let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return .init(roomProxy: roomProxy, return .init(roomProxy: roomProxy,
userSession: UserSessionMock(.init(clientProxy: clientProxyMock)), userSession: UserSessionMock(.init(clientProxy: clientProxyMock)),
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
notificationSettingsProxy: notificationSettingsProxy, notificationSettingsProxy: notificationSettingsProxy,
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
appSettings: appSettings) appSettings: AppSettings())
} }
} }
@@ -14,7 +14,7 @@ struct RoomMemberDetailsScreenCoordinatorParameters {
let roomProxy: JoinedRoomProxyProtocol let roomProxy: JoinedRoomProxyProtocol
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
let appSettings: AppSettings let appSettings: AppSettings
} }
@@ -15,7 +15,7 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let userSession: UserSessionProtocol private let userSession: UserSessionProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private var actionsSubject: PassthroughSubject<RoomMemberDetailsScreenViewModelAction, Never> = .init() private var actionsSubject: PassthroughSubject<RoomMemberDetailsScreenViewModelAction, Never> = .init()
@@ -30,7 +30,7 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
roomProxy: JoinedRoomProxyProtocol, roomProxy: JoinedRoomProxyProtocol,
userSession: UserSessionProtocol, userSession: UserSessionProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
analytics: AnalyticsService, analytics: AnalyticsServiceProtocol,
appSettings: AppSettings) { appSettings: AppSettings) {
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.userSession = userSession self.userSession = userSession
@@ -236,14 +236,11 @@ struct RoomMemberDetailsScreen_Previews: PreviewProvider, TestablePreview {
clientProxyMock.directRoomForUserIDReturnValue = .success("roomID") clientProxyMock.directRoomForUserIDReturnValue = .success("roomID")
} }
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return RoomMemberDetailsScreenViewModel(userID: member.userID, return RoomMemberDetailsScreenViewModel(userID: member.userID,
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
userSession: UserSessionMock(.init(clientProxy: clientProxyMock)), userSession: UserSessionMock(.init(clientProxy: clientProxyMock)),
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
analytics: analytics, analytics: AnalyticsServiceMock.default(),
appSettings: appSettings) appSettings: AppSettings())
} }
} }
@@ -13,7 +13,7 @@ struct RoomMembersListScreenCoordinatorParameters {
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let roomProxy: JoinedRoomProxyProtocol let roomProxy: JoinedRoomProxyProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
} }
enum RoomMembersListScreenCoordinatorAction { enum RoomMembersListScreenCoordinatorAction {
@@ -15,7 +15,7 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
private let userSession: UserSessionProtocol private let userSession: UserSessionProtocol
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private var members: [RoomMemberProxyProtocol] = [] private var members: [RoomMemberProxyProtocol] = []
private var currentUserProxy: RoomMemberProxyProtocol? private var currentUserProxy: RoomMemberProxyProtocol?
@@ -30,7 +30,7 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
userSession: UserSessionProtocol, userSession: UserSessionProtocol,
roomProxy: JoinedRoomProxyProtocol, roomProxy: JoinedRoomProxyProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
analytics: AnalyticsService) { analytics: AnalyticsServiceProtocol) {
self.userSession = userSession self.userSession = userSession
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
@@ -262,6 +262,6 @@ struct RoomMembersListScreen_Previews: PreviewProvider, TestablePreview {
ownUserID: ownUserID, ownUserID: ownUserID,
powerLevelsConfiguration: .init(canUserInvite: false))), powerLevelsConfiguration: .init(canUserInvite: false))),
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
analytics: .mock()) analytics: AnalyticsServiceMock.default())
} }
} }
@@ -132,7 +132,7 @@ struct RoomMembersListMemberCell_Previews: PreviewProvider, TestablePreview {
static let viewModel = RoomMembersListScreenViewModel(userSession: UserSessionMock(.init()), static let viewModel = RoomMembersListScreenViewModel(userSession: UserSessionMock(.init()),
roomProxy: JoinedRoomProxyMock(.init(name: "Some room", members: [])), roomProxy: JoinedRoomProxyMock(.init(name: "Some room", members: [])),
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
analytics: .mock()) analytics: AnalyticsServiceMock.default())
static var previews: some View { static var previews: some View {
VStack(spacing: 0) { VStack(spacing: 0) {
Section("Invited/Joined") { Section("Invited/Joined") {
@@ -12,7 +12,7 @@ import SwiftUI
struct RoomRolesAndPermissionsScreenCoordinatorParameters { struct RoomRolesAndPermissionsScreenCoordinatorParameters {
let roomProxy: JoinedRoomProxyProtocol let roomProxy: JoinedRoomProxyProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
} }
enum RoomRolesAndPermissionsScreenCoordinatorAction { enum RoomRolesAndPermissionsScreenCoordinatorAction {
@@ -14,7 +14,7 @@ typealias RoomRolesAndPermissionsScreenViewModelType = StateStoreViewModelV2<Roo
class RoomRolesAndPermissionsScreenViewModel: RoomRolesAndPermissionsScreenViewModelType, RoomRolesAndPermissionsScreenViewModelProtocol { class RoomRolesAndPermissionsScreenViewModel: RoomRolesAndPermissionsScreenViewModelType, RoomRolesAndPermissionsScreenViewModelProtocol {
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private var ownUser: RoomMemberDetails? private var ownUser: RoomMemberDetails?
private var actionsSubject: PassthroughSubject<RoomRolesAndPermissionsScreenViewModelAction, Never> = .init() private var actionsSubject: PassthroughSubject<RoomRolesAndPermissionsScreenViewModelAction, Never> = .init()
@@ -22,7 +22,7 @@ class RoomRolesAndPermissionsScreenViewModel: RoomRolesAndPermissionsScreenViewM
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(initialPermissions: RoomPermissions? = nil, roomProxy: JoinedRoomProxyProtocol, userIndicatorController: UserIndicatorControllerProtocol, analytics: AnalyticsService) { init(initialPermissions: RoomPermissions? = nil, roomProxy: JoinedRoomProxyProtocol, userIndicatorController: UserIndicatorControllerProtocol, analytics: AnalyticsServiceProtocol) {
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.analytics = analytics self.analytics = analytics
@@ -121,12 +121,12 @@ struct RoomRolesAndPermissionsScreen_Previews: PreviewProvider, TestablePreview
static let viewModel = RoomRolesAndPermissionsScreenViewModel(initialPermissions: RoomPermissions(powerLevels: .mock), static let viewModel = RoomRolesAndPermissionsScreenViewModel(initialPermissions: RoomPermissions(powerLevels: .mock),
roomProxy: JoinedRoomProxyMock(.init(members: .allMembersAsAdmin)), roomProxy: JoinedRoomProxyMock(.init(members: .allMembersAsAdmin)),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
analytics: .mock()) analytics: AnalyticsServiceMock.default())
static let creatorViewModel = RoomRolesAndPermissionsScreenViewModel(initialPermissions: RoomPermissions(powerLevels: .mock), static let creatorViewModel = RoomRolesAndPermissionsScreenViewModel(initialPermissions: RoomPermissions(powerLevels: .mock),
roomProxy: JoinedRoomProxyMock(.init(members: .allMembersAsCreator)), roomProxy: JoinedRoomProxyMock(.init(members: .allMembersAsCreator)),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
analytics: .mock()) analytics: AnalyticsServiceMock.default())
static var previews: some View { static var previews: some View {
ElementNavigationStack { ElementNavigationStack {
RoomRolesAndPermissionsScreen(context: viewModel.context) RoomRolesAndPermissionsScreen(context: viewModel.context)
@@ -20,7 +20,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool
private let wysiwygViewModel: WysiwygComposerViewModel private let wysiwygViewModel: WysiwygComposerViewModel
private let completionSuggestionService: CompletionSuggestionServiceProtocol private let completionSuggestionService: CompletionSuggestionServiceProtocol
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let analyticsService: AnalyticsService private let analyticsService: AnalyticsServiceProtocol
private let draftService: ComposerDraftServiceProtocol private let draftService: ComposerDraftServiceProtocol
private var identityPinningViolations = [String: RoomMemberProxyProtocol]() private var identityPinningViolations = [String: RoomMemberProxyProtocol]()
@@ -52,7 +52,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool
mediaProvider: MediaProviderProtocol, mediaProvider: MediaProviderProtocol,
mentionDisplayHelper: MentionDisplayHelper, mentionDisplayHelper: MentionDisplayHelper,
appSettings: AppSettings, appSettings: AppSettings,
analyticsService: AnalyticsService, analyticsService: AnalyticsServiceProtocol,
composerDraftService: ComposerDraftServiceProtocol) { composerDraftService: ComposerDraftServiceProtocol) {
self.initialText = initialText self.initialText = initialText
self.wysiwygViewModel = wysiwygViewModel self.wysiwygViewModel = wysiwygViewModel
@@ -820,17 +820,14 @@ extension ComposerToolbarViewModel {
roomProxy.identityStatusChangesPublisher = .init([.init(userId: RoomMemberProxyMock.mockAlice.userID, changedTo: .verificationViolation)]) roomProxy.identityStatusChangesPublisher = .init([.init(userId: RoomMemberProxyMock.mockAlice.userID, changedTo: .verificationViolation)])
} }
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
let wysiwygViewModel = WysiwygComposerViewModel() let wysiwygViewModel = WysiwygComposerViewModel()
let viewModel = ComposerToolbarViewModel(roomProxy: roomProxy, let viewModel = ComposerToolbarViewModel(roomProxy: roomProxy,
wysiwygViewModel: wysiwygViewModel, wysiwygViewModel: wysiwygViewModel,
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init(suggestions: suggestions)), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init(suggestions: suggestions)),
mediaProvider: MediaProviderMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()),
mentionDisplayHelper: ComposerMentionDisplayHelper.mock, mentionDisplayHelper: ComposerMentionDisplayHelper.mock,
appSettings: appSettings, appSettings: AppSettings(),
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
composerDraftService: ComposerDraftServiceMock(.init())) composerDraftService: ComposerDraftServiceMock(.init()))
viewModel.state.bindings.composerFocused = focused viewModel.state.bindings.composerFocused = focused
viewModel.state.bindings.plainComposerText = NSAttributedString(string: message) viewModel.state.bindings.plainComposerText = NSAttributedString(string: message)
@@ -89,7 +89,7 @@ struct RoomAttachmentPicker_Previews: PreviewProvider, TestablePreview {
mediaProvider: MediaProviderMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()),
mentionDisplayHelper: ComposerMentionDisplayHelper.mock, mentionDisplayHelper: ComposerMentionDisplayHelper.mock,
appSettings: appSettings, appSettings: appSettings,
analyticsService: .mock(settings: appSettings), analyticsService: AnalyticsServiceMock.default(),
composerDraftService: ComposerDraftServiceMock(.init())) composerDraftService: ComposerDraftServiceMock(.init()))
} }
@@ -26,7 +26,7 @@ struct RoomScreenCoordinatorParameters {
let appMediator: AppMediatorProtocol let appMediator: AppMediatorProtocol
let appSettings: AppSettings let appSettings: AppSettings
let appHooks: AppHooks let appHooks: AppHooks
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
let composerDraftService: ComposerDraftServiceProtocol let composerDraftService: ComposerDraftServiceProtocol
let timelineControllerFactory: TimelineControllerFactoryProtocol let timelineControllerFactory: TimelineControllerFactoryProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
@@ -18,7 +18,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
private let clientProxy: ClientProxyProtocol private let clientProxy: ClientProxyProtocol
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private let analyticsService: AnalyticsService private let analyticsService: AnalyticsServiceProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private var initialSelectedPinnedEventID: String? private var initialSelectedPinnedEventID: String?
@@ -56,7 +56,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
ongoingCallRoomIDPublisher: CurrentValuePublisher<String?, Never>, ongoingCallRoomIDPublisher: CurrentValuePublisher<String?, Never>,
appSettings: AppSettings, appSettings: AppSettings,
appHooks: AppHooks, appHooks: AppHooks,
analyticsService: AnalyticsService, analyticsService: AnalyticsServiceProtocol,
userIndicatorController: UserIndicatorControllerProtocol) { userIndicatorController: UserIndicatorControllerProtocol) {
clientProxy = userSession.clientProxy clientProxy = userSession.clientProxy
self.roomProxy = roomProxy self.roomProxy = roomProxy
@@ -457,16 +457,13 @@ extension RoomScreenViewModel {
static func mock(roomProxyMock: JoinedRoomProxyMock, static func mock(roomProxyMock: JoinedRoomProxyMock,
clientProxyMock: ClientProxyMock = ClientProxyMock(.init()), clientProxyMock: ClientProxyMock = ClientProxyMock(.init()),
appHooks: AppHooks = AppHooks()) -> RoomScreenViewModel { appHooks: AppHooks = AppHooks()) -> RoomScreenViewModel {
let appSettings = AppSettings() RoomScreenViewModel(userSession: UserSessionMock(.init(clientProxy: clientProxyMock)),
let analytics = AnalyticsService.mock(settings: appSettings)
return RoomScreenViewModel(userSession: UserSessionMock(.init(clientProxy: clientProxyMock)),
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
initialSelectedPinnedEventID: nil, initialSelectedPinnedEventID: nil,
ongoingCallRoomIDPublisher: .init(.init(nil)), ongoingCallRoomIDPublisher: .init(.init(nil)),
appSettings: appSettings, appSettings: AppSettings(),
appHooks: appHooks, appHooks: appHooks,
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
userIndicatorController: UserIndicatorControllerMock.default) userIndicatorController: UserIndicatorControllerMock.default)
} }
} }
@@ -258,8 +258,6 @@ struct RoomScreen_Previews: PreviewProvider, TestablePreview {
let roomViewModel = RoomScreenViewModel.mock(roomProxyMock: roomProxyMock) let roomViewModel = RoomScreenViewModel.mock(roomProxyMock: roomProxyMock)
let appSettings = AppSettings() let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
let timelineViewModel = TimelineViewModel(roomProxy: roomProxyMock, let timelineViewModel = TimelineViewModel(roomProxy: roomProxyMock,
timelineController: MockTimelineController(), timelineController: MockTimelineController(),
userSession: UserSessionMock(.init()), userSession: UserSessionMock(.init()),
@@ -267,7 +265,7 @@ struct RoomScreen_Previews: PreviewProvider, TestablePreview {
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: appSettings, appSettings: appSettings,
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -11,7 +11,7 @@ import SwiftUI
struct AdvancedSettingsScreenCoordinatorParameters { struct AdvancedSettingsScreenCoordinatorParameters {
let appSettings: AppSettings let appSettings: AppSettings
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
let clientProxy: ClientProxyProtocol let clientProxy: ClientProxyProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
} }
@@ -12,7 +12,7 @@ import SwiftUI
typealias AdvancedSettingsScreenViewModelType = StateStoreViewModelV2<AdvancedSettingsScreenViewState, AdvancedSettingsScreenViewAction> typealias AdvancedSettingsScreenViewModelType = StateStoreViewModelV2<AdvancedSettingsScreenViewState, AdvancedSettingsScreenViewAction>
class AdvancedSettingsScreenViewModel: AdvancedSettingsScreenViewModelType, AdvancedSettingsScreenViewModelProtocol { class AdvancedSettingsScreenViewModel: AdvancedSettingsScreenViewModelType, AdvancedSettingsScreenViewModelProtocol {
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private let clientProxy: ClientProxyProtocol private let clientProxy: ClientProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
@@ -20,7 +20,7 @@ class AdvancedSettingsScreenViewModel: AdvancedSettingsScreenViewModelType, Adva
private var hideInviteAvatarsTask: Task<Void, Never>? private var hideInviteAvatarsTask: Task<Void, Never>?
init(advancedSettings: AdvancedSettingsProtocol, init(advancedSettings: AdvancedSettingsProtocol,
analytics: AnalyticsService, analytics: AnalyticsServiceProtocol,
clientProxy: ClientProxyProtocol, clientProxy: ClientProxyProtocol,
userIndicatorController: UserIndicatorControllerProtocol) { userIndicatorController: UserIndicatorControllerProtocol) {
self.analytics = analytics self.analytics = analytics
@@ -164,7 +164,7 @@ struct AdvancedSettingsScreen_Previews: PreviewProvider, TestablePreview {
AppSettings.resetAllSettings() AppSettings.resetAllSettings()
let appSettings = AppSettings() let appSettings = AppSettings()
return AdvancedSettingsScreenViewModel(advancedSettings: appSettings, return AdvancedSettingsScreenViewModel(advancedSettings: appSettings,
analytics: .mock(settings: appSettings), analytics: AnalyticsServiceMock.default(),
clientProxy: ClientProxyMock(.init()), clientProxy: ClientProxyMock(.init()),
userIndicatorController: UserIndicatorControllerMock.default) userIndicatorController: UserIndicatorControllerMock.default)
}() }()
@@ -11,7 +11,7 @@ import SwiftUI
struct AnalyticsSettingsScreenCoordinatorParameters { struct AnalyticsSettingsScreenCoordinatorParameters {
let appSettings: AppSettings let appSettings: AppSettings
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
} }
final class AnalyticsSettingsScreenCoordinator: CoordinatorProtocol { final class AnalyticsSettingsScreenCoordinator: CoordinatorProtocol {
@@ -12,9 +12,9 @@ import SwiftUI
typealias AnalyticsSettingsScreenViewModelType = StateStoreViewModelV2<AnalyticsSettingsScreenViewState, AnalyticsSettingsScreenViewAction> typealias AnalyticsSettingsScreenViewModelType = StateStoreViewModelV2<AnalyticsSettingsScreenViewState, AnalyticsSettingsScreenViewAction>
class AnalyticsSettingsScreenViewModel: AnalyticsSettingsScreenViewModelType, AnalyticsSettingsScreenViewModelProtocol { class AnalyticsSettingsScreenViewModel: AnalyticsSettingsScreenViewModelType, AnalyticsSettingsScreenViewModelProtocol {
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
init(appSettings: AppSettings, analytics: AnalyticsService) { init(appSettings: AppSettings, analytics: AnalyticsServiceProtocol) {
self.analytics = analytics self.analytics = analytics
let strings = AnalyticsSettingsScreenStrings(termsURL: appSettings.analyticsTermsURL) let strings = AnalyticsSettingsScreenStrings(termsURL: appSettings.analyticsTermsURL)
@@ -40,9 +40,8 @@ struct AnalyticsSettingsScreen: View {
struct AnalyticsSettingsScreen_Previews: PreviewProvider, TestablePreview { struct AnalyticsSettingsScreen_Previews: PreviewProvider, TestablePreview {
static var previews: some View { static var previews: some View {
let appSettings = AppSettings() let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
let viewModel = AnalyticsSettingsScreenViewModel(appSettings: appSettings, let viewModel = AnalyticsSettingsScreenViewModel(appSettings: appSettings,
analytics: analytics) analytics: AnalyticsServiceMock.default())
AnalyticsSettingsScreen(context: viewModel.context) AnalyticsSettingsScreen(context: viewModel.context)
} }
} }
@@ -126,20 +126,17 @@ struct SpaceSettingsScreen_Previews: PreviewProvider, TestablePreview {
static let userViewModel = makeViewModel(members: .allMembers) static let userViewModel = makeViewModel(members: .allMembers)
static func makeViewModel(members: [RoomMemberProxyMock]) -> RoomDetailsScreenViewModel { static func makeViewModel(members: [RoomMemberProxyMock]) -> RoomDetailsScreenViewModel {
let appSettings = AppSettings() RoomDetailsScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Space",
let analytics = AnalyticsService.mock(settings: appSettings)
return RoomDetailsScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Space",
avatarURL: .mockMXCAvatar, avatarURL: .mockMXCAvatar,
isSpace: true, isSpace: true,
canonicalAlias: "#space:matrix.org", canonicalAlias: "#space:matrix.org",
members: members)), members: members)),
userSession: UserSessionMock(.init()), userSession: UserSessionMock(.init()),
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
appSettings: appSettings) appSettings: AppSettings())
} }
static var previews: some View { static var previews: some View {
@@ -14,7 +14,7 @@ struct StartChatScreenCoordinatorParameters {
let userDiscoveryService: UserDiscoveryServiceProtocol let userDiscoveryService: UserDiscoveryServiceProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let appSettings: AppSettings let appSettings: AppSettings
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
} }
enum StartChatScreenCoordinatorAction { enum StartChatScreenCoordinatorAction {
@@ -14,7 +14,7 @@ typealias StartChatScreenViewModelType = StateStoreViewModel<StartChatScreenView
class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenViewModelProtocol { class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenViewModelProtocol {
private let userSession: UserSessionProtocol private let userSession: UserSessionProtocol
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let userDiscoveryService: UserDiscoveryServiceProtocol private let userDiscoveryService: UserDiscoveryServiceProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
@@ -27,7 +27,7 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie
} }
init(userSession: UserSessionProtocol, init(userSession: UserSessionProtocol,
analytics: AnalyticsService, analytics: AnalyticsServiceProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
userDiscoveryService: UserDiscoveryServiceProtocol, userDiscoveryService: UserDiscoveryServiceProtocol,
appSettings: AppSettings) { appSettings: AppSettings) {
@@ -83,14 +83,11 @@ struct JoinRoomByAddressView_Previews: PreviewProvider, TestablePreview {
let userDiscoveryService = UserDiscoveryServiceMock() let userDiscoveryService = UserDiscoveryServiceMock()
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice]) userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice])
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return StartChatScreenViewModel(userSession: userSession, return StartChatScreenViewModel(userSession: userSession,
analytics: analytics, analytics: AnalyticsServiceMock.default(),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
userDiscoveryService: userDiscoveryService, userDiscoveryService: userDiscoveryService,
appSettings: appSettings) appSettings: AppSettings())
}() }()
static var previews: some View { static var previews: some View {
@@ -159,7 +159,7 @@ struct StartChatScreen_Previews: PreviewProvider, TestablePreview {
let userDiscoveryService = UserDiscoveryServiceMock() let userDiscoveryService = UserDiscoveryServiceMock()
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice]) userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice])
return StartChatScreenViewModel(userSession: userSession, return StartChatScreenViewModel(userSession: userSession,
analytics: .mock(settings: appSettings), analytics: AnalyticsServiceMock.default(),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
userDiscoveryService: userDiscoveryService, userDiscoveryService: userDiscoveryService,
appSettings: appSettings) appSettings: appSettings)
@@ -22,7 +22,7 @@ struct ThreadTimelineScreenCoordinatorParameters {
let completionSuggestionService: CompletionSuggestionServiceProtocol let completionSuggestionService: CompletionSuggestionServiceProtocol
let appMediator: AppMediatorProtocol let appMediator: AppMediatorProtocol
let appSettings: AppSettings let appSettings: AppSettings
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
let composerDraftService: ComposerDraftServiceProtocol let composerDraftService: ComposerDraftServiceProtocol
let timelineControllerFactory: TimelineControllerFactoryProtocol let timelineControllerFactory: TimelineControllerFactoryProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
@@ -41,7 +41,7 @@ class TimelineInteractionHandler {
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let appMediator: AppMediatorProtocol private let appMediator: AppMediatorProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private let analyticsService: AnalyticsService private let analyticsService: AnalyticsServiceProtocol
private let emojiProvider: EmojiProviderProtocol private let emojiProvider: EmojiProviderProtocol
private let linkMetadataProvider: LinkMetadataProviderProtocol private let linkMetadataProvider: LinkMetadataProviderProtocol
private let timelineControllerFactory: TimelineControllerFactoryProtocol private let timelineControllerFactory: TimelineControllerFactoryProtocol
@@ -68,7 +68,7 @@ class TimelineInteractionHandler {
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
appMediator: AppMediatorProtocol, appMediator: AppMediatorProtocol,
appSettings: AppSettings, appSettings: AppSettings,
analyticsService: AnalyticsService, analyticsService: AnalyticsServiceProtocol,
emojiProvider: EmojiProviderProtocol, emojiProvider: EmojiProviderProtocol,
linkMetadataProvider: LinkMetadataProviderProtocol, linkMetadataProvider: LinkMetadataProviderProtocol,
timelineControllerFactory: TimelineControllerFactoryProtocol) { timelineControllerFactory: TimelineControllerFactoryProtocol) {
@@ -29,7 +29,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let appMediator: AppMediatorProtocol private let appMediator: AppMediatorProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private let analyticsService: AnalyticsService private let analyticsService: AnalyticsServiceProtocol
private let emojiProvider: EmojiProviderProtocol private let emojiProvider: EmojiProviderProtocol
private let timelineControllerFactory: TimelineControllerFactoryProtocol private let timelineControllerFactory: TimelineControllerFactoryProtocol
@@ -55,7 +55,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
appMediator: AppMediatorProtocol, appMediator: AppMediatorProtocol,
appSettings: AppSettings, appSettings: AppSettings,
analyticsService: AnalyticsService, analyticsService: AnalyticsServiceProtocol,
emojiProvider: EmojiProviderProtocol, emojiProvider: EmojiProviderProtocol,
linkMetadataProvider: LinkMetadataProviderProtocol, linkMetadataProvider: LinkMetadataProviderProtocol,
timelineControllerFactory: TimelineControllerFactoryProtocol) { timelineControllerFactory: TimelineControllerFactoryProtocol) {
@@ -1059,8 +1059,6 @@ extension TimelineViewModel {
let roomProxy = JoinedRoomProxyMock(.init(name: "Preview room", predecessor: hasPredecessor ? .init(roomId: UUID().uuidString) : nil)) let roomProxy = JoinedRoomProxyMock(.init(name: "Preview room", predecessor: hasPredecessor ? .init(roomId: UUID().uuidString) : nil))
let appSettings = AppSettings() let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return TimelineViewModel(roomProxy: roomProxy, return TimelineViewModel(roomProxy: roomProxy,
focussedEventID: nil, focussedEventID: nil,
timelineController: timelineController ?? MockTimelineController(timelineKind: timelineKind), timelineController: timelineController ?? MockTimelineController(timelineKind: timelineKind),
@@ -1069,7 +1067,7 @@ extension TimelineViewModel {
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: appSettings, appSettings: appSettings,
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -46,7 +46,6 @@ struct ReadReceiptsSummaryView_Previews: PreviewProvider, TestablePreview {
let roomProxyMock = JoinedRoomProxyMock(.init(name: "Room", members: members)) let roomProxyMock = JoinedRoomProxyMock(.init(name: "Room", members: members))
let appSettings = AppSettings() let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return TimelineViewModel(roomProxy: roomProxyMock, return TimelineViewModel(roomProxy: roomProxyMock,
timelineController: MockTimelineController(), timelineController: MockTimelineController(),
@@ -55,7 +54,7 @@ struct ReadReceiptsSummaryView_Previews: PreviewProvider, TestablePreview {
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: appSettings, appSettings: appSettings,
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -346,7 +346,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: appSettings, appSettings: appSettings,
analyticsService: .mock(settings: appSettings), analyticsService: AnalyticsServiceMock.default(),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -365,7 +365,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: appSettings, appSettings: appSettings,
analyticsService: .mock(settings: appSettings), analyticsService: AnalyticsServiceMock.default(),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -81,8 +81,6 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview {
static let viewModel = { static let viewModel = {
let appSettings = AppSettings() let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Test", members: members)), return TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Test", members: members)),
timelineController: MockTimelineController(), timelineController: MockTimelineController(),
userSession: UserSessionMock(.init()), userSession: UserSessionMock(.init()),
@@ -90,7 +88,7 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview {
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: appSettings, appSettings: appSettings,
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -91,8 +91,6 @@ struct HighlightedTimelineItemTimeline_Previews: PreviewProvider {
static let composerViewModel = ComposerToolbarViewModel.mock() static let composerViewModel = ComposerToolbarViewModel.mock()
static let timelineViewModel = { static let timelineViewModel = {
let appSettings = AppSettings() let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return TimelineViewModel(roomProxy: roomProxyMock, return TimelineViewModel(roomProxy: roomProxyMock,
focussedEventID: focussedEventID, focussedEventID: focussedEventID,
timelineController: MockTimelineController(), timelineController: MockTimelineController(),
@@ -101,7 +99,7 @@ struct HighlightedTimelineItemTimeline_Previews: PreviewProvider {
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: appSettings, appSettings: appSettings,
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -148,8 +148,6 @@ struct TimelineView_Previews: PreviewProvider { // Not testable as this preview
static let composerViewModel = ComposerToolbarViewModel.mock() static let composerViewModel = ComposerToolbarViewModel.mock()
static let timelineViewModel = { static let timelineViewModel = {
let appSettings = AppSettings() let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return TimelineViewModel(roomProxy: roomProxyMock, return TimelineViewModel(roomProxy: roomProxyMock,
timelineController: MockTimelineController(), timelineController: MockTimelineController(),
userSession: UserSessionMock(.init()), userSession: UserSessionMock(.init()),
@@ -157,7 +155,7 @@ struct TimelineView_Previews: PreviewProvider { // Not testable as this preview
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: appSettings, appSettings: appSettings,
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -14,7 +14,7 @@ struct UserProfileScreenCoordinatorParameters {
let isPresentedModally: Bool let isPresentedModally: Bool
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let analytics: AnalyticsService let analytics: AnalyticsServiceProtocol
let appSettings: AppSettings let appSettings: AppSettings
} }
@@ -15,7 +15,7 @@ typealias UserProfileScreenViewModelType = StateStoreViewModelV2<UserProfileScre
class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScreenViewModelProtocol { class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScreenViewModelProtocol {
private let userSession: UserSessionProtocol private let userSession: UserSessionProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private var actionsSubject: PassthroughSubject<UserProfileScreenViewModelAction, Never> = .init() private var actionsSubject: PassthroughSubject<UserProfileScreenViewModelAction, Never> = .init()
@@ -27,7 +27,7 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr
isPresentedModally: Bool, isPresentedModally: Bool,
userSession: UserSessionProtocol, userSession: UserSessionProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
analytics: AnalyticsService, analytics: AnalyticsServiceProtocol,
appSettings: AppSettings) { appSettings: AppSettings) {
self.userSession = userSession self.userSession = userSession
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
@@ -143,14 +143,11 @@ struct UserProfileScreen_Previews: PreviewProvider, TestablePreview {
clientProxyMock.directRoomForUserIDReturnValue = .success("roomID") clientProxyMock.directRoomForUserIDReturnValue = .success("roomID")
} }
let appSettings = AppSettings()
let analytics = AnalyticsService.mock(settings: appSettings)
return UserProfileScreenViewModel(userID: userID, return UserProfileScreenViewModel(userID: userID,
isPresentedModally: false, isPresentedModally: false,
userSession: UserSessionMock(.init(clientProxy: clientProxyMock)), userSession: UserSessionMock(.init(clientProxy: clientProxyMock)),
userIndicatorController: UserIndicatorControllerMock.default, userIndicatorController: UserIndicatorControllerMock.default,
analytics: analytics, analytics: AnalyticsServiceMock.default(),
appSettings: appSettings) appSettings: AppSettings())
} }
} }
@@ -10,26 +10,11 @@ import AnalyticsEvents
import Combine import Combine
import PostHog import PostHog
/// A class responsible for managing a variety of analytics clients class AnalyticsService: AnalyticsServiceProtocol {
/// and sending events through these clients.
///
/// Events may include user activity, or app health data such as crashes,
/// non-fatal issues and performance. `Analytics` class serves as a façade
/// to all these use cases.
///
/// ## Creating Analytics Events
///
/// Events are managed in a shared repo for all Element clients https://github.com/matrix-org/matrix-analytics-events
/// To add a new event create a PR to that repo with the new/updated schema. Once merged
/// into `main`, update the AnalyticsEvents Swift package in `project.yml`.
///
class AnalyticsService {
/// The analytics client to send events with. /// The analytics client to send events with.
private let client: AnalyticsClientProtocol private let client: AnalyticsClientProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
/// A signpost client for performance testing the app. This client doesn't respect the
/// `isRunning` state or behave any differently when `start`/`reset` are called.
let signpost = Signposter() let signpost = Signposter()
init(client: AnalyticsClientProtocol, appSettings: AppSettings) { init(client: AnalyticsClientProtocol, appSettings: AppSettings) {
@@ -37,7 +22,6 @@ class AnalyticsService {
self.appSettings = appSettings self.appSettings = appSettings
} }
/// Whether to show the user the analytics opt in prompt.
var shouldShowAnalyticsPrompt: Bool { var shouldShowAnalyticsPrompt: Bool {
// Only show the prompt once, and when analytics are enabled in BuildSettings. // Only show the prompt once, and when analytics are enabled in BuildSettings.
appSettings.analyticsConsentState == .unknown && appSettings.canPromptForAnalytics appSettings.analyticsConsentState == .unknown && appSettings.canPromptForAnalytics
@@ -47,13 +31,11 @@ class AnalyticsService {
appSettings.analyticsConsentState == .optedIn appSettings.analyticsConsentState == .optedIn
} }
/// Opts in to analytics tracking with the supplied user session.
func optIn() { func optIn() {
appSettings.analyticsConsentState = .optedIn appSettings.analyticsConsentState = .optedIn
startIfEnabled() startIfEnabled()
} }
/// Stops analytics tracking and calls `reset` to clear any IDs and event queues.
func optOut() { func optOut() {
appSettings.analyticsConsentState = .optedOut appSettings.analyticsConsentState = .optedOut
@@ -64,7 +46,6 @@ class AnalyticsService {
MXLog.info("Stopped.") MXLog.info("Stopped.")
} }
/// Starts the analytics client if the user has opted in, otherwise does nothing.
func startIfEnabled() { func startIfEnabled() {
guard isEnabled, !client.isRunning, let configuration = appSettings.analyticsConfiguration else { return } guard isEnabled, !client.isRunning, let configuration = appSettings.analyticsConfiguration else { return }
@@ -76,16 +57,11 @@ class AnalyticsService {
MXLog.info("Started.") MXLog.info("Started.")
} }
/// Resets the any IDs and event queues in the analytics client. This method should
/// be called on sign-out to maintain opt-in status, whilst ensuring the next
/// account used isn't associated with the previous one.
/// Note: **MUST** be called before stopping PostHog or the reset is ignored.
func reset() { func reset() {
client.reset() client.reset()
MXLog.info("Reset.") MXLog.info("Reset.")
} }
/// Reset the consent state for analytics
func resetConsentState() { func resetConsentState() {
MXLog.warning("Resetting consent state for analytics.") MXLog.warning("Resetting consent state for analytics.")
appSettings.analyticsConsentState = .unknown appSettings.analyticsConsentState = .unknown
@@ -104,9 +80,6 @@ class AnalyticsService {
// MARK: - Public tracking methods // MARK: - Public tracking methods
extension AnalyticsService { 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: AnalyticsEvent.MobileScreen.ScreenName, duration milliseconds: Int? = nil) { func track(screen: AnalyticsEvent.MobileScreen.ScreenName, duration milliseconds: Int? = nil) {
MXLog.debug("\(screen)") MXLog.debug("\(screen)")
let event = AnalyticsEvent.MobileScreen(durationMs: milliseconds, screenName: screen) let event = AnalyticsEvent.MobileScreen(durationMs: milliseconds, screenName: screen)
@@ -117,12 +90,6 @@ extension AnalyticsService {
capture(event: AnalyticsEvent.Interaction(index: index, interactionType: .Touch, name: name)) capture(event: AnalyticsEvent.Interaction(index: index, interactionType: .Touch, name: name))
} }
/// Track the presentation of a screen
/// - Parameter context: To provide additional context or description for the error
/// - Parameter domain: The domain to which the error belongs to.
/// - Parameter name: The name of the error
/// - Parameter timeToDecryptMillis: The time it took to decrypt the event in milliseconds, needs to be used only to track UTD errors, otherwise if the error is nort related to UTD it should be nil.
/// Can be found in `UnableToDecryptInfo`. In case the `UnableToDecryptInfo` contains the value as nil, pass it as `-1`
func trackError(context: String?, domain: AnalyticsEvent.Error.Domain, func trackError(context: String?, domain: AnalyticsEvent.Error.Domain,
name: AnalyticsEvent.Error.Name, name: AnalyticsEvent.Error.Name,
timeToDecryptMillis: Int? = nil, timeToDecryptMillis: Int? = nil,
@@ -145,19 +112,10 @@ extension AnalyticsService {
wasVisibleToUser: wasVisibleToUser)) wasVisibleToUser: wasVisibleToUser))
} }
/// Track the creation of a room
/// - Parameter isDM: true if the created room is a direct message, false otherwise
func trackCreatedRoom(isDM: Bool) { func trackCreatedRoom(isDM: Bool) {
capture(event: AnalyticsEvent.CreatedRoom(isDM: isDM)) capture(event: AnalyticsEvent.CreatedRoom(isDM: isDM))
} }
/// Track the composer
/// - Parameters:
/// - inThread: whether the composer is used in a Thread
/// - isEditing: whether the composer is used to edit a message
/// - isReply: whether the composer is used to reply a message
/// - messageType: the type of the message
/// - startsThread: whether the composer is used to start a new thread
func trackComposer(inThread: Bool, func trackComposer(inThread: Bool,
isEditing: Bool, isEditing: Bool,
isReply: Bool, isReply: Bool,
@@ -170,19 +128,10 @@ extension AnalyticsService {
startsThread: startsThread)) startsThread: startsThread))
} }
/// Track the presentation of a room
/// - Parameters:
/// - isDM: whether the room is a direct message
/// - isSpace: whether the room is a space
func trackViewRoom(isDM: Bool, isSpace: Bool) { func trackViewRoom(isDM: Bool, isSpace: Bool) {
capture(event: AnalyticsEvent.ViewRoom(activeSpace: nil, isDM: isDM, isSpace: isSpace, trigger: nil, viaKeyboard: nil)) capture(event: AnalyticsEvent.ViewRoom(activeSpace: nil, isDM: isDM, isSpace: isSpace, trigger: nil, viaKeyboard: nil))
} }
/// Track the action of joining a room
/// - Parameters:
/// - isDM: whether the room is a direct message
/// - isSpace: whether the room is a space
/// - activeMemberCount: the number of active members in the room
func trackJoinedRoom(isDM: Bool, isSpace: Bool, activeMemberCount: UInt) { func trackJoinedRoom(isDM: Bool, isSpace: Bool, activeMemberCount: UInt) {
guard let roomSize = AnalyticsEvent.JoinedRoom.RoomSize(memberCount: activeMemberCount) else { guard let roomSize = AnalyticsEvent.JoinedRoom.RoomSize(memberCount: activeMemberCount) else {
MXLog.error("invalid room size") MXLog.error("invalid room size")
@@ -191,27 +140,20 @@ extension AnalyticsService {
capture(event: AnalyticsEvent.JoinedRoom(isDM: isDM, isSpace: isSpace, roomSize: roomSize, trigger: nil)) capture(event: AnalyticsEvent.JoinedRoom(isDM: isDM, isSpace: isSpace, roomSize: roomSize, trigger: nil))
} }
/// Track the action of creating a poll
/// - Parameters:
/// - isUndisclosed: whether the poll is undisclosed
/// - numberOfAnswers: the number of options in the poll
func trackPollCreated(isUndisclosed: Bool, numberOfAnswers: Int) { func trackPollCreated(isUndisclosed: Bool, numberOfAnswers: Int) {
capture(event: AnalyticsEvent.PollCreation(action: .Create, capture(event: AnalyticsEvent.PollCreation(action: .Create,
isUndisclosed: isUndisclosed, isUndisclosed: isUndisclosed,
numberOfAnswers: numberOfAnswers)) numberOfAnswers: numberOfAnswers))
} }
/// Track the action of voting on a poll
func trackPollVote() { func trackPollVote() {
capture(event: AnalyticsEvent.PollVote(doNotUse: nil)) capture(event: AnalyticsEvent.PollVote(doNotUse: nil))
} }
/// Track the action of ending a poll
func trackPollEnd() { func trackPollEnd() {
capture(event: AnalyticsEvent.PollEnd(doNotUse: nil)) capture(event: AnalyticsEvent.PollEnd(doNotUse: nil))
} }
/// Track a room moderation action.
func trackRoomModeration(action: AnalyticsEvent.RoomModeration.Action, role: RoomRole?) { func trackRoomModeration(action: AnalyticsEvent.RoomModeration.Action, role: RoomRole?) {
let role = role.map(AnalyticsEvent.RoomModeration.Role.init) let role = role.map(AnalyticsEvent.RoomModeration.Role.init)
capture(event: AnalyticsEvent.RoomModeration(action: action, role: role)) capture(event: AnalyticsEvent.RoomModeration(action: action, role: role))
@@ -0,0 +1,160 @@
//
// Copyright 2026 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import AnalyticsEvents
/// A service responsible for managing analytics clients and sending events through them.
///
/// Events may include user activity, or app health data such as crashes,
/// non-fatal issues and performance.
///
/// ## Creating Analytics Events
///
/// Events are managed in a shared repo for all Element clients https://github.com/matrix-org/matrix-analytics-events
/// To add a new event create a PR to that repo with the new/updated schema. Once merged
/// into `main`, update the AnalyticsEvents Swift package in `project.yml`.
protocol AnalyticsServiceProtocol: AnyObject {
/// A signpost client for performance testing the app. This client doesn't respect the
/// `isRunning` state or behave any differently when `start`/`reset` are called.
var signpost: Signposter { get }
/// Whether to show the user the analytics opt in prompt.
var shouldShowAnalyticsPrompt: Bool { get }
/// Whether analytics tracking is currently enabled.
var isEnabled: Bool { get }
/// Opts in to analytics tracking.
func optIn()
/// Stops analytics tracking and calls `reset` to clear any IDs and event queues.
func optOut()
/// Starts the analytics client if the user has opted in, otherwise does nothing.
func startIfEnabled()
/// Resets any IDs and event queues in the analytics client. This method should
/// be called on sign-out to maintain opt-in status, whilst ensuring the next
/// account used isn't associated with the previous one.
/// Note: **MUST** be called before stopping PostHog or the reset is ignored.
func reset()
/// Resets the consent state for analytics.
func resetConsentState()
/// Track the presentation of a screen.
/// - Parameter screen: The screen that was shown.
/// - Parameter milliseconds: An optional value representing how long the screen was shown for in milliseconds.
func track(screen: AnalyticsEvent.MobileScreen.ScreenName, duration milliseconds: Int?)
func trackInteraction(index: Int?, name: AnalyticsEvent.Interaction.Name)
/// Track an analytics error event.
/// - Parameter context: Additional context or description for the error.
/// - Parameter domain: The domain to which the error belongs.
/// - Parameter name: The name of the error.
/// - Parameter timeToDecryptMillis: The time it took to decrypt the event in milliseconds. Only used for UTD errors; pass `nil` otherwise.
/// Can be found in `UnableToDecryptInfo`. If `UnableToDecryptInfo` contains `nil` for this value, pass `-1`.
func trackError(context: String?, domain: AnalyticsEvent.Error.Domain,
name: AnalyticsEvent.Error.Name,
timeToDecryptMillis: Int?,
eventLocalAgeMillis: Int?,
isFederated: Bool?,
isMatrixDotOrg: Bool?,
userTrustsOwnIdentity: Bool?,
wasVisibleToUser: Bool?)
/// Track the creation of a room.
/// - Parameter isDM: `true` if the created room is a direct message, `false` otherwise.
func trackCreatedRoom(isDM: Bool)
/// Track the composer.
/// - Parameters:
/// - inThread: Whether the composer is used in a thread.
/// - isEditing: Whether the composer is used to edit a message.
/// - isReply: Whether the composer is used to reply to a message.
/// - messageType: The type of the message.
/// - startsThread: Whether the composer is used to start a new thread.
func trackComposer(inThread: Bool,
isEditing: Bool,
isReply: Bool,
messageType: AnalyticsEvent.Composer.MessageType,
startsThread: Bool?)
/// Track the presentation of a room.
/// - Parameters:
/// - isDM: Whether the room is a direct message.
/// - isSpace: Whether the room is a space.
func trackViewRoom(isDM: Bool, isSpace: Bool)
/// Track the action of joining a room.
/// - Parameters:
/// - isDM: Whether the room is a direct message.
/// - isSpace: Whether the room is a space.
/// - activeMemberCount: The number of active members in the room.
func trackJoinedRoom(isDM: Bool, isSpace: Bool, activeMemberCount: UInt)
/// Track the action of creating a poll.
/// - Parameters:
/// - isUndisclosed: Whether the poll is undisclosed.
/// - numberOfAnswers: The number of options in the poll.
func trackPollCreated(isUndisclosed: Bool, numberOfAnswers: Int)
/// Track the action of voting on a poll.
func trackPollVote()
/// Track the action of ending a poll.
func trackPollEnd()
/// Track a room moderation action.
func trackRoomModeration(action: AnalyticsEvent.RoomModeration.Action, role: RoomRole?)
func trackSessionSecurityState(_ state: SessionSecurityState)
func updateUserProperties(_ userProperties: AnalyticsEvent.UserProperties)
func trackPinUnpinEvent(_ event: AnalyticsEvent.PinUnpinAction)
}
// sourcery: AutoMockable
extension AnalyticsServiceProtocol {
func track(screen: AnalyticsEvent.MobileScreen.ScreenName) {
track(screen: screen, duration: nil)
}
func trackInteraction(name: AnalyticsEvent.Interaction.Name) {
trackInteraction(index: nil, name: name)
}
@_disfavoredOverload // make sure this doesn't cause infinite recursion
func trackError(context: String?,
domain: AnalyticsEvent.Error.Domain,
name: AnalyticsEvent.Error.Name,
timeToDecryptMillis: Int? = nil,
eventLocalAgeMillis: Int? = nil,
isFederated: Bool? = nil,
isMatrixDotOrg: Bool? = nil,
userTrustsOwnIdentity: Bool? = nil,
wasVisibleToUser: Bool? = nil) {
trackError(context: context,
domain: domain,
name: name,
timeToDecryptMillis: timeToDecryptMillis,
eventLocalAgeMillis: eventLocalAgeMillis,
isFederated: isFederated,
isMatrixDotOrg: isMatrixDotOrg,
userTrustsOwnIdentity: userTrustsOwnIdentity,
wasVisibleToUser: wasVisibleToUser)
}
func trackComposer(inThread: Bool,
isEditing: Bool,
isReply: Bool,
startsThread: Bool?) {
trackComposer(inThread: inThread,
isEditing: isEditing,
isReply: isReply,
messageType: .Text,
startsThread: startsThread)
}
}
@@ -9,5 +9,5 @@
import SwiftUI import SwiftUI
extension EnvironmentValues { extension EnvironmentValues {
@Entry var analyticsService: AnalyticsService? @Entry var analyticsService: AnalyticsServiceProtocol?
} }
@@ -17,7 +17,7 @@ class ClientProxy: ClientProxyProtocol {
private let client: ClientProtocol private let client: ClientProtocol
private let networkMonitor: NetworkMonitorProtocol private let networkMonitor: NetworkMonitorProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private let analyticsService: AnalyticsService private let analyticsService: AnalyticsServiceProtocol
let mediaLoader: MediaLoaderProtocol let mediaLoader: MediaLoaderProtocol
private let clientQueue: DispatchQueue private let clientQueue: DispatchQueue
@@ -202,7 +202,7 @@ class ClientProxy: ClientProxyProtocol {
init(client: ClientProtocol, init(client: ClientProtocol,
networkMonitor: NetworkMonitorProtocol, networkMonitor: NetworkMonitorProtocol,
appSettings: AppSettings, appSettings: AppSettings,
analyticsService: AnalyticsService) async throws { analyticsService: AnalyticsServiceProtocol) async throws {
self.client = client self.client = client
self.networkMonitor = networkMonitor self.networkMonitor = networkMonitor
self.appSettings = appSettings self.appSettings = appSettings
@@ -9,10 +9,10 @@
import Foundation import Foundation
class PollInteractionHandler: PollInteractionHandlerProtocol { class PollInteractionHandler: PollInteractionHandlerProtocol {
let analyticsService: AnalyticsService let analyticsService: AnalyticsServiceProtocol
let timelineController: TimelineControllerProtocol let timelineController: TimelineControllerProtocol
init(analyticsService: AnalyticsService, timelineController: TimelineControllerProtocol) { init(analyticsService: AnalyticsServiceProtocol, timelineController: TimelineControllerProtocol) {
self.analyticsService = analyticsService self.analyticsService = analyticsService
self.timelineController = timelineController self.timelineController = timelineController
} }
@@ -15,7 +15,7 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
private let roomListService: RoomListServiceProtocol private let roomListService: RoomListServiceProtocol
private let room: RoomProtocol private let room: RoomProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private let analyticsService: AnalyticsService private let analyticsService: AnalyticsServiceProtocol
private let eventStringBuilder: RoomEventStringBuilder private let eventStringBuilder: RoomEventStringBuilder
// periphery:ignore - required for instance retention in the rust codebase // periphery:ignore - required for instance retention in the rust codebase
@@ -79,7 +79,7 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
init(roomListService: RoomListServiceProtocol, init(roomListService: RoomListServiceProtocol,
room: RoomProtocol, room: RoomProtocol,
appSettings: AppSettings, appSettings: AppSettings,
analyticsService: AnalyticsService, analyticsService: AnalyticsServiceProtocol,
eventStringBuilder: RoomEventStringBuilder) async throws { eventStringBuilder: RoomEventStringBuilder) async throws {
self.roomListService = roomListService self.roomListService = roomListService
self.room = room self.room = room
@@ -12,7 +12,7 @@ import MatrixRustSDK
class UserSessionStore: UserSessionStoreProtocol { class UserSessionStore: UserSessionStoreProtocol {
private let keychainController: KeychainControllerProtocol private let keychainController: KeychainControllerProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private let analyticsService: AnalyticsService private let analyticsService: AnalyticsServiceProtocol
private let networkMonitor: NetworkMonitorProtocol private let networkMonitor: NetworkMonitorProtocol
private let appHooks: AppHooks private let appHooks: AppHooks
@@ -32,7 +32,7 @@ class UserSessionStore: UserSessionStoreProtocol {
init(keychainController: KeychainControllerProtocol, init(keychainController: KeychainControllerProtocol,
appSettings: AppSettings, appSettings: AppSettings,
analyticsService: AnalyticsService, analyticsService: AnalyticsServiceProtocol,
appHooks: AppHooks, appHooks: AppHooks,
networkMonitor: NetworkMonitorProtocol) { networkMonitor: NetworkMonitorProtocol) {
self.keychainController = keychainController self.keychainController = keychainController
@@ -13,7 +13,7 @@ import UIKit
class UITestsAppCoordinator: AppCoordinatorProtocol, SecureWindowManagerDelegate { class UITestsAppCoordinator: AppCoordinatorProtocol, SecureWindowManagerDelegate {
private let appSettings: AppSettings private let appSettings: AppSettings
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let navigationRootCoordinator: NavigationRootCoordinator private let navigationRootCoordinator: NavigationRootCoordinator
@@ -42,7 +42,7 @@ class UITestsAppCoordinator: AppCoordinatorProtocol, SecureWindowManagerDelegate
let analyticsClient = AnalyticsClientMock() let analyticsClient = AnalyticsClientMock()
analyticsClient.isRunning = false analyticsClient.isRunning = false
analytics = .mock(analyticsClient, settings: appSettings) analytics = AnalyticsServiceMock.default()
userIndicatorController = UserIndicatorController() userIndicatorController = UserIndicatorController()
windowManager.delegate = self windowManager.delegate = self
@@ -115,7 +115,7 @@ class MockScreen: Identifiable {
let navigationRootCoordinator: NavigationRootCoordinator let navigationRootCoordinator: NavigationRootCoordinator
private let appSettings: AppSettings private let appSettings: AppSettings
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private var client: UITestsSignalling.Client? private var client: UITestsSignalling.Client?
@@ -127,7 +127,7 @@ class MockScreen: Identifiable {
windowManager: SecureWindowManagerProtocol, windowManager: SecureWindowManagerProtocol,
navigationRootCoordinator: NavigationRootCoordinator, navigationRootCoordinator: NavigationRootCoordinator,
appSettings: AppSettings, appSettings: AppSettings,
analytics: AnalyticsService, analytics: AnalyticsServiceProtocol,
userIndicatorController: UserIndicatorControllerProtocol) { userIndicatorController: UserIndicatorControllerProtocol) {
self.id = id self.id = id
self.windowManager = windowManager self.windowManager = windowManager
@@ -20,7 +20,9 @@ final class AnalyticsSettingsScreenViewModelTests {
init() { init() {
AppSettings.resetAllSettings() AppSettings.resetAllSettings()
appSettings = AppSettings() appSettings = AppSettings()
analytics = .mock(settings: appSettings) let client = AnalyticsClientMock()
client.isRunning = false
analytics = AnalyticsService(client: client, appSettings: appSettings)
viewModel = AnalyticsSettingsScreenViewModel(appSettings: appSettings, viewModel = AnalyticsSettingsScreenViewModel(appSettings: appSettings,
analytics: analytics) analytics: analytics)
+2 -2
View File
@@ -13,7 +13,7 @@ import Testing
final class AnalyticsTests { final class AnalyticsTests {
private let appSettings: AppSettings private let appSettings: AppSettings
private let analytics: AnalyticsService private let analytics: AnalyticsServiceProtocol
private let analyticsClient: AnalyticsClientMock private let analyticsClient: AnalyticsClientMock
private var posthogMock: PHGPostHogMock private var posthogMock: PHGPostHogMock
@@ -24,7 +24,7 @@ final class AnalyticsTests {
analyticsClient = AnalyticsClientMock() analyticsClient = AnalyticsClientMock()
analyticsClient.isRunning = false analyticsClient.isRunning = false
analytics = .mock(analyticsClient, settings: appSettings) analytics = AnalyticsService(client: analyticsClient, appSettings: appSettings)
posthogMock = PHGPostHogMock() posthogMock = PHGPostHogMock()
posthogMock.configureMockBehavior() posthogMock.configureMockBehavior()
@@ -32,8 +32,6 @@ struct ChatsTabFlowCoordinatorTests {
init() async throws { init() async throws {
let appSettings = AppSettings() let appSettings = AppSettings()
let analytics: AnalyticsService = .mock(settings: appSettings)
clientProxy = ClientProxyMock(.init(userID: "hi@bob", roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))))) clientProxy = ClientProxyMock(.init(userID: "hi@bob", roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))))
timelineControllerFactory = TimelineControllerFactoryMock(.init()) timelineControllerFactory = TimelineControllerFactoryMock(.init())
@@ -50,7 +48,7 @@ struct ChatsTabFlowCoordinatorTests {
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: appSettings, appSettings: appSettings,
appHooks: AppHooks(), appHooks: AppHooks(),
analytics: analytics, analytics: AnalyticsServiceMock.default(),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
notificationManager: notificationManager, notificationManager: notificationManager,
stateMachineFactory: stateMachineFactory) stateMachineFactory: stateMachineFactory)
@@ -97,7 +97,7 @@ final class ComposerToolbarViewModelTests {
mediaProvider: MediaProviderMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()),
mentionDisplayHelper: ComposerMentionDisplayHelper.mock, mentionDisplayHelper: ComposerMentionDisplayHelper.mock,
appSettings: appSettings, appSettings: appSettings,
analyticsService: .mock(settings: appSettings), analyticsService: AnalyticsServiceMock.default(),
composerDraftService: draftServiceMock) composerDraftService: draftServiceMock)
#expect(viewModel.state.suggestions == suggestions) #expect(viewModel.state.suggestions == suggestions)
@@ -716,7 +716,7 @@ final class ComposerToolbarViewModelTests {
mediaProvider: MediaProviderMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()),
mentionDisplayHelper: ComposerMentionDisplayHelper.mock, mentionDisplayHelper: ComposerMentionDisplayHelper.mock,
appSettings: appSettings, appSettings: appSettings,
analyticsService: .mock(settings: appSettings), analyticsService: AnalyticsServiceMock.default(),
composerDraftService: draftServiceMock) composerDraftService: draftServiceMock)
var fulfillment = deferFulfillment(viewModel.context.$viewState, message: "Composer is disabled") { $0.canSend == false } var fulfillment = deferFulfillment(viewModel.context.$viewState, message: "Composer is disabled") { $0.canSend == false }
@@ -763,7 +763,7 @@ final class ComposerToolbarViewModelTests {
mediaProvider: MediaProviderMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()),
mentionDisplayHelper: ComposerMentionDisplayHelper.mock, mentionDisplayHelper: ComposerMentionDisplayHelper.mock,
appSettings: appSettings, appSettings: appSettings,
analyticsService: .mock(settings: appSettings), analyticsService: AnalyticsServiceMock.default(),
composerDraftService: draftServiceMock) composerDraftService: draftServiceMock)
var fulfillment = deferFulfillment(viewModel.context.$viewState, message: "Composer is disabled") { $0.canSend == false } var fulfillment = deferFulfillment(viewModel.context.$viewState, message: "Composer is disabled") { $0.canSend == false }
@@ -799,7 +799,7 @@ final class ComposerToolbarViewModelTests {
mediaProvider: MediaProviderMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()),
mentionDisplayHelper: ComposerMentionDisplayHelper.mock, mentionDisplayHelper: ComposerMentionDisplayHelper.mock,
appSettings: appSettings, appSettings: appSettings,
analyticsService: .mock(settings: appSettings), analyticsService: AnalyticsServiceMock.default(),
composerDraftService: draftServiceMock) composerDraftService: draftServiceMock)
let deferred = deferFulfillment(viewModel.context.$viewState, message: "Composer should be enabled") { $0.canSend == true } let deferred = deferFulfillment(viewModel.context.$viewState, message: "Composer should be enabled") { $0.canSend == true }
@@ -825,7 +825,7 @@ final class ComposerToolbarViewModelTests {
mediaProvider: MediaProviderMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()),
mentionDisplayHelper: ComposerMentionDisplayHelper.mock, mentionDisplayHelper: ComposerMentionDisplayHelper.mock,
appSettings: appSettings, appSettings: appSettings,
analyticsService: .mock(settings: appSettings), analyticsService: AnalyticsServiceMock.default(),
composerDraftService: draftServiceMock) composerDraftService: draftServiceMock)
viewModel.context.composerFormattingEnabled = true viewModel.context.composerFormattingEnabled = true
} }
@@ -18,7 +18,6 @@ final class CreateRoomScreenViewModelTests {
var userSession: UserSessionMock! var userSession: UserSessionMock!
private let appSettings: AppSettings private let appSettings: AppSettings
private let analytics: AnalyticsService
private let usersSubject = CurrentValueSubject<[UserProfileProxy], Never>([]) private let usersSubject = CurrentValueSubject<[UserProfileProxy], Never>([])
@@ -28,7 +27,6 @@ final class CreateRoomScreenViewModelTests {
init() { init() {
appSettings = AppSettings() appSettings = AppSettings()
analytics = .mock(settings: appSettings)
} }
deinit { deinit {
@@ -95,7 +93,7 @@ final class CreateRoomScreenViewModelTests {
spaceSelectionMode: .none, spaceSelectionMode: .none,
shouldShowCancelButton: false, shouldShowCancelButton: false,
userSession: userSession, userSession: userSession,
analytics: analytics, analytics: AnalyticsServiceMock.default(),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: appSettings) appSettings: appSettings)
self.viewModel = viewModel self.viewModel = viewModel
@@ -345,7 +343,7 @@ final class CreateRoomScreenViewModelTests {
spaceSelectionMode: spacesSelectionMode, spaceSelectionMode: spacesSelectionMode,
shouldShowCancelButton: false, shouldShowCancelButton: false,
userSession: userSession, userSession: userSession,
analytics: analytics, analytics: AnalyticsServiceMock.default(),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: appSettings) appSettings: appSettings)
self.viewModel = viewModel self.viewModel = viewModel
@@ -21,16 +21,12 @@ final class HomeScreenViewModelTests {
var roomSummaryProvider: RoomSummaryProviderMock! var roomSummaryProvider: RoomSummaryProviderMock!
var notificationManager: NotificationManagerMock! var notificationManager: NotificationManagerMock!
private let appSettings: AppSettings private let appSettings: AppSettings
private let analytics: AnalyticsService
private let userIndicatorController: UserIndicatorControllerProtocol
var cancellables = Set<AnyCancellable>() var cancellables = Set<AnyCancellable>()
init() { init() {
AppSettings.resetAllSettings() AppSettings.resetAllSettings()
appSettings = AppSettings() appSettings = AppSettings()
analytics = .mock(settings: appSettings)
userIndicatorController = UserIndicatorControllerMock.default
} }
deinit { deinit {
@@ -444,9 +440,9 @@ final class HomeScreenViewModelTests {
viewModel = HomeScreenViewModel(userSession: userSession, viewModel = HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
appSettings: appSettings, appSettings: appSettings,
analyticsService: analytics, analyticsService: AnalyticsServiceMock.default(),
notificationManager: notificationManager, notificationManager: notificationManager,
userIndicatorController: userIndicatorController) userIndicatorController: UserIndicatorControllerMock.default)
} }
} }

Some files were not shown because too many files have changed in this diff Show More