diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 09b42745a..928c30960 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -700,6 +700,7 @@ 748F482FEF4E04D61C39AAD7 /* EmojiPickerScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = F174A5627CDB3CAF280D1880 /* EmojiPickerScreenModels.swift */; }; 74DF5BC17DE9F51E077FD457 /* LinkNewDeviceServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA257D747DD7E6FFA5C2BE2D /* LinkNewDeviceServiceMock.swift */; }; 7501442D52A65F73DF79FFD4 /* PaginationIndicatorRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B987FC3FDBAA0E1C5AA235C /* PaginationIndicatorRoomTimelineItem.swift */; }; + 75072C529C9C363F531721F1 /* RecoveryKeyScreenHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE97AD18893E2D8120F559D /* RecoveryKeyScreenHook.swift */; }; 754602A7B2AAD443C4228ED4 /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = 4278261E147DB2DE5CFB7FC5 /* PostHog */; }; 755395927DDD6EBDDA5E217A /* SettingsFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F7A6CEEA4A2815B0F0F55 /* SettingsFlowCoordinator.swift */; }; 755727E0B756430DFFEC4732 /* SessionVerificationViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF05DA24F71B455E8EFEBC3B /* SessionVerificationViewModelTests.swift */; }; @@ -2329,6 +2330,7 @@ 7ACFFE7814849BB200CE0969 /* SpaceAddRoomsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceAddRoomsScreenModels.swift; sourceTree = ""; }; 7AE094FCB6387D268C436161 /* SecureBackupScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupScreenViewModel.swift; sourceTree = ""; }; 7AE75941583A033A9EDC9FE0 /* RoomChangePermissionsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangePermissionsScreenViewModel.swift; sourceTree = ""; }; + 7AE97AD18893E2D8120F559D /* RecoveryKeyScreenHook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryKeyScreenHook.swift; sourceTree = ""; }; 7AFD012C3A9F5EF276DDD4AA /* AnalyticsPromptScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptScreenViewModelProtocol.swift; sourceTree = ""; }; 7B04BD3874D736127A8156B8 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; 7B19B2BCC779ED934E0BBC2A /* AudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayer.swift; sourceTree = ""; }; @@ -3748,6 +3750,7 @@ 8B89D6C760E8CAE29CA28FB1 /* CompoundHook.swift */, 0F60FDB3AEFC60830BD289FE /* DeveloperOptionsScreenHook.swift */, 04CAC77224E949D8E758E2E3 /* OAuthPresenterHook.swift */, + 7AE97AD18893E2D8120F559D /* RecoveryKeyScreenHook.swift */, D5D186A6DB8FAC5C9D0E4D61 /* RemoteSettingsHook.swift */, B343C5255FB408DDE853CFDF /* RoomScreenHook.swift */, 2A95C9B8299A36A6495DECA6 /* TracingHook.swift */, @@ -8748,6 +8751,7 @@ ABD29E06DD1224812E750AF8 /* ReadReceiptCell.swift in Sources */, 1653275750CE11F5CE94DDFD /* ReadReceiptsSummaryView.swift in Sources */, C76892321558E75101E68ED6 /* ReadableFrameModifier.swift in Sources */, + 75072C529C9C363F531721F1 /* RecoveryKeyScreenHook.swift in Sources */, AF19D65A9C60C6B2646F3210 /* RedactedRoomTimelineItem.swift in Sources */, 8CFDA5F1562479CB3A34D277 /* RedactedRoomTimelineView.swift in Sources */, 3A2640C52505BAB3D983A92A /* RemotePreference.swift in Sources */, diff --git a/ElementX/Sources/AppHooks/AppHooks.swift b/ElementX/Sources/AppHooks/AppHooks.swift index b72828743..8f09a5096 100644 --- a/ElementX/Sources/AppHooks/AppHooks.swift +++ b/ElementX/Sources/AppHooks/AppHooks.swift @@ -68,6 +68,15 @@ final class AppHooks: AppHooksProtocol { func registerDeveloperOptionsScreenHook(_ hook: DeveloperOptionsScreenHookProtocol) { _developerOptionsScreenHook.withLock { $0 = hook } } + + private let _recoveryKeyScreenHook: Mutex = Mutex(DefaultRecoveryKeyScreenHook()) + var recoveryKeyScreenHook: RecoveryKeyScreenHookProtocol { + _recoveryKeyScreenHook.withLock { $0 } + } + + func registerRecoveryKeyScreenHook(_ hook: RecoveryKeyScreenHookProtocol) { + _recoveryKeyScreenHook.withLock { $0 = hook } + } #endif private let _tracingHook: Mutex = Mutex(DefaultTracingHook()) diff --git a/ElementX/Sources/AppHooks/Hooks/RecoveryKeyScreenHook.swift b/ElementX/Sources/AppHooks/Hooks/RecoveryKeyScreenHook.swift new file mode 100644 index 000000000..2b90ed53d --- /dev/null +++ b/ElementX/Sources/AppHooks/Hooks/RecoveryKeyScreenHook.swift @@ -0,0 +1,23 @@ +// +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +// Please see LICENSE files in the repository root for full details. +// + +import Combine +import SwiftUI + +@MainActor protocol SecureBackupRecoveryKeyCoordinatorProtocol: CoordinatorProtocol { + var actions: AnyPublisher { get } +} + +protocol RecoveryKeyScreenHookProtocol { + @MainActor func makeCoordinator(parameters: SecureBackupRecoveryKeyScreenCoordinatorParameters, homeserver: String) -> any SecureBackupRecoveryKeyCoordinatorProtocol +} + +struct DefaultRecoveryKeyScreenHook: RecoveryKeyScreenHookProtocol { + @MainActor func makeCoordinator(parameters: SecureBackupRecoveryKeyScreenCoordinatorParameters, homeserver: String) -> any SecureBackupRecoveryKeyCoordinatorProtocol { + SecureBackupRecoveryKeyScreenCoordinator(parameters: parameters) + } +} diff --git a/ElementX/Sources/FlowCoordinators/ChatsTabFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/ChatsTabFlowCoordinator.swift index ec3445eed..470bc4a9a 100644 --- a/ElementX/Sources/FlowCoordinators/ChatsTabFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/ChatsTabFlowCoordinator.swift @@ -672,7 +672,8 @@ class ChatsTabFlowCoordinator: FlowCoordinatorProtocol { userIndicatorController: flowParameters.userIndicatorController, isModallyPresented: true) - let coordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: parameters) + let coordinator = flowParameters.appHooks.recoveryKeyScreenHook.makeCoordinator(parameters: parameters, + homeserver: userSession.clientProxy.homeserver) coordinator.actions.sink { [weak self] action in guard let self else { return } switch action { diff --git a/ElementX/Sources/FlowCoordinators/EncryptionSettingsFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/EncryptionSettingsFlowCoordinator.swift index e103f996a..e6dd287d2 100644 --- a/ElementX/Sources/FlowCoordinators/EncryptionSettingsFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/EncryptionSettingsFlowCoordinator.swift @@ -18,6 +18,7 @@ enum EncryptionSettingsFlowCoordinatorAction: Equatable { struct EncryptionSettingsFlowCoordinatorParameters { let userSession: UserSessionProtocol let appSettings: AppSettings + let appHooks: AppHooks let userIndicatorController: UserIndicatorControllerProtocol let navigationStackCoordinator: NavigationStackCoordinator } @@ -25,6 +26,7 @@ struct EncryptionSettingsFlowCoordinatorParameters { class EncryptionSettingsFlowCoordinator: FlowCoordinatorProtocol { private let userSession: UserSessionProtocol private let appSettings: AppSettings + private let appHooks: AppHooks private let userIndicatorController: UserIndicatorControllerProtocol private let navigationStackCoordinator: NavigationStackCoordinator @@ -65,6 +67,7 @@ class EncryptionSettingsFlowCoordinator: FlowCoordinatorProtocol { init(parameters: EncryptionSettingsFlowCoordinatorParameters) { userSession = parameters.userSession appSettings = parameters.appSettings + appHooks = parameters.appHooks userIndicatorController = parameters.userIndicatorController navigationStackCoordinator = parameters.navigationStackCoordinator @@ -158,9 +161,11 @@ class EncryptionSettingsFlowCoordinator: FlowCoordinatorProtocol { private func presentRecoveryKeyScreen() { let sheetNavigationStackCoordinator = NavigationStackCoordinator() - let coordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: .init(secureBackupController: userSession.clientProxy.secureBackupController, - userIndicatorController: userIndicatorController, - isModallyPresented: true)) + let parameters = SecureBackupRecoveryKeyScreenCoordinatorParameters(secureBackupController: userSession.clientProxy.secureBackupController, + userIndicatorController: userIndicatorController, + isModallyPresented: true) + let coordinator = appHooks.recoveryKeyScreenHook.makeCoordinator(parameters: parameters, + homeserver: userSession.clientProxy.homeserver) coordinator.actions.sink { [weak self] action in guard let self else { return } diff --git a/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift index 0ffc288a8..787cd4a41 100644 --- a/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift @@ -302,7 +302,8 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol { userIndicatorController: userIndicatorController, isModallyPresented: false) - let coordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: parameters) + let coordinator = appHooks.recoveryKeyScreenHook.makeCoordinator(parameters: parameters, + homeserver: userSession.clientProxy.homeserver) coordinator.actions .sink { action in diff --git a/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift index 71b615ad7..f812f383c 100644 --- a/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift @@ -143,6 +143,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol { private func startEncryptionSettingsFlow(animated: Bool) { let coordinator = EncryptionSettingsFlowCoordinator(parameters: .init(userSession: flowParameters.userSession, appSettings: flowParameters.appSettings, + appHooks: flowParameters.appHooks, userIndicatorController: flowParameters.userIndicatorController, navigationStackCoordinator: navigationStackCoordinator)) coordinator.actionsPublisher.sink { [weak self] action in diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 53464f47b..b405816a0 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -10963,30 +10963,44 @@ class SecureBackupControllerMock: SecureBackupControllerProtocol, @unchecked Sen } //MARK: - generateRecoveryKey - private let generateRecoveryKeyCallsCountLock = NSLock() - private var generateRecoveryKeyUnderlyingCallsCount = 0 - var generateRecoveryKeyCallsCount: Int { - get { generateRecoveryKeyCallsCountLock.withLock { generateRecoveryKeyUnderlyingCallsCount } } - set { generateRecoveryKeyCallsCountLock.withLock { generateRecoveryKeyUnderlyingCallsCount = newValue } } + private let generateRecoveryKeyWithPassphraseCallsCountLock = NSLock() + private var generateRecoveryKeyWithPassphraseUnderlyingCallsCount = 0 + var generateRecoveryKeyWithPassphraseCallsCount: Int { + get { generateRecoveryKeyWithPassphraseCallsCountLock.withLock { generateRecoveryKeyWithPassphraseUnderlyingCallsCount } } + set { generateRecoveryKeyWithPassphraseCallsCountLock.withLock { generateRecoveryKeyWithPassphraseUnderlyingCallsCount = newValue } } } - var generateRecoveryKeyCalled: Bool { - return generateRecoveryKeyCallsCount > 0 + var generateRecoveryKeyWithPassphraseCalled: Bool { + return generateRecoveryKeyWithPassphraseCallsCount > 0 + } + private let generateRecoveryKeyWithPassphraseReceivedPassphraseLock = NSLock() + private var generateRecoveryKeyWithPassphraseUnderlyingReceivedPassphrase: String? + var generateRecoveryKeyWithPassphraseReceivedPassphrase: String? { + get { generateRecoveryKeyWithPassphraseReceivedPassphraseLock.withLock { generateRecoveryKeyWithPassphraseUnderlyingReceivedPassphrase } } + set { generateRecoveryKeyWithPassphraseReceivedPassphraseLock.withLock { generateRecoveryKeyWithPassphraseUnderlyingReceivedPassphrase = newValue } } + } + private let generateRecoveryKeyWithPassphraseReceivedInvocationsLock = NSLock() + private var generateRecoveryKeyWithPassphraseUnderlyingReceivedInvocations: [String?] = [] + var generateRecoveryKeyWithPassphraseReceivedInvocations: [String?] { + get { generateRecoveryKeyWithPassphraseReceivedInvocationsLock.withLock { generateRecoveryKeyWithPassphraseUnderlyingReceivedInvocations } } + set { generateRecoveryKeyWithPassphraseReceivedInvocationsLock.withLock { generateRecoveryKeyWithPassphraseUnderlyingReceivedInvocations = newValue } } } - private let generateRecoveryKeyReturnValueLock = NSLock() - private var generateRecoveryKeyUnderlyingReturnValue: Result! - var generateRecoveryKeyReturnValue: Result! { - get { generateRecoveryKeyReturnValueLock.withLock { generateRecoveryKeyUnderlyingReturnValue } } - set { generateRecoveryKeyReturnValueLock.withLock { generateRecoveryKeyUnderlyingReturnValue = newValue } } + private let generateRecoveryKeyWithPassphraseReturnValueLock = NSLock() + private var generateRecoveryKeyWithPassphraseUnderlyingReturnValue: Result! + var generateRecoveryKeyWithPassphraseReturnValue: Result! { + get { generateRecoveryKeyWithPassphraseReturnValueLock.withLock { generateRecoveryKeyWithPassphraseUnderlyingReturnValue } } + set { generateRecoveryKeyWithPassphraseReturnValueLock.withLock { generateRecoveryKeyWithPassphraseUnderlyingReturnValue = newValue } } } - var generateRecoveryKeyClosure: (() async -> Result)? + var generateRecoveryKeyWithPassphraseClosure: ((String?) async -> Result)? - func generateRecoveryKey() async -> Result { - generateRecoveryKeyCallsCountLock.withLock { generateRecoveryKeyUnderlyingCallsCount += 1 } - if let generateRecoveryKeyClosure = generateRecoveryKeyClosure { - return await generateRecoveryKeyClosure() + func generateRecoveryKey(withPassphrase passphrase: String?) async -> Result { + generateRecoveryKeyWithPassphraseCallsCountLock.withLock { generateRecoveryKeyWithPassphraseUnderlyingCallsCount += 1 } + generateRecoveryKeyWithPassphraseReceivedPassphrase = passphrase + generateRecoveryKeyWithPassphraseReceivedInvocationsLock.withLock { generateRecoveryKeyWithPassphraseUnderlyingReceivedInvocations.append(passphrase) } + if let generateRecoveryKeyWithPassphraseClosure = generateRecoveryKeyWithPassphraseClosure { + return await generateRecoveryKeyWithPassphraseClosure(passphrase) } else { - return generateRecoveryKeyReturnValue + return generateRecoveryKeyWithPassphraseReturnValue } } //MARK: - confirmRecoveryKey diff --git a/ElementX/Sources/Mocks/SecureBackupControllerMock.swift b/ElementX/Sources/Mocks/SecureBackupControllerMock.swift index 8e89cd067..f9c500fcc 100644 --- a/ElementX/Sources/Mocks/SecureBackupControllerMock.swift +++ b/ElementX/Sources/Mocks/SecureBackupControllerMock.swift @@ -36,7 +36,7 @@ extension SecureBackupControllerMock { return .success(()) } - generateRecoveryKeyClosure = { + generateRecoveryKeyWithPassphraseClosure = { _ in recoveryStateSubject.send(.enabled) return .success("a1B2 C3d4 E5F6 g7H8 i9J0 K1l2 M3n4 O5p6 Q7R8 s9T0 U1v2 W3X4") } diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenCoordinator.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenCoordinator.swift index 97f645ec6..d810ff3b0 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenCoordinator.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenCoordinator.swift @@ -19,7 +19,7 @@ enum SecureBackupRecoveryKeyScreenCoordinatorAction { case complete } -final class SecureBackupRecoveryKeyScreenCoordinator: CoordinatorProtocol { +final class SecureBackupRecoveryKeyScreenCoordinator: SecureBackupRecoveryKeyCoordinatorProtocol { private let parameters: SecureBackupRecoveryKeyScreenCoordinatorParameters private var viewModel: SecureBackupRecoveryKeyScreenViewModelProtocol diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift index 6d1b0d47e..eecd5f366 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift @@ -41,7 +41,7 @@ class SecureBackupRecoveryKeyScreenViewModel: SecureBackupRecoveryKeyScreenViewM state.isGeneratingKey = true Task { - switch await secureBackupController.generateRecoveryKey() { + switch await secureBackupController.generateRecoveryKey(withPassphrase: nil) { case .success(let key): state.recoveryKey = key case .failure(let error): @@ -101,7 +101,7 @@ class SecureBackupRecoveryKeyScreenViewModel: SecureBackupRecoveryKeyScreenViewM } } -extension SecureBackupRecoveryState { +private extension SecureBackupRecoveryState { var viewMode: SecureBackupRecoveryKeyScreenViewMode { switch self { case .disabled: diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift index d2f4c388d..77d7c529d 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift @@ -278,9 +278,9 @@ struct SecureBackupRecoveryKeyScreen_Previews: PreviewProvider, TestablePreview backupController.recoveryState = CurrentValueSubject(recoveryState).asCurrentValuePublisher() if let key { - backupController.generateRecoveryKeyReturnValue = .success(key) + backupController.generateRecoveryKeyWithPassphraseReturnValue = .success(key) } else { - backupController.generateRecoveryKeyClosure = { + backupController.generateRecoveryKeyWithPassphraseClosure = { _ in try? await Task.sleep(for: .seconds(1000)) return .success("youshouldntseeme") } diff --git a/ElementX/Sources/Services/SecureBackup/SecureBackupController.swift b/ElementX/Sources/Services/SecureBackup/SecureBackupController.swift index 0dcab45b2..16c79e9e4 100644 --- a/ElementX/Sources/Services/SecureBackup/SecureBackupController.swift +++ b/ElementX/Sources/Services/SecureBackup/SecureBackupController.swift @@ -110,19 +110,18 @@ class SecureBackupController: SecureBackupControllerProtocol { return .success(()) } - func generateRecoveryKey() async -> Result { + func generateRecoveryKey(withPassphrase passphrase: String?) async -> Result { do { - guard recoveryState.value == .disabled else { + if recoveryState.value == .disabled { + MXLog.info("Enabling recovery") + } else { MXLog.info("Resetting recovery key") - - let key = try await encryption.resetRecoveryKey() - return .success(key) } - MXLog.info("Enabling recovery") - var keyUploadErrored = false - let recoveryKey = try await encryption.enableRecovery(waitForBackupsToUpload: false, passphrase: nil, progressListener: SDKListener { [weak self] state in + // Note: `enableRecovery` also handles the reset case (equivalent to `resetRecoveryKey`). + // Despite the name, it is the correct call for both initial setup and key rotation. + let recoveryKey = try await encryption.enableRecovery(waitForBackupsToUpload: false, passphrase: passphrase, progressListener: SDKListener { [weak self] state in guard let self else { return } switch state { diff --git a/ElementX/Sources/Services/SecureBackup/SecureBackupControllerProtocol.swift b/ElementX/Sources/Services/SecureBackup/SecureBackupControllerProtocol.swift index 1e0aa725d..6143a85e8 100644 --- a/ElementX/Sources/Services/SecureBackup/SecureBackupControllerProtocol.swift +++ b/ElementX/Sources/Services/SecureBackup/SecureBackupControllerProtocol.swift @@ -55,7 +55,7 @@ protocol SecureBackupControllerProtocol { func enable() async -> Result func disable() async -> Result - func generateRecoveryKey() async -> Result + func generateRecoveryKey(withPassphrase passphrase: String?) async -> Result func confirmRecoveryKey(_ key: String) async -> Result func waitForKeyBackupUpload(uploadStateSubject: CurrentValueSubject) async -> Result diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index e2f9dc0d6..0f6b181be 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -748,6 +748,7 @@ class MockScreen: Identifiable { let coordinator = EncryptionSettingsFlowCoordinator(parameters: .init(userSession: userSession, appSettings: appSettings, + appHooks: AppHooks(), userIndicatorController: UserIndicatorControllerMock(), navigationStackCoordinator: navigationStackCoordinator)) retainedState.append(coordinator) diff --git a/Enterprise b/Enterprise index ee4aca0bd..eaa8ac510 160000 --- a/Enterprise +++ b/Enterprise @@ -1 +1 @@ -Subproject commit ee4aca0bd5ecb6e346b7f3c488cd47f5f880a37f +Subproject commit eaa8ac5101e4b1947fa9fd92766736e0ce903e2c