Replace AppSettings with UserSettings from the UserSessionFlow and below.

Remove some redundant injections when passed alongside a user session too.

# Conflicts:
#	ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift
#	ElementX/Sources/Screens/Timeline/TimelineViewModel.swift
This commit is contained in:
Doug
2026-09-25 13:51:33 +01:00
committed by Stefan Ceriu
parent 54cc1cba60
commit fccad5fc30
117 changed files with 554 additions and 594 deletions
+8
View File
@@ -431,6 +431,7 @@
44BDD670FF9095ACE240A3A2 /* VoiceMessageMediaManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC4F10BDD56FA77FEC742333 /* VoiceMessageMediaManagerTests.swift */; }; 44BDD670FF9095ACE240A3A2 /* VoiceMessageMediaManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC4F10BDD56FA77FEC742333 /* VoiceMessageMediaManagerTests.swift */; };
44D3EA8583C54BA3D8058499 /* SyncServiceSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33DBBBC78047C669166C9F7C /* SyncServiceSDKMock.swift */; }; 44D3EA8583C54BA3D8058499 /* SyncServiceSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33DBBBC78047C669166C9F7C /* SyncServiceSDKMock.swift */; };
44F0E1B576C7599DF8022071 /* SentrySwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 75361A9D8A3C5501EADB225D /* SentrySwiftUI */; }; 44F0E1B576C7599DF8022071 /* SentrySwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 75361A9D8A3C5501EADB225D /* SentrySwiftUI */; };
452507BCE97B3EA0F24E206E /* UserSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 100DA305FE28BB3B6AEDE1FD /* UserSettings.swift */; };
454311EAC17D778E19F46592 /* NotificationPermissionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91868EB98818044E6FEBE532 /* NotificationPermissionsScreenCoordinator.swift */; }; 454311EAC17D778E19F46592 /* NotificationPermissionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91868EB98818044E6FEBE532 /* NotificationPermissionsScreenCoordinator.swift */; };
454F8DDC4442C0DE54094902 /* LABiometryType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3F219838588C62198E726E3 /* LABiometryType.swift */; }; 454F8DDC4442C0DE54094902 /* LABiometryType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3F219838588C62198E726E3 /* LABiometryType.swift */; };
45D6DC594816288983627484 /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; }; 45D6DC594816288983627484 /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; };
@@ -1122,6 +1123,7 @@
AF2ABA2794E376B64104C964 /* MockSoftLogoutScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5644919DB2022397D9D5825A /* MockSoftLogoutScreenState.swift */; }; AF2ABA2794E376B64104C964 /* MockSoftLogoutScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5644919DB2022397D9D5825A /* MockSoftLogoutScreenState.swift */; };
AF33B9044498211C3D82F1E1 /* UNTextInputNotificationResponse+Creator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 130ED565A078F7E0B59D9D25 /* UNTextInputNotificationResponse+Creator.swift */; }; AF33B9044498211C3D82F1E1 /* UNTextInputNotificationResponse+Creator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 130ED565A078F7E0B59D9D25 /* UNTextInputNotificationResponse+Creator.swift */; };
AF4510502727E2ABC326C5FD /* test_animated_image.gif in Resources */ = {isa = PBXBuildFile; fileRef = BDB0DD6E869B4764958CD7AB /* test_animated_image.gif */; }; AF4510502727E2ABC326C5FD /* test_animated_image.gif in Resources */ = {isa = PBXBuildFile; fileRef = BDB0DD6E869B4764958CD7AB /* test_animated_image.gif */; };
AF640383EC85741D33F7558F /* UserSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 100DA305FE28BB3B6AEDE1FD /* UserSettings.swift */; };
AF8BFA37791E1756EE243E08 /* SettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B8F0ED874DF8C9A51B0AB6F /* SettingsScreenCoordinator.swift */; }; AF8BFA37791E1756EE243E08 /* SettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B8F0ED874DF8C9A51B0AB6F /* SettingsScreenCoordinator.swift */; };
AFE2AB612A1460E49578D746 /* JoinRoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BDCCD2F6B405C14B9BCE94E /* JoinRoomScreenCoordinator.swift */; }; AFE2AB612A1460E49578D746 /* JoinRoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BDCCD2F6B405C14B9BCE94E /* JoinRoomScreenCoordinator.swift */; };
B004A08BE32F06832BF41CFC /* RoomDetailsScreenHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3674212CAB6C5143DD437AC4 /* RoomDetailsScreenHook.swift */; }; B004A08BE32F06832BF41CFC /* RoomDetailsScreenHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3674212CAB6C5143DD437AC4 /* RoomDetailsScreenHook.swift */; };
@@ -1360,6 +1362,7 @@
D5E8EE8A288EFCCF646860EA /* KnockRequestsBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47F441A78A5CAA9E2937E463 /* KnockRequestsBannerView.swift */; }; D5E8EE8A288EFCCF646860EA /* KnockRequestsBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47F441A78A5CAA9E2937E463 /* KnockRequestsBannerView.swift */; };
D5FE90A6AF5FD5AE91BD37C7 /* NotificationSettingsEditScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 780258F1B9D15E30549FF4BE /* NotificationSettingsEditScreenViewModel.swift */; }; D5FE90A6AF5FD5AE91BD37C7 /* NotificationSettingsEditScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 780258F1B9D15E30549FF4BE /* NotificationSettingsEditScreenViewModel.swift */; };
D6152E21036B88C44ECB22E7 /* EncryptionResetPasswordScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 303D9438EFB481F57A366E82 /* EncryptionResetPasswordScreenViewModel.swift */; }; D6152E21036B88C44ECB22E7 /* EncryptionResetPasswordScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 303D9438EFB481F57A366E82 /* EncryptionResetPasswordScreenViewModel.swift */; };
D61FCF6ABC1490F630E76AEC /* UserSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 100DA305FE28BB3B6AEDE1FD /* UserSettings.swift */; };
D63974A88CF2BC721F109C77 /* Compound in Frameworks */ = {isa = PBXBuildFile; productRef = DCA3C4A997AD28E6918D4CE5 /* Compound */; }; D63974A88CF2BC721F109C77 /* Compound in Frameworks */ = {isa = PBXBuildFile; productRef = DCA3C4A997AD28E6918D4CE5 /* Compound */; };
D69CCEC785E6C819551B402F /* SearchScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5413213B56FB76C1071E2BFB /* SearchScreenViewModelTests.swift */; }; D69CCEC785E6C819551B402F /* SearchScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5413213B56FB76C1071E2BFB /* SearchScreenViewModelTests.swift */; };
D6DE764B17FB4A9A12C33BF4 /* MessageComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F1DF3FFFE5ED2B8133F43A7 /* MessageComposer.swift */; }; D6DE764B17FB4A9A12C33BF4 /* MessageComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F1DF3FFFE5ED2B8133F43A7 /* MessageComposer.swift */; };
@@ -1853,6 +1856,7 @@
0F793C422BDACE0C60C774F4 /* UserIdentityProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIdentityProxyProtocol.swift; sourceTree = "<group>"; }; 0F793C422BDACE0C60C774F4 /* UserIdentityProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIdentityProxyProtocol.swift; sourceTree = "<group>"; };
0FA60F848D1C14F873F9621A /* RoomMemberDetailsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreenCoordinator.swift; sourceTree = "<group>"; }; 0FA60F848D1C14F873F9621A /* RoomMemberDetailsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreenCoordinator.swift; sourceTree = "<group>"; };
0FD22AEFFA20065494ED2333 /* AppAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAppearance.swift; sourceTree = "<group>"; }; 0FD22AEFFA20065494ED2333 /* AppAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAppearance.swift; sourceTree = "<group>"; };
100DA305FE28BB3B6AEDE1FD /* UserSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettings.swift; sourceTree = "<group>"; };
104B7747499487538483FEAF /* SpacesScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpacesScreen.swift; sourceTree = "<group>"; }; 104B7747499487538483FEAF /* SpacesScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpacesScreen.swift; sourceTree = "<group>"; };
105429F29096729EDD3152CF /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/SAS.strings; sourceTree = "<group>"; }; 105429F29096729EDD3152CF /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/SAS.strings; sourceTree = "<group>"; };
1059E2AE7878CF7820592637 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; }; 1059E2AE7878CF7820592637 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
@@ -6897,6 +6901,7 @@
095B8ED0CA87F4A0A4858719 /* SearchBreadcrumb.swift */, 095B8ED0CA87F4A0A4858719 /* SearchBreadcrumb.swift */,
10D751743B20B314D84D79FE /* TrackedUserDefaults.swift */, 10D751743B20B314D84D79FE /* TrackedUserDefaults.swift */,
0CCA4547BE58E17899E95CDE /* UserDefaultsProtocol.swift */, 0CCA4547BE58E17899E95CDE /* UserDefaultsProtocol.swift */,
100DA305FE28BB3B6AEDE1FD /* UserSettings.swift */,
F2F56C9FFE8D55375CED9741 /* VolatileUserDefaults.swift */, F2F56C9FFE8D55375CED9741 /* VolatileUserDefaults.swift */,
); );
path = Settings; path = Settings;
@@ -8311,6 +8316,7 @@
D98B5EE8C4F5A2CE84687AE8 /* UTType.swift in Sources */, D98B5EE8C4F5A2CE84687AE8 /* UTType.swift in Sources */,
34357B287357BC0B9715DD51 /* UserAgentBuilder.swift in Sources */, 34357B287357BC0B9715DD51 /* UserAgentBuilder.swift in Sources */,
1B1404DD4BB2C5079B074210 /* UserDefaultsProtocol.swift in Sources */, 1B1404DD4BB2C5079B074210 /* UserDefaultsProtocol.swift in Sources */,
D61FCF6ABC1490F630E76AEC /* UserSettings.swift in Sources */,
28A494A66D0F21EC1EC4EB11 /* VolatileUserDefaults.swift in Sources */, 28A494A66D0F21EC1EC4EB11 /* VolatileUserDefaults.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@@ -8560,6 +8566,7 @@
26252AA9AED64010788F4C26 /* UIView.swift in Sources */, 26252AA9AED64010788F4C26 /* UIView.swift in Sources */,
66E9202BED03B5BB00E812A1 /* URL.swift in Sources */, 66E9202BED03B5BB00E812A1 /* URL.swift in Sources */,
29E411E37D3FBF3899B3ED69 /* UserDefaultsProtocol.swift in Sources */, 29E411E37D3FBF3899B3ED69 /* UserDefaultsProtocol.swift in Sources */,
452507BCE97B3EA0F24E206E /* UserSettings.swift in Sources */,
CDD76A7D42E1B47F7E423AA8 /* VolatileUserDefaults.swift in Sources */, CDD76A7D42E1B47F7E423AA8 /* VolatileUserDefaults.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@@ -9675,6 +9682,7 @@
7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */, 7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */,
79D57E9AE03A2DC689D14EA2 /* UserSessionStoreMock.swift in Sources */, 79D57E9AE03A2DC689D14EA2 /* UserSessionStoreMock.swift in Sources */,
AC69B6DF15FC451AB2945036 /* UserSessionStoreProtocol.swift in Sources */, AC69B6DF15FC451AB2945036 /* UserSessionStoreProtocol.swift in Sources */,
AF640383EC85741D33F7558F /* UserSettings.swift in Sources */,
9707AF8D41667FA9B35E8953 /* UserToInvite.swift in Sources */, 9707AF8D41667FA9B35E8953 /* UserToInvite.swift in Sources */,
2447FADEF13225BB6227B977 /* VerificationBadge.swift in Sources */, 2447FADEF13225BB6227B977 /* VerificationBadge.swift in Sources */,
5C33976A720B64094CBC56B1 /* VideoMediaEventsTimelineView.swift in Sources */, 5C33976A720B64094CBC56B1 /* VideoMediaEventsTimelineView.swift in Sources */,
@@ -775,11 +775,11 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
let flowParameters = CommonFlowParameters(userSession: userSession, let flowParameters = CommonFlowParameters(userSession: userSession,
bugReportService: bugReportService, bugReportService: bugReportService,
elementCallService: elementCallService, elementCallService: elementCallService,
timelineControllerFactory: TimelineControllerFactory(appSettings: appSettings), timelineControllerFactory: TimelineControllerFactory(userSettings: userSession.userSettings),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(userSettings: userSession.userSettings),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
appMediator: appMediator, appMediator: appMediator,
appSettings: appSettings, userSettings: userSession.userSettings, // TODO: @pixlwave Remove this in favour of the userSession.
appHooks: appHooks, appHooks: appHooks,
analytics: analyticsService, analytics: analyticsService,
userIndicatorController: userIndicatorController, userIndicatorController: userIndicatorController,
@@ -31,7 +31,7 @@ struct CommonFlowParameters {
let emojiProvider: EmojiProviderProtocol let emojiProvider: EmojiProviderProtocol
let linkMetadataProvider: LinkMetadataProviderProtocol let linkMetadataProvider: LinkMetadataProviderProtocol
let appMediator: AppMediatorProtocol let appMediator: AppMediatorProtocol
let appSettings: AppSettings let userSettings: UserSettings
let appHooks: AppHooks let appHooks: AppHooks
let analytics: AnalyticsServiceProtocol let analytics: AnalyticsServiceProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
@@ -383,7 +383,7 @@ class ChatsTabFlowCoordinator: FlowCoordinatorProtocol {
let parameters = HomeScreenCoordinatorParameters(userSession: userSession, let parameters = HomeScreenCoordinatorParameters(userSession: userSession,
bugReportService: flowParameters.bugReportService, bugReportService: flowParameters.bugReportService,
selectedRoomPublisher: selectedRoomSubject.asCurrentValuePublisher(), selectedRoomPublisher: selectedRoomSubject.asCurrentValuePublisher(),
appSettings: flowParameters.appSettings, userSettings: flowParameters.userSettings,
analyticsService: flowParameters.analytics, analyticsService: flowParameters.analytics,
notificationManager: flowParameters.notificationManager, notificationManager: flowParameters.notificationManager,
userIndicatorController: flowParameters.userIndicatorController) userIndicatorController: flowParameters.userIndicatorController)
@@ -687,7 +687,6 @@ class ChatsTabFlowCoordinator: FlowCoordinatorProtocol {
let sheetNavigationStackCoordinator = NavigationStackCoordinator() let sheetNavigationStackCoordinator = NavigationStackCoordinator()
let parameters = EncryptionResetFlowCoordinatorParameters(userSession: userSession, let parameters = EncryptionResetFlowCoordinatorParameters(userSession: userSession,
appMediator: flowParameters.appMediator, appMediator: flowParameters.appMediator,
appSettings: flowParameters.appSettings,
appHooks: flowParameters.appHooks, appHooks: flowParameters.appHooks,
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
navigationStackCoordinator: sheetNavigationStackCoordinator, navigationStackCoordinator: sheetNavigationStackCoordinator,
@@ -20,7 +20,6 @@ enum EncryptionResetFlowCoordinatorAction: Equatable {
struct EncryptionResetFlowCoordinatorParameters { struct EncryptionResetFlowCoordinatorParameters {
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let appMediator: AppMediatorProtocol let appMediator: AppMediatorProtocol
let appSettings: AppSettings
let appHooks: AppHooks let appHooks: AppHooks
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let navigationStackCoordinator: NavigationStackCoordinator let navigationStackCoordinator: NavigationStackCoordinator
@@ -30,7 +29,7 @@ struct EncryptionResetFlowCoordinatorParameters {
class EncryptionResetFlowCoordinator: FlowCoordinatorProtocol { class EncryptionResetFlowCoordinator: FlowCoordinatorProtocol {
private let userSession: UserSessionProtocol private let userSession: UserSessionProtocol
private let appMediator: AppMediatorProtocol private let appMediator: AppMediatorProtocol
private let appSettings: AppSettings private let userSettings: UserSettings
private let appHooks: AppHooks private let appHooks: AppHooks
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
@@ -67,7 +66,7 @@ class EncryptionResetFlowCoordinator: FlowCoordinatorProtocol {
init(parameters: EncryptionResetFlowCoordinatorParameters) { init(parameters: EncryptionResetFlowCoordinatorParameters) {
userSession = parameters.userSession userSession = parameters.userSession
appMediator = parameters.appMediator appMediator = parameters.appMediator
appSettings = parameters.appSettings userSettings = parameters.userSession.userSettings
appHooks = parameters.appHooks appHooks = parameters.appHooks
userIndicatorController = parameters.userIndicatorController userIndicatorController = parameters.userIndicatorController
navigationStackCoordinator = parameters.navigationStackCoordinator navigationStackCoordinator = parameters.navigationStackCoordinator
@@ -165,7 +164,7 @@ class EncryptionResetFlowCoordinator: FlowCoordinatorProtocol {
accountSettingsPresenter = OAuthAccountSettingsPresenter(accountURL: url, accountSettingsPresenter = OAuthAccountSettingsPresenter(accountURL: url,
presentationAnchor: windowManager.mainWindow, presentationAnchor: windowManager.mainWindow,
appMediator: appMediator, appMediator: appMediator,
appSettings: appSettings, userSettings: userSettings,
appHooks: appHooks) appHooks: appHooks)
accountSettingsPresenter?.start() accountSettingsPresenter?.start()
} }
@@ -17,7 +17,6 @@ enum EncryptionSettingsFlowCoordinatorAction: Equatable {
struct EncryptionSettingsFlowCoordinatorParameters { struct EncryptionSettingsFlowCoordinatorParameters {
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let appSettings: AppSettings
let appHooks: AppHooks let appHooks: AppHooks
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let navigationStackCoordinator: NavigationStackCoordinator let navigationStackCoordinator: NavigationStackCoordinator
@@ -25,7 +24,7 @@ struct EncryptionSettingsFlowCoordinatorParameters {
class EncryptionSettingsFlowCoordinator: FlowCoordinatorProtocol { class EncryptionSettingsFlowCoordinator: FlowCoordinatorProtocol {
private let userSession: UserSessionProtocol private let userSession: UserSessionProtocol
private let appSettings: AppSettings private let userSettings: UserSettings
private let appHooks: AppHooks private let appHooks: AppHooks
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let navigationStackCoordinator: NavigationStackCoordinator private let navigationStackCoordinator: NavigationStackCoordinator
@@ -66,7 +65,7 @@ class EncryptionSettingsFlowCoordinator: FlowCoordinatorProtocol {
init(parameters: EncryptionSettingsFlowCoordinatorParameters) { init(parameters: EncryptionSettingsFlowCoordinatorParameters) {
userSession = parameters.userSession userSession = parameters.userSession
appSettings = parameters.appSettings userSettings = parameters.userSession.userSettings
appHooks = parameters.appHooks appHooks = parameters.appHooks
userIndicatorController = parameters.userIndicatorController userIndicatorController = parameters.userIndicatorController
navigationStackCoordinator = parameters.navigationStackCoordinator navigationStackCoordinator = parameters.navigationStackCoordinator
@@ -139,7 +138,7 @@ class EncryptionSettingsFlowCoordinator: FlowCoordinatorProtocol {
} }
private func presentSecureBackupScreen(animated: Bool = true) { private func presentSecureBackupScreen(animated: Bool = true) {
let coordinator = SecureBackupScreenCoordinator(parameters: .init(appSettings: appSettings, let coordinator = SecureBackupScreenCoordinator(parameters: .init(userSettings: userSettings,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
userIndicatorController: userIndicatorController)) userIndicatorController: userIndicatorController))
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
@@ -84,7 +84,7 @@ class MediaEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
userSession: userSession, userSession: userSession,
mediaPlayerProvider: MediaPlayerProvider(), mediaPlayerProvider: MediaPlayerProvider(),
appMediator: flowParameters.appMediator, appMediator: flowParameters.appMediator,
appSettings: flowParameters.appSettings, userSettings: flowParameters.userSettings,
analytics: flowParameters.analytics, analytics: flowParameters.analytics,
emojiProvider: flowParameters.emojiProvider, emojiProvider: flowParameters.emojiProvider,
linkMetadataProvider: flowParameters.linkMetadataProvider, linkMetadataProvider: flowParameters.linkMetadataProvider,
@@ -21,7 +21,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
private let appLockService: AppLockServiceProtocol private let appLockService: AppLockServiceProtocol
private let analyticsService: AnalyticsServiceProtocol private let analyticsService: AnalyticsServiceProtocol
private let appMediator: AppMediatorProtocol private let appMediator: AppMediatorProtocol
private let appSettings: AppSettings private let userSettings: UserSettings
private let appHooks: AppHooks private let appHooks: AppHooks
private let notificationManager: NotificationManagerProtocol private let notificationManager: NotificationManagerProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
@@ -69,7 +69,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
self.appLockService = appLockService self.appLockService = appLockService
analyticsService = flowParameters.analytics analyticsService = flowParameters.analytics
appMediator = flowParameters.appMediator appMediator = flowParameters.appMediator
appSettings = flowParameters.appSettings userSettings = flowParameters.userSettings
appHooks = flowParameters.appHooks appHooks = flowParameters.appHooks
notificationManager = flowParameters.notificationManager notificationManager = flowParameters.notificationManager
userIndicatorController = flowParameters.userIndicatorController userIndicatorController = flowParameters.userIndicatorController
@@ -94,7 +94,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
value == .verified, value == .verified,
stateMachine.state == .identityConfirmation else { return } stateMachine.state == .identityConfirmation else { return }
appSettings.hasRunIdentityConfirmationOnboarding = true userSettings.hasRunIdentityConfirmationOnboarding = true
stateMachine.tryEvent(.nextSkippingIdentityConfirmed) stateMachine.tryEvent(.nextSkippingIdentityConfirmed)
} }
} }
@@ -129,11 +129,11 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
private var requiresVerification: Bool { private var requiresVerification: Bool {
// We want to make sure onboarding finishes but also every time the user becomes unverified (e.g. account reset) // We want to make sure onboarding finishes but also every time the user becomes unverified (e.g. account reset)
!appSettings.hasRunIdentityConfirmationOnboarding || userSession.sessionSecurityStatePublisher.value.verificationState == .unverified !userSettings.hasRunIdentityConfirmationOnboarding || userSession.sessionSecurityStatePublisher.value.verificationState == .unverified
} }
private var requiresAppLockSetup: Bool { private var requiresAppLockSetup: Bool {
appSettings.appLockIsMandatory && !appLockService.isEnabled userSettings.appLockIsMandatory && !appLockService.isEnabled
} }
private var requiresAnalyticsSetup: Bool { private var requiresAnalyticsSetup: Bool {
@@ -141,7 +141,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
} }
private var requiresNotificationsSetup: Bool { private var requiresNotificationsSetup: Bool {
!appSettings.hasRunNotificationPermissionsOnboarding !userSettings.hasRunNotificationPermissionsOnboarding
} }
private func configureStateMachine() { private func configureStateMachine() {
@@ -225,7 +225,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
presentNotificationPermissionsScreen() presentNotificationPermissionsScreen()
case (_, _, .finished): case (_, _, .finished):
isNewLogin = false isNewLogin = false
appSettings.hasSignedInBefore = true userSettings.hasSignedInBefore = true
actionsSubject.send(.dismiss) actionsSubject.send(.dismiss)
stateMachine.tryState(.initial) stateMachine.tryState(.initial)
case (.finished, _, .initial): case (.finished, _, .initial):
@@ -248,7 +248,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
private func presentIdentityConfirmationScreen() { private func presentIdentityConfirmationScreen() {
let parameters = IdentityConfirmationScreenCoordinatorParameters(userSession: userSession, let parameters = IdentityConfirmationScreenCoordinatorParameters(userSession: userSession,
appSettings: appSettings, userSettings: userSettings,
userIndicatorController: userIndicatorController) userIndicatorController: userIndicatorController)
let coordinator = IdentityConfirmationScreenCoordinator(parameters: parameters) let coordinator = IdentityConfirmationScreenCoordinator(parameters: parameters)
@@ -261,7 +261,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
case .recoveryKey: case .recoveryKey:
presentRecoveryKeyScreen() presentRecoveryKeyScreen()
case .skip: case .skip:
appSettings.hasRunIdentityConfirmationOnboarding = true userSettings.hasRunIdentityConfirmationOnboarding = true
stateMachine.tryEvent(.nextSkippingIdentityConfirmed) stateMachine.tryEvent(.nextSkippingIdentityConfirmed)
case .reset: case .reset:
startEncryptionResetFlow() startEncryptionResetFlow()
@@ -281,7 +281,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
let parameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationController, let parameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationController,
flow: .deviceInitiator, flow: .deviceInitiator,
appSettings: appSettings, userSettings: userSettings,
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
let coordinator = SessionVerificationScreenCoordinator(parameters: parameters) let coordinator = SessionVerificationScreenCoordinator(parameters: parameters)
@@ -322,7 +322,6 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
let resetNavigationStackCoordinator = NavigationStackCoordinator() let resetNavigationStackCoordinator = NavigationStackCoordinator()
let coordinator = EncryptionResetFlowCoordinator(parameters: .init(userSession: userSession, let coordinator = EncryptionResetFlowCoordinator(parameters: .init(userSession: userSession,
appMediator: appMediator, appMediator: appMediator,
appSettings: appSettings,
appHooks: appHooks, appHooks: appHooks,
userIndicatorController: userIndicatorController, userIndicatorController: userIndicatorController,
navigationStackCoordinator: resetNavigationStackCoordinator, navigationStackCoordinator: resetNavigationStackCoordinator,
@@ -385,7 +384,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
} }
private func presentAnalyticsPromptScreen() { private func presentAnalyticsPromptScreen() {
let coordinator = AnalyticsPromptScreenCoordinator(analytics: analyticsService, termsURL: appSettings.analyticsTermsURL) let coordinator = AnalyticsPromptScreenCoordinator(analytics: analyticsService, termsURL: userSettings.analyticsTermsURL)
coordinator.actions coordinator.actions
.sink { [weak self] action in .sink { [weak self] action in
@@ -408,7 +407,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
guard let self else { return } guard let self else { return }
switch action { switch action {
case .done: case .done:
appSettings.hasRunNotificationPermissionsOnboarding = true userSettings.hasRunNotificationPermissionsOnboarding = true
stateMachine.tryEvent(.next) stateMachine.tryEvent(.next)
} }
} }
@@ -69,7 +69,7 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
userSession: userSession, userSession: userSession,
mediaPlayerProvider: MediaPlayerProvider(), mediaPlayerProvider: MediaPlayerProvider(),
appMediator: flowParameters.appMediator, appMediator: flowParameters.appMediator,
appSettings: flowParameters.appSettings, userSettings: flowParameters.userSettings,
analytics: flowParameters.analytics, analytics: flowParameters.analytics,
emojiProvider: flowParameters.emojiProvider, emojiProvider: flowParameters.emojiProvider,
linkMetadataProvider: flowParameters.linkMetadataProvider, linkMetadataProvider: flowParameters.linkMetadataProvider,
@@ -107,11 +107,11 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
let stackCoordinator = NavigationStackCoordinator() let stackCoordinator = NavigationStackCoordinator()
let params = LocationSharingScreenCoordinatorParameters(interactionMode: interactionMode, let params = LocationSharingScreenCoordinatorParameters(interactionMode: interactionMode,
mapURLBuilder: flowParameters.appSettings.mapTilerConfiguration.publisher.value, mapURLBuilder: flowParameters.userSettings.mapTilerConfiguration.publisher.value,
roomProxy: roomProxy, roomProxy: roomProxy,
timelineController: timelineController, timelineController: timelineController,
liveLocationManager: flowParameters.userSession.liveLocationManager, liveLocationManager: flowParameters.userSession.liveLocationManager,
appSettings: flowParameters.appSettings, userSettings: flowParameters.userSettings,
appMediator: flowParameters.appMediator, appMediator: flowParameters.appMediator,
analytics: flowParameters.analytics, analytics: flowParameters.analytics,
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
@@ -238,7 +238,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
defer { hideLoadingIndicator() } defer { hideLoadingIndicator() }
switch await roomProxy.loadOrFetchEventDetails(for: eventID) { switch await roomProxy.loadOrFetchEventDetails(for: eventID) {
case .success(let event): case .success(let event):
if flowParameters.appSettings.threadsEnabled, let threadRootEventID = event.threadRootEventId() { if flowParameters.userSettings.threadsEnabled, let threadRootEventID = event.threadRootEventId() {
if case .thread(threadRootEventID: threadRootEventID, _) = stateMachine.state, let threadCoordinator = childThreadScreenCoordinators.last { if case .thread(threadRootEventID: threadRootEventID, _) = stateMachine.state, let threadCoordinator = childThreadScreenCoordinators.last {
threadCoordinator.focusOnEvent(eventID: eventID) threadCoordinator.focusOnEvent(eventID: eventID)
} else { } else {
@@ -318,7 +318,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
// Otherwise check if the focussed event exists to handle a possible error or theaded event. // Otherwise check if the focussed event exists to handle a possible error or theaded event.
switch await roomProxy.loadOrFetchEventDetails(for: focusEvent.eventID) { switch await roomProxy.loadOrFetchEventDetails(for: focusEvent.eventID) {
case .success(let event): case .success(let event):
if flowParameters.appSettings.threadsEnabled, let threadRootEventID = event.threadRootEventId() { if flowParameters.userSettings.threadsEnabled, let threadRootEventID = event.threadRootEventId() {
stateMachine.tryEvent(.presentRoom(presentationAction: .thread(rootEventID: threadRootEventID, stateMachine.tryEvent(.presentRoom(presentationAction: .thread(rootEventID: threadRootEventID,
focusEvent: .init(eventID: focusEvent.eventID, focusEvent: .init(eventID: focusEvent.eventID,
shouldSetPin: focusEvent.shouldSetPin))), shouldSetPin: focusEvent.shouldSetPin))),
@@ -681,7 +681,6 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
completionSuggestionService: completionSuggestionService, completionSuggestionService: completionSuggestionService,
ongoingCallRoomIDPublisher: flowParameters.ongoingCallRoomIDPublisher, ongoingCallRoomIDPublisher: flowParameters.ongoingCallRoomIDPublisher,
appMediator: flowParameters.appMediator, appMediator: flowParameters.appMediator,
appSettings: flowParameters.appSettings,
appHooks: flowParameters.appHooks, appHooks: flowParameters.appHooks,
analytics: flowParameters.analytics, analytics: flowParameters.analytics,
composerDraftService: composerDraftService, composerDraftService: composerDraftService,
@@ -799,7 +798,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
linkMetadataProvider: flowParameters.linkMetadataProvider, linkMetadataProvider: flowParameters.linkMetadataProvider,
completionSuggestionService: completionSuggestionService, completionSuggestionService: completionSuggestionService,
appMediator: flowParameters.appMediator, appMediator: flowParameters.appMediator,
appSettings: flowParameters.appSettings, userSettings: flowParameters.userSettings,
analytics: flowParameters.analytics, analytics: flowParameters.analytics,
composerDraftService: composerDraftService, composerDraftService: composerDraftService,
timelineControllerFactory: flowParameters.timelineControllerFactory, timelineControllerFactory: flowParameters.timelineControllerFactory,
@@ -857,7 +856,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let coordinator = JoinRoomScreenCoordinator(parameters: .init(source: .generic(roomID: roomID, via: via), let coordinator = JoinRoomScreenCoordinator(parameters: .init(source: .generic(roomID: roomID, via: via),
userSession: userSession, userSession: userSession,
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
appSettings: flowParameters.appSettings)) userSettings: flowParameters.userSettings))
joinRoomScreenCoordinator = coordinator joinRoomScreenCoordinator = coordinator
@@ -940,7 +939,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
private func presentRoomDetails(isRoot: Bool, animated: Bool) async { private func presentRoomDetails(isRoot: Bool, animated: Bool) async {
let params = RoomDetailsScreenCoordinatorParameters(roomProxy: roomProxy, let params = RoomDetailsScreenCoordinatorParameters(roomProxy: roomProxy,
userSession: userSession, userSession: userSession,
appSettings: flowParameters.appSettings, userSettings: flowParameters.userSettings,
appHooks: flowParameters.appHooks, appHooks: flowParameters.appHooks,
analyticsService: flowParameters.analytics, analyticsService: flowParameters.analytics,
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
@@ -1020,7 +1019,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let roomDetailsEditParameters = RoomDetailsEditScreenCoordinatorParameters(roomProxy: roomProxy, let roomDetailsEditParameters = RoomDetailsEditScreenCoordinatorParameters(roomProxy: roomProxy,
userSession: userSession, userSession: userSession,
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: flowParameters.appSettings), mediaUploadingPreprocessor: MediaUploadingPreprocessor(userSettings: flowParameters.userSettings),
navigationStackCoordinator: stackCoordinator, navigationStackCoordinator: stackCoordinator,
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
orientationManager: flowParameters.appMediator.windowManager) orientationManager: flowParameters.appMediator.windowManager)
@@ -1120,9 +1119,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let parameters = MediaUploadPreviewScreenCoordinatorParameters(mediaURLs: mediaURLs, let parameters = MediaUploadPreviewScreenCoordinatorParameters(mediaURLs: mediaURLs,
caption: caption, caption: caption,
title: title, title: title,
shouldShowCaptionWarning: flowParameters.appSettings.shouldShowMediaCaptionWarning, shouldShowCaptionWarning: flowParameters.userSettings.shouldShowMediaCaptionWarning,
galleryEnabled: flowParameters.appSettings.galleryEnabled, galleryEnabled: flowParameters.userSettings.galleryEnabled,
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: flowParameters.appSettings), mediaUploadingPreprocessor: MediaUploadingPreprocessor(userSettings: flowParameters.userSettings),
timelineController: timelineController, timelineController: timelineController,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
userIndicatorController: flowParameters.userIndicatorController) userIndicatorController: flowParameters.userIndicatorController)
@@ -1177,11 +1176,11 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let stackCoordinator = NavigationStackCoordinator() let stackCoordinator = NavigationStackCoordinator()
let params = LocationSharingScreenCoordinatorParameters(interactionMode: interactionMode, let params = LocationSharingScreenCoordinatorParameters(interactionMode: interactionMode,
mapURLBuilder: flowParameters.appSettings.mapTilerConfiguration.publisher.value, mapURLBuilder: flowParameters.userSettings.mapTilerConfiguration.publisher.value,
roomProxy: roomProxy, roomProxy: roomProxy,
timelineController: timelineController, timelineController: timelineController,
liveLocationManager: flowParameters.userSession.liveLocationManager, liveLocationManager: flowParameters.userSession.liveLocationManager,
appSettings: flowParameters.appSettings, userSettings: flowParameters.userSettings,
appMediator: flowParameters.appMediator, appMediator: flowParameters.appMediator,
analytics: flowParameters.analytics, analytics: flowParameters.analytics,
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
@@ -1326,7 +1325,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
userNotificationCenter: UNUserNotificationCenter.current(), userNotificationCenter: UNUserNotificationCenter.current(),
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
isModallyPresented: true, isModallyPresented: true,
appSettings: flowParameters.appSettings) userSettings: flowParameters.userSettings)
let coordinator = NotificationSettingsScreenCoordinator(parameters: parameters) let coordinator = NotificationSettingsScreenCoordinator(parameters: parameters)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
switch action { switch action {
@@ -1421,7 +1420,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let coordinator = SecurityAndPrivacyScreenCoordinator(parameters: .init(roomProxy: roomProxy, let coordinator = SecurityAndPrivacyScreenCoordinator(parameters: .init(roomProxy: roomProxy,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
appSetting: flowParameters.appSettings)) appSetting: flowParameters.userSettings))
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -73,7 +73,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
private func presentSettingsScreen(animated: Bool) { private func presentSettingsScreen(animated: Bool) {
let settingsScreenCoordinator = SettingsScreenCoordinator(parameters: .init(userSession: flowParameters.userSession, let settingsScreenCoordinator = SettingsScreenCoordinator(parameters: .init(userSession: flowParameters.userSession,
appSettings: flowParameters.appSettings, userSettings: flowParameters.userSettings,
isBugReportServiceEnabled: flowParameters.bugReportService.isEnabled, isBugReportServiceEnabled: flowParameters.bugReportService.isEnabled,
isInSecondaryWindow: isInSecondaryWindow, isInSecondaryWindow: isInSecondaryWindow,
userIndicatorController: flowParameters.userIndicatorController)) userIndicatorController: flowParameters.userIndicatorController))
@@ -129,7 +129,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
} }
private func presentLabs() { private func presentLabs() {
let coordinator = LabsScreenCoordinator(parameters: .init(appSettings: flowParameters.appSettings)) let coordinator = LabsScreenCoordinator(parameters: .init(userSettings: flowParameters.userSettings))
coordinator.actions coordinator.actions
.sink { [weak self] action in .sink { [weak self] action in
switch action { switch action {
@@ -144,7 +144,6 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
private func startEncryptionSettingsFlow() { private func startEncryptionSettingsFlow() {
let coordinator = EncryptionSettingsFlowCoordinator(parameters: .init(userSession: flowParameters.userSession, let coordinator = EncryptionSettingsFlowCoordinator(parameters: .init(userSession: flowParameters.userSession,
appSettings: flowParameters.appSettings,
appHooks: flowParameters.appHooks, appHooks: flowParameters.appHooks,
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
navigationStackCoordinator: navigationStackCoordinator)) navigationStackCoordinator: navigationStackCoordinator))
@@ -181,7 +180,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
private func presentUserDetailsEditScreen() { private func presentUserDetailsEditScreen() {
let coordinator = UserDetailsEditScreenCoordinator(parameters: .init(orientationManager: flowParameters.windowManager, let coordinator = UserDetailsEditScreenCoordinator(parameters: .init(orientationManager: flowParameters.windowManager,
userSession: flowParameters.userSession, userSession: flowParameters.userSession,
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: flowParameters.appSettings), mediaUploadingPreprocessor: MediaUploadingPreprocessor(userSettings: flowParameters.userSettings),
navigationStackCoordinator: navigationStackCoordinator, navigationStackCoordinator: navigationStackCoordinator,
userIndicatorController: flowParameters.userIndicatorController)) userIndicatorController: flowParameters.userIndicatorController))
coordinator.actions coordinator.actions
@@ -225,7 +224,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
} }
private func presentAnalyticsScreen() { private func presentAnalyticsScreen() {
let coordinator = AnalyticsSettingsScreenCoordinator(parameters: .init(appSettings: flowParameters.appSettings, let coordinator = AnalyticsSettingsScreenCoordinator(parameters: .init(userSettings: flowParameters.userSettings,
analytics: flowParameters.analytics)) analytics: flowParameters.analytics))
navigationStackCoordinator.push(coordinator) navigationStackCoordinator.push(coordinator)
} }
@@ -251,11 +250,11 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
} }
private func presentLegalInformationScreen() { private func presentLegalInformationScreen() {
navigationStackCoordinator.push(LegalInformationScreenCoordinator(appSettings: flowParameters.appSettings)) navigationStackCoordinator.push(LegalInformationScreenCoordinator(userSettings: flowParameters.userSettings))
} }
private func presentBlockedUsersScreen() { private func presentBlockedUsersScreen() {
let coordinator = BlockedUsersScreenCoordinator(parameters: .init(hideProfiles: flowParameters.appSettings.hideIgnoredUserProfiles, let coordinator = BlockedUsersScreenCoordinator(parameters: .init(hideProfiles: flowParameters.userSettings.hideIgnoredUserProfiles,
userSession: flowParameters.userSession, userSession: flowParameters.userSession,
userIndicatorController: flowParameters.userIndicatorController)) userIndicatorController: flowParameters.userIndicatorController))
navigationStackCoordinator.push(coordinator) navigationStackCoordinator.push(coordinator)
@@ -267,13 +266,13 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
userNotificationCenter: UNUserNotificationCenter.current(), userNotificationCenter: UNUserNotificationCenter.current(),
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
isModallyPresented: false, isModallyPresented: false,
appSettings: flowParameters.appSettings) userSettings: flowParameters.userSettings)
let coordinator = NotificationSettingsScreenCoordinator(parameters: notificationParameters) let coordinator = NotificationSettingsScreenCoordinator(parameters: notificationParameters)
navigationStackCoordinator.push(coordinator) navigationStackCoordinator.push(coordinator)
} }
private func presentAdvancedSettings() { private func presentAdvancedSettings() {
let coordinator = AdvancedSettingsScreenCoordinator(parameters: .init(appSettings: flowParameters.appSettings, let coordinator = AdvancedSettingsScreenCoordinator(parameters: .init(userSettings: flowParameters.userSettings,
analytics: flowParameters.analytics, analytics: flowParameters.analytics,
clientProxy: flowParameters.userSession.clientProxy, clientProxy: flowParameters.userSession.clientProxy,
userIndicatorController: flowParameters.userIndicatorController)) userIndicatorController: flowParameters.userIndicatorController))
@@ -281,7 +280,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
} }
private func presentDeveloperOptions() { private func presentDeveloperOptions() {
let coordinator = DeveloperOptionsScreenCoordinator(appSettings: flowParameters.appSettings, let coordinator = DeveloperOptionsScreenCoordinator(appSettings: flowParameters.userSettings.app,
appHooks: flowParameters.appHooks, appHooks: flowParameters.appHooks,
clientProxy: flowParameters.userSession.clientProxy) clientProxy: flowParameters.userSession.clientProxy)
@@ -327,7 +326,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
accountSettingsPresenter = OAuthAccountSettingsPresenter(accountURL: url, accountSettingsPresenter = OAuthAccountSettingsPresenter(accountURL: url,
presentationAnchor: flowParameters.windowManager.mainWindow, presentationAnchor: flowParameters.windowManager.mainWindow,
appMediator: flowParameters.appMediator, appMediator: flowParameters.appMediator,
appSettings: flowParameters.appSettings, userSettings: flowParameters.userSettings,
appHooks: flowParameters.appHooks, appHooks: flowParameters.appHooks,
continuation: continuation) continuation: continuation)
accountSettingsPresenter?.start() accountSettingsPresenter?.start()
@@ -400,7 +400,7 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
let parameters = JoinRoomScreenCoordinatorParameters(source: .space(spaceServiceRoom), let parameters = JoinRoomScreenCoordinatorParameters(source: .space(spaceServiceRoom),
userSession: flowParameters.userSession, userSession: flowParameters.userSession,
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
appSettings: flowParameters.appSettings) userSettings: flowParameters.userSettings)
let coordinator = JoinRoomScreenCoordinator(parameters: parameters) let coordinator = JoinRoomScreenCoordinator(parameters: parameters)
coordinator.actionsPublisher coordinator.actionsPublisher
.sink { [weak self] action in .sink { [weak self] action in
@@ -211,7 +211,7 @@ final class SpaceSettingsFlowCoordinator: FlowCoordinatorProtocol {
private func presentSpaceSettings(animated: Bool) { private func presentSpaceSettings(animated: Bool) {
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(roomProxy: roomProxy, let coordinator = RoomDetailsScreenCoordinator(parameters: .init(roomProxy: roomProxy,
userSession: flowParameters.userSession, userSession: flowParameters.userSession,
appSettings: flowParameters.appSettings, userSettings: flowParameters.userSettings,
appHooks: flowParameters.appHooks, appHooks: flowParameters.appHooks,
analyticsService: flowParameters.analytics, analyticsService: flowParameters.analytics,
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
@@ -252,7 +252,7 @@ final class SpaceSettingsFlowCoordinator: FlowCoordinatorProtocol {
let stackCoordinator = NavigationStackCoordinator() let stackCoordinator = NavigationStackCoordinator()
let parameters = RoomDetailsEditScreenCoordinatorParameters(roomProxy: roomProxy, let parameters = RoomDetailsEditScreenCoordinatorParameters(roomProxy: roomProxy,
userSession: flowParameters.userSession, userSession: flowParameters.userSession,
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: flowParameters.appSettings), mediaUploadingPreprocessor: MediaUploadingPreprocessor(userSettings: flowParameters.userSettings),
navigationStackCoordinator: stackCoordinator, navigationStackCoordinator: stackCoordinator,
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
orientationManager: flowParameters.appMediator.windowManager) orientationManager: flowParameters.appMediator.windowManager)
@@ -278,7 +278,7 @@ final class SpaceSettingsFlowCoordinator: FlowCoordinatorProtocol {
let coordinator = SecurityAndPrivacyScreenCoordinator(parameters: .init(roomProxy: roomProxy, let coordinator = SecurityAndPrivacyScreenCoordinator(parameters: .init(roomProxy: roomProxy,
clientProxy: flowParameters.userSession.clientProxy, clientProxy: flowParameters.userSession.clientProxy,
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
appSetting: flowParameters.appSettings)) appSetting: flowParameters.userSettings))
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -214,7 +214,7 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
shouldShowCancelButton: isRoot, shouldShowCancelButton: isRoot,
userSession: flowParameters.userSession, userSession: flowParameters.userSession,
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
appSettings: flowParameters.appSettings, userSettings: flowParameters.userSettings,
analytics: flowParameters.analytics) analytics: flowParameters.analytics)
let coordinator = CreateRoomScreenCoordinator(parameters: createParameters) let coordinator = CreateRoomScreenCoordinator(parameters: createParameters)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
@@ -82,29 +82,29 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
self.appLockService = appLockService self.appLockService = appLockService
self.flowParameters = flowParameters self.flowParameters = flowParameters
presenceService = PresenceService(clientProxy: flowParameters.userSession.clientProxy, presenceService = PresenceService(clientProxy: flowParameters.userSession.clientProxy,
appSettings: flowParameters.appSettings) userSettings: flowParameters.userSettings)
navigationTabCoordinator = NavigationTabCoordinator() navigationTabCoordinator = NavigationTabCoordinator()
navigationRootCoordinator.setRootCoordinator(navigationTabCoordinator) navigationRootCoordinator.setRootCoordinator(navigationTabCoordinator)
let chatsSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator(hideBrandChrome: flowParameters.appSettings.hideBrandChrome)) let chatsSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator(hideBrandChrome: flowParameters.userSettings.hideBrandChrome))
chatsTabFlowCoordinator = ChatsTabFlowCoordinator(navigationSplitCoordinator: chatsSplitCoordinator, chatsTabFlowCoordinator = ChatsTabFlowCoordinator(navigationSplitCoordinator: chatsSplitCoordinator,
flowParameters: flowParameters) flowParameters: flowParameters)
chatsTabDetails = .init(tag: HomeTab.chats, title: L10n.screenHomeTabChats, icon: \.chat, selectedIcon: \.chatSolid) chatsTabDetails = .init(tag: HomeTab.chats, title: L10n.screenHomeTabChats, icon: \.chat, selectedIcon: \.chatSolid)
chatsTabDetails.navigationSplitCoordinator = chatsSplitCoordinator chatsTabDetails.navigationSplitCoordinator = chatsSplitCoordinator
let spacesSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator(hideBrandChrome: flowParameters.appSettings.hideBrandChrome)) let spacesSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator(hideBrandChrome: flowParameters.userSettings.hideBrandChrome))
spacesTabFlowCoordinator = SpacesTabFlowCoordinator(navigationSplitCoordinator: spacesSplitCoordinator, spacesTabFlowCoordinator = SpacesTabFlowCoordinator(navigationSplitCoordinator: spacesSplitCoordinator,
flowParameters: flowParameters) flowParameters: flowParameters)
spacesTabDetails = .init(tag: HomeTab.spaces, title: L10n.screenHomeTabSpaces, icon: \.space, selectedIcon: \.spaceSolid) spacesTabDetails = .init(tag: HomeTab.spaces, title: L10n.screenHomeTabSpaces, icon: \.space, selectedIcon: \.spaceSolid)
spacesTabDetails.navigationSplitCoordinator = spacesSplitCoordinator spacesTabDetails.navigationSplitCoordinator = spacesSplitCoordinator
if flowParameters.appSettings.globalSearchEnabled, #available(iOS 26.0, *) { if flowParameters.userSettings.globalSearchEnabled, #available(iOS 26.0, *) {
let searchCoordinator = SearchScreenCoordinator(parameters: .init(roomSummaryProvider: flowParameters.userSession.clientProxy.alternateRoomSummaryProvider, let searchCoordinator = SearchScreenCoordinator(parameters: .init(roomSummaryProvider: flowParameters.userSession.clientProxy.alternateRoomSummaryProvider,
clientProxy: flowParameters.userSession.clientProxy, clientProxy: flowParameters.userSession.clientProxy,
mediaProvider: flowParameters.userSession.mediaProvider, mediaProvider: flowParameters.userSession.mediaProvider,
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
appSettings: flowParameters.appSettings)) userSettings: flowParameters.userSettings))
let searchStackCoordinator = NavigationStackCoordinator() let searchStackCoordinator = NavigationStackCoordinator()
searchStackCoordinator.setRootCoordinator(searchCoordinator) searchStackCoordinator.setRootCoordinator(searchCoordinator)
@@ -425,7 +425,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
let parameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationController, let parameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationController,
flow: flow, flow: flow,
appSettings: flowParameters.appSettings, userSettings: flowParameters.userSettings,
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
let coordinator = SessionVerificationScreenCoordinator(parameters: parameters) let coordinator = SessionVerificationScreenCoordinator(parameters: parameters)
@@ -463,8 +463,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
presentCallScreen(configuration: .init(roomProxy: roomProxy, presentCallScreen(configuration: .init(roomProxy: roomProxy,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
clientID: InfoPlistReader.main.bundleIdentifier, clientID: InfoPlistReader.main.bundleIdentifier,
elementCallBaseURL: flowParameters.appSettings.elementCallBaseURL, elementCallBaseURL: flowParameters.userSettings.elementCallBaseURL,
elementCallBaseURLOverride: flowParameters.appSettings.elementCallBaseURLOverride, elementCallBaseURLOverride: flowParameters.userSettings.elementCallBaseURLOverride,
voiceOnly: voiceOnly, voiceOnly: voiceOnly,
colorScheme: colorScheme)) colorScheme: colorScheme))
} }
@@ -486,7 +486,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
let callScreenCoordinator = CallScreenCoordinator(parameters: .init(elementCallService: flowParameters.elementCallService, let callScreenCoordinator = CallScreenCoordinator(parameters: .init(elementCallService: flowParameters.elementCallService,
configuration: configuration, configuration: configuration,
allowPictureInPicture: true, allowPictureInPicture: true,
appSettings: flowParameters.appSettings, userSettings: flowParameters.userSettings,
analytics: flowParameters.analytics)) analytics: flowParameters.analytics))
callScreenCoordinator.actions callScreenCoordinator.actions
@@ -64,7 +64,7 @@ extension ClientFactoryMock {
ClientSDKMock(.init(userID: credentials.userID)) ClientSDKMock(.init(userID: credentials.userID))
} }
makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksClosure = { credentials, _, _, _, _ in makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksClosure = { credentials, _, _, _, _ in
ClientSDKMock(.init(userID: credentials.userID)) ClientSDKMock(.init(userID: credentials.userID))
} }
} }
@@ -2346,48 +2346,48 @@ nonisolated class ClientFactoryMock: ClientFactoryProtocol, @unchecked Sendable
} }
//MARK: - makeNSEClient //MARK: - makeNSEClient
nonisolated(unsafe) var makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksThrowableError: Error? nonisolated(unsafe) var makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksThrowableError: Error?
private let makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksCallsCountLock = NSLock() private let makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksCallsCountLock = NSLock()
private nonisolated(unsafe) var makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksUnderlyingCallsCount = 0 private nonisolated(unsafe) var makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksUnderlyingCallsCount = 0
var makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksCallsCount: Int { var makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksCallsCount: Int {
get { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksCallsCountLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksUnderlyingCallsCount } } get { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksCallsCountLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksUnderlyingCallsCount } }
set { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksCallsCountLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksUnderlyingCallsCount = newValue } } set { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksCallsCountLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksUnderlyingCallsCount = newValue } }
} }
var makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksCalled: Bool { var makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksCalled: Bool {
return makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksCallsCount > 0 return makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksCallsCount > 0
} }
private let makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksReceivedArgumentsLock = NSLock() private let makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksReceivedArgumentsLock = NSLock()
private nonisolated(unsafe) var makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksUnderlyingReceivedArguments: (credentials: KeychainCredentials, roomID: String, clientSessionDelegate: ClientSessionDelegate, appSettings: CommonSettingsProtocol, appHooks: AppHooks)? private nonisolated(unsafe) var makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksUnderlyingReceivedArguments: (credentials: KeychainCredentials, roomID: String, clientSessionDelegate: ClientSessionDelegate, userSettings: CommonSettingsProtocol, appHooks: AppHooks)?
var makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksReceivedArguments: (credentials: KeychainCredentials, roomID: String, clientSessionDelegate: ClientSessionDelegate, appSettings: CommonSettingsProtocol, appHooks: AppHooks)? { var makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksReceivedArguments: (credentials: KeychainCredentials, roomID: String, clientSessionDelegate: ClientSessionDelegate, userSettings: CommonSettingsProtocol, appHooks: AppHooks)? {
get { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksReceivedArgumentsLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksUnderlyingReceivedArguments } } get { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksReceivedArgumentsLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksUnderlyingReceivedArguments } }
set { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksReceivedArgumentsLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksUnderlyingReceivedArguments = newValue } } set { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksReceivedArgumentsLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksUnderlyingReceivedArguments = newValue } }
} }
private let makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksReceivedInvocationsLock = NSLock() private let makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksReceivedInvocationsLock = NSLock()
private nonisolated(unsafe) var makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksUnderlyingReceivedInvocations: [(credentials: KeychainCredentials, roomID: String, clientSessionDelegate: ClientSessionDelegate, appSettings: CommonSettingsProtocol, appHooks: AppHooks)] = [] private nonisolated(unsafe) var makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksUnderlyingReceivedInvocations: [(credentials: KeychainCredentials, roomID: String, clientSessionDelegate: ClientSessionDelegate, userSettings: CommonSettingsProtocol, appHooks: AppHooks)] = []
var makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksReceivedInvocations: [(credentials: KeychainCredentials, roomID: String, clientSessionDelegate: ClientSessionDelegate, appSettings: CommonSettingsProtocol, appHooks: AppHooks)] { var makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksReceivedInvocations: [(credentials: KeychainCredentials, roomID: String, clientSessionDelegate: ClientSessionDelegate, userSettings: CommonSettingsProtocol, appHooks: AppHooks)] {
get { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksReceivedInvocationsLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksUnderlyingReceivedInvocations } } get { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksReceivedInvocationsLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksUnderlyingReceivedInvocations } }
set { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksReceivedInvocationsLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksUnderlyingReceivedInvocations = newValue } } set { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksReceivedInvocationsLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksUnderlyingReceivedInvocations = newValue } }
} }
private let makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksReturnValueLock = NSLock() private let makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksReturnValueLock = NSLock()
private nonisolated(unsafe) var makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksUnderlyingReturnValue: ClientProtocol! private nonisolated(unsafe) var makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksUnderlyingReturnValue: ClientProtocol!
var makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksReturnValue: ClientProtocol! { var makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksReturnValue: ClientProtocol! {
get { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksReturnValueLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksUnderlyingReturnValue } } get { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksReturnValueLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksUnderlyingReturnValue } }
set { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksReturnValueLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksUnderlyingReturnValue = newValue } } set { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksReturnValueLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksUnderlyingReturnValue = newValue } }
} }
nonisolated(unsafe) var makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksClosure: ((KeychainCredentials, String, ClientSessionDelegate, CommonSettingsProtocol, AppHooks) async throws -> ClientProtocol)? nonisolated(unsafe) var makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksClosure: ((KeychainCredentials, String, ClientSessionDelegate, CommonSettingsProtocol, AppHooks) async throws -> ClientProtocol)?
@concurrent func makeNSEClient(credentials: KeychainCredentials, roomID: String, clientSessionDelegate: ClientSessionDelegate, appSettings: CommonSettingsProtocol, appHooks: AppHooks) async throws -> ClientProtocol { @concurrent func makeNSEClient(credentials: KeychainCredentials, roomID: String, clientSessionDelegate: ClientSessionDelegate, userSettings: CommonSettingsProtocol, appHooks: AppHooks) async throws -> ClientProtocol {
if let error = makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksThrowableError { if let error = makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksThrowableError {
throw error throw error
} }
makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksCallsCountLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksUnderlyingCallsCount += 1 } makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksCallsCountLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksUnderlyingCallsCount += 1 }
makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksReceivedArguments = (credentials: credentials, roomID: roomID, clientSessionDelegate: clientSessionDelegate, appSettings: appSettings, appHooks: appHooks) makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksReceivedArguments = (credentials: credentials, roomID: roomID, clientSessionDelegate: clientSessionDelegate, userSettings: userSettings, appHooks: appHooks)
makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksReceivedInvocationsLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksUnderlyingReceivedInvocations.append((credentials: credentials, roomID: roomID, clientSessionDelegate: clientSessionDelegate, appSettings: appSettings, appHooks: appHooks)) } makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksReceivedInvocationsLock.withLock { makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksUnderlyingReceivedInvocations.append((credentials: credentials, roomID: roomID, clientSessionDelegate: clientSessionDelegate, userSettings: userSettings, appHooks: appHooks)) }
if let makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksClosure = makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksClosure { if let makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksClosure = makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksClosure {
return try await makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksClosure(credentials, roomID, clientSessionDelegate, appSettings, appHooks) return try await makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksClosure(credentials, roomID, clientSessionDelegate, userSettings, appHooks)
} else { } else {
return makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksReturnValue return makeNSEClientCredentialsRoomIDClientSessionDelegateUserSettingsAppHooksReturnValue
} }
} }
} }
@@ -14533,6 +14533,11 @@ nonisolated class UserSessionMock: UserSessionProtocol, @unchecked Sendable {
set(value) { underlyingClientProxy = value } set(value) { underlyingClientProxy = value }
} }
nonisolated(unsafe) var underlyingClientProxy: ClientProxyProtocol! nonisolated(unsafe) var underlyingClientProxy: ClientProxyProtocol!
var userSettings: UserSettings {
get { return underlyingUserSettings }
set(value) { underlyingUserSettings = value }
}
nonisolated(unsafe) var underlyingUserSettings: UserSettings!
var mediaProvider: MediaProviderProtocol { var mediaProvider: MediaProviderProtocol {
get { return underlyingMediaProvider } get { return underlyingMediaProvider }
set(value) { underlyingMediaProvider = value } set(value) { underlyingMediaProvider = value }
@@ -11,6 +11,7 @@ import Foundation
struct UserSessionMockConfiguration { struct UserSessionMockConfiguration {
var clientProxy: ClientProxyProtocol = ClientProxyMock(.init()) var clientProxy: ClientProxyProtocol = ClientProxyMock(.init())
var userSettings: UserSettings = .volatile()
var contentScannerService: ContentScannerServiceProtocol? var contentScannerService: ContentScannerServiceProtocol?
} }
@@ -19,6 +20,8 @@ struct UserSessionMockConfiguration {
self.init() self.init()
clientProxy = configuration.clientProxy clientProxy = configuration.clientProxy
userSettings = configuration.userSettings
mediaProvider = MediaProviderMock(.init()) mediaProvider = MediaProviderMock(.init())
voiceMessageMediaManager = VoiceMessageMediaManagerMock() voiceMessageMediaManager = VoiceMessageMediaManagerMock()
contentScannerService = configuration.contentScannerService contentScannerService = configuration.contentScannerService
@@ -56,7 +56,7 @@ private struct VisualListItemLabelStyle: LabelStyle {
// MARK: - Previews // MARK: - Previews
struct VisualListItem_Previews: PreviewProvider, TestablePreview { struct VisualListItem_Previews: PreviewProvider, TestablePreview {
static let strings = AnalyticsPromptScreenStrings(termsURL: AppSettings.volatile().analyticsTermsURL) static let strings = AnalyticsPromptScreenStrings(termsURL: UserSettings.volatile().analyticsTermsURL)
static var testImage1: some View { static var testImage1: some View {
Image(systemName: "circle") Image(systemName: "circle")
@@ -14,7 +14,7 @@ struct CallScreenCoordinatorParameters {
let elementCallService: ElementCallServiceProtocol let elementCallService: ElementCallServiceProtocol
let configuration: ElementCallConfiguration let configuration: ElementCallConfiguration
let allowPictureInPicture: Bool let allowPictureInPicture: Bool
let appSettings: AppSettings let userSettings: UserSettings
let analytics: AnalyticsServiceProtocol let analytics: AnalyticsServiceProtocol
} }
@@ -45,7 +45,7 @@ final class CallScreenCoordinator: CoordinatorProtocol {
viewModel = CallScreenViewModel(elementCallService: parameters.elementCallService, viewModel = CallScreenViewModel(elementCallService: parameters.elementCallService,
configuration: parameters.configuration, configuration: parameters.configuration,
allowPictureInPicture: parameters.allowPictureInPicture, allowPictureInPicture: parameters.allowPictureInPicture,
appSettings: parameters.appSettings, userSettings: parameters.userSettings,
analyticsService: parameters.analytics) analyticsService: parameters.analytics)
} }
@@ -17,7 +17,7 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
private let elementCallService: ElementCallServiceProtocol private let elementCallService: ElementCallServiceProtocol
private let configuration: ElementCallConfiguration private let configuration: ElementCallConfiguration
private let isPictureInPictureAllowed: Bool private let isPictureInPictureAllowed: Bool
private let appSettings: AppSettings private let userSettings: UserSettings
private let analyticsService: AnalyticsServiceProtocol private let analyticsService: AnalyticsServiceProtocol
private let widgetDriver: ElementCallWidgetDriverProtocol private let widgetDriver: ElementCallWidgetDriverProtocol
@@ -39,11 +39,11 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
init(elementCallService: ElementCallServiceProtocol, init(elementCallService: ElementCallServiceProtocol,
configuration: ElementCallConfiguration, configuration: ElementCallConfiguration,
allowPictureInPicture: Bool, allowPictureInPicture: Bool,
appSettings: AppSettings, userSettings: UserSettings,
analyticsService: AnalyticsServiceProtocol) { analyticsService: AnalyticsServiceProtocol) {
self.elementCallService = elementCallService self.elementCallService = elementCallService
self.configuration = configuration self.configuration = configuration
self.appSettings = appSettings self.userSettings = userSettings
self.analyticsService = analyticsService self.analyticsService = analyticsService
isPictureInPictureAllowed = allowPictureInPicture isPictureInPictureAllowed = allowPictureInPicture
@@ -165,13 +165,13 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
// We only set the analytics configuration if analytics are enabled // We only set the analytics configuration if analytics are enabled
let analyticsConfiguration: ElementCallAnalyticsConfiguration? = if analyticsService.isEnabled { let analyticsConfiguration: ElementCallAnalyticsConfiguration? = if analyticsService.isEnabled {
.init(posthogAPIHost: appSettings.elementCallPosthogAPIHost, .init(posthogAPIHost: userSettings.elementCallPosthogAPIHost,
posthogAPIKey: appSettings.elementCallPosthogAPIKey, posthogAPIKey: userSettings.elementCallPosthogAPIKey,
sentryDSN: appSettings.elementCallPosthogSentryDSN) sentryDSN: userSettings.elementCallPosthogSentryDSN)
} else { } else {
nil nil
} }
let rageshakeURL: String? = if case let .url(baseURL) = appSettings.bugReportRageshakeURL.publisher.value { let rageshakeURL: String? = if case let .url(baseURL) = userSettings.bugReportRageshakeURL.publisher.value {
baseURL.absoluteString baseURL.absoluteString
} else { } else {
nil nil
@@ -405,7 +405,7 @@ struct CallScreen_Previews: PreviewProvider {
voiceOnly: false, voiceOnly: false,
colorScheme: .light), colorScheme: .light),
allowPictureInPicture: false, allowPictureInPicture: false,
appSettings: .volatile(), userSettings: .volatile(),
analyticsService: AnalyticsServiceMock(.init())) analyticsService: AnalyticsServiceMock(.init()))
} }
} }
@@ -15,7 +15,7 @@ struct CreateRoomScreenCoordinatorParameters {
let shouldShowCancelButton: Bool let shouldShowCancelButton: Bool
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let appSettings: AppSettings let userSettings: UserSettings
let analytics: AnalyticsServiceProtocol let analytics: AnalyticsServiceProtocol
} }
@@ -41,7 +41,7 @@ final class CreateRoomScreenCoordinator: CoordinatorProtocol {
userSession: parameters.userSession, userSession: parameters.userSession,
analytics: parameters.analytics, analytics: parameters.analytics,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
appSettings: parameters.appSettings) userSettings: parameters.userSettings)
} }
func start() { func start() {
@@ -33,9 +33,9 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
userSession: UserSessionProtocol, userSession: UserSessionProtocol,
analytics: AnalyticsServiceProtocol, analytics: AnalyticsServiceProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
appSettings: AppSettings) { userSettings: UserSettings) {
self.userSession = userSession self.userSession = userSession
mediaUploadingPreprocessor = MediaUploadingPreprocessor(appSettings: appSettings) mediaUploadingPreprocessor = MediaUploadingPreprocessor(userSettings: userSettings)
self.analytics = analytics self.analytics = analytics
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
@@ -63,7 +63,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
shouldShowCancelButton: shouldShowCancelButton, shouldShowCancelButton: shouldShowCancelButton,
roomName: "", roomName: "",
serverName: userSession.clientProxy.userIDServerName ?? "", serverName: userSession.clientProxy.userIDServerName ?? "",
isKnockingFeatureEnabled: appSettings.knockingEnabled, isKnockingFeatureEnabled: userSettings.knockingEnabled,
canSelectSpace: canSelectSpace, canSelectSpace: canSelectSpace,
aliasLocalPart: roomAliasNameFromRoomDisplayName(roomName: ""), aliasLocalPart: roomAliasNameFromRoomDisplayName(roomName: ""),
bindings: bindings), bindings: bindings),
@@ -451,8 +451,8 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
isSpace: Bool = false, isSpace: Bool = false,
selectionMode: CreateRoomScreenSpaceSelectionMode = .editableSpacesList(preSelectedSpace: nil), selectionMode: CreateRoomScreenSpaceSelectionMode = .editableSpacesList(preSelectedSpace: nil),
isAliasAvailable: Bool = true) -> CreateRoomScreenViewModel { isAliasAvailable: Bool = true) -> CreateRoomScreenViewModel {
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
appSettings.knockingEnabled = isKnockingEnabled userSettings.knockingEnabled = isKnockingEnabled
let clientProxy = ClientProxyMock(.init(userIDServerName: "example.org", let clientProxy = ClientProxyMock(.init(userIDServerName: "example.org",
userID: "@userid:example.com")) userID: "@userid:example.com"))
@@ -467,6 +467,6 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
userSession: userSession, userSession: userSession,
analytics: AnalyticsServiceMock(.init()), analytics: AnalyticsServiceMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: appSettings) userSettings: userSettings)
} }
} }
@@ -65,7 +65,7 @@ struct CreateRoomSpaceSelectionSheet_Previews: PreviewProvider, TestablePreview
clientProxy.spaceService = SpaceServiceProxyMock(.init(editableSpaces: .mockJoinedSpaces2)) clientProxy.spaceService = SpaceServiceProxyMock(.init(editableSpaces: .mockJoinedSpaces2))
let userSession = UserSessionMock(.init(clientProxy: clientProxy)) let userSession = UserSessionMock(.init(clientProxy: clientProxy))
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
return CreateRoomScreenViewModel(isSpace: false, return CreateRoomScreenViewModel(isSpace: false,
spaceSelectionMode: .editableSpacesList(preSelectedSpace: nil), spaceSelectionMode: .editableSpacesList(preSelectedSpace: nil),
@@ -73,7 +73,7 @@ struct CreateRoomSpaceSelectionSheet_Previews: PreviewProvider, TestablePreview
userSession: userSession, userSession: userSession,
analytics: AnalyticsServiceMock(.init()), analytics: AnalyticsServiceMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: appSettings) userSettings: userSettings)
}() }()
static var previews: some View { static var previews: some View {
@@ -103,7 +103,7 @@ struct EmojiPickerScreen: View {
@available(iOS 26.0, *) @available(iOS 26.0, *)
struct EmojiPickerScreen_Previews: PreviewProvider, TestablePreview { struct EmojiPickerScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = EmojiPickerScreenViewModel(selectedEmojis: ["😀", "😄"], static let viewModel = EmojiPickerScreenViewModel(selectedEmojis: ["😀", "😄"],
emojiProvider: EmojiProvider(appSettings: .volatile()), emojiProvider: EmojiProvider(userSettings: .volatile()),
continuation: AsyncStream<String>.makeStream().continuation) continuation: AsyncStream<String>.makeStream().continuation)
static var previews: some View { static var previews: some View {
@@ -115,7 +115,7 @@ struct EmojiPickerScreen_Previews: PreviewProvider, TestablePreview {
struct EmojiPickerScreenSheet_Previews: PreviewProvider { struct EmojiPickerScreenSheet_Previews: PreviewProvider {
static let viewModel = EmojiPickerScreenViewModel(selectedEmojis: ["😀", "😄"], static let viewModel = EmojiPickerScreenViewModel(selectedEmojis: ["😀", "😄"],
emojiProvider: EmojiProvider(appSettings: .volatile()), emojiProvider: EmojiProvider(userSettings: .volatile()),
continuation: AsyncStream<String>.makeStream().continuation) continuation: AsyncStream<String>.makeStream().continuation)
static var previews: some View { static var previews: some View {
@@ -13,7 +13,7 @@ struct HomeScreenCoordinatorParameters {
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let bugReportService: BugReportServiceProtocol let bugReportService: BugReportServiceProtocol
let selectedRoomPublisher: CurrentValuePublisher<String?, Never> let selectedRoomPublisher: CurrentValuePublisher<String?, Never>
let appSettings: AppSettings let userSettings: UserSettings
let analyticsService: AnalyticsServiceProtocol let analyticsService: AnalyticsServiceProtocol
let notificationManager: NotificationManagerProtocol let notificationManager: NotificationManagerProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
@@ -50,7 +50,7 @@ final class HomeScreenCoordinator: CoordinatorProtocol {
init(parameters: HomeScreenCoordinatorParameters) { init(parameters: HomeScreenCoordinatorParameters) {
viewModel = HomeScreenViewModel(userSession: parameters.userSession, viewModel = HomeScreenViewModel(userSession: parameters.userSession,
selectedRoomPublisher: parameters.selectedRoomPublisher, selectedRoomPublisher: parameters.selectedRoomPublisher,
appSettings: parameters.appSettings, userSettings: parameters.userSettings,
analyticsService: parameters.analyticsService, analyticsService: parameters.analyticsService,
bugReportService: parameters.bugReportService, bugReportService: parameters.bugReportService,
notificationManager: parameters.notificationManager, notificationManager: parameters.notificationManager,
@@ -18,7 +18,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
private let spaceFilterSubject: CurrentValueSubject<SpaceServiceFilter?, Never> private let spaceFilterSubject: CurrentValueSubject<SpaceServiceFilter?, Never>
private let analyticsService: AnalyticsServiceProtocol private let analyticsService: AnalyticsServiceProtocol
private let bugReportService: BugReportServiceProtocol private let bugReportService: BugReportServiceProtocol
private let appSettings: AppSettings private let userSettings: UserSettings
private let notificationManager: NotificationManagerProtocol private let notificationManager: NotificationManagerProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
@@ -32,7 +32,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
// swiftlint:disable:next function_body_length // swiftlint:disable:next function_body_length
init(userSession: UserSessionProtocol, init(userSession: UserSessionProtocol,
selectedRoomPublisher: CurrentValuePublisher<String?, Never>, selectedRoomPublisher: CurrentValuePublisher<String?, Never>,
appSettings: AppSettings, userSettings: UserSettings,
analyticsService: AnalyticsServiceProtocol, analyticsService: AnalyticsServiceProtocol,
bugReportService: BugReportServiceProtocol, bugReportService: BugReportServiceProtocol,
notificationManager: NotificationManagerProtocol, notificationManager: NotificationManagerProtocol,
@@ -40,7 +40,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
self.userSession = userSession self.userSession = userSession
self.analyticsService = analyticsService self.analyticsService = analyticsService
self.bugReportService = bugReportService self.bugReportService = bugReportService
self.appSettings = appSettings self.userSettings = userSettings
self.notificationManager = notificationManager self.notificationManager = notificationManager
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
@@ -49,10 +49,10 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
roomSummaryProvider = userSession.clientProxy.roomSummaryProvider roomSummaryProvider = userSession.clientProxy.roomSummaryProvider
super.init(initialViewState: .init(userProfile: userSession.clientProxy.userProfilePublisher.value, super.init(initialViewState: .init(userProfile: userSession.clientProxy.userProfilePublisher.value,
bindings: .init(filtersState: .init(appSettings: appSettings))), bindings: .init(filtersState: .init(userSettings: userSettings))),
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
if appSettings.globalSearchEnabled, #available(iOS 26.0, *) { if userSettings.globalSearchEnabled, #available(iOS 26.0, *) {
state.isRoomListSearchEnabled = false state.isRoomListSearchEnabled = false
} }
@@ -113,21 +113,21 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
.weakAssign(to: \.state.selectedRoomID, on: self) .weakAssign(to: \.state.selectedRoomID, on: self)
.store(in: &cancellables) .store(in: &cancellables)
appSettings.showAllRoomListActivityPublisher userSettings.showAllRoomListActivityPublisher
.removeDuplicates() .removeDuplicates()
.sink { [weak self] _ in .sink { [weak self] _ in
self?.updateRooms() self?.updateRooms()
} }
.store(in: &cancellables) .store(in: &cancellables)
appSettings.seenInvitesPublisher userSettings.seenInvitesPublisher
.removeDuplicates() .removeDuplicates()
.sink { [weak self] _ in .sink { [weak self] _ in
self?.updateRooms() self?.updateRooms()
} }
.store(in: &cancellables) .store(in: &cancellables)
appSettings.hasSeenNewSoundBannerPublisher userSettings.hasSeenNewSoundBannerPublisher
.sink { [weak self] hasSeenNewSoundBanner in .sink { [weak self] hasSeenNewSoundBanner in
self?.state.shouldShowNewSoundBanner = !hasSeenNewSoundBanner self?.state.shouldShowNewSoundBanner = !hasSeenNewSoundBanner
} }
@@ -210,7 +210,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
case .skipRecoveryKeyConfirmation: case .skipRecoveryKeyConfirmation:
state.securityBannerMode = .dismissed state.securityBannerMode = .dismissed
case .dismissNewSoundBanner: case .dismissNewSoundBanner:
appSettings.hasSeenNewSoundBanner = true userSettings.hasSeenNewSoundBanner = true
case .updateVisibleItemRange(let range): case .updateVisibleItemRange(let range):
roomSummaryProvider?.updateVisibleRange(range) roomSummaryProvider?.updateVisibleRange(range)
case .startChat: case .startChat:
@@ -260,7 +260,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
case .success: case .success:
analyticsService.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle) analyticsService.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle)
if case .failure(let error) = await roomProxy.markAsRead(receiptType: appSettings.sharePresence ? .read : .readPrivate) { if case .failure(let error) = await roomProxy.markAsRead(receiptType: userSettings.sharePresence ? .read : .readPrivate) {
MXLog.error("Failed marking room \(roomIdentifier) as read with error: \(error)") MXLog.error("Failed marking room \(roomIdentifier) as read with error: \(error)")
} }
case .failure(let error): case .failure(let error):
@@ -372,11 +372,11 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
} }
var rooms = [HomeScreenRoom]() var rooms = [HomeScreenRoom]()
let seenInvites = appSettings.seenInvites let seenInvites = userSettings.seenInvites
for summary in roomSummaryProvider.roomListPublisher.value { for summary in roomSummaryProvider.roomListPublisher.value {
let room = HomeScreenRoom(summary: summary, let room = HomeScreenRoom(summary: summary,
showAllActivity: appSettings.showAllRoomListActivity, showAllActivity: userSettings.showAllRoomListActivity,
seenInvites: seenInvites) seenInvites: seenInvites)
rooms.append(room) rooms.append(room)
} }
@@ -513,7 +513,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
analyticsService.trackJoinedRoom(isDM: roomProxy.info.isDirect, analyticsService.trackJoinedRoom(isDM: roomProxy.info.isDirect,
isSpace: roomProxy.info.isSpace, isSpace: roomProxy.info.isSpace,
activeMemberCount: UInt(roomProxy.info.activeMembersCount)) activeMemberCount: UInt(roomProxy.info.activeMembersCount))
appSettings.seenInvites.remove(roomProxy.id) userSettings.seenInvites.remove(roomProxy.id)
} }
private func showDeclineInviteConfirmationAlert(roomID: String) async { private func showDeclineInviteConfirmationAlert(roomID: String) async {
@@ -563,7 +563,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
switch result { switch result {
case .success: case .success:
await notificationManager.removeDeliveredMessageNotifications(for: roomID) // Normally handled by the room flow, but that's never presented in this case. await notificationManager.removeDeliveredMessageNotifications(for: roomID) // Normally handled by the room flow, but that's never presented in this case.
appSettings.seenInvites.remove(roomID) userSettings.seenInvites.remove(roomID)
case .failure: case .failure:
displayError() displayError()
} }
@@ -89,21 +89,21 @@ enum RoomListFilter: Int, CaseIterable, Identifiable {
struct RoomListFiltersState { struct RoomListFiltersState {
private(set) var activeFilters: OrderedSet<RoomListFilter> private(set) var activeFilters: OrderedSet<RoomListFilter>
private let appSettings: AppSettings private let userSettings: UserSettings
init(activeFilters: OrderedSet<RoomListFilter> = [], appSettings: AppSettings) { init(activeFilters: OrderedSet<RoomListFilter> = [], userSettings: UserSettings) {
self.activeFilters = .init(activeFilters) self.activeFilters = .init(activeFilters)
self.appSettings = appSettings self.userSettings = userSettings
} }
var availableFilters: [RoomListFilter] { var availableFilters: [RoomListFilter] {
var availableFilters = OrderedSet(RoomListFilter.availableFilters) var availableFilters = OrderedSet(RoomListFilter.availableFilters)
if !appSettings.lowPriorityFilterEnabled { if !userSettings.lowPriorityFilterEnabled {
availableFilters.remove(.lowPriority) availableFilters.remove(.lowPriority)
} }
if !appSettings.mentionsFilterEnabled { if !userSettings.mentionsFilterEnabled {
availableFilters.remove(.mentions) availableFilters.remove(.mentions)
} }
@@ -61,10 +61,10 @@ struct RoomListFiltersEmptyStateView_Previews: PreviewProvider, TestablePreview
VStack(spacing: 24) { VStack(spacing: 24) {
ForEach(RoomListFilter.allCases) { filter in ForEach(RoomListFilter.allCases) { filter in
RoomListFiltersEmptyStateView(state: .init(activeFilters: [filter], RoomListFiltersEmptyStateView(state: .init(activeFilters: [filter],
appSettings: .volatile())) userSettings: .volatile()))
} }
RoomListFiltersEmptyStateView(state: .init(activeFilters: [.people, .favourites], RoomListFiltersEmptyStateView(state: .init(activeFilters: [.people, .favourites],
appSettings: .volatile())) userSettings: .volatile()))
} }
.padding(.bottom) .padding(.bottom)
.previewLayout(.sizeThatFits) .previewLayout(.sizeThatFits)
@@ -92,10 +92,10 @@ struct RoomListFiltersView: View {
struct RoomListFiltersView_Previews: PreviewProvider, TestablePreview { struct RoomListFiltersView_Previews: PreviewProvider, TestablePreview {
static var previews: some View { static var previews: some View {
RoomListFiltersView(state: .constant(.init(appSettings: .volatile()))) RoomListFiltersView(state: .constant(.init(userSettings: .volatile())))
RoomListFiltersView(state: .constant(.init(activeFilters: [.rooms, .favourites], RoomListFiltersView(state: .constant(.init(activeFilters: [.rooms, .favourites],
appSettings: .volatile()))) userSettings: .volatile())))
RoomListFiltersView(state: .constant(.init(activeFilters: [.lowPriority], RoomListFiltersView(state: .constant(.init(activeFilters: [.lowPriority],
appSettings: .volatile()))) userSettings: .volatile())))
} }
} }
@@ -234,7 +234,7 @@ struct HomeScreen_Previews: PreviewProvider, TestablePreview {
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
appSettings: .volatile(), userSettings: .volatile(),
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
bugReportService: BugReportServiceMock(.init()), bugReportService: BugReportServiceMock(.init()),
notificationManager: NotificationManagerMock(), notificationManager: NotificationManagerMock(),
@@ -145,7 +145,7 @@ struct HomeScreenEmptyStateView_Previews: PreviewProvider, TestablePreview {
roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded([]))))))) roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded([])))))))
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
appSettings: .volatile(), userSettings: .volatile(),
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
bugReportService: BugReportServiceMock(.init()), bugReportService: BugReportServiceMock(.init()),
notificationManager: NotificationManagerMock(), notificationManager: NotificationManagerMock(),
@@ -201,7 +201,7 @@ struct HomeScreenInviteCell_Previews: PreviewProvider, TestablePreview {
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
appSettings: .volatile(), userSettings: .volatile(),
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
bugReportService: BugReportServiceMock(.init()), bugReportService: BugReportServiceMock(.init()),
notificationManager: NotificationManagerMock(), notificationManager: NotificationManagerMock(),
@@ -133,7 +133,7 @@ struct HomeScreenKnockedCell_Previews: PreviewProvider, TestablePreview {
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
appSettings: .volatile(), userSettings: .volatile(),
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
bugReportService: BugReportServiceMock(.init()), bugReportService: BugReportServiceMock(.init()),
notificationManager: NotificationManagerMock(), notificationManager: NotificationManagerMock(),
@@ -122,7 +122,7 @@ struct HomeScreenRecoveryKeyConfirmationBanner_Previews: PreviewProvider, Testab
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
appSettings: .volatile(), userSettings: .volatile(),
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
bugReportService: BugReportServiceMock(.init()), bugReportService: BugReportServiceMock(.init()),
notificationManager: NotificationManagerMock(), notificationManager: NotificationManagerMock(),
@@ -13,7 +13,7 @@ struct JoinRoomScreenCoordinatorParameters {
let source: JoinRoomScreenSource let source: JoinRoomScreenSource
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let appSettings: AppSettings let userSettings: UserSettings
} }
enum JoinRoomScreenSource { enum JoinRoomScreenSource {
@@ -53,7 +53,7 @@ final class JoinRoomScreenCoordinator: CoordinatorProtocol {
init(parameters: JoinRoomScreenCoordinatorParameters) { init(parameters: JoinRoomScreenCoordinatorParameters) {
viewModel = JoinRoomScreenViewModel(source: parameters.source, viewModel = JoinRoomScreenViewModel(source: parameters.source,
appSettings: parameters.appSettings, userSettings: parameters.userSettings,
userSession: parameters.userSession, userSession: parameters.userSession,
userIndicatorController: parameters.userIndicatorController) userIndicatorController: parameters.userIndicatorController)
} }
@@ -14,7 +14,7 @@ typealias JoinRoomScreenViewModelType = StateStoreViewModel<JoinRoomScreenViewSt
class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewModelProtocol { class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewModelProtocol {
private let source: JoinRoomScreenSource private let source: JoinRoomScreenSource
private let appSettings: AppSettings private let userSettings: UserSettings
private let clientProxy: ClientProxyProtocol private let clientProxy: ClientProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
@@ -30,11 +30,11 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo
} }
init(source: JoinRoomScreenSource, init(source: JoinRoomScreenSource,
appSettings: AppSettings, userSettings: UserSettings,
userSession: UserSessionProtocol, userSession: UserSessionProtocol,
userIndicatorController: UserIndicatorControllerProtocol) { userIndicatorController: UserIndicatorControllerProtocol) {
self.source = source self.source = source
self.appSettings = appSettings self.userSettings = userSettings
clientProxy = userSession.clientProxy clientProxy = userSession.clientProxy
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
@@ -52,7 +52,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo
.sink { mode in .sink { mode in
switch mode { switch mode {
case .invited: case .invited:
appSettings.seenInvites.insert(roomID) userSettings.seenInvites.insert(roomID)
default: default:
break break
} }
@@ -350,7 +350,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo
guard !hasSentJoinAction else { return } guard !hasSentJoinAction else { return }
let roomID = state.roomID let roomID = state.roomID
appSettings.seenInvites.remove(roomID) userSettings.seenInvites.remove(roomID)
guard state.roomDetails?.isSpace == true else { guard state.roomDetails?.isSpace == true else {
hasSentJoinAction = true hasSentJoinAction = true
@@ -464,7 +464,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo
return false return false
} }
appSettings.seenInvites.remove(roomID) userSettings.seenInvites.remove(roomID)
actionsSubject.send(.dismiss) actionsSubject.send(.dismiss)
return true return true
@@ -402,7 +402,7 @@ struct JoinRoomScreenPreviewWrapper: Identifiable {
self.isSpace = isSpace self.isSpace = isSpace
self.customPreviewName = customPreviewName self.customPreviewName = customPreviewName
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
let clientProxy = ClientProxyMock(.init(hideInviteAvatars: hideInviteAvatars)) let clientProxy = ClientProxyMock(.init(hideInviteAvatars: hideInviteAvatars))
clientProxy.canJoinRoomWithReturnValue = canJoinRoom clientProxy.canJoinRoomWithReturnValue = canJoinRoom
@@ -455,7 +455,7 @@ struct JoinRoomScreenPreviewWrapper: Identifiable {
} }
viewModel = JoinRoomScreenViewModel(source: source, viewModel = JoinRoomScreenViewModel(source: source,
appSettings: appSettings, userSettings: userSettings,
userSession: UserSessionMock(.init(clientProxy: clientProxy)), userSession: UserSessionMock(.init(clientProxy: clientProxy)),
userIndicatorController: UserIndicatorControllerMock()) userIndicatorController: UserIndicatorControllerMock())
} }
@@ -10,7 +10,7 @@ import Combine
import SwiftUI import SwiftUI
struct LabsScreenCoordinatorParameters { struct LabsScreenCoordinatorParameters {
let appSettings: AppSettings let userSettings: UserSettings
} }
enum LabsScreenCoordinatorAction { enum LabsScreenCoordinatorAction {
@@ -28,7 +28,7 @@ final class LabsScreenCoordinator: CoordinatorProtocol {
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
init(parameters: LabsScreenCoordinatorParameters) { init(parameters: LabsScreenCoordinatorParameters) {
viewModel = LabsScreenViewModel(labsOptions: parameters.appSettings) viewModel = LabsScreenViewModel(labsOptions: parameters.userSettings.app)
} }
func start() { func start() {
@@ -15,7 +15,7 @@ struct LocationSharingScreenCoordinatorParameters {
let roomProxy: JoinedRoomProxyProtocol let roomProxy: JoinedRoomProxyProtocol
let timelineController: TimelineControllerProtocol let timelineController: TimelineControllerProtocol
let liveLocationManager: LiveLocationManagerProtocol let liveLocationManager: LiveLocationManagerProtocol
let appSettings: AppSettings let userSettings: UserSettings
let appMediator: AppMediatorProtocol let appMediator: AppMediatorProtocol
let analytics: AnalyticsServiceProtocol let analytics: AnalyticsServiceProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
@@ -45,7 +45,7 @@ final class LocationSharingScreenCoordinator: CoordinatorProtocol {
roomProxy: parameters.roomProxy, roomProxy: parameters.roomProxy,
timelineController: parameters.timelineController, timelineController: parameters.timelineController,
liveLocationManager: parameters.liveLocationManager, liveLocationManager: parameters.liveLocationManager,
appSettings: parameters.appSettings, userSettings: parameters.userSettings,
analytics: parameters.analytics, analytics: parameters.analytics,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
mediaProvider: parameters.mediaProvider) mediaProvider: parameters.mediaProvider)
@@ -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 appSettings: AppSettings private let userSettings: UserSettings
private let analytics: AnalyticsServiceProtocol private let analytics: AnalyticsServiceProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let notificationCenter: NotificationCenter private let notificationCenter: NotificationCenter
@@ -37,7 +37,7 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati
roomProxy: JoinedRoomProxyProtocol, roomProxy: JoinedRoomProxyProtocol,
timelineController: TimelineControllerProtocol, timelineController: TimelineControllerProtocol,
liveLocationManager: LiveLocationManagerProtocol, liveLocationManager: LiveLocationManagerProtocol,
appSettings: AppSettings, userSettings: UserSettings,
analytics: AnalyticsServiceProtocol, analytics: AnalyticsServiceProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
mediaProvider: MediaProviderProtocol, mediaProvider: MediaProviderProtocol,
@@ -45,7 +45,7 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.timelineController = timelineController self.timelineController = timelineController
self.liveLocationManager = liveLocationManager self.liveLocationManager = liveLocationManager
self.appSettings = appSettings self.userSettings = userSettings
self.analytics = analytics self.analytics = analytics
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.notificationCenter = notificationCenter self.notificationCenter = notificationCenter
@@ -173,7 +173,7 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati
} }
.store(in: &cancellables) .store(in: &cancellables)
appSettings.liveLocationSharingSessionsByRoomIDPublisher userSettings.liveLocationSharingSessionsByRoomIDPublisher
.map { [roomID = roomProxy.id] sessions in sessions[roomID] != nil } .map { [roomID = roomProxy.id] sessions in sessions[roomID] != nil }
.removeDuplicates() .removeDuplicates()
.sink { [weak self] isSharingLiveLocationOnThisDevice in .sink { [weak self] isSharingLiveLocationOnThisDevice in
@@ -261,14 +261,14 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati
} }
private func showLiveLocationFlow() { private func showLiveLocationFlow() {
if appSettings.liveLocationDisclaimerDisplayed { if userSettings.liveLocationDisclaimerDisplayed {
showLiveLocationDurationPicker() showLiveLocationDurationPicker()
} else { } else {
state.bindings.alertInfo = .init(alertID: .liveLocationDisclaimer, state.bindings.alertInfo = .init(alertID: .liveLocationDisclaimer,
primaryButton: .init(title: L10n.actionDecline, role: .cancel, action: nil), primaryButton: .init(title: L10n.actionDecline, role: .cancel, action: nil),
secondaryButton: .init(title: L10n.actionAccept) { [weak self] in secondaryButton: .init(title: L10n.actionAccept) { [weak self] in
guard let self else { return } guard let self else { return }
appSettings.liveLocationDisclaimerDisplayed = true userSettings.liveLocationDisclaimerDisplayed = true
// Delay so SwiftUI finishes dismissing the current alert // Delay so SwiftUI finishes dismissing the current alert
// before presenting the next one. // before presenting the next one.
DispatchQueue.main.async { DispatchQueue.main.async {
@@ -432,14 +432,14 @@ 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.volatile() let userSettings = UserSettings.volatile()
return LocationSharingScreenViewModel(interactionMode: interactionMode, return LocationSharingScreenViewModel(interactionMode: interactionMode,
mapURLBuilder: appSettings.mapTilerConfiguration.publisher.value, mapURLBuilder: userSettings.mapTilerConfiguration.publisher.value,
roomProxy: roomProxy, roomProxy: roomProxy,
timelineController: TimelineControllerMock(.init()), timelineController: TimelineControllerMock(.init()),
liveLocationManager: LiveLocationManagerMock(), liveLocationManager: LiveLocationManagerMock(),
appSettings: appSettings, userSettings: userSettings,
analytics: AnalyticsServiceMock(.init()), analytics: AnalyticsServiceMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
mediaProvider: MediaProviderMock(.init())) mediaProvider: MediaProviderMock(.init()))
@@ -16,7 +16,7 @@ struct MediaEventsTimelineScreenCoordinatorParameters {
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let mediaPlayerProvider: MediaPlayerProviderProtocol let mediaPlayerProvider: MediaPlayerProviderProtocol
let appMediator: AppMediatorProtocol let appMediator: AppMediatorProtocol
let appSettings: AppSettings let userSettings: UserSettings
let analytics: AnalyticsServiceProtocol let analytics: AnalyticsServiceProtocol
let emojiProvider: EmojiProviderProtocol let emojiProvider: EmojiProviderProtocol
let linkMetadataProvider: LinkMetadataProviderProtocol let linkMetadataProvider: LinkMetadataProviderProtocol
@@ -46,7 +46,6 @@ final class MediaEventsTimelineScreenCoordinator: CoordinatorProtocol {
mediaPlayerProvider: parameters.mediaPlayerProvider, mediaPlayerProvider: parameters.mediaPlayerProvider,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
appMediator: parameters.appMediator, appMediator: parameters.appMediator,
appSettings: parameters.appSettings,
analyticsService: parameters.analytics, analyticsService: parameters.analytics,
emojiProvider: parameters.emojiProvider, emojiProvider: parameters.emojiProvider,
linkMetadataProvider: parameters.linkMetadataProvider, linkMetadataProvider: parameters.linkMetadataProvider,
@@ -58,7 +57,6 @@ final class MediaEventsTimelineScreenCoordinator: CoordinatorProtocol {
mediaPlayerProvider: parameters.mediaPlayerProvider, mediaPlayerProvider: parameters.mediaPlayerProvider,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
appMediator: parameters.appMediator, appMediator: parameters.appMediator,
appSettings: parameters.appSettings,
analyticsService: parameters.analytics, analyticsService: parameters.analytics,
emojiProvider: parameters.emojiProvider, emojiProvider: parameters.emojiProvider,
linkMetadataProvider: parameters.linkMetadataProvider, linkMetadataProvider: parameters.linkMetadataProvider,
@@ -307,16 +307,14 @@ struct MediaEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
makeTimelineController(screenMode: screenMode) makeTimelineController(screenMode: screenMode)
} }
let appSettings = AppSettings.volatile()
return TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")), return TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")),
timelineController: timelineController, timelineController: timelineController,
userSession: UserSessionMock(.init(contentScannerService: contentScannerService)), userSession: UserSessionMock(.init(contentScannerService: contentScannerService)),
mediaPlayerProvider: MediaPlayerProviderMock(), mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock(.init()), appMediator: AppMediatorMock(.init()),
appSettings: appSettings,
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(userSettings: .volatile()),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
} }
@@ -393,7 +393,7 @@ struct MediaUploadPreviewScreen_Previews: PreviewProvider, TestablePreview {
title: "App Icon.png", title: "App Icon.png",
shouldShowCaptionWarning: true, shouldShowCaptionWarning: true,
galleryEnabled: true, galleryEnabled: true,
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: .volatile()), mediaUploadingPreprocessor: MediaUploadingPreprocessor(userSettings: .volatile()),
timelineController: TimelineControllerMock(.init()), timelineController: TimelineControllerMock(.init()),
clientProxy: ClientProxyMock(.init()), clientProxy: ClientProxyMock(.init()),
userIndicatorController: UserIndicatorControllerMock()) userIndicatorController: UserIndicatorControllerMock())
@@ -102,6 +102,6 @@ struct AnalyticsPromptScreen_Previews: PreviewProvider, TestablePreview {
} }
static func makeViewModel(showTerms: Bool = true) -> AnalyticsPromptScreenViewModel { static func makeViewModel(showTerms: Bool = true) -> AnalyticsPromptScreenViewModel {
AnalyticsPromptScreenViewModel(termsURL: showTerms ? AppSettings.volatile().analyticsTermsURL : nil) AnalyticsPromptScreenViewModel(termsURL: showTerms ? UserSettings.volatile().analyticsTermsURL : nil)
} }
} }
@@ -11,7 +11,7 @@ import SwiftUI
struct IdentityConfirmationScreenCoordinatorParameters { struct IdentityConfirmationScreenCoordinatorParameters {
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let appSettings: AppSettings let userSettings: UserSettings
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
} }
@@ -36,7 +36,7 @@ final class IdentityConfirmationScreenCoordinator: CoordinatorProtocol {
init(parameters: IdentityConfirmationScreenCoordinatorParameters) { init(parameters: IdentityConfirmationScreenCoordinatorParameters) {
viewModel = IdentityConfirmationScreenViewModel(userSession: parameters.userSession, viewModel = IdentityConfirmationScreenViewModel(userSession: parameters.userSession,
appSettings: parameters.appSettings, userSettings: parameters.userSettings,
userIndicatorController: parameters.userIndicatorController) userIndicatorController: parameters.userIndicatorController)
} }
@@ -20,11 +20,11 @@ class IdentityConfirmationScreenViewModel: IdentityConfirmationScreenViewModelTy
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(userSession: UserSessionProtocol, appSettings: AppSettings, userIndicatorController: UserIndicatorControllerProtocol) { init(userSession: UserSessionProtocol, userSettings: UserSettings, userIndicatorController: UserIndicatorControllerProtocol) {
self.userSession = userSession self.userSession = userSession
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
super.init(initialViewState: IdentityConfirmationScreenViewState(learnMoreURL: appSettings.deviceVerificationURL)) super.init(initialViewState: IdentityConfirmationScreenViewState(learnMoreURL: userSettings.deviceVerificationURL))
Task { [weak self] in Task { [weak self] in
for await state in userSession.sessionSecurityStatePublisher.values { for await state in userSession.sessionSecurityStatePublisher.values {
@@ -142,7 +142,7 @@ struct IdentityConfirmationScreen_Previews: PreviewProvider, TestablePreview {
userSession.sessionSecurityStatePublisher = CurrentValuePublisher<SessionSecurityState, Never>(.init(verificationState: .unverified, recoveryState: recoveryState)) userSession.sessionSecurityStatePublisher = CurrentValuePublisher<SessionSecurityState, Never>(.init(verificationState: .unverified, recoveryState: recoveryState))
return IdentityConfirmationScreenViewModel(userSession: userSession, return IdentityConfirmationScreenViewModel(userSession: userSession,
appSettings: .volatile(), userSettings: .volatile(),
userIndicatorController: UserIndicatorControllerMock()) userIndicatorController: UserIndicatorControllerMock())
} }
} }
@@ -33,7 +33,7 @@ enum SessionVerificationScreenFlow {
struct SessionVerificationScreenCoordinatorParameters { struct SessionVerificationScreenCoordinatorParameters {
let sessionVerificationControllerProxy: SessionVerificationControllerProxyProtocol let sessionVerificationControllerProxy: SessionVerificationControllerProxyProtocol
let flow: SessionVerificationScreenFlow let flow: SessionVerificationScreenFlow
let appSettings: AppSettings let userSettings: UserSettings
let mediaProvider: MediaProviderProtocol let mediaProvider: MediaProviderProtocol
} }
@@ -50,7 +50,7 @@ final class SessionVerificationScreenCoordinator: CoordinatorProtocol {
init(parameters: SessionVerificationScreenCoordinatorParameters) { init(parameters: SessionVerificationScreenCoordinatorParameters) {
viewModel = SessionVerificationScreenViewModel(sessionVerificationControllerProxy: parameters.sessionVerificationControllerProxy, viewModel = SessionVerificationScreenViewModel(sessionVerificationControllerProxy: parameters.sessionVerificationControllerProxy,
flow: parameters.flow, flow: parameters.flow,
appSettings: parameters.appSettings, userSettings: parameters.userSettings,
mediaProvider: parameters.mediaProvider) mediaProvider: parameters.mediaProvider)
} }
@@ -25,7 +25,7 @@ class SessionVerificationScreenViewModel: SessionVerificationViewModelType, Sess
init(sessionVerificationControllerProxy: SessionVerificationControllerProxyProtocol, init(sessionVerificationControllerProxy: SessionVerificationControllerProxyProtocol,
flow: SessionVerificationScreenFlow, flow: SessionVerificationScreenFlow,
appSettings: AppSettings, userSettings: UserSettings,
mediaProvider: MediaProviderProtocol, mediaProvider: MediaProviderProtocol,
verificationState: SessionVerificationScreenStateMachine.State = .initial) { verificationState: SessionVerificationScreenStateMachine.State = .initial) {
self.sessionVerificationControllerProxy = sessionVerificationControllerProxy self.sessionVerificationControllerProxy = sessionVerificationControllerProxy
@@ -34,7 +34,7 @@ class SessionVerificationScreenViewModel: SessionVerificationViewModelType, Sess
stateMachine = SessionVerificationScreenStateMachine(state: verificationState) stateMachine = SessionVerificationScreenStateMachine(state: verificationState)
super.init(initialViewState: .init(flow: flow, super.init(initialViewState: .init(flow: flow,
learnMoreURL: appSettings.encryptionURL, learnMoreURL: userSettings.encryptionURL,
verificationState: verificationState), verificationState: verificationState),
mediaProvider: mediaProvider) mediaProvider: mediaProvider)
@@ -289,7 +289,7 @@ struct SessionVerification_Previews: PreviewProvider, TestablePreview {
flow: SessionVerificationScreenFlow = .deviceInitiator) -> some View { flow: SessionVerificationScreenFlow = .deviceInitiator) -> some View {
let viewModel = SessionVerificationScreenViewModel(sessionVerificationControllerProxy: SessionVerificationControllerProxyMock.configureMock(), let viewModel = SessionVerificationScreenViewModel(sessionVerificationControllerProxy: SessionVerificationControllerProxyMock.configureMock(),
flow: flow, flow: flow,
appSettings: .volatile(), userSettings: .volatile(),
mediaProvider: MediaProviderMock(.init()), mediaProvider: MediaProviderMock(.init()),
verificationState: state) verificationState: state)
@@ -15,7 +15,7 @@ struct PinnedEventsTimelineScreenCoordinatorParameters {
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let mediaPlayerProvider: MediaPlayerProviderProtocol let mediaPlayerProvider: MediaPlayerProviderProtocol
let appMediator: AppMediatorProtocol let appMediator: AppMediatorProtocol
let appSettings: AppSettings let userSettings: UserSettings
let analytics: AnalyticsServiceProtocol let analytics: AnalyticsServiceProtocol
let emojiProvider: EmojiProviderProtocol let emojiProvider: EmojiProviderProtocol
let linkMetadataProvider: LinkMetadataProviderProtocol let linkMetadataProvider: LinkMetadataProviderProtocol
@@ -46,7 +46,7 @@ final class PinnedEventsTimelineScreenCoordinator: CoordinatorProtocol {
init(parameters: PinnedEventsTimelineScreenCoordinatorParameters) { init(parameters: PinnedEventsTimelineScreenCoordinatorParameters) {
viewModel = PinnedEventsTimelineScreenViewModel(roomProxy: parameters.roomProxy, viewModel = PinnedEventsTimelineScreenViewModel(roomProxy: parameters.roomProxy,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
appSettings: parameters.appSettings, userSettings: parameters.userSettings,
analyticsService: parameters.analytics) analyticsService: parameters.analytics)
timelineViewModel = TimelineViewModel(roomProxy: parameters.roomProxy, timelineViewModel = TimelineViewModel(roomProxy: parameters.roomProxy,
timelineController: parameters.timelineController, timelineController: parameters.timelineController,
@@ -54,7 +54,6 @@ final class PinnedEventsTimelineScreenCoordinator: CoordinatorProtocol {
mediaPlayerProvider: parameters.mediaPlayerProvider, mediaPlayerProvider: parameters.mediaPlayerProvider,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
appMediator: parameters.appMediator, appMediator: parameters.appMediator,
appSettings: parameters.appSettings,
analyticsService: parameters.analytics, analyticsService: parameters.analytics,
emojiProvider: parameters.emojiProvider, emojiProvider: parameters.emojiProvider,
linkMetadataProvider: parameters.linkMetadataProvider, linkMetadataProvider: parameters.linkMetadataProvider,
@@ -14,7 +14,7 @@ typealias PinnedEventsTimelineScreenViewModelType = StateStoreViewModel<PinnedEv
class PinnedEventsTimelineScreenViewModel: PinnedEventsTimelineScreenViewModelType, PinnedEventsTimelineScreenViewModelProtocol { class PinnedEventsTimelineScreenViewModel: PinnedEventsTimelineScreenViewModelType, PinnedEventsTimelineScreenViewModelProtocol {
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let appSettings: AppSettings private let userSettings: UserSettings
private let analyticsService: AnalyticsServiceProtocol private let analyticsService: AnalyticsServiceProtocol
private let actionsSubject: PassthroughSubject<PinnedEventsTimelineScreenViewModelAction, Never> = .init() private let actionsSubject: PassthroughSubject<PinnedEventsTimelineScreenViewModelAction, Never> = .init()
@@ -24,11 +24,11 @@ class PinnedEventsTimelineScreenViewModel: PinnedEventsTimelineScreenViewModelTy
init(roomProxy: JoinedRoomProxyProtocol, init(roomProxy: JoinedRoomProxyProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
appSettings: AppSettings, userSettings: UserSettings,
analyticsService: AnalyticsServiceProtocol) { analyticsService: AnalyticsServiceProtocol) {
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.appSettings = appSettings self.userSettings = userSettings
self.analyticsService = analyticsService self.analyticsService = analyticsService
super.init(initialViewState: PinnedEventsTimelineScreenViewState()) super.init(initialViewState: PinnedEventsTimelineScreenViewState())
} }
@@ -77,7 +77,7 @@ class PinnedEventsTimelineScreenViewModel: PinnedEventsTimelineScreenViewModelTy
private func viewInRoomTimeline(eventID: String) async { private func viewInRoomTimeline(eventID: String) async {
switch await roomProxy.loadOrFetchEventDetails(for: eventID) { switch await roomProxy.loadOrFetchEventDetails(for: eventID) {
case .success(let event): case .success(let event):
let threadRootEventID: String? = if appSettings.threadsEnabled { let threadRootEventID: String? = if userSettings.threadsEnabled {
event.threadRootEventId() event.threadRootEventId()
} else { } else {
nil nil
@@ -83,7 +83,7 @@ struct PinnedEventsTimelineScreen: View {
struct PinnedEventsTimelineScreen_Previews: PreviewProvider, TestablePreview { struct PinnedEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = PinnedEventsTimelineScreenViewModel(roomProxy: JoinedRoomProxyMock(.init()), static let viewModel = PinnedEventsTimelineScreenViewModel(roomProxy: JoinedRoomProxyMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: .volatile(), userSettings: .volatile(),
analyticsService: AnalyticsServiceMock(.init())) analyticsService: AnalyticsServiceMock(.init()))
static let emptyTimelineViewModel = makeTimelineViewModel(timelineItems: []) static let emptyTimelineViewModel = makeTimelineViewModel(timelineItems: [])
@@ -46,7 +46,7 @@ struct QRCodeLoginScreenViewState: BindableState {
var state: QRCodeLoginState var state: QRCodeLoginState
let mode: QRCodeLoginScreenMode let mode: QRCodeLoginScreenMode
/// Whether or not it is possible for the screen to start the manual sign in flow. This was added to avoid /// Whether or not it is possible for the screen to start the manual sign in flow. This was added to avoid
/// having to handle server configuration when ``AppSettings.allowOtherAccountProviders`` is false. /// having to handle server configuration when ``UserSettings.allowOtherAccountProviders`` is false.
let canSignInManually: Bool let canSignInManually: Bool
let instructions = QRCodeLoginScreenInstructions() let instructions = QRCodeLoginScreenInstructions()
@@ -177,7 +177,7 @@ struct RoomDetailsEditScreen_Previews: PreviewProvider, TestablePreview {
return RoomDetailsEditScreenViewModel(roomProxy: roomProxy, return RoomDetailsEditScreenViewModel(roomProxy: roomProxy,
userSession: UserSessionMock(.init()), userSession: UserSessionMock(.init()),
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: .volatile()), mediaUploadingPreprocessor: MediaUploadingPreprocessor(userSettings: .volatile()),
userIndicatorController: UserIndicatorControllerMock()) userIndicatorController: UserIndicatorControllerMock())
} }
} }
@@ -12,7 +12,7 @@ import SwiftUI
struct RoomDetailsScreenCoordinatorParameters { struct RoomDetailsScreenCoordinatorParameters {
let roomProxy: JoinedRoomProxyProtocol let roomProxy: JoinedRoomProxyProtocol
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let appSettings: AppSettings let userSettings: UserSettings
let appHooks: AppHooks let appHooks: AppHooks
let analyticsService: AnalyticsServiceProtocol let analyticsService: AnalyticsServiceProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
@@ -54,7 +54,7 @@ final class RoomDetailsScreenCoordinator: CoordinatorProtocol {
isSpace = parameters.roomProxy.infoPublisher.value.isSpace isSpace = parameters.roomProxy.infoPublisher.value.isSpace
viewModel = RoomDetailsScreenViewModel(roomProxy: parameters.roomProxy, viewModel = RoomDetailsScreenViewModel(roomProxy: parameters.roomProxy,
userSession: parameters.userSession, userSession: parameters.userSession,
appSettings: parameters.appSettings, userSettings: parameters.userSettings,
appHooks: parameters.appHooks, appHooks: parameters.appHooks,
analyticsService: parameters.analyticsService, analyticsService: parameters.analyticsService,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
@@ -45,7 +45,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
init(roomProxy: JoinedRoomProxyProtocol, init(roomProxy: JoinedRoomProxyProtocol,
userSession: UserSessionProtocol, userSession: UserSessionProtocol,
appSettings: AppSettings, userSettings: UserSettings,
appHooks: AppHooks, appHooks: AppHooks,
analyticsService: AnalyticsServiceProtocol, analyticsService: AnalyticsServiceProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
@@ -67,7 +67,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
topicSummary: topic?.unattributedStringByReplacingNewlinesWithSpaces(), topicSummary: topic?.unattributedStringByReplacingNewlinesWithSpaces(),
joinedMembersCount: roomProxy.infoPublisher.value.joinedMembersCount, joinedMembersCount: roomProxy.infoPublisher.value.joinedMembersCount,
notificationSettingsState: .loading, notificationSettingsState: .loading,
isNativeCallingEnabled: appSettings.nativeCallEnabled, isNativeCallingEnabled: userSettings.nativeCallEnabled,
bindings: .init()) bindings: .init())
super.init(initialViewState: appHooks.roomDetailsScreenHook.update(viewState), super.init(initialViewState: appHooks.roomDetailsScreenHook.update(viewState),
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
@@ -441,7 +441,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
return .init(roomProxy: roomProxy, return .init(roomProxy: roomProxy,
userSession: UserSessionMock(.init()), userSession: UserSessionMock(.init()),
appSettings: AppSettings.volatile(), userSettings: UserSettings.volatile(),
appHooks: AppHooks(), appHooks: AppHooks(),
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
@@ -470,7 +470,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
return .init(roomProxy: roomProxy, return .init(roomProxy: roomProxy,
userSession: UserSessionMock(.init()), userSession: UserSessionMock(.init()),
appSettings: AppSettings.volatile(), userSettings: UserSettings.volatile(),
appHooks: AppHooks(), appHooks: AppHooks(),
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
@@ -509,7 +509,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
return .init(roomProxy: roomProxy, return .init(roomProxy: roomProxy,
userSession: UserSessionMock(.init(clientProxy: clientProxyMock)), userSession: UserSessionMock(.init(clientProxy: clientProxyMock)),
appSettings: AppSettings.volatile(), userSettings: UserSettings.volatile(),
appHooks: AppHooks(), appHooks: AppHooks(),
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
@@ -65,7 +65,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool
completionSuggestionService: CompletionSuggestionServiceProtocol, completionSuggestionService: CompletionSuggestionServiceProtocol,
mediaProvider: MediaProviderProtocol, mediaProvider: MediaProviderProtocol,
mentionDisplayHelper: MentionDisplayHelper, mentionDisplayHelper: MentionDisplayHelper,
appSettings: AppSettings, userSettings: UserSettings,
analyticsService: AnalyticsServiceProtocol, analyticsService: AnalyticsServiceProtocol,
composerDraftService: ComposerDraftServiceProtocol) { composerDraftService: ComposerDraftServiceProtocol) {
self.initialText = initialText self.initialText = initialText
@@ -80,7 +80,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool
super.init(initialViewState: ComposerToolbarViewState(wysiwygViewModel: wysiwygViewModel, super.init(initialViewState: ComposerToolbarViewState(wysiwygViewModel: wysiwygViewModel,
isRoomEncrypted: roomProxy.infoPublisher.value.isEncrypted, isRoomEncrypted: roomProxy.infoPublisher.value.isEncrypted,
isLocationSharingEnabled: appSettings.mapTilerConfiguration.publisher.value.isEnabled, isLocationSharingEnabled: userSettings.mapTilerConfiguration.publisher.value.isEnabled,
bindings: .init()), bindings: .init()),
mediaProvider: mediaProvider) mediaProvider: mediaProvider)
@@ -852,7 +852,7 @@ extension ComposerToolbarViewModel {
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init(suggestions: suggestions)), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init(suggestions: suggestions)),
mediaProvider: MediaProviderMock(.init()), mediaProvider: MediaProviderMock(.init()),
mentionDisplayHelper: ComposerMentionDisplayHelper.mock, mentionDisplayHelper: ComposerMentionDisplayHelper.mock,
appSettings: .volatile(), userSettings: .volatile(),
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
composerDraftService: ComposerDraftServiceMock(.init())) composerDraftService: ComposerDraftServiceMock(.init()))
viewModel.state.bindings.composerFocused = focused viewModel.state.bindings.composerFocused = focused
@@ -81,14 +81,14 @@ struct RoomAttachmentPicker_Previews: PreviewProvider, TestablePreview {
static let viewModel = makeViewModel() static let viewModel = makeViewModel()
static func makeViewModel() -> ComposerToolbarViewModel { static func makeViewModel() -> ComposerToolbarViewModel {
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
return ComposerToolbarViewModel(roomProxy: JoinedRoomProxyMock(.init()), return ComposerToolbarViewModel(roomProxy: JoinedRoomProxyMock(.init()),
wysiwygViewModel: WysiwygComposerViewModel(), wysiwygViewModel: WysiwygComposerViewModel(),
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
mediaProvider: MediaProviderMock(.init()), mediaProvider: MediaProviderMock(.init()),
mentionDisplayHelper: ComposerMentionDisplayHelper.mock, mentionDisplayHelper: ComposerMentionDisplayHelper.mock,
appSettings: appSettings, userSettings: userSettings,
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
composerDraftService: ComposerDraftServiceMock(.init())) composerDraftService: ComposerDraftServiceMock(.init()))
} }
@@ -24,7 +24,6 @@ struct RoomScreenCoordinatorParameters {
let completionSuggestionService: CompletionSuggestionServiceProtocol let completionSuggestionService: CompletionSuggestionServiceProtocol
let ongoingCallRoomIDPublisher: CurrentValuePublisher<String?, Never> let ongoingCallRoomIDPublisher: CurrentValuePublisher<String?, Never>
let appMediator: AppMediatorProtocol let appMediator: AppMediatorProtocol
let appSettings: AppSettings
let appHooks: AppHooks let appHooks: AppHooks
let analytics: AnalyticsServiceProtocol let analytics: AnalyticsServiceProtocol
let composerDraftService: ComposerDraftServiceProtocol let composerDraftService: ComposerDraftServiceProtocol
@@ -57,7 +56,7 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
private var roomViewModel: RoomScreenViewModelProtocol private var roomViewModel: RoomScreenViewModelProtocol
private var timelineViewModel: TimelineViewModelProtocol private var timelineViewModel: TimelineViewModelProtocol
private var composerViewModel: ComposerToolbarViewModelProtocol private var composerViewModel: ComposerToolbarViewModelProtocol
private let appSettings: AppSettings private let userSettings: UserSettings
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
@@ -67,7 +66,7 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
} }
init(parameters: RoomScreenCoordinatorParameters) { init(parameters: RoomScreenCoordinatorParameters) {
appSettings = parameters.appSettings userSettings = parameters.userSession.userSettings
var selectedPinnedEventID: String? var selectedPinnedEventID: String?
if let focussedEvent = parameters.focussedEvent { if let focussedEvent = parameters.focussedEvent {
@@ -78,7 +77,6 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
roomProxy: parameters.roomProxy, roomProxy: parameters.roomProxy,
initialSelectedPinnedEventID: selectedPinnedEventID, initialSelectedPinnedEventID: selectedPinnedEventID,
ongoingCallRoomIDPublisher: parameters.ongoingCallRoomIDPublisher, ongoingCallRoomIDPublisher: parameters.ongoingCallRoomIDPublisher,
appSettings: parameters.appSettings,
appHooks: parameters.appHooks, appHooks: parameters.appHooks,
analyticsService: parameters.analytics, analyticsService: parameters.analytics,
userIndicatorController: parameters.userIndicatorController) userIndicatorController: parameters.userIndicatorController)
@@ -90,7 +88,6 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
mediaPlayerProvider: parameters.mediaPlayerProvider, mediaPlayerProvider: parameters.mediaPlayerProvider,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
appMediator: parameters.appMediator, appMediator: parameters.appMediator,
appSettings: parameters.appSettings,
analyticsService: parameters.analytics, analyticsService: parameters.analytics,
emojiProvider: parameters.emojiProvider, emojiProvider: parameters.emojiProvider,
linkMetadataProvider: parameters.linkMetadataProvider, linkMetadataProvider: parameters.linkMetadataProvider,
@@ -106,7 +103,7 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
completionSuggestionService: parameters.completionSuggestionService, completionSuggestionService: parameters.completionSuggestionService,
mediaProvider: parameters.userSession.mediaProvider, mediaProvider: parameters.userSession.mediaProvider,
mentionDisplayHelper: ComposerMentionDisplayHelper(timelineContext: timelineViewModel.context), mentionDisplayHelper: ComposerMentionDisplayHelper(timelineContext: timelineViewModel.context),
appSettings: parameters.appSettings, userSettings: parameters.userSession.userSettings,
analyticsService: parameters.analytics, analyticsService: parameters.analytics,
composerDraftService: parameters.composerDraftService) composerDraftService: parameters.composerDraftService)
self.composerViewModel = composerViewModel self.composerViewModel = composerViewModel
@@ -125,13 +122,13 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
case .displayReportContent(let itemID, let senderID): case .displayReportContent(let itemID, let senderID):
actionsSubject.send(.presentReportContent(itemID: itemID, senderID: senderID)) actionsSubject.send(.presentReportContent(itemID: itemID, senderID: senderID))
case .displayCameraPicker: case .displayCameraPicker:
actionsSubject.send(.presentMediaUploadPicker(mode: .init(source: .camera, selectionType: .multiple(galleryEnabled: appSettings.galleryEnabled)), actionsSubject.send(.presentMediaUploadPicker(mode: .init(source: .camera, selectionType: .multiple(galleryEnabled: userSettings.galleryEnabled)),
caption: composerViewModel.context.plainComposerText)) caption: composerViewModel.context.plainComposerText))
case .displayMediaPicker: case .displayMediaPicker:
actionsSubject.send(.presentMediaUploadPicker(mode: .init(source: .photoLibrary, selectionType: .multiple(galleryEnabled: appSettings.galleryEnabled)), actionsSubject.send(.presentMediaUploadPicker(mode: .init(source: .photoLibrary, selectionType: .multiple(galleryEnabled: userSettings.galleryEnabled)),
caption: composerViewModel.context.plainComposerText)) caption: composerViewModel.context.plainComposerText))
case .displayDocumentPicker: case .displayDocumentPicker:
actionsSubject.send(.presentMediaUploadPicker(mode: .init(source: .documents(), selectionType: .multiple(galleryEnabled: appSettings.galleryEnabled)), actionsSubject.send(.presentMediaUploadPicker(mode: .init(source: .documents(), selectionType: .multiple(galleryEnabled: userSettings.galleryEnabled)),
caption: composerViewModel.context.plainComposerText)) caption: composerViewModel.context.plainComposerText))
case .displayMediaPreview(let mediaPreviewViewModel): case .displayMediaPreview(let mediaPreviewViewModel):
roomViewModel.displayMediaPreview(mediaPreviewViewModel) roomViewModel.displayMediaPreview(mediaPreviewViewModel)
@@ -17,7 +17,7 @@ typealias RoomScreenViewModelType = StateStoreViewModel<RoomScreenViewState, Roo
class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol { 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 userSettings: UserSettings
private let analyticsService: AnalyticsServiceProtocol private let analyticsService: AnalyticsServiceProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
@@ -54,13 +54,12 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
roomProxy: JoinedRoomProxyProtocol, roomProxy: JoinedRoomProxyProtocol,
initialSelectedPinnedEventID: String?, initialSelectedPinnedEventID: String?,
ongoingCallRoomIDPublisher: CurrentValuePublisher<String?, Never>, ongoingCallRoomIDPublisher: CurrentValuePublisher<String?, Never>,
appSettings: AppSettings,
appHooks: AppHooks, appHooks: AppHooks,
analyticsService: AnalyticsServiceProtocol, analyticsService: AnalyticsServiceProtocol,
userIndicatorController: UserIndicatorControllerProtocol) { userIndicatorController: UserIndicatorControllerProtocol) {
clientProxy = userSession.clientProxy clientProxy = userSession.clientProxy
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.appSettings = appSettings userSettings = userSession.userSettings
self.analyticsService = analyticsService self.analyticsService = analyticsService
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
@@ -69,7 +68,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
let viewState = RoomScreenViewState(roomTitle: roomProxy.infoPublisher.value.displayNameOrID, let viewState = RoomScreenViewState(roomTitle: roomProxy.infoPublisher.value.displayNameOrID,
roomAvatar: roomProxy.infoPublisher.value.avatar, roomAvatar: roomProxy.infoPublisher.value.avatar,
isNativeCallingEnabled: appSettings.nativeCallEnabled, isNativeCallingEnabled: userSettings.nativeCallEnabled,
hasOngoingCall: roomProxy.infoPublisher.value.hasRoomCall, hasOngoingCall: roomProxy.infoPublisher.value.hasRoomCall,
isDM: roomProxy.infoPublisher.value.isDM, isDM: roomProxy.infoPublisher.value.isDM,
hasSuccessor: roomProxy.infoPublisher.value.successor != nil, hasSuccessor: roomProxy.infoPublisher.value.successor != nil,
@@ -128,7 +127,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
Task { Task {
// When navigating away from the room, we need to mark the room as both read // When navigating away from the room, we need to mark the room as both read
// and fully read for Synapse to clear this room from the app's badge count. // and fully read for Synapse to clear this room from the app's badge count.
_ = await roomProxy.markAsRead(receiptType: appSettings.sharePresence ? .read : .readPrivate) _ = await roomProxy.markAsRead(receiptType: userSettings.sharePresence ? .read : .readPrivate)
_ = await roomProxy.markAsRead(receiptType: .fullyRead) _ = await roomProxy.markAsRead(receiptType: .fullyRead)
} }
// Work around QLPreviewController dismissal issues, see the InteractiveQuickLookModifier. // Work around QLPreviewController dismissal issues, see the InteractiveQuickLookModifier.
@@ -167,11 +166,11 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
// MARK: - Private // MARK: - Private
private func setupSubscriptions(ongoingCallRoomIDPublisher: CurrentValuePublisher<String?, Never>) { private func setupSubscriptions(ongoingCallRoomIDPublisher: CurrentValuePublisher<String?, Never>) {
appSettings.threadsEnabledPublisher userSettings.threadsEnabledPublisher
.weakAssign(to: \.state.roomThreadListEnabled, on: self) .weakAssign(to: \.state.roomThreadListEnabled, on: self)
.store(in: &cancellables) .store(in: &cancellables)
appSettings.liveLocationSharingSessionsByRoomIDPublisher userSettings.liveLocationSharingSessionsByRoomIDPublisher
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [weak self] sessionsByRoomID in .sink { [weak self] sessionsByRoomID in
guard let self else { return } guard let self else { return }
@@ -265,10 +264,10 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
if let member = identityVerificationViolations.values.first { if let member = identityVerificationViolations.values.first {
state.footerDetails = .verificationViolation(member: member, state.footerDetails = .verificationViolation(member: member,
learnMoreURL: appSettings.identityPinningViolationDetailsURL) learnMoreURL: userSettings.identityPinningViolationDetailsURL)
} else if let member = identityPinningViolations.values.first { } else if let member = identityPinningViolations.values.first {
state.footerDetails = .pinViolation(member: member, state.footerDetails = .pinViolation(member: member,
learnMoreURL: appSettings.identityPinningViolationDetailsURL) learnMoreURL: userSettings.identityPinningViolationDetailsURL)
} else { } else {
state.footerDetails = nil state.footerDetails = nil
} }
@@ -429,7 +428,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
Task { Task {
switch await roomProxy.loadOrFetchEventDetails(for: eventID) { switch await roomProxy.loadOrFetchEventDetails(for: eventID) {
case .success(let event): case .success(let event):
if appSettings.threadsEnabled, if userSettings.threadsEnabled,
let threadRootEventID = event.threadRootEventId() { let threadRootEventID = event.threadRootEventId() {
actionsSubject.send(.focusEvent(eventID: threadRootEventID)) actionsSubject.send(.focusEvent(eventID: threadRootEventID))
actionsSubject.send(.displayThread(threadRootEventID: threadRootEventID, focussedEventID: eventID)) actionsSubject.send(.displayThread(threadRootEventID: threadRootEventID, focussedEventID: eventID))
@@ -466,7 +465,6 @@ extension RoomScreenViewModel {
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
initialSelectedPinnedEventID: nil, initialSelectedPinnedEventID: nil,
ongoingCallRoomIDPublisher: .init(.init(nil)), ongoingCallRoomIDPublisher: .init(.init(nil)),
appSettings: .volatile(),
appHooks: appHooks, appHooks: appHooks,
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
userIndicatorController: UserIndicatorControllerMock()) userIndicatorController: UserIndicatorControllerMock())
@@ -395,19 +395,19 @@ struct RoomScreen_Previews: PreviewProvider, TestablePreview {
powerLevelsConfiguration: .init(canUserSendMessage: canSendMessage))) powerLevelsConfiguration: .init(canUserSendMessage: canSendMessage)))
let roomViewModel = RoomScreenViewModel.mock(roomProxyMock: roomProxyMock) let roomViewModel = RoomScreenViewModel.mock(roomProxyMock: roomProxyMock)
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
appSettings.messageMultiSelectEnabled = isSelecting userSettings.messageMultiSelectEnabled = isSelecting
let userSession = UserSessionMock(.init(userSettings: userSettings))
let timelineController = TimelineControllerMock(.init()) let timelineController = TimelineControllerMock(.init())
let timelineViewModel = TimelineViewModel(roomProxy: roomProxyMock, let timelineViewModel = TimelineViewModel(roomProxy: roomProxyMock,
timelineController: timelineController, timelineController: timelineController,
userSession: UserSessionMock(.init()), userSession: userSession,
mediaPlayerProvider: MediaPlayerProviderMock(), mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock(.init()), appMediator: AppMediatorMock(.init()),
appSettings: appSettings,
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(userSettings: userSettings),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -13,7 +13,7 @@ struct SearchScreenCoordinatorParameters {
let clientProxy: ClientProxyProtocol let clientProxy: ClientProxyProtocol
let mediaProvider: MediaProviderProtocol let mediaProvider: MediaProviderProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let appSettings: AppSettings let userSettings: UserSettings
} }
enum SearchScreenCoordinatorAction { enum SearchScreenCoordinatorAction {
@@ -36,7 +36,7 @@ final class SearchScreenCoordinator: CoordinatorProtocol {
clientProxy: parameters.clientProxy, clientProxy: parameters.clientProxy,
mediaProvider: parameters.mediaProvider, mediaProvider: parameters.mediaProvider,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
appSettings: parameters.appSettings) userSettings: parameters.userSettings)
} }
func start() { func start() {
@@ -19,7 +19,7 @@ class SearchScreenViewModel: SearchScreenViewModelType, SearchScreenViewModelPro
private let clientProxy: ClientProxyProtocol private let clientProxy: ClientProxyProtocol
private let searchService: SearchServiceProxyProtocol private let searchService: SearchServiceProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let appSettings: AppSettings private let userSettings: UserSettings
private var searchQueryObservationTask: Task<Void, Never>? private var searchQueryObservationTask: Task<Void, Never>?
private var searchModeObservationTask: Task<Void, Never>? private var searchModeObservationTask: Task<Void, Never>?
private var loadingObservationTask: Task<Void, Never>? private var loadingObservationTask: Task<Void, Never>?
@@ -36,14 +36,14 @@ class SearchScreenViewModel: SearchScreenViewModelType, SearchScreenViewModelPro
clientProxy: ClientProxyProtocol, clientProxy: ClientProxyProtocol,
mediaProvider: MediaProviderProtocol, mediaProvider: MediaProviderProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
appSettings: AppSettings, userSettings: UserSettings,
initialSearchQuery: String = "", initialSearchQuery: String = "",
initialSearchMode: SearchScreenMode = .rooms) { initialSearchMode: SearchScreenMode = .rooms) {
self.roomSummaryProvider = roomSummaryProvider self.roomSummaryProvider = roomSummaryProvider
self.clientProxy = clientProxy self.clientProxy = clientProxy
searchService = clientProxy.searchService searchService = clientProxy.searchService
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.appSettings = appSettings self.userSettings = userSettings
super.init(initialViewState: SearchScreenViewState(bindings: .init(searchQuery: initialSearchQuery, searchMode: initialSearchMode)), super.init(initialViewState: SearchScreenViewState(bindings: .init(searchQuery: initialSearchQuery, searchMode: initialSearchMode)),
mediaProvider: mediaProvider) mediaProvider: mediaProvider)
@@ -73,7 +73,7 @@ class SearchScreenViewModel: SearchScreenViewModelType, SearchScreenViewModelPro
.store(in: &cancellables) .store(in: &cancellables)
// The room list is empty on a cold start, so the room breadcrumbs are re-resolved as it loads. // The room list is empty on a cold start, so the room breadcrumbs are re-resolved as it loads.
appSettings.searchBreadcrumbsPublisher userSettings.searchBreadcrumbsPublisher
.combineLatest(clientProxy.staticRoomSummaryProvider.roomListPublisher) .combineLatest(clientProxy.staticRoomSummaryProvider.roomListPublisher)
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [weak self] storedBreadcrumbs, _ in .sink { [weak self] storedBreadcrumbs, _ in
@@ -118,7 +118,7 @@ class SearchScreenViewModel: SearchScreenViewModelType, SearchScreenViewModelPro
} }
updateRooms(with: roomSummaryProvider.roomListPublisher.value) updateRooms(with: roomSummaryProvider.roomListPublisher.value)
state.breadcrumbs = makeBreadcrumbs(from: appSettings.searchBreadcrumbs) state.breadcrumbs = makeBreadcrumbs(from: userSettings.searchBreadcrumbs)
} }
isolated deinit { isolated deinit {
@@ -215,13 +215,13 @@ class SearchScreenViewModel: SearchScreenViewModelType, SearchScreenViewModelPro
let searchQuery = state.bindings.searchQuery let searchQuery = state.bindings.searchQuery
let newBreadcrumbs: [SearchBreadcrumb] = searchQuery.isEmpty ? [.room(roomID: roomID)] : [.query(searchQuery), .room(roomID: roomID)] let newBreadcrumbs: [SearchBreadcrumb] = searchQuery.isEmpty ? [.room(roomID: roomID)] : [.query(searchQuery), .room(roomID: roomID)]
var breadcrumbs = appSettings.searchBreadcrumbs var breadcrumbs = userSettings.searchBreadcrumbs
for breadcrumb in newBreadcrumbs { for breadcrumb in newBreadcrumbs {
breadcrumbs.removeAll { $0 == breadcrumb } breadcrumbs.removeAll { $0 == breadcrumb }
breadcrumbs.insert(breadcrumb, at: 0) breadcrumbs.insert(breadcrumb, at: 0)
} }
appSettings.searchBreadcrumbs = Array(breadcrumbs.prefix(Self.maximumBreadcrumbCount)) userSettings.searchBreadcrumbs = Array(breadcrumbs.prefix(Self.maximumBreadcrumbCount))
} }
private func setActiveTabLoading(_ isLoading: Bool) { private func setActiveTabLoading(_ isLoading: Bool) {
@@ -577,31 +577,31 @@ struct SearchScreen_Previews: PreviewProvider, TestablePreview {
clientProxy: makeClientProxy(), clientProxy: makeClientProxy(),
mediaProvider: MediaProviderMock(.init()), mediaProvider: MediaProviderMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: AppSettings.volatile()) userSettings: UserSettings.volatile())
static let noResultsViewModel = SearchScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded([]))), static let noResultsViewModel = SearchScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded([]))),
clientProxy: makeClientProxy(), clientProxy: makeClientProxy(),
mediaProvider: MediaProviderMock(.init()), mediaProvider: MediaProviderMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: AppSettings.volatile(), userSettings: UserSettings.volatile(),
initialSearchQuery: "John Doe") initialSearchQuery: "John Doe")
static let roomsViewModel = SearchScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))), static let roomsViewModel = SearchScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))),
clientProxy: makeClientProxy(), clientProxy: makeClientProxy(),
mediaProvider: MediaProviderMock(.init()), mediaProvider: MediaProviderMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: AppSettings.volatile(), userSettings: UserSettings.volatile(),
initialSearchQuery: "Foundation") initialSearchQuery: "Foundation")
static let messagesViewModel = SearchScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded([]))), static let messagesViewModel = SearchScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded([]))),
clientProxy: makeClientProxy(searchService: makeSearchService(results: .mockResults)), clientProxy: makeClientProxy(searchService: makeSearchService(results: .mockResults)),
mediaProvider: MediaProviderMock(.init()), mediaProvider: MediaProviderMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: AppSettings.volatile(), userSettings: UserSettings.volatile(),
initialSearchQuery: "Foundation", initialSearchQuery: "Foundation",
initialSearchMode: .messages) initialSearchMode: .messages)
static let loadingMessagesViewModel = SearchScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded([]))), static let loadingMessagesViewModel = SearchScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded([]))),
clientProxy: makeClientProxy(searchService: makeSearchService(paginationState: .loading)), clientProxy: makeClientProxy(searchService: makeSearchService(paginationState: .loading)),
mediaProvider: MediaProviderMock(.init()), mediaProvider: MediaProviderMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: AppSettings.volatile(), userSettings: UserSettings.volatile(),
initialSearchQuery: "Foundation", initialSearchQuery: "Foundation",
initialSearchMode: .messages) initialSearchMode: .messages)
@@ -609,11 +609,11 @@ struct SearchScreen_Previews: PreviewProvider, TestablePreview {
clientProxy: makeClientProxy(), clientProxy: makeClientProxy(),
mediaProvider: MediaProviderMock(.init()), mediaProvider: MediaProviderMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: makeAppSettings(breadcrumbs: [.room(roomID: "!room1:matrix.org"), userSettings: makeUserSettings(breadcrumbs: [.room(roomID: "!room1:matrix.org"),
.query("Bob"), .query("Bob"),
.room(roomID: "!room5:matrix.org"), .room(roomID: "!room5:matrix.org"),
.query("Element HR"), .query("Element HR"),
.query("Alice")])) .query("Alice")]))
static var previews: some View { static var previews: some View {
ElementNavigationStack { ElementNavigationStack {
@@ -655,10 +655,10 @@ struct SearchScreen_Previews: PreviewProvider, TestablePreview {
return mock return mock
} }
private static func makeAppSettings(breadcrumbs: [SearchBreadcrumb]) -> AppSettings { private static func makeUserSettings(breadcrumbs: [SearchBreadcrumb]) -> UserSettings {
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
appSettings.searchBreadcrumbs = breadcrumbs userSettings.searchBreadcrumbs = breadcrumbs
return appSettings return userSettings
} }
private static func makeClientProxy(searchService: SearchServiceProxyMock = makeSearchService()) -> ClientProxyMock { private static func makeClientProxy(searchService: SearchServiceProxyMock = makeSearchService()) -> ClientProxyMock {
@@ -10,7 +10,7 @@ import Combine
import SwiftUI import SwiftUI
struct SecureBackupScreenCoordinatorParameters { struct SecureBackupScreenCoordinatorParameters {
let appSettings: AppSettings let userSettings: UserSettings
let clientProxy: ClientProxyProtocol let clientProxy: ClientProxyProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
} }
@@ -33,7 +33,7 @@ final class SecureBackupScreenCoordinator: CoordinatorProtocol {
init(parameters: SecureBackupScreenCoordinatorParameters) { init(parameters: SecureBackupScreenCoordinatorParameters) {
viewModel = SecureBackupScreenViewModel(secureBackupController: parameters.clientProxy.secureBackupController, viewModel = SecureBackupScreenViewModel(secureBackupController: parameters.clientProxy.secureBackupController,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
chatBackupDetailsURL: parameters.appSettings.chatBackupDetailsURL) chatBackupDetailsURL: parameters.userSettings.chatBackupDetailsURL)
} }
func start() { func start() {
@@ -13,7 +13,7 @@ struct SecurityAndPrivacyScreenCoordinatorParameters {
let roomProxy: JoinedRoomProxyProtocol let roomProxy: JoinedRoomProxyProtocol
let clientProxy: ClientProxyProtocol let clientProxy: ClientProxyProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let appSetting: AppSettings let appSetting: UserSettings
} }
enum SecurityAndPrivacyScreenCoordinatorAction { enum SecurityAndPrivacyScreenCoordinatorAction {
@@ -36,7 +36,7 @@ final class SecurityAndPrivacyScreenCoordinator: CoordinatorProtocol {
viewModel = SecurityAndPrivacyScreenViewModel(roomProxy: parameters.roomProxy, viewModel = SecurityAndPrivacyScreenViewModel(roomProxy: parameters.roomProxy,
clientProxy: parameters.clientProxy, clientProxy: parameters.clientProxy,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
appSettings: parameters.appSetting) userSettings: parameters.appSetting)
} }
func start() { func start() {
@@ -16,7 +16,7 @@ class SecurityAndPrivacyScreenViewModel: SecurityAndPrivacyScreenViewModelType,
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let clientProxy: ClientProxyProtocol private let clientProxy: ClientProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let appSettings: AppSettings private let userSettings: UserSettings
private let actionsSubject: PassthroughSubject<SecurityAndPrivacyScreenViewModelAction, Never> = .init() private let actionsSubject: PassthroughSubject<SecurityAndPrivacyScreenViewModelAction, Never> = .init()
var actionsPublisher: AnyPublisher<SecurityAndPrivacyScreenViewModelAction, Never> { var actionsPublisher: AnyPublisher<SecurityAndPrivacyScreenViewModelAction, Never> {
@@ -26,18 +26,18 @@ class SecurityAndPrivacyScreenViewModel: SecurityAndPrivacyScreenViewModelType,
init(roomProxy: JoinedRoomProxyProtocol, init(roomProxy: JoinedRoomProxyProtocol,
clientProxy: ClientProxyProtocol, clientProxy: ClientProxyProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
appSettings: AppSettings) { userSettings: UserSettings) {
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.clientProxy = clientProxy self.clientProxy = clientProxy
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.appSettings = appSettings self.userSettings = userSettings
super.init(initialViewState: SecurityAndPrivacyScreenViewState(accessType: roomProxy.infoPublisher.value.joinRule.toSecurityAndPrivacyRoomAccessType, super.init(initialViewState: SecurityAndPrivacyScreenViewState(accessType: roomProxy.infoPublisher.value.joinRule.toSecurityAndPrivacyRoomAccessType,
isEncryptionEnabled: roomProxy.infoPublisher.value.isEncrypted, isEncryptionEnabled: roomProxy.infoPublisher.value.isEncrypted,
historyVisibility: roomProxy.infoPublisher.value.historyVisibility.toSecurityAndPrivacyHistoryVisibility, historyVisibility: roomProxy.infoPublisher.value.historyVisibility.toSecurityAndPrivacyHistoryVisibility,
isSpace: roomProxy.infoPublisher.value.isSpace, isSpace: roomProxy.infoPublisher.value.isSpace,
isKnockingEnabled: appSettings.knockingEnabled, isKnockingEnabled: userSettings.knockingEnabled,
historySharingDetailsURL: appSettings.historySharingDetailsURL)) historySharingDetailsURL: userSettings.historySharingDetailsURL))
if let powerLevels = roomProxy.infoPublisher.value.powerLevels { if let powerLevels = roomProxy.infoPublisher.value.powerLevels {
setupPermissions(powerLevels: powerLevels) setupPermissions(powerLevels: powerLevels)
@@ -151,7 +151,7 @@ class SecurityAndPrivacyScreenViewModel: SecurityAndPrivacyScreenViewModelType,
.weakAssign(to: \.state.isSpace, on: self) .weakAssign(to: \.state.isSpace, on: self)
.store(in: &cancellables) .store(in: &cancellables)
appSettings.knockingEnabledPublisher userSettings.knockingEnabledPublisher
.weakAssign(to: \.state.isKnockingEnabled, on: self) .weakAssign(to: \.state.isKnockingEnabled, on: self)
.store(in: &cancellables) .store(in: &cancellables)
} }
@@ -160,7 +160,7 @@ class SecurityAndPrivacyScreenViewModel: SecurityAndPrivacyScreenViewModelType,
state.canEditAddress = powerLevels.canOwnUser(sendStateEvent: .roomCanonicalAlias) state.canEditAddress = powerLevels.canOwnUser(sendStateEvent: .roomCanonicalAlias)
state.canEditJoinRule = powerLevels.canOwnUser(sendStateEvent: .roomJoinRules) state.canEditJoinRule = powerLevels.canOwnUser(sendStateEvent: .roomJoinRules)
state.canEditHistoryVisibility = powerLevels.canOwnUser(sendStateEvent: .roomHistoryVisibility) state.canEditHistoryVisibility = powerLevels.canOwnUser(sendStateEvent: .roomHistoryVisibility)
state.canEnableEncryption = powerLevels.canOwnUser(sendStateEvent: .roomEncryption) && !appSettings.forceDisableE2EE.publisher.value state.canEnableEncryption = powerLevels.canOwnUser(sendStateEvent: .roomEncryption) && !userSettings.forceDisableE2EE.publisher.value
} }
private func setupRoomDirectoryVisibility() { private func setupRoomDirectoryVisibility() {
@@ -233,7 +233,7 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
joinRule: .invite)), joinRule: .invite)),
clientProxy: ClientProxyMock(.init()), clientProxy: ClientProxyMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: .volatile()) userSettings: .volatile())
static let publicViewModel = SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false, static let publicViewModel = SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false,
canonicalAlias: "#room:matrix.org", canonicalAlias: "#room:matrix.org",
@@ -242,17 +242,17 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
isVisibleInPublicDirectory: true)), isVisibleInPublicDirectory: true)),
clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org")), clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org")),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: .volatile()) userSettings: .volatile())
static let publicNoAddressViewModel = SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false, static let publicNoAddressViewModel = SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false,
members: .allMembersAsCreator, members: .allMembersAsCreator,
joinRule: .public)), joinRule: .public)),
clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org")), clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org")),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: .volatile()) userSettings: .volatile())
static let singleSpaceMembersViewModel = { static let singleSpaceMembersViewModel = {
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
let space = [SpaceServiceRoom].mockSingleRoom[0] let space = [SpaceServiceRoom].mockSingleRoom[0]
@@ -264,11 +264,11 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org", clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org",
spaceServiceConfiguration: .init(joinedParentSpaces: [space]))), spaceServiceConfiguration: .init(joinedParentSpaces: [space]))),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: appSettings) userSettings: userSettings)
}() }()
static let multipleSpacesMembersViewModel = { static let multipleSpacesMembersViewModel = {
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
let spaces = [SpaceServiceRoom].mockJoinedSpaces let spaces = [SpaceServiceRoom].mockJoinedSpaces
@@ -280,12 +280,12 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org", clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org",
spaceServiceConfiguration: .init(joinedParentSpaces: spaces))), spaceServiceConfiguration: .init(joinedParentSpaces: spaces))),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: appSettings) userSettings: userSettings)
}() }()
static let askToJoinViewModel = { static let askToJoinViewModel = {
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
appSettings.knockingEnabled = true userSettings.knockingEnabled = true
return SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false, return SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false,
canonicalAlias: "#room:matrix.org", canonicalAlias: "#room:matrix.org",
@@ -294,12 +294,12 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
isVisibleInPublicDirectory: true)), isVisibleInPublicDirectory: true)),
clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org")), clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org")),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: appSettings) userSettings: userSettings)
}() }()
static let singleAskToJoinSpaceMembersViewModel = { static let singleAskToJoinSpaceMembersViewModel = {
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
appSettings.knockingEnabled = true userSettings.knockingEnabled = true
let space = [SpaceServiceRoom].mockSingleRoom[0] let space = [SpaceServiceRoom].mockSingleRoom[0]
@@ -311,12 +311,12 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org", clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org",
spaceServiceConfiguration: .init(joinedParentSpaces: [space]))), spaceServiceConfiguration: .init(joinedParentSpaces: [space]))),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: appSettings) userSettings: userSettings)
}() }()
static let multipleAskToJoinSpacesMembersViewModel = { static let multipleAskToJoinSpacesMembersViewModel = {
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
appSettings.knockingEnabled = true userSettings.knockingEnabled = true
let spaces = [SpaceServiceRoom].mockJoinedSpaces let spaces = [SpaceServiceRoom].mockJoinedSpaces
@@ -328,7 +328,7 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org", clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org",
spaceServiceConfiguration: .init(joinedParentSpaces: spaces))), spaceServiceConfiguration: .init(joinedParentSpaces: spaces))),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: appSettings) userSettings: userSettings)
}() }()
static let publicSpaceViewModel = SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isSpace: true, static let publicSpaceViewModel = SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isSpace: true,
@@ -339,7 +339,7 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
isVisibleInPublicDirectory: true)), isVisibleInPublicDirectory: true)),
clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org")), clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org")),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: .volatile()) userSettings: .volatile())
static var previews: some View { static var previews: some View {
ElementNavigationStack { ElementNavigationStack {
@@ -27,11 +27,11 @@ class OAuthAccountSettingsPresenter: NSObject {
init(accountURL: URL, init(accountURL: URL,
presentationAnchor: UIWindow, presentationAnchor: UIWindow,
appMediator: AppMediatorProtocol, appMediator: AppMediatorProtocol,
appSettings: AppSettings, userSettings: UserSettings,
appHooks: AppHooks, appHooks: AppHooks,
continuation: Continuation? = nil) { continuation: Continuation? = nil) {
self.accountURL = accountURL self.accountURL = accountURL
redirectURL = appSettings.oAuthRedirectURL redirectURL = userSettings.oAuthRedirectURL
self.presentationAnchor = presentationAnchor self.presentationAnchor = presentationAnchor
self.appMediator = appMediator self.appMediator = appMediator
self.appHooks = appHooks self.appHooks = appHooks
@@ -10,7 +10,7 @@ import Combine
import SwiftUI import SwiftUI
struct AdvancedSettingsScreenCoordinatorParameters { struct AdvancedSettingsScreenCoordinatorParameters {
let appSettings: AppSettings let userSettings: UserSettings
let analytics: AnalyticsServiceProtocol let analytics: AnalyticsServiceProtocol
let clientProxy: ClientProxyProtocol let clientProxy: ClientProxyProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
@@ -20,7 +20,7 @@ final class AdvancedSettingsScreenCoordinator: CoordinatorProtocol {
private var viewModel: AdvancedSettingsScreenViewModelProtocol private var viewModel: AdvancedSettingsScreenViewModelProtocol
init(parameters: AdvancedSettingsScreenCoordinatorParameters) { init(parameters: AdvancedSettingsScreenCoordinatorParameters) {
viewModel = AdvancedSettingsScreenViewModel(advancedSettings: parameters.appSettings, viewModel = AdvancedSettingsScreenViewModel(advancedSettings: parameters.userSettings.app,
analytics: parameters.analytics, analytics: parameters.analytics,
clientProxy: parameters.clientProxy, clientProxy: parameters.clientProxy,
userIndicatorController: parameters.userIndicatorController) userIndicatorController: parameters.userIndicatorController)
@@ -48,7 +48,7 @@ class AdvancedSettingsScreenViewModel: AdvancedSettingsScreenViewModelType, Adva
override func process(viewAction: AdvancedSettingsScreenViewAction) { override func process(viewAction: AdvancedSettingsScreenViewAction) {
switch viewAction { switch viewAction {
case .optimizeMediaUploadsChanged: case .optimizeMediaUploadsChanged:
// Note: Using a view action here as sinking the AppSettings publisher tracks the initial value. // Note: Using a view action here as sinking the UserSettings publisher tracks the initial value.
analytics.trackInteraction(name: state.bindings.optimizeMediaUploads ? .MobileSettingsOptimizeMediaUploadsEnabled : .MobileSettingsOptimizeMediaUploadsDisabled) analytics.trackInteraction(name: state.bindings.optimizeMediaUploads ? .MobileSettingsOptimizeMediaUploadsEnabled : .MobileSettingsOptimizeMediaUploadsDisabled)
case let .updateHideInviteAvatars(value): case let .updateHideInviteAvatars(value):
hideInviteAvatarsTask = Task { [weak self] in await self?.updateHideInviteAvatars(value) } hideInviteAvatarsTask = Task { [weak self] in await self?.updateHideInviteAvatars(value) }
@@ -160,13 +160,10 @@ private extension AppAppearance {
// MARK: - Previews // MARK: - Previews
struct AdvancedSettingsScreen_Previews: PreviewProvider, TestablePreview { struct AdvancedSettingsScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = { static let viewModel = AdvancedSettingsScreenViewModel(advancedSettings: AppSettings.volatile(),
let appSettings = AppSettings.volatile() analytics: AnalyticsServiceMock(.init()),
return AdvancedSettingsScreenViewModel(advancedSettings: appSettings, clientProxy: ClientProxyMock(.init()),
analytics: AnalyticsServiceMock(.init()), userIndicatorController: UserIndicatorControllerMock())
clientProxy: ClientProxyMock(.init()),
userIndicatorController: UserIndicatorControllerMock())
}()
static var previews: some View { static var previews: some View {
ElementNavigationStack { ElementNavigationStack {
@@ -10,7 +10,7 @@ import Combine
import SwiftUI import SwiftUI
struct AnalyticsSettingsScreenCoordinatorParameters { struct AnalyticsSettingsScreenCoordinatorParameters {
let appSettings: AppSettings let userSettings: UserSettings
let analytics: AnalyticsServiceProtocol let analytics: AnalyticsServiceProtocol
} }
@@ -18,7 +18,7 @@ final class AnalyticsSettingsScreenCoordinator: CoordinatorProtocol {
private let viewModel: AnalyticsSettingsScreenViewModel private let viewModel: AnalyticsSettingsScreenViewModel
init(parameters: AnalyticsSettingsScreenCoordinatorParameters) { init(parameters: AnalyticsSettingsScreenCoordinatorParameters) {
viewModel = AnalyticsSettingsScreenViewModel(appSettings: parameters.appSettings, viewModel = AnalyticsSettingsScreenViewModel(userSettings: parameters.userSettings,
analytics: parameters.analytics) analytics: parameters.analytics)
} }
@@ -14,16 +14,16 @@ typealias AnalyticsSettingsScreenViewModelType = StateStoreViewModelV2<Analytics
class AnalyticsSettingsScreenViewModel: AnalyticsSettingsScreenViewModelType, AnalyticsSettingsScreenViewModelProtocol { class AnalyticsSettingsScreenViewModel: AnalyticsSettingsScreenViewModelType, AnalyticsSettingsScreenViewModelProtocol {
private let analytics: AnalyticsServiceProtocol private let analytics: AnalyticsServiceProtocol
init(appSettings: AppSettings, analytics: AnalyticsServiceProtocol) { init(userSettings: UserSettings, analytics: AnalyticsServiceProtocol) {
self.analytics = analytics self.analytics = analytics
let strings = AnalyticsSettingsScreenStrings(termsURL: appSettings.analyticsTermsURL) let strings = AnalyticsSettingsScreenStrings(termsURL: userSettings.analyticsTermsURL)
let bindings = AnalyticsSettingsScreenViewStateBindings(enableAnalytics: analytics.isEnabled) let bindings = AnalyticsSettingsScreenViewStateBindings(enableAnalytics: analytics.isEnabled)
let state = AnalyticsSettingsScreenViewState(strings: strings, bindings: bindings) let state = AnalyticsSettingsScreenViewState(strings: strings, bindings: bindings)
super.init(initialViewState: state) super.init(initialViewState: state)
appSettings.analyticsConsentStatePublisher userSettings.analyticsConsentStatePublisher
.map { $0 == .optedIn } .map { $0 == .optedIn }
.weakAssign(to: \.state.bindings.enableAnalytics, on: self) .weakAssign(to: \.state.bindings.enableAnalytics, on: self)
.store(in: &cancellables) .store(in: &cancellables)
@@ -39,8 +39,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.volatile() let userSettings = UserSettings.volatile()
let viewModel = AnalyticsSettingsScreenViewModel(appSettings: appSettings, let viewModel = AnalyticsSettingsScreenViewModel(userSettings: userSettings,
analytics: AnalyticsServiceMock(.init())) analytics: AnalyticsServiceMock(.init()))
AnalyticsSettingsScreen(context: viewModel.context) AnalyticsSettingsScreen(context: viewModel.context)
} }
@@ -12,8 +12,8 @@ import SwiftUI
final class LegalInformationScreenCoordinator: CoordinatorProtocol { final class LegalInformationScreenCoordinator: CoordinatorProtocol {
private let viewModel: LegalInformationScreenViewModelProtocol private let viewModel: LegalInformationScreenViewModelProtocol
init(appSettings: AppSettings) { init(userSettings: UserSettings) {
viewModel = LegalInformationScreenViewModel(appSettings: appSettings) viewModel = LegalInformationScreenViewModel(userSettings: userSettings)
} }
func toPresentable() -> AnyView { func toPresentable() -> AnyView {
@@ -12,9 +12,9 @@ import SwiftUI
typealias LegalInformationScreenViewModelType = StateStoreViewModelV2<LegalInformationScreenViewState, LegalInformationScreenViewAction> typealias LegalInformationScreenViewModelType = StateStoreViewModelV2<LegalInformationScreenViewState, LegalInformationScreenViewAction>
class LegalInformationScreenViewModel: LegalInformationScreenViewModelType, LegalInformationScreenViewModelProtocol { class LegalInformationScreenViewModel: LegalInformationScreenViewModelType, LegalInformationScreenViewModelProtocol {
init(appSettings: AppSettings) { init(userSettings: UserSettings) {
super.init(initialViewState: LegalInformationScreenViewState(copyrightURL: appSettings.copyrightURL, super.init(initialViewState: LegalInformationScreenViewState(copyrightURL: userSettings.copyrightURL,
acceptableUseURL: appSettings.acceptableUseURL, acceptableUseURL: userSettings.acceptableUseURL,
privacyURL: appSettings.privacyURL)) privacyURL: userSettings.privacyURL))
} }
} }
@@ -33,7 +33,7 @@ struct LegalInformationScreen: View {
// MARK: - Previews // MARK: - Previews
struct LegalInformationScreen_Previews: PreviewProvider, TestablePreview { struct LegalInformationScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = LegalInformationScreenViewModel(appSettings: .volatile()) static let viewModel = LegalInformationScreenViewModel(userSettings: .volatile())
static var previews: some View { static var previews: some View {
LegalInformationScreen(context: viewModel.context) LegalInformationScreen(context: viewModel.context)
} }
@@ -15,7 +15,7 @@ struct NotificationSettingsScreenCoordinatorParameters {
let userNotificationCenter: UserNotificationCenterProtocol let userNotificationCenter: UserNotificationCenterProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let isModallyPresented: Bool let isModallyPresented: Bool
let appSettings: AppSettings let userSettings: UserSettings
} }
enum NotificationSettingsScreenCoordinatorAction { enum NotificationSettingsScreenCoordinatorAction {
@@ -39,9 +39,9 @@ final class NotificationSettingsScreenCoordinator: CoordinatorProtocol {
init(parameters: NotificationSettingsScreenCoordinatorParameters) { init(parameters: NotificationSettingsScreenCoordinatorParameters) {
self.parameters = parameters self.parameters = parameters
viewModel = NotificationSettingsScreenViewModel(appSettings: parameters.appSettings, viewModel = NotificationSettingsScreenViewModel(userSettings: parameters.userSettings,
userNotificationCenter: parameters.userNotificationCenter, userNotificationCenter: parameters.userNotificationCenter,
notificationToneManager: NotificationToneManager(appSettings: parameters.appSettings), notificationToneManager: NotificationToneManager(userSettings: parameters.userSettings),
notificationSettingsProxy: parameters.userSession.clientProxy.notificationSettings, notificationSettingsProxy: parameters.userSession.clientProxy.notificationSettings,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
isModallyPresented: parameters.isModallyPresented) isModallyPresented: parameters.isModallyPresented)
@@ -13,7 +13,7 @@ typealias NotificationSettingsScreenViewModelType = StateStoreViewModelV2<Notifi
class NotificationSettingsScreenViewModel: NotificationSettingsScreenViewModelType, NotificationSettingsScreenViewModelProtocol { class NotificationSettingsScreenViewModel: NotificationSettingsScreenViewModelType, NotificationSettingsScreenViewModelProtocol {
private var actionsSubject: PassthroughSubject<NotificationSettingsScreenViewModelAction, Never> = .init() private var actionsSubject: PassthroughSubject<NotificationSettingsScreenViewModelAction, Never> = .init()
private let appSettings: AppSettings private let userSettings: UserSettings
private let userNotificationCenter: UserNotificationCenterProtocol private let userNotificationCenter: UserNotificationCenterProtocol
private let notificationSettingsProxy: NotificationSettingsProxyProtocol private let notificationSettingsProxy: NotificationSettingsProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
@@ -25,34 +25,34 @@ class NotificationSettingsScreenViewModel: NotificationSettingsScreenViewModelTy
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(appSettings: AppSettings, init(userSettings: UserSettings,
userNotificationCenter: UserNotificationCenterProtocol, userNotificationCenter: UserNotificationCenterProtocol,
notificationToneManager: NotificationToneManagerProtocol, notificationToneManager: NotificationToneManagerProtocol,
notificationSettingsProxy: NotificationSettingsProxyProtocol, notificationSettingsProxy: NotificationSettingsProxyProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
isModallyPresented: Bool) { isModallyPresented: Bool) {
self.appSettings = appSettings self.userSettings = userSettings
self.userNotificationCenter = userNotificationCenter self.userNotificationCenter = userNotificationCenter
self.notificationSettingsProxy = notificationSettingsProxy self.notificationSettingsProxy = notificationSettingsProxy
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
notificationTonePreviewer = AudioPlayer() notificationTonePreviewer = AudioPlayer()
self.notificationToneManager = notificationToneManager self.notificationToneManager = notificationToneManager
let bindings = NotificationSettingsScreenViewStateBindings(enableNotifications: appSettings.enableNotifications, let bindings = NotificationSettingsScreenViewStateBindings(enableNotifications: userSettings.enableNotifications,
showAllRoomListActivity: appSettings.showAllRoomListActivity) showAllRoomListActivity: userSettings.showAllRoomListActivity)
super.init(initialViewState: NotificationSettingsScreenViewState(bindings: bindings, super.init(initialViewState: NotificationSettingsScreenViewState(bindings: bindings,
isModallyPresented: isModallyPresented, isModallyPresented: isModallyPresented,
selectedAlertTone: appSettings.selectedNotificationTone ?? NotificationToneManager.defaultElementXMessageTone, selectedAlertTone: userSettings.selectedNotificationTone ?? NotificationToneManager.defaultElementXMessageTone,
availableCustomTones: notificationToneManager.customTones(), availableCustomTones: notificationToneManager.customTones(),
// macos lacks default sounds and its sandbox Sounds directory is immutable // macos lacks default sounds and its sandbox Sounds directory is immutable
customToneSelectionEnabled: !ProcessInfo.processInfo.isiOSAppOnMac)) customToneSelectionEnabled: !ProcessInfo.processInfo.isiOSAppOnMac))
// Listen for changes to AppSettings. // Listen for changes to UserSettings.
appSettings.enableNotificationsPublisher userSettings.enableNotificationsPublisher
.weakAssign(to: \.state.bindings.enableNotifications, on: self) .weakAssign(to: \.state.bindings.enableNotifications, on: self)
.store(in: &cancellables) .store(in: &cancellables)
appSettings.selectedNotificationTonePublisher userSettings.selectedNotificationTonePublisher
.map { $0 ?? NotificationToneManager.defaultElementXMessageTone } .map { $0 ?? NotificationToneManager.defaultElementXMessageTone }
.weakAssign(to: \.state.selectedAlertTone, on: self) .weakAssign(to: \.state.selectedAlertTone, on: self)
.store(in: &cancellables) .store(in: &cancellables)
@@ -104,7 +104,7 @@ class NotificationSettingsScreenViewModel: NotificationSettingsScreenViewModelTy
} }
Task { await enableInvitations(state.bindings.invitationsEnabled) } Task { await enableInvitations(state.bindings.invitationsEnabled) }
case .showAllRoomListActivityChanged: case .showAllRoomListActivityChanged:
appSettings.showAllRoomListActivity = state.bindings.showAllRoomListActivity userSettings.showAllRoomListActivity = state.bindings.showAllRoomListActivity
case .close: case .close:
actionsSubject.send(.close) actionsSubject.send(.close)
case .fixConfigurationMismatchTapped: case .fixConfigurationMismatchTapped:
@@ -127,7 +127,7 @@ class NotificationSettingsScreenViewModel: NotificationSettingsScreenViewModelTy
} }
func toggleNotifications() { func toggleNotifications() {
appSettings.enableNotifications.toggle() userSettings.enableNotifications.toggle()
} }
private func setupDidBecomeActiveSubscription() { private func setupDidBecomeActiveSubscription() {
@@ -302,7 +302,7 @@ class NotificationSettingsScreenViewModel: NotificationSettingsScreenViewModelTy
try notificationToneManager.deleteCustomTone(tone) try notificationToneManager.deleteCustomTone(tone)
if tone == state.selectedAlertTone { if tone == state.selectedAlertTone {
appSettings.selectedNotificationTone = nil userSettings.selectedNotificationTone = nil
} }
} catch { } catch {
MXLog.error("Error deleting alert tone \(tone.label): \(error)") MXLog.error("Error deleting alert tone \(tone.label): \(error)")
@@ -261,7 +261,7 @@ struct NotificationSettingsScreen: View {
@available(iOS 26.0, *) @available(iOS 26.0, *)
struct NotificationSettingsScreen_Previews: PreviewProvider, TestablePreview { struct NotificationSettingsScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel: NotificationSettingsScreenViewModel = { static let viewModel: NotificationSettingsScreenViewModel = {
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
let notificationCenter = UserNotificationCenterMock() let notificationCenter = UserNotificationCenterMock()
notificationCenter.authorizationStatusReturnValue = .notDetermined notificationCenter.authorizationStatusReturnValue = .notDetermined
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init()) let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
@@ -278,7 +278,7 @@ struct NotificationSettingsScreen_Previews: PreviewProvider, TestablePreview {
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "John Doe")))) let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "John Doe"))))
var viewModel = NotificationSettingsScreenViewModel(appSettings: appSettings, var viewModel = NotificationSettingsScreenViewModel(userSettings: userSettings,
userNotificationCenter: notificationCenter, userNotificationCenter: notificationCenter,
notificationToneManager: NotificationToneManagerMock(.init()), notificationToneManager: NotificationToneManagerMock(.init()),
notificationSettingsProxy: notificationSettingsProxy, notificationSettingsProxy: notificationSettingsProxy,
@@ -289,7 +289,7 @@ struct NotificationSettingsScreen_Previews: PreviewProvider, TestablePreview {
}() }()
static let viewModelConfigurationMismatch: NotificationSettingsScreenViewModel = { static let viewModelConfigurationMismatch: NotificationSettingsScreenViewModel = {
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
let notificationCenter = UserNotificationCenterMock() let notificationCenter = UserNotificationCenterMock()
notificationCenter.authorizationStatusReturnValue = .notDetermined notificationCenter.authorizationStatusReturnValue = .notDetermined
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init()) let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
@@ -308,7 +308,7 @@ struct NotificationSettingsScreen_Previews: PreviewProvider, TestablePreview {
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "John Doe")))) let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "John Doe"))))
var viewModel = NotificationSettingsScreenViewModel(appSettings: appSettings, var viewModel = NotificationSettingsScreenViewModel(userSettings: userSettings,
userNotificationCenter: notificationCenter, userNotificationCenter: notificationCenter,
notificationToneManager: NotificationToneManagerMock(.init()), notificationToneManager: NotificationToneManagerMock(.init()),
notificationSettingsProxy: notificationSettingsProxy, notificationSettingsProxy: notificationSettingsProxy,
@@ -11,7 +11,7 @@ import SwiftUI
struct SettingsScreenCoordinatorParameters { struct SettingsScreenCoordinatorParameters {
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let appSettings: AppSettings let userSettings: UserSettings
let isBugReportServiceEnabled: Bool let isBugReportServiceEnabled: Bool
let isInSecondaryWindow: Bool let isInSecondaryWindow: Bool
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
@@ -51,7 +51,7 @@ final class SettingsScreenCoordinator: CoordinatorProtocol {
init(parameters: SettingsScreenCoordinatorParameters) { init(parameters: SettingsScreenCoordinatorParameters) {
viewModel = SettingsScreenViewModel(userSession: parameters.userSession, viewModel = SettingsScreenViewModel(userSession: parameters.userSession,
appSettings: parameters.appSettings, userSettings: parameters.userSettings,
isBugReportServiceEnabled: parameters.isBugReportServiceEnabled, isBugReportServiceEnabled: parameters.isBugReportServiceEnabled,
isInSecondaryWindow: parameters.isInSecondaryWindow, isInSecondaryWindow: parameters.isInSecondaryWindow,
userIndicatorController: parameters.userIndicatorController) userIndicatorController: parameters.userIndicatorController)
@@ -12,7 +12,7 @@ import SwiftUI
typealias SettingsScreenViewModelType = StateStoreViewModelV2<SettingsScreenViewState, SettingsScreenViewAction> typealias SettingsScreenViewModelType = StateStoreViewModelV2<SettingsScreenViewState, SettingsScreenViewAction>
class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewModelProtocol { class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewModelProtocol {
private let appSettings: AppSettings private let userSettings: UserSettings
private let clientProxy: ClientProxyProtocol private let clientProxy: ClientProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
@@ -22,26 +22,26 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(userSession: UserSessionProtocol, appSettings: AppSettings, isBugReportServiceEnabled: Bool, isInSecondaryWindow: Bool, userIndicatorController: UserIndicatorControllerProtocol) { init(userSession: UserSessionProtocol, userSettings: UserSettings, isBugReportServiceEnabled: Bool, isInSecondaryWindow: Bool, userIndicatorController: UserIndicatorControllerProtocol) {
self.appSettings = appSettings self.userSettings = userSettings
clientProxy = userSession.clientProxy clientProxy = userSession.clientProxy
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
super.init(initialViewState: .init(deviceID: userSession.clientProxy.deviceID, super.init(initialViewState: .init(deviceID: userSession.clientProxy.deviceID,
userProfile: userSession.clientProxy.userProfilePublisher.value, userProfile: userSession.clientProxy.userProfilePublisher.value,
showLinkNewDeviceButton: appSettings.linkNewDeviceEnabled, showLinkNewDeviceButton: userSettings.linkNewDeviceEnabled,
showAccountDeactivation: userSession.clientProxy.canDeactivateAccount, showAccountDeactivation: userSession.clientProxy.canDeactivateAccount,
showDeveloperOptions: appSettings.developerOptionsEnabled, showDeveloperOptions: userSettings.developerOptionsEnabled,
showAnalyticsSettings: appSettings.canPromptForAnalytics, showAnalyticsSettings: userSettings.canPromptForAnalytics,
isBugReportServiceEnabled: isBugReportServiceEnabled, isBugReportServiceEnabled: isBugReportServiceEnabled,
navigationBarVisibility: isInSecondaryWindow ? .hidden : .automatic), navigationBarVisibility: isInSecondaryWindow ? .hidden : .automatic),
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
appSettings.developerOptionsEnabledPublisher userSettings.developerOptionsEnabledPublisher
.weakAssign(to: \.state.showDeveloperOptions, on: self) .weakAssign(to: \.state.showDeveloperOptions, on: self)
.store(in: &cancellables) .store(in: &cancellables)
appSettings.linkNewDeviceEnabledPublisher userSettings.linkNewDeviceEnabledPublisher
.weakAssign(to: \.state.showLinkNewDeviceButton, on: self) .weakAssign(to: \.state.showLinkNewDeviceButton, on: self)
.store(in: &cancellables) .store(in: &cancellables)
@@ -138,7 +138,7 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
case .labs: case .labs:
actionsSubject.send(.labs) actionsSubject.send(.labs)
case .enableDeveloperOptions: case .enableDeveloperOptions:
appSettings.developerOptionsEnabled.toggle() userSettings.developerOptionsEnabled.toggle()
case .developerOptions: case .developerOptions:
actionsSubject.send(.developerOptions) actionsSubject.send(.developerOptions)
case .deactivateAccount: case .deactivateAccount:
@@ -313,7 +313,7 @@ struct SettingsScreen_Previews: PreviewProvider, TestablePreview {
displayName: "Alice Liddell", displayName: "Alice Liddell",
status: .mockFocussing)))) status: .mockFocussing))))
return SettingsScreenViewModel(userSession: userSession, return SettingsScreenViewModel(userSession: userSession,
appSettings: .volatile(), userSettings: .volatile(),
isBugReportServiceEnabled: isBugReportServiceEnabled, isBugReportServiceEnabled: isBugReportServiceEnabled,
isInSecondaryWindow: false, isInSecondaryWindow: false,
userIndicatorController: UserIndicatorControllerMock()) userIndicatorController: UserIndicatorControllerMock())
@@ -145,7 +145,7 @@ struct UserDetailsEditScreen_Previews: PreviewProvider, TestablePreview {
canChangeDisplayName: canChangeProfile)))) canChangeDisplayName: canChangeProfile))))
return UserDetailsEditScreenViewModel(userSession: userSession, return UserDetailsEditScreenViewModel(userSession: userSession,
mediaUploadingPreprocessor: .init(appSettings: .volatile()), mediaUploadingPreprocessor: .init(userSettings: .volatile()),
userIndicatorController: UserIndicatorControllerMock()) userIndicatorController: UserIndicatorControllerMock())
} }
} }
@@ -132,7 +132,7 @@ struct SpaceSettingsScreen_Previews: PreviewProvider, TestablePreview {
canonicalAlias: "#space:matrix.org", canonicalAlias: "#space:matrix.org",
members: members)), members: members)),
userSession: UserSessionMock(.init()), userSession: UserSessionMock(.init()),
appSettings: AppSettings.volatile(), userSettings: UserSettings.volatile(),
appHooks: AppHooks(), appHooks: AppHooks(),
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
@@ -154,7 +154,7 @@ struct StartChatScreen: View {
struct StartChatScreen_Previews: PreviewProvider, TestablePreview { struct StartChatScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = { static let viewModel = {
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com")))) let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
let userDiscoveryService = UserDiscoveryServiceMock() let userDiscoveryService = UserDiscoveryServiceMock()
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice]) userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice])
@@ -21,7 +21,7 @@ struct ThreadTimelineScreenCoordinatorParameters {
let linkMetadataProvider: LinkMetadataProviderProtocol let linkMetadataProvider: LinkMetadataProviderProtocol
let completionSuggestionService: CompletionSuggestionServiceProtocol let completionSuggestionService: CompletionSuggestionServiceProtocol
let appMediator: AppMediatorProtocol let appMediator: AppMediatorProtocol
let appSettings: AppSettings let userSettings: UserSettings
let analytics: AnalyticsServiceProtocol let analytics: AnalyticsServiceProtocol
let composerDraftService: ComposerDraftServiceProtocol let composerDraftService: ComposerDraftServiceProtocol
let timelineControllerFactory: TimelineControllerFactoryProtocol let timelineControllerFactory: TimelineControllerFactoryProtocol
@@ -46,7 +46,7 @@ final class ThreadTimelineScreenCoordinator: CoordinatorProtocol {
private let viewModel: ThreadTimelineScreenViewModelProtocol private let viewModel: ThreadTimelineScreenViewModelProtocol
private let timelineViewModel: TimelineViewModelProtocol private let timelineViewModel: TimelineViewModelProtocol
private var composerViewModel: ComposerToolbarViewModelProtocol private var composerViewModel: ComposerToolbarViewModelProtocol
private let appSettings: AppSettings private let userSettings: UserSettings
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
@@ -56,7 +56,7 @@ final class ThreadTimelineScreenCoordinator: CoordinatorProtocol {
} }
init(parameters: ThreadTimelineScreenCoordinatorParameters) { init(parameters: ThreadTimelineScreenCoordinatorParameters) {
appSettings = parameters.appSettings userSettings = parameters.userSettings
viewModel = ThreadTimelineScreenViewModel(roomProxy: parameters.roomProxy, userSession: parameters.userSession) viewModel = ThreadTimelineScreenViewModel(roomProxy: parameters.roomProxy, userSession: parameters.userSession)
@@ -67,7 +67,6 @@ final class ThreadTimelineScreenCoordinator: CoordinatorProtocol {
mediaPlayerProvider: parameters.mediaPlayerProvider, mediaPlayerProvider: parameters.mediaPlayerProvider,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
appMediator: parameters.appMediator, appMediator: parameters.appMediator,
appSettings: parameters.appSettings,
analyticsService: parameters.analytics, analyticsService: parameters.analytics,
emojiProvider: parameters.emojiProvider, emojiProvider: parameters.emojiProvider,
linkMetadataProvider: parameters.linkMetadataProvider, linkMetadataProvider: parameters.linkMetadataProvider,
@@ -84,7 +83,7 @@ final class ThreadTimelineScreenCoordinator: CoordinatorProtocol {
completionSuggestionService: parameters.completionSuggestionService, completionSuggestionService: parameters.completionSuggestionService,
mediaProvider: parameters.userSession.mediaProvider, mediaProvider: parameters.userSession.mediaProvider,
mentionDisplayHelper: ComposerMentionDisplayHelper(timelineContext: timelineViewModel.context), mentionDisplayHelper: ComposerMentionDisplayHelper(timelineContext: timelineViewModel.context),
appSettings: parameters.appSettings, userSettings: parameters.userSettings,
analyticsService: parameters.analytics, analyticsService: parameters.analytics,
composerDraftService: parameters.composerDraftService) composerDraftService: parameters.composerDraftService)
} }
@@ -110,13 +109,13 @@ final class ThreadTimelineScreenCoordinator: CoordinatorProtocol {
case .displayReportContent(let itemID, let senderID): case .displayReportContent(let itemID, let senderID):
actionsSubject.send(.presentReportContent(itemID: itemID, senderID: senderID)) actionsSubject.send(.presentReportContent(itemID: itemID, senderID: senderID))
case .displayCameraPicker: case .displayCameraPicker:
actionsSubject.send(.presentMediaUploadPicker(mode: .init(source: .camera, selectionType: .multiple(galleryEnabled: appSettings.galleryEnabled)), actionsSubject.send(.presentMediaUploadPicker(mode: .init(source: .camera, selectionType: .multiple(galleryEnabled: userSettings.galleryEnabled)),
caption: composerViewModel.context.plainComposerText)) caption: composerViewModel.context.plainComposerText))
case .displayMediaPicker: case .displayMediaPicker:
actionsSubject.send(.presentMediaUploadPicker(mode: .init(source: .photoLibrary, selectionType: .multiple(galleryEnabled: appSettings.galleryEnabled)), actionsSubject.send(.presentMediaUploadPicker(mode: .init(source: .photoLibrary, selectionType: .multiple(galleryEnabled: userSettings.galleryEnabled)),
caption: composerViewModel.context.plainComposerText)) caption: composerViewModel.context.plainComposerText))
case .displayDocumentPicker: case .displayDocumentPicker:
actionsSubject.send(.presentMediaUploadPicker(mode: .init(source: .documents(), selectionType: .multiple(galleryEnabled: appSettings.galleryEnabled)), actionsSubject.send(.presentMediaUploadPicker(mode: .init(source: .documents(), selectionType: .multiple(galleryEnabled: userSettings.galleryEnabled)),
caption: composerViewModel.context.plainComposerText)) caption: composerViewModel.context.plainComposerText))
case .displayMediaPreview(let mediaPreviewViewModel): case .displayMediaPreview(let mediaPreviewViewModel):
viewModel.displayMediaPreview(mediaPreviewViewModel) viewModel.displayMediaPreview(mediaPreviewViewModel)
@@ -40,7 +40,7 @@ class TimelineInteractionHandler {
private let voiceMessageRecorder: VoiceMessageRecorderProtocol private let voiceMessageRecorder: VoiceMessageRecorderProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let appMediator: AppMediatorProtocol private let appMediator: AppMediatorProtocol
private let appSettings: AppSettings private let userSettings: UserSettings
private let analyticsService: AnalyticsServiceProtocol private let analyticsService: AnalyticsServiceProtocol
private let emojiProvider: EmojiProviderProtocol private let emojiProvider: EmojiProviderProtocol
private let linkMetadataProvider: LinkMetadataProviderProtocol private let linkMetadataProvider: LinkMetadataProviderProtocol
@@ -78,7 +78,6 @@ class TimelineInteractionHandler {
voiceMessageRecorder: VoiceMessageRecorderProtocol, voiceMessageRecorder: VoiceMessageRecorderProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
appMediator: AppMediatorProtocol, appMediator: AppMediatorProtocol,
appSettings: AppSettings,
analyticsService: AnalyticsServiceProtocol, analyticsService: AnalyticsServiceProtocol,
emojiProvider: EmojiProviderProtocol, emojiProvider: EmojiProviderProtocol,
linkMetadataProvider: LinkMetadataProviderProtocol, linkMetadataProvider: LinkMetadataProviderProtocol,
@@ -90,7 +89,7 @@ class TimelineInteractionHandler {
self.voiceMessageRecorder = voiceMessageRecorder self.voiceMessageRecorder = voiceMessageRecorder
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.appMediator = appMediator self.appMediator = appMediator
self.appSettings = appSettings userSettings = userSession.userSettings
self.analyticsService = analyticsService self.analyticsService = analyticsService
self.emojiProvider = emojiProvider self.emojiProvider = emojiProvider
self.linkMetadataProvider = linkMetadataProvider self.linkMetadataProvider = linkMetadataProvider
@@ -456,8 +455,8 @@ class TimelineInteractionHandler {
// MARK: Audio Playback // MARK: Audio Playback
func changePlaybackSpeed(for itemID: TimelineItemIdentifier) { func changePlaybackSpeed(for itemID: TimelineItemIdentifier) {
let nextSpeed = appSettings.voiceMessagePlaybackSpeed.next let nextSpeed = userSettings.voiceMessagePlaybackSpeed.next
appSettings.voiceMessagePlaybackSpeed = nextSpeed userSettings.voiceMessagePlaybackSpeed = nextSpeed
audioPlayerState(for: itemID)?.setPlaybackSpeed(nextSpeed) audioPlayerState(for: itemID)?.setPlaybackSpeed(nextSpeed)
} }
@@ -589,8 +588,8 @@ class TimelineInteractionHandler {
title: L10n.commonVoiceMessage, title: L10n.commonVoiceMessage,
duration: voiceMessageRoomTimelineItem.content.duration, duration: voiceMessageRoomTimelineItem.content.duration,
waveform: voiceMessageRoomTimelineItem.content.waveform, waveform: voiceMessageRoomTimelineItem.content.waveform,
playbackSpeed: appSettings.voiceMessagePlaybackSpeed, playbackSpeed: userSettings.voiceMessagePlaybackSpeed,
playbackSpeedPublisher: appSettings.voiceMessagePlaybackSpeedPublisher) playbackSpeedPublisher: userSettings.voiceMessagePlaybackSpeedPublisher)
mediaPlayerProvider.register(audioPlayerState: playerState) mediaPlayerProvider.register(audioPlayerState: playerState)
return playerState return playerState
} }
@@ -711,7 +710,6 @@ class TimelineInteractionHandler {
mediaPlayerProvider: mediaPlayerProvider, mediaPlayerProvider: mediaPlayerProvider,
userIndicatorController: userIndicatorController, userIndicatorController: userIndicatorController,
appMediator: appMediator, appMediator: appMediator,
appSettings: appSettings,
analyticsService: analyticsService, analyticsService: analyticsService,
emojiProvider: emojiProvider, emojiProvider: emojiProvider,
linkMetadataProvider: linkMetadataProvider, linkMetadataProvider: linkMetadataProvider,
@@ -29,7 +29,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
private let mediaPlayerProvider: MediaPlayerProviderProtocol private let mediaPlayerProvider: MediaPlayerProviderProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let appMediator: AppMediatorProtocol private let appMediator: AppMediatorProtocol
private let appSettings: AppSettings private let userSettings: UserSettings
private let analyticsService: AnalyticsServiceProtocol private let analyticsService: AnalyticsServiceProtocol
private let emojiProvider: EmojiProviderProtocol private let emojiProvider: EmojiProviderProtocol
@@ -54,7 +54,6 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
mediaPlayerProvider: MediaPlayerProviderProtocol, mediaPlayerProvider: MediaPlayerProviderProtocol,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
appMediator: AppMediatorProtocol, appMediator: AppMediatorProtocol,
appSettings: AppSettings,
analyticsService: AnalyticsServiceProtocol, analyticsService: AnalyticsServiceProtocol,
emojiProvider: EmojiProviderProtocol, emojiProvider: EmojiProviderProtocol,
linkMetadataProvider: LinkMetadataProviderProtocol, linkMetadataProvider: LinkMetadataProviderProtocol,
@@ -63,7 +62,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
self.timelineController = timelineController self.timelineController = timelineController
self.userSession = userSession self.userSession = userSession
self.mediaPlayerProvider = mediaPlayerProvider self.mediaPlayerProvider = mediaPlayerProvider
self.appSettings = appSettings userSettings = userSession.userSettings
self.analyticsService = analyticsService self.analyticsService = analyticsService
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.appMediator = appMediator self.appMediator = appMediator
@@ -78,7 +77,6 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
voiceMessageRecorder: voiceMessageRecorder, voiceMessageRecorder: voiceMessageRecorder,
userIndicatorController: userIndicatorController, userIndicatorController: userIndicatorController,
appMediator: appMediator, appMediator: appMediator,
appSettings: appSettings,
analyticsService: analyticsService, analyticsService: analyticsService,
emojiProvider: emojiProvider, emojiProvider: emojiProvider,
linkMetadataProvider: linkMetadataProvider, linkMetadataProvider: linkMetadataProvider,
@@ -99,16 +97,16 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
timelineState: TimelineState(focussedEvent: focussedEventID.map { .init(eventID: $0, appearance: .immediate) }), timelineState: TimelineState(focussedEvent: focussedEventID.map { .init(eventID: $0, appearance: .immediate) }),
ownUserID: roomProxy.ownUserID, ownUserID: roomProxy.ownUserID,
hideTimelineMedia: hideTimelineMedia, hideTimelineMedia: hideTimelineMedia,
isViewSourceEnabled: appSettings.viewSourceEnabled, isViewSourceEnabled: userSettings.viewSourceEnabled,
areThreadsEnabled: appSettings.threadsEnabled, areThreadsEnabled: userSettings.threadsEnabled,
linkPreviewsEnabled: appSettings.linkPreviewsEnabled, linkPreviewsEnabled: userSettings.linkPreviewsEnabled,
jumpToReadMarkerEnabled: appSettings.jumpToReadMarkerEnabled, jumpToReadMarkerEnabled: userSettings.jumpToReadMarkerEnabled,
messageSelection: .init(isEnabled: appSettings.messageMultiSelectEnabled), messageSelection: .init(isEnabled: userSettings.messageMultiSelectEnabled),
hasPredecessor: roomProxy.predecessorRoom != nil, hasPredecessor: roomProxy.predecessorRoom != nil,
pinnedEventIDs: roomProxy.infoPublisher.value.pinnedEventIDs, pinnedEventIDs: roomProxy.infoPublisher.value.pinnedEventIDs,
emojiProvider: emojiProvider, emojiProvider: emojiProvider,
linkMetadataProvider: hideTimelineMedia ? nil : linkMetadataProvider, linkMetadataProvider: hideTimelineMedia ? nil : linkMetadataProvider,
mapTilerConfiguration: appSettings.mapTilerConfiguration.publisher.value, mapTilerConfiguration: userSettings.mapTilerConfiguration.publisher.value,
bindings: .init(reactionsCollapsed: [:])), bindings: .init(reactionsCollapsed: [:])),
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
contentScannerService: userSession.contentScannerService) contentScannerService: userSession.contentScannerService)
@@ -274,7 +272,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
case .voiceMessage(let voiceMessageAction): case .voiceMessage(let voiceMessageAction):
processVoiceMessageAction(voiceMessageAction) processVoiceMessageAction(voiceMessageAction)
case .contentChanged(let isEmpty): case .contentChanged(let isEmpty):
guard appSettings.sharePresence else { guard userSettings.sharePresence else {
return return
} }
@@ -501,7 +499,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
} }
.store(in: &cancellables) .store(in: &cancellables)
setupAppSettingsSubscriptions() setupUserSettingsSubscriptions()
roomProxy.membersPublisher roomProxy.membersPublisher
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
@@ -510,7 +508,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
roomProxy.typingMembersPublisher roomProxy.typingMembersPublisher
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.filter { [weak self] _ in self?.appSettings.sharePresence ?? false } .filter { [weak self] _ in self?.userSettings.sharePresence ?? false }
.weakAssign(to: \.state.typingMembers, on: self) .weakAssign(to: \.state.typingMembers, on: self)
.store(in: &cancellables) .store(in: &cancellables)
@@ -562,7 +560,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
func viewInRoomTimeline(eventID: String) async { func viewInRoomTimeline(eventID: String) async {
switch await roomProxy.loadOrFetchEventDetails(for: eventID) { switch await roomProxy.loadOrFetchEventDetails(for: eventID) {
case .success(let event): case .success(let event):
let threadRootEventID: String? = if appSettings.threadsEnabled { let threadRootEventID: String? = if userSettings.threadsEnabled {
event.threadRootEventId() event.threadRootEventId()
} else { } else {
nil nil
@@ -573,20 +571,20 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
} }
} }
private func setupAppSettingsSubscriptions() { private func setupUserSettingsSubscriptions() {
appSettings.sharePresencePublisher userSettings.sharePresencePublisher
.weakAssign(to: \.state.showReadReceipts, on: self) .weakAssign(to: \.state.showReadReceipts, on: self)
.store(in: &cancellables) .store(in: &cancellables)
appSettings.viewSourceEnabledPublisher userSettings.viewSourceEnabledPublisher
.weakAssign(to: \.state.isViewSourceEnabled, on: self) .weakAssign(to: \.state.isViewSourceEnabled, on: self)
.store(in: &cancellables) .store(in: &cancellables)
appSettings.threadsEnabledPublisher userSettings.threadsEnabledPublisher
.weakAssign(to: \.state.areThreadsEnabled, on: self) .weakAssign(to: \.state.areThreadsEnabled, on: self)
.store(in: &cancellables) .store(in: &cancellables)
appSettings.jumpToReadMarkerEnabledPublisher userSettings.jumpToReadMarkerEnabledPublisher
.weakAssign(to: \.state.jumpToReadMarkerEnabled, on: self) .weakAssign(to: \.state.jumpToReadMarkerEnabled, on: self)
.store(in: &cancellables) .store(in: &cancellables)
@@ -1148,7 +1146,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
primaryButton: .init(title: L10n.actionOk, action: nil), primaryButton: .init(title: L10n.actionOk, action: nil),
secondaryButton: .init(title: L10n.actionLearnMore) { [weak self] in secondaryButton: .init(title: L10n.actionLearnMore) { [weak self] in
guard let self else { return } guard let self else { return }
appMediator.open(appSettings.historySharingDetailsURL) appMediator.open(userSettings.historySharingDetailsURL)
}) })
case .inviteAgain: case .inviteAgain:
state.bindings.alertInfo = .init(id: .inviteAgain, state.bindings.alertInfo = .init(id: .inviteAgain,
@@ -1199,7 +1197,7 @@ extension TimelineViewModel {
extension TimelineViewModel { extension TimelineViewModel {
private func setupSelectionSubscriptions() { private func setupSelectionSubscriptions() {
appSettings.messageMultiSelectEnabledPublisher userSettings.messageMultiSelectEnabledPublisher
.sink { [weak self] isEnabled in .sink { [weak self] isEnabled in
self?.state.messageSelection.isEnabled = isEnabled self?.state.messageSelection.isEnabled = isEnabled
if !isEnabled { if !isEnabled {
@@ -1300,8 +1298,6 @@ extension TimelineViewModel {
clientProxyMock.roomSummaryForIdentifierReturnValue = .mock(id: "!room:matrix.org", name: "Room", canonicalAlias: "#room:matrix.org") clientProxyMock.roomSummaryForIdentifierReturnValue = .mock(id: "!room:matrix.org", name: "Room", canonicalAlias: "#room:matrix.org")
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.volatile()
return TimelineViewModel(roomProxy: roomProxy, return TimelineViewModel(roomProxy: roomProxy,
focussedEventID: nil, focussedEventID: nil,
timelineController: timelineController ?? TimelineControllerMock(.init(timelineKind: timelineKind)), timelineController: timelineController ?? TimelineControllerMock(.init(timelineKind: timelineKind)),
@@ -1309,9 +1305,8 @@ extension TimelineViewModel {
mediaPlayerProvider: MediaPlayerProviderMock(), mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock(.init()), appMediator: AppMediatorMock(.init()),
appSettings: appSettings,
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(userSettings: .volatile()),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
} }
@@ -340,7 +340,7 @@ struct TimelineItemMenu_Previews: PreviewProvider, TestablePreview {
isViewSourceEnabled: true, isViewSourceEnabled: true,
areThreadsEnabled: true, areThreadsEnabled: true,
timelineKind: .live, timelineKind: .live,
emojiProvider: EmojiProvider(appSettings: .volatile())) emojiProvider: EmojiProvider(userSettings: .volatile()))
guard let actions = provider.makeActions() else { return nil } guard let actions = provider.makeActions() else { return nil }
if var textItem = item as? TextRoomTimelineItem { if var textItem = item as? TextRoomTimelineItem {
@@ -45,17 +45,14 @@ struct ReadReceiptsSummaryView_Previews: PreviewProvider, TestablePreview {
] ]
let roomProxyMock = JoinedRoomProxyMock(.init(name: "Room", members: members)) let roomProxyMock = JoinedRoomProxyMock(.init(name: "Room", members: members))
let appSettings = AppSettings.volatile()
return TimelineViewModel(roomProxy: roomProxyMock, return TimelineViewModel(roomProxy: roomProxyMock,
timelineController: TimelineControllerMock(.init()), timelineController: TimelineControllerMock(.init()),
userSession: UserSessionMock(.init()), userSession: UserSessionMock(.init()),
mediaPlayerProvider: MediaPlayerProviderMock(), mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock(.init()), appMediator: AppMediatorMock(.init()),
appSettings: appSettings,
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(userSettings: .volatile()),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
}() }()
@@ -421,39 +421,39 @@ private extension TimelineItemKeyForwarder {
struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview { struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview {
static let viewModel: TimelineViewModel = { static let viewModel: TimelineViewModel = {
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
appSettings.threadsEnabled = true userSettings.threadsEnabled = true
let userSession = UserSessionMock(.init(userSettings: userSettings))
let roomProxy = JoinedRoomProxyMock(.init()) let roomProxy = JoinedRoomProxyMock(.init())
return TimelineViewModel(roomProxy: roomProxy, return TimelineViewModel(roomProxy: roomProxy,
focussedEventID: nil, focussedEventID: nil,
timelineController: TimelineControllerMock(.init()), timelineController: TimelineControllerMock(.init()),
userSession: UserSessionMock(.init()), userSession: userSession,
mediaPlayerProvider: MediaPlayerProviderMock(), mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock(.init()), appMediator: AppMediatorMock(.init()),
appSettings: appSettings,
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(userSettings: userSettings),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
}() }()
static let viewModelWithPins: TimelineViewModel = { static let viewModelWithPins: TimelineViewModel = {
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
appSettings.threadsEnabled = true userSettings.threadsEnabled = true
let userSession = UserSessionMock(.init(userSettings: userSettings))
let roomProxy = JoinedRoomProxyMock(.init(name: "Preview Room", pinnedEventIDs: ["pinned"])) let roomProxy = JoinedRoomProxyMock(.init(name: "Preview Room", pinnedEventIDs: ["pinned"]))
return TimelineViewModel(roomProxy: roomProxy, return TimelineViewModel(roomProxy: roomProxy,
focussedEventID: nil, focussedEventID: nil,
timelineController: TimelineControllerMock(.init()), timelineController: TimelineControllerMock(.init()),
userSession: UserSessionMock(.init()), userSession: userSession,
mediaPlayerProvider: MediaPlayerProviderMock(), mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock(.init()), appMediator: AppMediatorMock(.init()),
appSettings: appSettings,
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(userSettings: userSettings),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
}() }()
@@ -461,19 +461,19 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
static let unsafeViewModel = TimelineViewModel.mock(contentScannerService: ContentScannerServiceMock(.init(scanResult: false))) static let unsafeViewModel = TimelineViewModel.mock(contentScannerService: ContentScannerServiceMock(.init(scanResult: false)))
static let selectingViewModel: TimelineViewModel = { static let selectingViewModel: TimelineViewModel = {
let appSettings = AppSettings.volatile() let userSettings = UserSettings.volatile()
appSettings.messageMultiSelectEnabled = true userSettings.messageMultiSelectEnabled = true
let userSession = UserSessionMock(.init(userSettings: userSettings))
let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init()), let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init()),
focussedEventID: nil, focussedEventID: nil,
timelineController: TimelineControllerMock(.init()), timelineController: TimelineControllerMock(.init()),
userSession: UserSessionMock(.init()), userSession: userSession,
mediaPlayerProvider: MediaPlayerProviderMock(), mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock(.init()), appMediator: AppMediatorMock(.init()),
appSettings: appSettings,
analyticsService: AnalyticsServiceMock(.init()), analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(appSettings: appSettings), emojiProvider: EmojiProvider(userSettings: userSettings),
linkMetadataProvider: LinkMetadataProvider(), linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init())) timelineControllerFactory: TimelineControllerFactoryMock(.init()))
viewModel.state.messageSelection.selectedEventIDs = ["selected"] viewModel.state.messageSelection.selectedEventIDs = ["selected"]
@@ -79,21 +79,16 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview {
.mockMe .mockMe
] ]
static let viewModel = { static let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Test", members: members)),
let appSettings = AppSettings.volatile() timelineController: TimelineControllerMock(.init()),
userSession: UserSessionMock(.init()),
return TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Test", members: members)), mediaPlayerProvider: MediaPlayerProviderMock(),
timelineController: TimelineControllerMock(.init()), userIndicatorController: UserIndicatorControllerMock(),
userSession: UserSessionMock(.init()), appMediator: AppMediatorMock(.init()),
mediaPlayerProvider: MediaPlayerProviderMock(), analyticsService: AnalyticsServiceMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), emojiProvider: EmojiProvider(userSettings: .volatile()),
appMediator: AppMediatorMock(.init()), linkMetadataProvider: LinkMetadataProvider(),
appSettings: appSettings, timelineControllerFactory: TimelineControllerFactoryMock(.init()))
analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
}()
static let singleReceipt = [ReadReceipt(userID: RoomMemberProxyMock.mockAlice.userID, formattedTimestamp: "Now")] static let singleReceipt = [ReadReceipt(userID: RoomMemberProxyMock.mockAlice.userID, formattedTimestamp: "Now")]
static let doubleReceipt = [ReadReceipt(userID: RoomMemberProxyMock.mockAlice.userID, formattedTimestamp: "Now"), static let doubleReceipt = [ReadReceipt(userID: RoomMemberProxyMock.mockAlice.userID, formattedTimestamp: "Now"),
@@ -89,22 +89,17 @@ struct HighlightedTimelineItemTimeline_Previews: PreviewProvider {
static let roomViewModel = RoomScreenViewModel.mock(roomProxyMock: roomProxyMock) static let roomViewModel = RoomScreenViewModel.mock(roomProxyMock: roomProxyMock)
static let focussedEventID = "TimelineFixtures.default.5" static let focussedEventID = "TimelineFixtures.default.5"
static let composerViewModel = ComposerToolbarViewModel.mock() static let composerViewModel = ComposerToolbarViewModel.mock()
static let timelineViewModel = { static let timelineViewModel = TimelineViewModel(roomProxy: roomProxyMock,
let appSettings = AppSettings.volatile() focussedEventID: focussedEventID,
timelineController: TimelineControllerMock(.init()),
return TimelineViewModel(roomProxy: roomProxyMock, userSession: UserSessionMock(.init()),
focussedEventID: focussedEventID, mediaPlayerProvider: MediaPlayerProviderMock(),
timelineController: TimelineControllerMock(.init()), userIndicatorController: UserIndicatorControllerMock(),
userSession: UserSessionMock(.init()), appMediator: AppMediatorMock(.init()),
mediaPlayerProvider: MediaPlayerProviderMock(), analyticsService: AnalyticsServiceMock(.init()),
userIndicatorController: UserIndicatorControllerMock(), emojiProvider: EmojiProvider(userSettings: .volatile()),
appMediator: AppMediatorMock(.init()), linkMetadataProvider: LinkMetadataProvider(),
appSettings: appSettings, timelineControllerFactory: TimelineControllerFactoryMock(.init()))
analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
}()
static var previews: some View { static var previews: some View {
ElementNavigationStack { ElementNavigationStack {

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