From 6190a03afcc70788213550160b12cb95d4c077ae Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:21:36 +0000 Subject: [PATCH] Directly show Recovery Key and Encryption Reset screens from the home screen banner. (#3482) --- .../UserSessionFlowCoordinator.swift | 73 ++++++++++++++++++- ...erSessionFlowCoordinatorStateMachine.swift | 30 +++++++- .../HomeScreen/HomeScreenCoordinator.swift | 9 ++- .../Screens/HomeScreen/HomeScreenModels.swift | 6 +- .../HomeScreen/HomeScreenViewModel.swift | 6 +- ...eScreenRecoveryKeyConfirmationBanner.swift | 11 ++- ...firmationBanner-iPad-en-GB.Out-of-sync.png | 4 +- ...irmationBanner-iPad-pseudo.Out-of-sync.png | 4 +- ...tionBanner-iPhone-16-en-GB.Out-of-sync.png | 4 +- ...ionBanner-iPhone-16-pseudo.Out-of-sync.png | 4 +- 10 files changed, 130 insertions(+), 21 deletions(-) diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index 8e2431e541..42b6f782f3 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -42,6 +42,9 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { // periphery:ignore - retaining purpose private var bugReportFlowCoordinator: BugReportFlowCoordinator? + // periphery:ignore - retaining purpose + private var encryptionResetFlowCoordinator: EncryptionResetFlowCoordinator? + // periphery:ignore - retaining purpose private var globalSearchScreenCoordinator: GlobalSearchScreenCoordinator? @@ -263,6 +266,16 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { case (.feedbackScreen, .dismissedFeedbackScreen, .roomList): break + case (.roomList, .showRecoveryKeyScreen, .recoveryKeyScreen): + presentRecoveryKeyScreen(animated: animated) + case (.recoveryKeyScreen, .dismissedRecoveryKeyScreen, .roomList): + break + + case (.roomList, .startEncryptionResetFlow, .encryptionResetFlow): + startEncryptionResetFlow(animated: animated) + case (.encryptionResetFlow, .finishedEncryptionResetFlow, .roomList): + break + case (.roomList, .showStartChatScreen, .startChatScreen): presentStartChat(animated: animated) case (.startChatScreen, .dismissedStartChatScreen, .roomList): @@ -453,8 +466,10 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { settingsFlowCoordinator.handleAppRoute(.settings, animated: true) case .presentFeedbackScreen: stateMachine.processEvent(.feedbackScreen) - case .presentSecureBackupSettings: - settingsFlowCoordinator.handleAppRoute(.chatBackupSettings, animated: true) + case .presentRecoveryKeyScreen: + stateMachine.processEvent(.showRecoveryKeyScreen) + case .presentEncryptionResetScreen: + stateMachine.processEvent(.startEncryptionResetFlow) case .presentStartChatScreen: stateMachine.processEvent(.showStartChatScreen) case .presentGlobalSearch: @@ -697,7 +712,59 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { navigationSplitCoordinator.setOverlayCoordinator(nil) } - // MARK: Secure backup confirmation + // MARK: Secure backup + + private func presentRecoveryKeyScreen(animated: Bool) { + let sheetNavigationStackCoordinator = NavigationStackCoordinator() + let parameters = SecureBackupRecoveryKeyScreenCoordinatorParameters(secureBackupController: userSession.clientProxy.secureBackupController, + userIndicatorController: ServiceLocator.shared.userIndicatorController, + isModallyPresented: true) + + let coordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: parameters) + coordinator.actions.sink { [weak self] action in + guard let self else { return } + switch action { + case .complete: + navigationSplitCoordinator.setSheetCoordinator(nil) + } + } + .store(in: &cancellables) + + sheetNavigationStackCoordinator.setRootCoordinator(coordinator) + + navigationSplitCoordinator.setSheetCoordinator(sheetNavigationStackCoordinator, animated: animated) { [weak self] in + self?.stateMachine.processEvent(.dismissedRecoveryKeyScreen) + } + } + + private func startEncryptionResetFlow(animated: Bool) { + let sheetNavigationStackCoordinator = NavigationStackCoordinator() + let parameters = EncryptionResetFlowCoordinatorParameters(userSession: userSession, + userIndicatorController: ServiceLocator.shared.userIndicatorController, + navigationStackCoordinator: sheetNavigationStackCoordinator, + windowManger: appMediator.windowManager) + + let coordinator = EncryptionResetFlowCoordinator(parameters: parameters) + coordinator.actionsPublisher.sink { [weak self] action in + guard let self else { return } + switch action { + case .resetComplete: + encryptionResetFlowCoordinator = nil + navigationSplitCoordinator.setSheetCoordinator(nil) + case .cancel: + encryptionResetFlowCoordinator = nil + navigationSplitCoordinator.setSheetCoordinator(nil) + } + } + .store(in: &cancellables) + + coordinator.start() + encryptionResetFlowCoordinator = coordinator + + navigationSplitCoordinator.setSheetCoordinator(sheetNavigationStackCoordinator, animated: animated) { [weak self] in + self?.stateMachine.processEvent(.finishedEncryptionResetFlow) + } + } private func presentSecureBackupLogoutConfirmationScreen() { let coordinator = SecureBackupLogoutConfirmationScreenCoordinator(parameters: .init(secureBackupController: userSession.clientProxy.secureBackupController, diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinatorStateMachine.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinatorStateMachine.swift index 96189d7b12..5fe2d18c6f 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinatorStateMachine.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinatorStateMachine.swift @@ -24,6 +24,12 @@ class UserSessionFlowCoordinatorStateMachine { /// Showing the settings screen case settingsScreen(selectedRoomID: String?) + /// Showing the recovery key screen. + case recoveryKeyScreen(selectedRoomID: String?) + + /// Showing the encryption reset flow. + case encryptionResetFlow(selectedRoomID: String?) + /// Showing the start chat screen case startChatScreen(selectedRoomID: String?) @@ -44,6 +50,8 @@ class UserSessionFlowCoordinatorStateMachine { case .roomList(let selectedRoomID), .feedbackScreen(let selectedRoomID), .settingsScreen(let selectedRoomID), + .recoveryKeyScreen(let selectedRoomID), + .encryptionResetFlow(let selectedRoomID), .startChatScreen(let selectedRoomID), .logoutConfirmationScreen(let selectedRoomID), .roomDirectorySearchScreen(let selectedRoomID): @@ -79,12 +87,22 @@ class UserSessionFlowCoordinatorStateMachine { /// The feedback screen has been dismissed case dismissedFeedbackScreen + /// Request presentation of the recovery key screen. + case showRecoveryKeyScreen + /// The recovery key screen has been dismissed. + case dismissedRecoveryKeyScreen + + /// Request presentation of the encryption reset flow. + case startEncryptionResetFlow + /// The encryption reset flow is complete and has been dismissed. + case finishedEncryptionResetFlow + /// Request the start of the start chat flow case showStartChatScreen /// Start chat has been dismissed case dismissedStartChatScreen - /// Logout has been requested and this is the last sesion + /// Logout has been requested and this is the last session case showLogoutConfirmationScreen /// Logout has been cancelled case dismissedLogoutConfirmationScreen @@ -136,6 +154,16 @@ class UserSessionFlowCoordinatorStateMachine { case (.feedbackScreen(let selectedRoomID), .dismissedFeedbackScreen): return .roomList(selectedRoomID: selectedRoomID) + case (.roomList(let selectedRoomID), .showRecoveryKeyScreen): + return .recoveryKeyScreen(selectedRoomID: selectedRoomID) + case (.recoveryKeyScreen(let selectedRoomID), .dismissedRecoveryKeyScreen): + return .roomList(selectedRoomID: selectedRoomID) + + case (.roomList(let selectedRoomID), .startEncryptionResetFlow): + return .encryptionResetFlow(selectedRoomID: selectedRoomID) + case (.encryptionResetFlow(let selectedRoomID), .finishedEncryptionResetFlow): + return .roomList(selectedRoomID: selectedRoomID) + case (.roomList(let selectedRoomID), .showStartChatScreen): return .startChatScreen(selectedRoomID: selectedRoomID) case (.startChatScreen(let selectedRoomID), .dismissedStartChatScreen): diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift index d9b7a2481f..22753bc35c 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift @@ -20,7 +20,8 @@ enum HomeScreenCoordinatorAction { case roomLeft(roomIdentifier: String) case presentSettingsScreen case presentFeedbackScreen - case presentSecureBackupSettings + case presentRecoveryKeyScreen + case presentEncryptionResetScreen case presentStartChatScreen case presentGlobalSearch case presentRoomDirectorySearch @@ -63,8 +64,10 @@ final class HomeScreenCoordinator: CoordinatorProtocol { actionsSubject.send(.presentFeedbackScreen) case .presentSettingsScreen: actionsSubject.send(.presentSettingsScreen) - case .presentSecureBackupSettings: - actionsSubject.send(.presentSecureBackupSettings) + case .presentRecoveryKeyScreen: + actionsSubject.send(.presentRecoveryKeyScreen) + case .presentEncryptionResetScreen: + actionsSubject.send(.presentEncryptionResetScreen) case .presentStartChatScreen: actionsSubject.send(.presentStartChatScreen) case .presentGlobalSearch: diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift index d253a14be5..f0860160cc 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift @@ -13,7 +13,8 @@ enum HomeScreenViewModelAction { case presentRoom(roomIdentifier: String) case presentRoomDetails(roomIdentifier: String) case roomLeft(roomIdentifier: String) - case presentSecureBackupSettings + case presentRecoveryKeyScreen + case presentEncryptionResetScreen case presentSettingsScreen case presentFeedbackScreen case presentStartChatScreen @@ -30,7 +31,8 @@ enum HomeScreenViewAction { case confirmLeaveRoom(roomIdentifier: String) case showSettings case startChat - case confirmRecoveryKey + case manageRecoveryKey + case resetEncryption case skipRecoveryKeyConfirmation case confirmSlidingSyncUpgrade case skipSlidingSyncUpgrade diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index 674559ef80..3c8a241a11 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -138,8 +138,10 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol Task { await leaveRoom(roomID: roomIdentifier) } case .showSettings: actionsSubject.send(.presentSettingsScreen) - case .confirmRecoveryKey: - actionsSubject.send(.presentSecureBackupSettings) + case .manageRecoveryKey: + actionsSubject.send(.presentRecoveryKeyScreen) + case .resetEncryption: + actionsSubject.send(.presentEncryptionResetScreen) case .skipRecoveryKeyConfirmation: state.securityBannerMode = .dismissed case .confirmSlidingSyncUpgrade: diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift index 3215723301..3f4919eee9 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift @@ -56,14 +56,21 @@ struct HomeScreenRecoveryKeyConfirmationBanner: View { var buttons: some View { VStack(spacing: 16) { Button(actionTitle) { - context.send(viewAction: .confirmRecoveryKey) + context.send(viewAction: .manageRecoveryKey) } .frame(maxWidth: .infinity) .buttonStyle(.compound(.primary, size: .medium)) .accessibilityIdentifier(A11yIdentifiers.homeScreen.recoveryKeyConfirmationBannerContinue) if !requiresExtraAccountSetup { - // Missing encryption reset button to goes here once the flow exists. + Button { + context.send(viewAction: .resetEncryption) + } label: { + Text(L10n.confirmRecoveryKeyBannerSecondaryButtonTitle) + .padding(.vertical, 7) + .frame(maxWidth: .infinity) + } + .buttonStyle(.compound(.plain, size: .medium)) } } } diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png index d9e29da1ad..02bd87401a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d425040e4de7d03d440c14ad4c1255e9ecd0950e913e340e6828780b008dddbb -size 98068 +oid sha256:1b6c3bbad4b922a61086bca2821a6b2740999bf26df1fd22c979f6e25bd83f49 +size 105130 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png index 6ae3571a04..d1e6704b48 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:946bbda26c484f64738933c2c4fd3f4b8d095079d38947ef55c17deb4660b1a6 -size 109879 +oid sha256:1e47702cf4ad8b67fe144999a1c8102aed2f3d86bc59588904c7fc11fab09254 +size 118075 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Out-of-sync.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Out-of-sync.png index a0b3aa0e0b..09b95afc40 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Out-of-sync.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Out-of-sync.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:32b70c1911a7a413e0d6ae1748e3ea05929e951503629ead0b2fe3773f0bdd9d -size 56470 +oid sha256:2f3df65735d7447af2345df335ab5350a20fab9e978b6914621f28bbf88104d3 +size 62952 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Out-of-sync.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Out-of-sync.png index 4d2e4c92c1..9dc44dbd3e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Out-of-sync.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Out-of-sync.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9e6a4ef76a75e88c0a87e177fd11ec6d1bb9b1b6f843692d002f93b4fa75690 -size 79643 +oid sha256:c16d88503434c47f0d11664dcb9a172339e1638198fbbdb9ae607a91803789ff +size 90729