mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 13:37:11 +00:00
Encryption Flow Coordinators. (#3471)
* Manage the secure backup screens with flow coordinators.
* Add UI tests for the EncryptionSettingsFlowCoordinator.
* Realise that the settings flow can't reset anymore and remove the sub-flow 🤦♂️
* Add UI tests for the EncryptionResetFlowCoordinator.
This commit is contained in:
parent
85e1de2c1c
commit
8e26718d0b
@ -158,6 +158,7 @@
|
||||
21F29351EDD7B2A5534EE862 /* SecureBackupKeyBackupScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD558A898847C179E4B7A237 /* SecureBackupKeyBackupScreen.swift */; };
|
||||
22882C710BC99EC34A5024A0 /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; };
|
||||
22C5483D01EEB290B8339817 /* HomeScreenInviteCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FC33C3F6BF597E095CE9FA /* HomeScreenInviteCell.swift */; };
|
||||
230981086F0199F913434D6B /* EncryptionSettingsUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BF8E5D4C95974B96A18C80E /* EncryptionSettingsUITests.swift */; };
|
||||
2335D1AB954C151FD8779F45 /* RoomPermissionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0096BC5DA86AF6B6E5742AC /* RoomPermissionsTests.swift */; };
|
||||
23701DE32ACD6FD40AA992C3 /* MediaUploadingPreprocessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE203026B9AD3DB412439866 /* MediaUploadingPreprocessorTests.swift */; };
|
||||
237FC70AA257B935F53316BA /* SessionVerificationControllerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C55D7E514F9DE4E3D72FDCAD /* SessionVerificationControllerProxy.swift */; };
|
||||
@ -205,6 +206,7 @@
|
||||
2C5E832434EE94E21AB3B238 /* EmojiPickerScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EAE3E9D5EF4A6D5D9C6CFD /* EmojiPickerScreenViewModel.swift */; };
|
||||
2CA61BB208CD82EBDB58CD13 /* VideoRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0CBEAB5F796BEFBAF7BB6A /* VideoRoomTimelineView.swift */; };
|
||||
2CA6ABBC9A88EB89EA52FCCB /* ConfettiScene.scn in Resources */ = {isa = PBXBuildFile; fileRef = B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */; };
|
||||
2D0E3983288E2D35613AD681 /* SecureBackupControllerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AB29A2D95D3469B5F016655 /* SecureBackupControllerMock.swift */; };
|
||||
2D2D8A53B35BE8D8A01449C6 /* PinnedEventsBannerStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FA38E813BE14149F173F461 /* PinnedEventsBannerStateTests.swift */; };
|
||||
2DA27D78560D5F79B917E163 /* AudioConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E44E35AA87F49503E7B3BF6E /* AudioConverter.swift */; };
|
||||
2DD9D0FE7CB5CFC80D071451 /* AppLockScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3E67E09FE5A35D73818C39 /* AppLockScreenModels.swift */; };
|
||||
@ -353,6 +355,7 @@
|
||||
4CE638FD837ED72CD98AD9A9 /* AppHooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E4AB573FAEBB7B853DD04C /* AppHooks.swift */; };
|
||||
4D0F4385B7DDB68C66C78857 /* FormattedBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = C258C9C815272911A5B132C3 /* FormattedBodyText.swift */; };
|
||||
4D23D41B8109E010304050F8 /* QRCodeLoginScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA551A98778CEE7366838CE2 /* QRCodeLoginScreenCoordinator.swift */; };
|
||||
4D2B54233C7B2C04B4ABE55A /* EncryptionSettingsFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECB836DD8BE31931F51B8AC9 /* EncryptionSettingsFlowCoordinator.swift */; };
|
||||
4D4D236F0BBCDC4D2CBCCBB5 /* RoomChangePermissionsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = C729D95CB4588D4D9AAC3DFA /* RoomChangePermissionsScreenModels.swift */; };
|
||||
4DAEE2468669848B6C9F55B4 /* TimelineReadReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33035418BB35754232985871 /* TimelineReadReceiptsView.swift */; };
|
||||
4DEEFB73181C3B023DB42686 /* NetworkMonitorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1575947B7A6FE08C57FE5EE4 /* NetworkMonitorProtocol.swift */; };
|
||||
@ -448,6 +451,7 @@
|
||||
64D05250CEDE8B604119F6E6 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 981663D961C94270FA035FD0 /* Alert.swift */; };
|
||||
64E541F88F35BD126C4AFCA1 /* AppLockScreenPINKeypad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38345442415E07A931197C55 /* AppLockScreenPINKeypad.swift */; };
|
||||
64EE9D2CF7AD02EE53983CE1 /* FileRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F75EF13F49DD2204E760910 /* FileRoomTimelineView.swift */; };
|
||||
64F8590F4BEE4DA231F97D83 /* EncryptionResetFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A07B011547B201A836C03052 /* EncryptionResetFlowCoordinator.swift */; };
|
||||
651341E67C3514F9811A1EC1 /* LoginScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05F598B1B346DAF223651C91 /* LoginScreenCoordinator.swift */; };
|
||||
652ACCF104A8CEF30788963C /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1423AB065857FA546444DB15 /* NotificationManager.swift */; };
|
||||
6530865EB9A8C0F0AF0216DA /* ServerSelectionScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9501D11B4258DFA33BA3B40F /* ServerSelectionScreenModels.swift */; };
|
||||
@ -917,6 +921,7 @@
|
||||
C9BE065FA7D4E77E4C61CB69 /* MapLibreModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B81B6170DB690013CEB646F4 /* MapLibreModels.swift */; };
|
||||
C9F5B48D15B9BCAE1F8D564E /* RoomNotificationModeProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1511766C534367700C8DD75 /* RoomNotificationModeProxy.swift */; };
|
||||
CA12AE0DCD57D49CD96C699A /* WaveformCursorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB9EABCA9348DFA27439A809 /* WaveformCursorView.swift */; };
|
||||
CACD1352927336F01FC76612 /* EncryptionResetUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE4C76F31A382B8E4DD07583 /* EncryptionResetUITests.swift */; };
|
||||
CB137BFB3E083C33E398A6CB /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 020597E28A4BC8E1BE8EDF6E /* KeychainAccess */; };
|
||||
CB498F4E27AA0545DCEF0F6F /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 36B7FC232711031AA2B0D188 /* DTCoreText */; };
|
||||
CB6956565D858C523E3E3B16 /* ComposerDraftServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7E37072597F67C4DD8CC2DB /* ComposerDraftServiceProtocol.swift */; };
|
||||
@ -1149,6 +1154,7 @@
|
||||
FF34BF2AF731340AF9414A18 /* SwipeRightAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4552D3466B1453F287223ADA /* SwipeRightAction.swift */; };
|
||||
FF7E8ECC8E7E1D1851517536 /* PollFormScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347D708104CCEF771428C9A3 /* PollFormScreenViewModelTests.swift */; };
|
||||
FF9C06BBF6AC6F1CFFBEBFFC /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 90791B9C739C716A40E1B230 /* target.yml */; };
|
||||
FFD52DCDA6962055A363CC8F /* IdentityResetHandleSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EFB1D29B0870AFB6A56E9B8 /* IdentityResetHandleSDKMock.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -1475,6 +1481,7 @@
|
||||
3BAC027034248429A438886B /* AppMediatorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediatorMock.swift; sourceTree = "<group>"; };
|
||||
3BC1B7CB061C9865B2B91B56 /* QRCodeLoginScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeLoginScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
3BDCCD2F6B405C14B9BCE94E /* JoinRoomScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
3BF8E5D4C95974B96A18C80E /* EncryptionSettingsUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionSettingsUITests.swift; sourceTree = "<group>"; };
|
||||
3C368CAB3063EF275357ECD4 /* LoginScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
3C3E67E09FE5A35D73818C39 /* AppLockScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenModels.swift; sourceTree = "<group>"; };
|
||||
3CCD41CD67DB5DA0D436BFE9 /* VoiceMessageRoomPlaybackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomPlaybackView.swift; sourceTree = "<group>"; };
|
||||
@ -1553,6 +1560,7 @@
|
||||
49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UNNotificationRequest.swift; sourceTree = "<group>"; };
|
||||
4A2B5274C1D3D2999D643786 /* EncryptionResetPasswordScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetPasswordScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
4A5B4CD611DE7E94F5BA87B2 /* AppLockTimerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockTimerTests.swift; sourceTree = "<group>"; };
|
||||
4AB29A2D95D3469B5F016655 /* SecureBackupControllerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupControllerMock.swift; sourceTree = "<group>"; };
|
||||
4B2D4EEBE8C098BBADD10939 /* SecureBackupKeyBackupScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupKeyBackupScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
4B41FABA2B0AEF4389986495 /* LoginMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginMode.swift; sourceTree = "<group>"; };
|
||||
4BD371B60E07A5324B9507EF /* AnalyticsSettingsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
@ -1632,6 +1640,7 @@
|
||||
5E75948AA1FE1D1A7809931F /* AuthenticationServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServiceProtocol.swift; sourceTree = "<group>"; };
|
||||
5E9CBF577B9711CFBB4FA40D /* VoiceMessageRecordingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRecordingView.swift; sourceTree = "<group>"; };
|
||||
5EB2CAA266B921D128C35710 /* LegalInformationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
5EFB1D29B0870AFB6A56E9B8 /* IdentityResetHandleSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityResetHandleSDKMock.swift; sourceTree = "<group>"; };
|
||||
5F12E996BFBEB43815189ABF /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
5F4134FEFE4EB55759017408 /* UserSessionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionProtocol.swift; sourceTree = "<group>"; };
|
||||
5FACD034DB52525A3CEF2BDF /* SessionVerificationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreen.swift; sourceTree = "<group>"; };
|
||||
@ -1906,6 +1915,7 @@
|
||||
A019A12C866D64CF072024B9 /* AppLockSetupPINScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
A02D1A490944BF01A37586E1 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||
A057F2FDC14866C3026A89A4 /* NotificationManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManagerProtocol.swift; sourceTree = "<group>"; };
|
||||
A07B011547B201A836C03052 /* EncryptionResetFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetFlowCoordinator.swift; sourceTree = "<group>"; };
|
||||
A103580EBA06155B1343EF16 /* HomeScreenKnockedCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenKnockedCell.swift; sourceTree = "<group>"; };
|
||||
A12D3B1BCF920880CA8BBB6B /* UserIndicatorControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorControllerProtocol.swift; sourceTree = "<group>"; };
|
||||
A130A2251A15A7AACC84FD37 /* RoomPollsHistoryScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
@ -2035,6 +2045,7 @@
|
||||
BC8AA23D4F37CC26564F63C5 /* LayoutMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutMocks.swift; sourceTree = "<group>"; };
|
||||
BCDA016D05107DED3B9495CB /* TimelineItemDebugView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemDebugView.swift; sourceTree = "<group>"; };
|
||||
BE148A4FFEE853C5A281500C /* UNNotificationContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UNNotificationContent.swift; sourceTree = "<group>"; };
|
||||
BE4C76F31A382B8E4DD07583 /* EncryptionResetUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetUITests.swift; sourceTree = "<group>"; };
|
||||
BE78CAD0B964C66FD06EF83E /* DeactivateAccountScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeactivateAccountScreenModels.swift; sourceTree = "<group>"; };
|
||||
BE89A8BD65CCE3FCC925CA14 /* TimelineItemReplyDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemReplyDetails.swift; sourceTree = "<group>"; };
|
||||
BE9BBB18FB27F09032AD8769 /* NotificationPermissionsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissionsScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
@ -2244,6 +2255,7 @@
|
||||
EBEB8D9F4940E161B18FE4BC /* UITestsNotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestsNotificationCenter.swift; sourceTree = "<group>"; };
|
||||
EC589E641AE46EFB2962534D /* RoomMemberDetailsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsViewModelTests.swift; sourceTree = "<group>"; };
|
||||
EC5D7DA665E1F5F509C994C7 /* ScaledOffsetModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaledOffsetModifier.swift; sourceTree = "<group>"; };
|
||||
ECB836DD8BE31931F51B8AC9 /* EncryptionSettingsFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionSettingsFlowCoordinator.swift; sourceTree = "<group>"; };
|
||||
ECD5FCBA169B6A82F501CA1B /* AnalyticsSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
ECF79FB25E2D4BD6F50CE7C9 /* RoomMembersListScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
ED003DF1B7CF40E7073A2280 /* TracingConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracingConfiguration.swift; sourceTree = "<group>"; };
|
||||
@ -2935,6 +2947,7 @@
|
||||
FC83F47D2173B7538AA72E0E /* RoomSummaryProviderMock.swift */,
|
||||
D479DF730528153665E5782E /* RoomTimelineControllerFactoryMock.swift */,
|
||||
F74532E01B317C56C1BE8FA8 /* RoomTimelineProviderMock.swift */,
|
||||
4AB29A2D95D3469B5F016655 /* SecureBackupControllerMock.swift */,
|
||||
248649EBA5BC33DB93698734 /* SessionVerificationControllerProxyMock.swift */,
|
||||
7893780A1FD6E3F38B3E9049 /* UserIndicatorControllerMock.swift */,
|
||||
AAD01F7FC2BBAC7351948595 /* UserProfile+Mock.swift */,
|
||||
@ -3531,6 +3544,8 @@
|
||||
0DBB08A95EFA668F2CF27211 /* AppLockSetupFlowCoordinator.swift */,
|
||||
A9B069D7772DDF6513E0F1B8 /* AuthenticationFlowCoordinator.swift */,
|
||||
7367B3B9A8CAF902220F31D1 /* BugReportFlowCoordinator.swift */,
|
||||
A07B011547B201A836C03052 /* EncryptionResetFlowCoordinator.swift */,
|
||||
ECB836DD8BE31931F51B8AC9 /* EncryptionSettingsFlowCoordinator.swift */,
|
||||
C3285BD95B564CA2A948E511 /* OnboardingFlowCoordinator.swift */,
|
||||
A54AAF72E821B4084B7E4298 /* PinnedEventsTimelineFlowCoordinator.swift */,
|
||||
9A008E57D52B07B78DFAD1BB /* RoomFlowCoordinator.swift */,
|
||||
@ -4444,6 +4459,8 @@
|
||||
295E28C3B9EAADF519BF2F44 /* AuthenticationFlowCoordinatorUITests.swift */,
|
||||
C6FEA87EA3752203065ECE27 /* BugReportUITests.swift */,
|
||||
F8CEB4634C0DD7779C4AB504 /* CreateRoomScreenUITests.swift */,
|
||||
BE4C76F31A382B8E4DD07583 /* EncryptionResetUITests.swift */,
|
||||
3BF8E5D4C95974B96A18C80E /* EncryptionSettingsUITests.swift */,
|
||||
3368395F06AA180138E185B6 /* PollFormScreenUITests.swift */,
|
||||
C5B7A755E985FA14469E86B2 /* RoomMembersListScreenUITests.swift */,
|
||||
45571C2EBD98ED7E0CEA7AF7 /* RoomRolesAndPermissionsUITests.swift */,
|
||||
@ -5287,6 +5304,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8EAF4A49F3ACD8BB8B0D2371 /* ClientSDKMock.swift */,
|
||||
5EFB1D29B0870AFB6A56E9B8 /* IdentityResetHandleSDKMock.swift */,
|
||||
E8DE9D0D480D087D0F676B52 /* UserIdentitySDKMock.swift */,
|
||||
);
|
||||
path = SDK;
|
||||
@ -6470,6 +6488,7 @@
|
||||
03CDCA6243F89B194E3FAD17 /* EncryptionAuthenticity.swift in Sources */,
|
||||
FBD402E3170EB1ED0D1AA672 /* EncryptionKeyProvider.swift in Sources */,
|
||||
46A6DB0F78FB399BD59E2D41 /* EncryptionKeyProviderProtocol.swift in Sources */,
|
||||
64F8590F4BEE4DA231F97D83 /* EncryptionResetFlowCoordinator.swift in Sources */,
|
||||
0C6DF318E9C8F6461E6ABDE7 /* EncryptionResetPasswordScreen.swift in Sources */,
|
||||
36926D795D6D19177C7812F8 /* EncryptionResetPasswordScreenCoordinator.swift in Sources */,
|
||||
B1B255CE0E4306DD6E09D936 /* EncryptionResetPasswordScreenModels.swift in Sources */,
|
||||
@ -6480,6 +6499,7 @@
|
||||
97BAEDD9054FB5F233EE928B /* EncryptionResetScreenModels.swift in Sources */,
|
||||
EC3320639828BED8B3E5F2C6 /* EncryptionResetScreenViewModel.swift in Sources */,
|
||||
A0868BDE84D2140A885BE3C9 /* EncryptionResetScreenViewModelProtocol.swift in Sources */,
|
||||
4D2B54233C7B2C04B4ABE55A /* EncryptionSettingsFlowCoordinator.swift in Sources */,
|
||||
50539366B408780B232C1910 /* EstimatedWaveformView.swift in Sources */,
|
||||
F78BAD28482A467287A9A5A3 /* EventBasedMessageTimelineItemProtocol.swift in Sources */,
|
||||
02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */,
|
||||
@ -6527,6 +6547,7 @@
|
||||
D22345698F6548C1EE960940 /* IdentityConfirmedScreenModels.swift in Sources */,
|
||||
01681E8B20AD6F0D237F2DC1 /* IdentityConfirmedScreenViewModel.swift in Sources */,
|
||||
AADE7C2497A7B55D8BED7BD6 /* IdentityConfirmedScreenViewModelProtocol.swift in Sources */,
|
||||
FFD52DCDA6962055A363CC8F /* IdentityResetHandleSDKMock.swift in Sources */,
|
||||
BA31448FBD9697F8CB9A83CD /* ImageCache.swift in Sources */,
|
||||
7CD16990BA843BE9ED639129 /* ImageRoomTimelineItem.swift in Sources */,
|
||||
B796A25F282C0A340D1B9C12 /* ImageRoomTimelineItemContent.swift in Sources */,
|
||||
@ -6868,6 +6889,7 @@
|
||||
67160204A8D362BB7D4AD259 /* Search.swift in Sources */,
|
||||
339BC18777912E1989F2F17D /* Section.swift in Sources */,
|
||||
F833D5B5BE6707F961FA88DB /* SecureBackupController.swift in Sources */,
|
||||
2D0E3983288E2D35613AD681 /* SecureBackupControllerMock.swift in Sources */,
|
||||
0C88044649BAEE6C49BFC43A /* SecureBackupControllerProtocol.swift in Sources */,
|
||||
21F29351EDD7B2A5534EE862 /* SecureBackupKeyBackupScreen.swift in Sources */,
|
||||
5BC6C4ADFE7F2A795ECDE130 /* SecureBackupKeyBackupScreenCoordinator.swift in Sources */,
|
||||
@ -7092,6 +7114,8 @@
|
||||
7756C4E90CABE6F14F7920A0 /* BugReportUITests.swift in Sources */,
|
||||
94D0F36A87E596A93C0C178A /* Bundle.swift in Sources */,
|
||||
9F19096BFA629C0AC282B1E4 /* CreateRoomScreenUITests.swift in Sources */,
|
||||
CACD1352927336F01FC76612 /* EncryptionResetUITests.swift in Sources */,
|
||||
230981086F0199F913434D6B /* EncryptionSettingsUITests.swift in Sources */,
|
||||
0CF81807BE5FBFC9E2BBCECF /* PollFormScreenUITests.swift in Sources */,
|
||||
44121202B4A260C98BF615A7 /* RoomMembersListScreenUITests.swift in Sources */,
|
||||
D29E046C1E3045E0346C479D /* RoomRolesAndPermissionsUITests.swift in Sources */,
|
||||
|
@ -0,0 +1,158 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import SwiftState
|
||||
|
||||
enum EncryptionResetFlowCoordinatorAction: Equatable {
|
||||
/// The flow is complete.
|
||||
case resetComplete
|
||||
/// The flow was cancelled.
|
||||
case cancel
|
||||
}
|
||||
|
||||
struct EncryptionResetFlowCoordinatorParameters {
|
||||
let userSession: UserSessionProtocol
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
let navigationStackCoordinator: NavigationStackCoordinator
|
||||
let windowManger: WindowManagerProtocol
|
||||
}
|
||||
|
||||
class EncryptionResetFlowCoordinator: FlowCoordinatorProtocol {
|
||||
private let userSession: UserSessionProtocol
|
||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||
|
||||
private let navigationStackCoordinator: NavigationStackCoordinator
|
||||
private let windowManager: WindowManagerProtocol
|
||||
|
||||
enum State: StateType {
|
||||
/// The state machine hasn't started.
|
||||
case initial
|
||||
/// The root screen for this flow.
|
||||
case encryptionResetScreen
|
||||
/// Confirming the user's password to continue.
|
||||
case confirmingPassword
|
||||
}
|
||||
|
||||
enum Event: EventType {
|
||||
/// The flow is being started.
|
||||
case start
|
||||
|
||||
/// The user needs to confirm their password to reset.
|
||||
case confirmPassword
|
||||
/// The user confirmed their password.
|
||||
case finishedConfirmingPassword
|
||||
}
|
||||
|
||||
private let stateMachine: StateMachine<State, Event>
|
||||
private var cancellables: Set<AnyCancellable> = []
|
||||
|
||||
private let actionsSubject: PassthroughSubject<EncryptionResetFlowCoordinatorAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<EncryptionResetFlowCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: EncryptionResetFlowCoordinatorParameters) {
|
||||
userSession = parameters.userSession
|
||||
userIndicatorController = parameters.userIndicatorController
|
||||
navigationStackCoordinator = parameters.navigationStackCoordinator
|
||||
windowManager = parameters.windowManger
|
||||
|
||||
stateMachine = .init(state: .initial)
|
||||
configureStateMachine()
|
||||
}
|
||||
|
||||
func start() {
|
||||
stateMachine.tryEvent(.start)
|
||||
}
|
||||
|
||||
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
||||
// There aren't any routes to this screen, so always clear the stack.
|
||||
clearRoute(animated: animated)
|
||||
}
|
||||
|
||||
func clearRoute(animated: Bool) {
|
||||
// As we push screens on top of an existing stack, popping to root wouldn't be safe.
|
||||
switch stateMachine.state {
|
||||
case .initial:
|
||||
break
|
||||
case .encryptionResetScreen:
|
||||
navigationStackCoordinator.pop(animated: animated)
|
||||
case .confirmingPassword:
|
||||
navigationStackCoordinator.pop(animated: animated) // Password screen.
|
||||
navigationStackCoordinator.pop(animated: animated) // EncryptionReset screen.
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func configureStateMachine() {
|
||||
stateMachine.addRoutes(event: .start, transitions: [.initial => .encryptionResetScreen]) { [weak self] _ in
|
||||
self?.presentEncryptionResetScreen()
|
||||
}
|
||||
|
||||
stateMachine.addRoutes(event: .confirmPassword, transitions: [.encryptionResetScreen => .confirmingPassword]) { [weak self] context in
|
||||
guard let passwordPublisher = context.userInfo as? PassthroughSubject<String, Never> else { fatalError("Expected a publisher in the userInfo.") }
|
||||
self?.presentPasswordScreen(passwordPublisher: passwordPublisher)
|
||||
}
|
||||
stateMachine.addRoutes(event: .finishedConfirmingPassword, transitions: [.confirmingPassword => .encryptionResetScreen])
|
||||
|
||||
stateMachine.addErrorHandler { context in
|
||||
fatalError("Unexpected transition: \(context)")
|
||||
}
|
||||
}
|
||||
|
||||
private func presentEncryptionResetScreen() {
|
||||
let coordinator = EncryptionResetScreenCoordinator(parameters: .init(clientProxy: userSession.clientProxy,
|
||||
userIndicatorController: userIndicatorController))
|
||||
|
||||
coordinator.actionsPublisher.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .requestOIDCAuthorisation(let url):
|
||||
presentOIDCAuthorization(for: url)
|
||||
case .requestPassword(let passwordPublisher):
|
||||
stateMachine.tryEvent(.confirmPassword, userInfo: passwordPublisher)
|
||||
case .cancel:
|
||||
actionsSubject.send(.cancel)
|
||||
case .resetFinished:
|
||||
actionsSubject.send(.resetComplete)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
}
|
||||
|
||||
private func presentPasswordScreen(passwordPublisher: PassthroughSubject<String, Never>) {
|
||||
let coordinator = EncryptionResetPasswordScreenCoordinator(parameters: .init(passwordPublisher: passwordPublisher))
|
||||
|
||||
coordinator.actionsPublisher.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .passwordEntered:
|
||||
navigationStackCoordinator.pop()
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationStackCoordinator.push(coordinator) { [stateMachine] in
|
||||
stateMachine.tryEvent(.finishedConfirmingPassword)
|
||||
}
|
||||
}
|
||||
|
||||
private var accountSettingsPresenter: OIDCAccountSettingsPresenter?
|
||||
private func presentOIDCAuthorization(for url: URL) {
|
||||
// Note to anyone in the future if you come back here to make this open in Safari instead of a WAS.
|
||||
// As of iOS 16, there is an issue on the simulator with accessing the cookie but it works on a device. 🤷♂️
|
||||
accountSettingsPresenter = OIDCAccountSettingsPresenter(accountURL: url, presentationAnchor: windowManager.mainWindow)
|
||||
accountSettingsPresenter?.start()
|
||||
}
|
||||
}
|
@ -0,0 +1,198 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import SwiftState
|
||||
|
||||
enum EncryptionSettingsFlowCoordinatorAction: Equatable {
|
||||
/// The flow is complete.
|
||||
case complete
|
||||
}
|
||||
|
||||
struct EncryptionSettingsFlowCoordinatorParameters {
|
||||
let userSession: UserSessionProtocol
|
||||
let appSettings: AppSettings
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
let navigationStackCoordinator: NavigationStackCoordinator
|
||||
}
|
||||
|
||||
class EncryptionSettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
private let userSession: UserSessionProtocol
|
||||
private let appSettings: AppSettings
|
||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||
private let navigationStackCoordinator: NavigationStackCoordinator
|
||||
|
||||
// periphery:ignore - retaining purpose
|
||||
private var encryptionResetFlowCoordinator: EncryptionResetFlowCoordinator?
|
||||
|
||||
enum State: StateType {
|
||||
/// The state machine hasn't started.
|
||||
case initial
|
||||
/// The root screen for this flow.
|
||||
case secureBackupScreen
|
||||
/// The user is managing their recovery key.
|
||||
case recoveryKeyScreen
|
||||
/// The user is disabling key backups.
|
||||
case keyBackupScreen
|
||||
}
|
||||
|
||||
enum Event: EventType {
|
||||
/// The flow is being started.
|
||||
case start
|
||||
|
||||
/// The user would like to manage their recovery key.
|
||||
case manageRecoveryKey
|
||||
/// The user finished managing their recovery key.
|
||||
case finishedManagingRecoveryKey
|
||||
|
||||
/// The user doesn't want to use key backup any more.
|
||||
case disableKeyBackup
|
||||
/// The key backup screen was dismissed.
|
||||
case finishedDisablingKeyBackup
|
||||
}
|
||||
|
||||
private let stateMachine: StateMachine<State, Event>
|
||||
private var cancellables: Set<AnyCancellable> = []
|
||||
|
||||
private let actionsSubject: PassthroughSubject<EncryptionSettingsFlowCoordinatorAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<EncryptionSettingsFlowCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: EncryptionSettingsFlowCoordinatorParameters) {
|
||||
userSession = parameters.userSession
|
||||
appSettings = parameters.appSettings
|
||||
userIndicatorController = parameters.userIndicatorController
|
||||
navigationStackCoordinator = parameters.navigationStackCoordinator
|
||||
|
||||
stateMachine = .init(state: .initial)
|
||||
configureStateMachine()
|
||||
}
|
||||
|
||||
func start() {
|
||||
stateMachine.tryEvent(.start)
|
||||
}
|
||||
|
||||
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
||||
switch appRoute {
|
||||
case .roomList, .room, .roomAlias, .childRoom, .childRoomAlias,
|
||||
.roomDetails, .roomMemberDetails, .userProfile,
|
||||
.event, .eventOnRoomAlias, .childEvent, .childEventOnRoomAlias,
|
||||
.call, .genericCallLink, .settings:
|
||||
// These routes aren't in this flow so clear the entire stack.
|
||||
clearRoute(animated: animated)
|
||||
case .chatBackupSettings:
|
||||
popToRootScreen(animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
func clearRoute(animated: Bool) {
|
||||
let fromState = stateMachine.state
|
||||
popToRootScreen(animated: animated)
|
||||
guard fromState != .initial else { return }
|
||||
navigationStackCoordinator.pop(animated: animated) // SecureBackup screen.
|
||||
}
|
||||
|
||||
func popToRootScreen(animated: Bool) {
|
||||
// As we push screens on top of an existing stack, a literal pop to root wouldn't be safe.
|
||||
switch stateMachine.state {
|
||||
case .initial, .secureBackupScreen:
|
||||
break
|
||||
case .recoveryKeyScreen:
|
||||
navigationStackCoordinator.setSheetCoordinator(nil, animated: animated) // RecoveryKey screen.
|
||||
case .keyBackupScreen:
|
||||
navigationStackCoordinator.setSheetCoordinator(nil, animated: animated) // KeyBackup screen.
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func configureStateMachine() {
|
||||
stateMachine.addRoutes(event: .start, transitions: [.initial => .secureBackupScreen]) { [weak self] _ in
|
||||
self?.presentSecureBackupScreen()
|
||||
}
|
||||
|
||||
stateMachine.addRoutes(event: .manageRecoveryKey, transitions: [.secureBackupScreen => .recoveryKeyScreen]) { [weak self] _ in
|
||||
self?.presentRecoveryKeyScreen()
|
||||
}
|
||||
stateMachine.addRoutes(event: .finishedManagingRecoveryKey, transitions: [.recoveryKeyScreen => .secureBackupScreen])
|
||||
|
||||
stateMachine.addRoutes(event: .disableKeyBackup, transitions: [.secureBackupScreen => .keyBackupScreen]) { [weak self] _ in
|
||||
self?.presentKeyBackupScreen()
|
||||
}
|
||||
stateMachine.addRoutes(event: .finishedDisablingKeyBackup, transitions: [.keyBackupScreen => .secureBackupScreen])
|
||||
|
||||
stateMachine.addErrorHandler { context in
|
||||
fatalError("Unexpected transition: \(context)")
|
||||
}
|
||||
}
|
||||
|
||||
private func presentSecureBackupScreen(animated: Bool = true) {
|
||||
let coordinator = SecureBackupScreenCoordinator(parameters: .init(appSettings: appSettings,
|
||||
clientProxy: userSession.clientProxy,
|
||||
userIndicatorController: userIndicatorController))
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .manageRecoveryKey:
|
||||
stateMachine.tryEvent(.manageRecoveryKey)
|
||||
case .disableKeyBackup:
|
||||
stateMachine.tryEvent(.disableKeyBackup)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationStackCoordinator.push(coordinator, animated: animated) { [weak self] in
|
||||
self?.actionsSubject.send(.complete)
|
||||
}
|
||||
}
|
||||
|
||||
private func presentRecoveryKeyScreen() {
|
||||
let sheetNavigationStackCoordinator = NavigationStackCoordinator()
|
||||
let coordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: .init(secureBackupController: userSession.clientProxy.secureBackupController,
|
||||
userIndicatorController: userIndicatorController,
|
||||
isModallyPresented: true))
|
||||
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .complete:
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
sheetNavigationStackCoordinator.setRootCoordinator(coordinator, animated: true)
|
||||
|
||||
navigationStackCoordinator.setSheetCoordinator(sheetNavigationStackCoordinator) { [stateMachine] in
|
||||
stateMachine.tryEvent(.finishedManagingRecoveryKey)
|
||||
}
|
||||
}
|
||||
|
||||
private func presentKeyBackupScreen() {
|
||||
let sheetNavigationStackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
let coordinator = SecureBackupKeyBackupScreenCoordinator(parameters: .init(secureBackupController: userSession.clientProxy.secureBackupController,
|
||||
userIndicatorController: userIndicatorController))
|
||||
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
switch action {
|
||||
case .done:
|
||||
self?.navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
sheetNavigationStackCoordinator.setRootCoordinator(coordinator, animated: true)
|
||||
|
||||
navigationStackCoordinator.setSheetCoordinator(sheetNavigationStackCoordinator) { [stateMachine] in
|
||||
stateMachine.tryEvent(.finishedDisablingKeyBackup)
|
||||
}
|
||||
}
|
||||
}
|
@ -46,6 +46,8 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
// periphery: ignore - used to store the coordinator to avoid deallocation
|
||||
private var appLockFlowCoordinator: AppLockSetupFlowCoordinator?
|
||||
// periphery: ignore - used to store the coordinator to avoid deallocation
|
||||
private var encryptionResetFlowCoordinator: EncryptionResetFlowCoordinator?
|
||||
|
||||
private let actionsSubject: PassthroughSubject<OnboardingFlowCoordinatorAction, Never> = .init()
|
||||
var actions: AnyPublisher<OnboardingFlowCoordinatorAction, Never> {
|
||||
@ -251,7 +253,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
|
||||
appSettings.hasRunIdentityConfirmationOnboarding = true
|
||||
stateMachine.tryEvent(.nextSkippingIdentityConfimed)
|
||||
case .reset:
|
||||
presentEncryptionResetScreen()
|
||||
startEncryptionResetFlow()
|
||||
case .logout:
|
||||
actionsSubject.send(.logout)
|
||||
}
|
||||
@ -295,12 +297,8 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .recoveryFixed:
|
||||
case .complete:
|
||||
break // Moving to next state is Handled by the global session verification listener
|
||||
case .resetEncryption:
|
||||
presentEncryptionResetScreen()
|
||||
default:
|
||||
MXLog.error("Unexpected recovery action: \(action)")
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
@ -308,31 +306,31 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
|
||||
presentCoordinator(coordinator)
|
||||
}
|
||||
|
||||
private func presentEncryptionResetScreen() {
|
||||
private func startEncryptionResetFlow() {
|
||||
let resetNavigationStackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
let coordinator = EncryptionResetScreenCoordinator(parameters: .init(clientProxy: userSession.clientProxy,
|
||||
navigationStackCoordinator: resetNavigationStackCoordinator,
|
||||
userIndicatorController: userIndicatorController))
|
||||
let coordinator = EncryptionResetFlowCoordinator(parameters: .init(userSession: userSession,
|
||||
userIndicatorController: userIndicatorController,
|
||||
navigationStackCoordinator: resetNavigationStackCoordinator,
|
||||
windowManger: windowManager))
|
||||
|
||||
coordinator.actionsPublisher.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .cancel:
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
case .requestOIDCAuthorisation(let url):
|
||||
presentOIDCAuthorisationScreen(url: url)
|
||||
case .resetFinished:
|
||||
case .resetComplete:
|
||||
// Moving to next state is handled by the global session verification listener
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
case .cancel:
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
resetNavigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
encryptionResetFlowCoordinator = coordinator
|
||||
coordinator.start()
|
||||
|
||||
navigationStackCoordinator.setSheetCoordinator(resetNavigationStackCoordinator)
|
||||
navigationStackCoordinator.setSheetCoordinator(resetNavigationStackCoordinator) { [weak self] in
|
||||
self?.encryptionResetFlowCoordinator = nil
|
||||
}
|
||||
}
|
||||
|
||||
private func presentIdentityConfirmedScreen() {
|
||||
@ -411,12 +409,4 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
|
||||
navigationStackCoordinator.push(coordinator, dismissalCallback: dismissalCallback)
|
||||
}
|
||||
}
|
||||
|
||||
private var accountSettingsPresenter: OIDCAccountSettingsPresenter?
|
||||
private func presentOIDCAuthorisationScreen(url: URL) {
|
||||
// Note to anyone in the future if you come back here to make this open in Safari instead of a WAS.
|
||||
// As of iOS 16, there is an issue on the simulator with accessing the cookie but it works on a device. 🤷♂️
|
||||
accountSettingsPresenter = OIDCAccountSettingsPresenter(accountURL: url, presentationAnchor: windowManager.mainWindow)
|
||||
accountSettingsPresenter?.start()
|
||||
}
|
||||
}
|
||||
|
@ -39,9 +39,10 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
// periphery:ignore - retaining purpose
|
||||
private var appLockSetupFlowCoordinator: AppLockSetupFlowCoordinator?
|
||||
|
||||
// periphery:ignore - retaining purpose
|
||||
private var bugReportFlowCoordinator: BugReportFlowCoordinator?
|
||||
// periphery:ignore - retaining purpose
|
||||
private var encryptionSettingsFlowCoordinator: EncryptionSettingsFlowCoordinator?
|
||||
|
||||
private let actionsSubject: PassthroughSubject<SettingsFlowCoordinatorAction, Never> = .init()
|
||||
var actions: AnyPublisher<SettingsFlowCoordinatorAction, Never> {
|
||||
@ -68,7 +69,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
// The navigation stack doesn't like it if the root and the push happen
|
||||
// on the same loop run
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
|
||||
self.presentSecureBackupScreen(animated: animated)
|
||||
self.startEncryptionSettingsFlow(animated: animated)
|
||||
}
|
||||
default:
|
||||
break
|
||||
@ -102,7 +103,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
self.actionsSubject.send(.runLogoutFlow)
|
||||
}
|
||||
case .secureBackup:
|
||||
presentSecureBackupScreen(animated: true)
|
||||
startEncryptionSettingsFlow(animated: true)
|
||||
case .userDetails:
|
||||
presentUserDetailsEditScreen()
|
||||
case let .manageAccount(url):
|
||||
@ -145,21 +146,22 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
actionsSubject.send(.presentedSettings)
|
||||
}
|
||||
|
||||
private func presentSecureBackupScreen(animated: Bool) {
|
||||
let coordinator = SecureBackupScreenCoordinator(parameters: .init(appSettings: parameters.appSettings,
|
||||
clientProxy: parameters.userSession.clientProxy,
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
userIndicatorController: parameters.userIndicatorController))
|
||||
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
private func startEncryptionSettingsFlow(animated: Bool) {
|
||||
let coordinator = EncryptionSettingsFlowCoordinator(parameters: .init(userSession: parameters.userSession,
|
||||
appSettings: parameters.appSettings,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
navigationStackCoordinator: navigationStackCoordinator))
|
||||
coordinator.actionsPublisher.sink { [weak self] action in
|
||||
switch action {
|
||||
case .requestOIDCAuthorisation(let url):
|
||||
self?.presentAccountManagementURL(url)
|
||||
case .complete:
|
||||
// The flow coordinator tidies up the stack, no need to do anything.
|
||||
self?.encryptionSettingsFlowCoordinator = nil
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationStackCoordinator.push(coordinator, animated: animated)
|
||||
encryptionSettingsFlowCoordinator = coordinator
|
||||
coordinator.start()
|
||||
}
|
||||
|
||||
private func presentUserDetailsEditScreen() {
|
||||
|
@ -13,6 +13,8 @@ struct ClientProxyMockConfiguration {
|
||||
var deviceID: String?
|
||||
var roomSummaryProvider: RoomSummaryProviderProtocol? = RoomSummaryProviderMock(.init())
|
||||
var roomDirectorySearchProxy: RoomDirectorySearchProxyProtocol?
|
||||
|
||||
var recoveryState: SecureBackupRecoveryState = .enabled
|
||||
}
|
||||
|
||||
enum ClientProxyMockError: Error {
|
||||
@ -78,12 +80,8 @@ extension ClientProxyMock {
|
||||
loadMediaThumbnailForSourceWidthHeightThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic)
|
||||
loadMediaFileForSourceFilenameThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic)
|
||||
|
||||
secureBackupController = {
|
||||
let secureBackupController = SecureBackupControllerMock()
|
||||
secureBackupController.underlyingRecoveryState = .init(CurrentValueSubject<SecureBackupRecoveryState, Never>(.enabled))
|
||||
secureBackupController.underlyingKeyBackupState = .init(CurrentValueSubject<SecureBackupKeyBackupState, Never>(.enabled))
|
||||
return secureBackupController
|
||||
}()
|
||||
secureBackupController = SecureBackupControllerMock(.init(recoveryState: configuration.recoveryState))
|
||||
resetIdentityReturnValue = .success(IdentityResetHandleSDKMock(.init()))
|
||||
|
||||
roomForIdentifierClosure = { [weak self] identifier in
|
||||
guard let room = self?.roomSummaryProvider?.roomListPublisher.value.first(where: { $0.id == identifier }) else {
|
||||
|
22
ElementX/Sources/Mocks/SDK/IdentityResetHandleSDKMock.swift
Normal file
22
ElementX/Sources/Mocks/SDK/IdentityResetHandleSDKMock.swift
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
extension IdentityResetHandleSDKMock {
|
||||
struct Configuration { }
|
||||
|
||||
convenience init(_ configuration: Configuration) {
|
||||
self.init()
|
||||
|
||||
authTypeReturnValue = .uiaa
|
||||
resetAuthClosure = { _ in
|
||||
try await Task.sleep(for: .seconds(60))
|
||||
}
|
||||
}
|
||||
}
|
48
ElementX/Sources/Mocks/SecureBackupControllerMock.swift
Normal file
48
ElementX/Sources/Mocks/SecureBackupControllerMock.swift
Normal file
@ -0,0 +1,48 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
|
||||
extension SecureBackupControllerMock {
|
||||
struct Configuration {
|
||||
var recoveryState: SecureBackupRecoveryState = .enabled
|
||||
var keyBackupState: SecureBackupKeyBackupState = .enabled
|
||||
}
|
||||
|
||||
convenience init(_ configuration: Configuration) {
|
||||
self.init()
|
||||
|
||||
let recoveryStateSubject = CurrentValueSubject<SecureBackupRecoveryState, Never>(configuration.recoveryState)
|
||||
underlyingRecoveryState = .init(recoveryStateSubject)
|
||||
|
||||
let keyBackupStateSubject = CurrentValueSubject<SecureBackupKeyBackupState, Never>(configuration.keyBackupState)
|
||||
underlyingKeyBackupState = .init(keyBackupStateSubject)
|
||||
|
||||
disableClosure = {
|
||||
recoveryStateSubject.send(.disabled)
|
||||
keyBackupStateSubject.send(.unknown)
|
||||
return .success(())
|
||||
}
|
||||
|
||||
enableClosure = {
|
||||
recoveryStateSubject.send(.disabled)
|
||||
keyBackupStateSubject.send(.enabled)
|
||||
return .success(())
|
||||
}
|
||||
|
||||
generateRecoveryKeyClosure = {
|
||||
recoveryStateSubject.send(.enabled)
|
||||
return .success("a1B2 C3d4 E5F6 g7H8 i9J0 K1l2 M3n4 O5p6 Q7R8 s9T0 U1v2 W3X4")
|
||||
}
|
||||
|
||||
confirmRecoveryKeyClosure = { _ in
|
||||
recoveryStateSubject.send(.enabled)
|
||||
return .success(())
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,8 @@ enum A11yIdentifiers {
|
||||
static let appLockSetupSettingsScreen = AppLockSetupSettingsScreen()
|
||||
static let bugReportScreen = BugReportScreen()
|
||||
static let changeServerScreen = ChangeServer()
|
||||
static let encryptionResetScreen = EncryptionResetScreen()
|
||||
static let encryptionResetPasswordScreen = EncryptionResetPasswordScreen()
|
||||
static let homeScreen = HomeScreen()
|
||||
static let loginScreen = LoginScreen()
|
||||
static let authenticationStartScreen = AuthenticationStartScreen()
|
||||
@ -24,6 +26,9 @@ enum A11yIdentifiers {
|
||||
static let roomDetailsScreen = RoomDetailsScreen()
|
||||
static let roomNotificationSettingsScreen = RoomNotificationSettingsScreen()
|
||||
static let roomRolesAndPermissionsScreen = RoomRolesAndPermissionsScreen()
|
||||
static let secureBackupScreen = SecureBackupScreen()
|
||||
static let secureBackupKeyBackupScreen = SecureBackupKeyBackupScreen()
|
||||
static let secureBackupRecoveryKeyScreen = SecureBackupRecoveryKeyScreen()
|
||||
static let serverConfirmationScreen = ServerConfirmationScreen()
|
||||
static let sessionVerificationScreen = SessionVerificationScreen()
|
||||
static let settingsScreen = SettingsScreen()
|
||||
@ -81,6 +86,15 @@ enum A11yIdentifiers {
|
||||
let dismiss = "change_server-dismiss"
|
||||
}
|
||||
|
||||
struct EncryptionResetScreen {
|
||||
let continueReset = "encryption_reset-continue_reset"
|
||||
}
|
||||
|
||||
struct EncryptionResetPasswordScreen {
|
||||
let passwordField = "encryption_reset_password-password_field"
|
||||
let submit = "encryption_reset_password-submit"
|
||||
}
|
||||
|
||||
struct HomeScreen {
|
||||
let userAvatar = "home_screen-user_avatar"
|
||||
let recoveryKeyConfirmationBannerContinue = "home_screen-recovery_key_confirmation_continue"
|
||||
@ -178,6 +192,23 @@ enum A11yIdentifiers {
|
||||
let memberModeration = "room_roles_and_permissions-member_moderation"
|
||||
}
|
||||
|
||||
struct SecureBackupScreen {
|
||||
let keyStorage = "secure_backup-key_storage"
|
||||
let recoveryKey = "secure_backup-recovery_key"
|
||||
}
|
||||
|
||||
struct SecureBackupKeyBackupScreen {
|
||||
let deleteKeyStorage = "secure_backup_key_backup-delete_key_storage"
|
||||
}
|
||||
|
||||
struct SecureBackupRecoveryKeyScreen {
|
||||
let generateRecoveryKey = "secure_backup_recovery_key-generate_recovery_key"
|
||||
let copyRecoveryKey = "secure_backup_recovery_key-copy_recovery_key"
|
||||
let done = "secure_backup_recovery_key-done"
|
||||
let recoveryKeyField = "secure_backup_recovery_key-recovery_key_field"
|
||||
let confirm = "secure_backup_recovery_key-confirm"
|
||||
}
|
||||
|
||||
struct ServerConfirmationScreen {
|
||||
let `continue` = "server_confirmation-continue"
|
||||
let changeServer = "server_confirmation-change_server"
|
||||
|
@ -8,17 +8,12 @@
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct EncryptionResetPasswordScreenCoordinatorParameters { }
|
||||
struct EncryptionResetPasswordScreenCoordinatorParameters {
|
||||
let passwordPublisher: PassthroughSubject<String, Never>
|
||||
}
|
||||
|
||||
enum EncryptionResetPasswordScreenCoordinatorAction: CustomStringConvertible {
|
||||
case resetIdentity(String)
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .resetIdentity:
|
||||
"resetIdentity"
|
||||
}
|
||||
}
|
||||
enum EncryptionResetPasswordScreenCoordinatorAction {
|
||||
case passwordEntered
|
||||
}
|
||||
|
||||
final class EncryptionResetPasswordScreenCoordinator: CoordinatorProtocol {
|
||||
@ -35,7 +30,7 @@ final class EncryptionResetPasswordScreenCoordinator: CoordinatorProtocol {
|
||||
init(parameters: EncryptionResetPasswordScreenCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
|
||||
viewModel = EncryptionResetPasswordScreenViewModel()
|
||||
viewModel = EncryptionResetPasswordScreenViewModel(passwordPublisher: parameters.passwordPublisher)
|
||||
}
|
||||
|
||||
func start() {
|
||||
@ -44,8 +39,8 @@ final class EncryptionResetPasswordScreenCoordinator: CoordinatorProtocol {
|
||||
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .resetIdentity(let password):
|
||||
self.actionsSubject.send(.resetIdentity(password))
|
||||
case .passwordEntered:
|
||||
self.actionsSubject.send(.passwordEntered)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
@ -7,15 +7,8 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
enum EncryptionResetPasswordScreenViewModelAction: CustomStringConvertible {
|
||||
case resetIdentity(String)
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .resetIdentity:
|
||||
"resetIdentity"
|
||||
}
|
||||
}
|
||||
enum EncryptionResetPasswordScreenViewModelAction {
|
||||
case passwordEntered
|
||||
}
|
||||
|
||||
struct EncryptionResetPasswordScreenViewState: BindableState {
|
||||
@ -28,5 +21,5 @@ struct EncryptionResetPasswordScreenViewStateBindings {
|
||||
}
|
||||
|
||||
enum EncryptionResetPasswordScreenViewAction {
|
||||
case resetIdentity
|
||||
case submit
|
||||
}
|
||||
|
@ -11,12 +11,16 @@ import SwiftUI
|
||||
typealias EncryptionResetPasswordScreenViewModelType = StateStoreViewModel<EncryptionResetPasswordScreenViewState, EncryptionResetPasswordScreenViewAction>
|
||||
|
||||
class EncryptionResetPasswordScreenViewModel: EncryptionResetPasswordScreenViewModelType, EncryptionResetPasswordScreenViewModelProtocol {
|
||||
private let passwordPublisher: PassthroughSubject<String, Never>
|
||||
|
||||
private let actionsSubject: PassthroughSubject<EncryptionResetPasswordScreenViewModelAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<EncryptionResetPasswordScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init() {
|
||||
init(passwordPublisher: PassthroughSubject<String, Never>) {
|
||||
self.passwordPublisher = passwordPublisher
|
||||
|
||||
super.init(initialViewState: .init(bindings: .init(password: "")))
|
||||
}
|
||||
|
||||
@ -26,8 +30,9 @@ class EncryptionResetPasswordScreenViewModel: EncryptionResetPasswordScreenViewM
|
||||
MXLog.info("View model: received view action: \(viewAction)")
|
||||
|
||||
switch viewAction {
|
||||
case .resetIdentity:
|
||||
actionsSubject.send(.resetIdentity(state.bindings.password))
|
||||
case .submit:
|
||||
passwordPublisher.send(state.bindings.password)
|
||||
actionsSubject.send(.passwordEntered)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Compound
|
||||
import SwiftUI
|
||||
|
||||
@ -32,9 +33,10 @@ struct EncryptionResetPasswordScreen: View {
|
||||
.padding(16)
|
||||
} bottomContent: {
|
||||
Button(L10n.actionResetIdentity, role: .destructive) {
|
||||
context.send(viewAction: .resetIdentity)
|
||||
context.send(viewAction: .submit)
|
||||
}
|
||||
.buttonStyle(.compound(.primary))
|
||||
.accessibilityIdentifier(A11yIdentifiers.encryptionResetPasswordScreen.submit)
|
||||
}
|
||||
.background()
|
||||
.backgroundStyle(.compound.bgCanvasDefault)
|
||||
@ -58,8 +60,9 @@ struct EncryptionResetPasswordScreen: View {
|
||||
.focused($textFieldFocus)
|
||||
.submitLabel(.done)
|
||||
.onSubmit {
|
||||
context.send(viewAction: .resetIdentity)
|
||||
context.send(viewAction: .submit)
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.encryptionResetPasswordScreen.passwordField)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,7 +70,8 @@ struct EncryptionResetPasswordScreen: View {
|
||||
// MARK: - Previews
|
||||
|
||||
struct EncryptionResetPasswordScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = EncryptionResetPasswordScreenViewModel()
|
||||
static let passwordPublisher = PassthroughSubject<String, Never>()
|
||||
static let viewModel = EncryptionResetPasswordScreenViewModel(passwordPublisher: passwordPublisher)
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
EncryptionResetPasswordScreen(context: viewModel.context)
|
||||
|
@ -9,14 +9,14 @@ import Combine
|
||||
import SwiftUI
|
||||
|
||||
enum EncryptionResetScreenCoordinatorAction {
|
||||
case cancel
|
||||
case requestOIDCAuthorisation(URL)
|
||||
case requestPassword(passwordPublisher: PassthroughSubject<String, Never>)
|
||||
case resetFinished
|
||||
case cancel
|
||||
}
|
||||
|
||||
struct EncryptionResetScreenCoordinatorParameters {
|
||||
let clientProxy: ClientProxyProtocol
|
||||
let navigationStackCoordinator: NavigationStackCoordinator
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
}
|
||||
|
||||
@ -43,10 +43,10 @@ final class EncryptionResetScreenCoordinator: CoordinatorProtocol {
|
||||
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .requestPassword:
|
||||
presentPasswordScreen()
|
||||
case .requestOIDCAuthorisation(let url):
|
||||
self.actionsSubject.send(.requestOIDCAuthorisation(url))
|
||||
case .requestPassword(let passwordPublisher):
|
||||
self.actionsSubject.send(.requestPassword(passwordPublisher: passwordPublisher))
|
||||
case .resetFinished:
|
||||
self.actionsSubject.send(.resetFinished)
|
||||
case .cancel:
|
||||
@ -63,23 +63,4 @@ final class EncryptionResetScreenCoordinator: CoordinatorProtocol {
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(EncryptionResetScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func presentPasswordScreen() {
|
||||
let coordinator = EncryptionResetPasswordScreenCoordinator(parameters: .init())
|
||||
|
||||
coordinator.actionsPublisher.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .resetIdentity(let password):
|
||||
viewModel.continueResetFlowWith(password: password)
|
||||
parameters.navigationStackCoordinator.pop()
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
parameters.navigationStackCoordinator.push(coordinator)
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,11 @@
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
|
||||
enum EncryptionResetScreenViewModelAction {
|
||||
case requestPassword
|
||||
case requestPassword(passwordPublisher: PassthroughSubject<String, Never>)
|
||||
case requestOIDCAuthorisation(url: URL)
|
||||
case resetFinished
|
||||
case cancel
|
||||
|
@ -21,6 +21,7 @@ class EncryptionResetScreenViewModel: EncryptionResetScreenViewModelType, Encryp
|
||||
}
|
||||
|
||||
private var identityResetHandle: IdentityResetHandle?
|
||||
private var passwordCancellable: AnyCancellable?
|
||||
|
||||
init(clientProxy: ClientProxyProtocol, userIndicatorController: UserIndicatorControllerProtocol) {
|
||||
self.clientProxy = clientProxy
|
||||
@ -46,12 +47,6 @@ class EncryptionResetScreenViewModel: EncryptionResetScreenViewModelType, Encryp
|
||||
}
|
||||
}
|
||||
|
||||
func continueResetFlowWith(password: String) {
|
||||
Task {
|
||||
await resetWith(password: password)
|
||||
}
|
||||
}
|
||||
|
||||
func stop() {
|
||||
Task {
|
||||
await identityResetHandle?.cancel()
|
||||
@ -80,7 +75,14 @@ class EncryptionResetScreenViewModel: EncryptionResetScreenViewModelType, Encryp
|
||||
|
||||
switch handle.authType() {
|
||||
case .uiaa:
|
||||
actionsSubject.send(.requestPassword)
|
||||
let passwordPublisher = PassthroughSubject<String, Never>()
|
||||
passwordCancellable = passwordPublisher.sink { [weak self] password in
|
||||
guard let self else { return }
|
||||
passwordCancellable = nil
|
||||
Task { await self.resetWith(password: password) }
|
||||
}
|
||||
|
||||
actionsSubject.send(.requestPassword(passwordPublisher: passwordPublisher))
|
||||
case .oidc(let oidcInfo):
|
||||
guard let url = URL(string: oidcInfo.approvalUrl) else {
|
||||
fatalError("Invalid URL received through identity reset handle: \(oidcInfo.approvalUrl)")
|
||||
|
@ -12,6 +12,5 @@ protocol EncryptionResetScreenViewModelProtocol {
|
||||
var actionsPublisher: AnyPublisher<EncryptionResetScreenViewModelAction, Never> { get }
|
||||
var context: EncryptionResetScreenViewModelType.Context { get }
|
||||
|
||||
func continueResetFlowWith(password: String)
|
||||
func stop()
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ struct EncryptionResetScreen: View {
|
||||
context.send(viewAction: .reset)
|
||||
}
|
||||
.buttonStyle(.compound(.primary))
|
||||
.accessibilityIdentifier(A11yIdentifiers.encryptionResetScreen.continueReset)
|
||||
}
|
||||
.background()
|
||||
.backgroundStyle(.compound.bgSubtleSecondary)
|
||||
|
@ -22,6 +22,7 @@ struct SecureBackupKeyBackupScreen: View {
|
||||
Text(L10n.screenChatBackupKeyBackupActionDisable)
|
||||
}
|
||||
.buttonStyle(.compound(.primary))
|
||||
.accessibilityIdentifier(A11yIdentifiers.secureBackupKeyBackupScreen.deleteKeyStorage)
|
||||
}
|
||||
.background()
|
||||
.backgroundStyle(.compound.bgCanvasDefault)
|
||||
|
@ -15,23 +15,22 @@ struct SecureBackupRecoveryKeyScreenCoordinatorParameters {
|
||||
}
|
||||
|
||||
enum SecureBackupRecoveryKeyScreenCoordinatorAction {
|
||||
case cancel
|
||||
case recoverySetUp
|
||||
case recoveryChanged
|
||||
case recoveryFixed
|
||||
case resetEncryption
|
||||
case complete
|
||||
}
|
||||
|
||||
final class SecureBackupRecoveryKeyScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: SecureBackupRecoveryKeyScreenCoordinatorParameters
|
||||
private var viewModel: SecureBackupRecoveryKeyScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<SecureBackupRecoveryKeyScreenCoordinatorAction, Never> = .init()
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
private let actionsSubject: PassthroughSubject<SecureBackupRecoveryKeyScreenCoordinatorAction, Never> = .init()
|
||||
var actions: AnyPublisher<SecureBackupRecoveryKeyScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: SecureBackupRecoveryKeyScreenCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
viewModel = SecureBackupRecoveryKeyScreenViewModel(secureBackupController: parameters.secureBackupController,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
isModallyPresented: parameters.isModallyPresented)
|
||||
@ -44,20 +43,19 @@ final class SecureBackupRecoveryKeyScreenCoordinator: CoordinatorProtocol {
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .cancel:
|
||||
self.actionsSubject.send(.cancel)
|
||||
self.actionsSubject.send(.complete)
|
||||
case .done(let mode):
|
||||
switch mode {
|
||||
case .setupRecovery:
|
||||
self.actionsSubject.send(.recoverySetUp)
|
||||
showSuccessIndicator(title: L10n.screenRecoveryKeySetupSuccess)
|
||||
case .changeRecovery:
|
||||
self.actionsSubject.send(.recoveryChanged)
|
||||
showSuccessIndicator(title: L10n.screenRecoveryKeyChangeSuccess)
|
||||
case .fixRecovery:
|
||||
self.actionsSubject.send(.recoveryFixed)
|
||||
showSuccessIndicator(title: L10n.screenRecoveryKeyConfirmSuccess)
|
||||
case .unknown:
|
||||
fatalError()
|
||||
}
|
||||
case .resetEncryption:
|
||||
self.actionsSubject.send(.resetEncryption)
|
||||
self.actionsSubject.send(.complete)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
@ -66,4 +64,14 @@ final class SecureBackupRecoveryKeyScreenCoordinator: CoordinatorProtocol {
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(SecureBackupRecoveryKeyScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func showSuccessIndicator(title: String) {
|
||||
parameters.userIndicatorController.submitIndicator(.init(id: .init(),
|
||||
type: .modal(progress: .none, interactiveDismissDisabled: false, allowsInteraction: false),
|
||||
title: title,
|
||||
iconName: "checkmark",
|
||||
persistent: false))
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import Foundation
|
||||
enum SecureBackupRecoveryKeyScreenViewModelAction {
|
||||
case done(mode: SecureBackupRecoveryKeyScreenViewMode)
|
||||
case cancel
|
||||
case resetEncryption
|
||||
}
|
||||
|
||||
enum SecureBackupRecoveryKeyScreenViewMode {
|
||||
@ -82,7 +81,6 @@ enum SecureBackupRecoveryKeyScreenViewAction {
|
||||
case copyKey
|
||||
case keySaved
|
||||
case confirmKey
|
||||
case resetEncryption
|
||||
case done
|
||||
case cancel
|
||||
}
|
||||
|
@ -78,13 +78,11 @@ class SecureBackupRecoveryKeyScreenViewModel: SecureBackupRecoveryKeyScreenViewM
|
||||
state.bindings.alertInfo = .init(id: .init(),
|
||||
title: L10n.screenRecoveryKeySetupConfirmationTitle,
|
||||
message: L10n.screenRecoveryKeySetupConfirmationDescription,
|
||||
primaryButton: .init(title: L10n.actionCancel, role: .cancel, action: nil),
|
||||
secondaryButton: .init(title: L10n.actionContinue, action: { [weak self] in
|
||||
primaryButton: .init(title: L10n.actionContinue) { [weak self] in
|
||||
guard let self else { return }
|
||||
actionsSubject.send(.done(mode: context.viewState.mode))
|
||||
}))
|
||||
case .resetEncryption:
|
||||
actionsSubject.send(.resetEncryption)
|
||||
},
|
||||
secondaryButton: .init(title: L10n.actionCancel, role: .cancel, action: nil))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,6 +89,7 @@ struct SecureBackupRecoveryKeyScreen: View {
|
||||
}
|
||||
.buttonStyle(.compound(.primary))
|
||||
.disabled(context.confirmationRecoveryKey.isEmpty)
|
||||
.accessibilityIdentifier(A11yIdentifiers.secureBackupRecoveryKeyScreen.confirm)
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,6 +112,7 @@ struct SecureBackupRecoveryKeyScreen: View {
|
||||
}
|
||||
.buttonStyle(.compound(.primary))
|
||||
.disabled(context.viewState.recoveryKey == nil || context.viewState.doneButtonEnabled == false)
|
||||
.accessibilityIdentifier(A11yIdentifiers.secureBackupRecoveryKeyScreen.done)
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,6 +142,7 @@ struct SecureBackupRecoveryKeyScreen: View {
|
||||
}
|
||||
.font(.compound.bodyLGSemibold)
|
||||
.padding(.vertical, 11)
|
||||
.accessibilityIdentifier(A11yIdentifiers.secureBackupRecoveryKeyScreen.generateRecoveryKey)
|
||||
} else {
|
||||
HStack(spacing: 8) {
|
||||
ProgressView()
|
||||
@ -163,6 +166,7 @@ struct SecureBackupRecoveryKeyScreen: View {
|
||||
}
|
||||
.tint(.compound.iconSecondary)
|
||||
.accessibilityLabel(L10n.actionCopy)
|
||||
.accessibilityIdentifier(A11yIdentifiers.secureBackupRecoveryKeyScreen.copyRecoveryKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -204,6 +208,7 @@ struct SecureBackupRecoveryKeyScreen: View {
|
||||
.onSubmit {
|
||||
context.send(viewAction: .confirmKey)
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.secureBackupRecoveryKeyScreen.recoveryKeyField)
|
||||
|
||||
if let subtitle = context.viewState.recoveryKeySubtitle {
|
||||
Text(subtitle)
|
||||
|
@ -11,12 +11,12 @@ import SwiftUI
|
||||
struct SecureBackupScreenCoordinatorParameters {
|
||||
let appSettings: AppSettings
|
||||
let clientProxy: ClientProxyProtocol
|
||||
weak var navigationStackCoordinator: NavigationStackCoordinator?
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
}
|
||||
|
||||
enum SecureBackupScreenCoordinatorAction {
|
||||
case requestOIDCAuthorisation(URL)
|
||||
case manageRecoveryKey
|
||||
case disableKeyBackup
|
||||
}
|
||||
|
||||
final class SecureBackupScreenCoordinator: CoordinatorProtocol {
|
||||
@ -43,53 +43,10 @@ final class SecureBackupScreenCoordinator: CoordinatorProtocol {
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .recoveryKey:
|
||||
let recoveryNavigationStackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
let recoveryKeyCoordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: .init(secureBackupController: parameters.clientProxy.secureBackupController,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
isModallyPresented: true))
|
||||
|
||||
recoveryKeyCoordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .cancel:
|
||||
parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
|
||||
case .recoverySetUp:
|
||||
showSuccessIndicator(title: L10n.screenRecoveryKeySetupSuccess)
|
||||
parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
|
||||
case .recoveryChanged:
|
||||
showSuccessIndicator(title: L10n.screenRecoveryKeyChangeSuccess)
|
||||
parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
|
||||
case .recoveryFixed:
|
||||
showSuccessIndicator(title: L10n.screenRecoveryKeyConfirmSuccess)
|
||||
parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
|
||||
case .resetEncryption:
|
||||
showEncryptionReset(recoveryNavigationStackCoordinator: recoveryNavigationStackCoordinator)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
recoveryNavigationStackCoordinator.setRootCoordinator(recoveryKeyCoordinator, animated: true)
|
||||
|
||||
parameters.navigationStackCoordinator?.setSheetCoordinator(recoveryNavigationStackCoordinator)
|
||||
case .keyBackup:
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
let keyBackupCoordinator = SecureBackupKeyBackupScreenCoordinator(parameters: .init(secureBackupController: parameters.clientProxy.secureBackupController,
|
||||
userIndicatorController: parameters.userIndicatorController))
|
||||
|
||||
keyBackupCoordinator.actions.sink { [weak self] action in
|
||||
switch action {
|
||||
case .done:
|
||||
self?.parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationStackCoordinator.setRootCoordinator(keyBackupCoordinator, animated: true)
|
||||
|
||||
parameters.navigationStackCoordinator?.setSheetCoordinator(navigationStackCoordinator)
|
||||
case .manageRecoveryKey:
|
||||
actionsSubject.send(.manageRecoveryKey)
|
||||
case .disableKeyBackup:
|
||||
actionsSubject.send(.disableKeyBackup)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
@ -98,41 +55,4 @@ final class SecureBackupScreenCoordinator: CoordinatorProtocol {
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(SecureBackupScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func showSuccessIndicator(title: String) {
|
||||
parameters.userIndicatorController.submitIndicator(.init(id: .init(),
|
||||
type: .modal(progress: .none, interactiveDismissDisabled: false, allowsInteraction: false),
|
||||
title: title,
|
||||
iconName: "checkmark",
|
||||
persistent: false))
|
||||
}
|
||||
|
||||
private func showEncryptionReset(recoveryNavigationStackCoordinator: NavigationStackCoordinator) {
|
||||
let resetNavigationStackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
let coordinator = EncryptionResetScreenCoordinator(parameters: .init(clientProxy: parameters.clientProxy,
|
||||
navigationStackCoordinator: resetNavigationStackCoordinator,
|
||||
userIndicatorController: parameters.userIndicatorController))
|
||||
|
||||
coordinator.actionsPublisher.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .cancel:
|
||||
recoveryNavigationStackCoordinator.setSheetCoordinator(nil)
|
||||
case .requestOIDCAuthorisation(let url):
|
||||
actionsSubject.send(.requestOIDCAuthorisation(url))
|
||||
case .resetFinished:
|
||||
parameters.navigationStackCoordinator?.setSheetCoordinator(nil) // Dismiss the recovery screen
|
||||
recoveryNavigationStackCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
resetNavigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
|
||||
recoveryNavigationStackCoordinator.setSheetCoordinator(resetNavigationStackCoordinator)
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,8 @@
|
||||
import Foundation
|
||||
|
||||
enum SecureBackupScreenViewModelAction {
|
||||
case recoveryKey
|
||||
case keyBackup
|
||||
case manageRecoveryKey
|
||||
case disableKeyBackup
|
||||
}
|
||||
|
||||
struct SecureBackupScreenViewState: BindableState {
|
||||
|
@ -48,7 +48,7 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup
|
||||
override func process(viewAction: SecureBackupScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .recoveryKey:
|
||||
actionsSubject.send(.recoveryKey)
|
||||
actionsSubject.send(.manageRecoveryKey)
|
||||
case .keyStorageToggled(let enable):
|
||||
let keyBackupState = secureBackupController.keyBackupState.value
|
||||
switch (keyBackupState, enable) {
|
||||
@ -57,7 +57,7 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup
|
||||
enableBackup()
|
||||
case (.enabled, false):
|
||||
state.bindings.keyStorageEnabled = keyBackupState.keyStorageToggleState // Reset the toggle in case the user cancels
|
||||
actionsSubject.send(.keyBackup)
|
||||
actionsSubject.send(.disableKeyBackup)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -53,7 +53,13 @@ struct SecureBackupScreen: View {
|
||||
.accessibilityElement(children: .combine)
|
||||
})
|
||||
|
||||
keyStorageToggle
|
||||
ListRow(label: .plain(title: L10n.screenChatBackupKeyStorageToggleTitle,
|
||||
description: context.viewState.keyStorageToggleDescription),
|
||||
kind: .toggle($context.keyStorageEnabled))
|
||||
.onChange(of: context.keyStorageEnabled) { _, newValue in
|
||||
context.send(viewAction: .keyStorageToggled(newValue))
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.secureBackupScreen.keyStorage)
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,15 +73,6 @@ struct SecureBackupScreen: View {
|
||||
return description
|
||||
}
|
||||
|
||||
private var keyStorageToggle: some View {
|
||||
ListRow(label: .plain(title: L10n.screenChatBackupKeyStorageToggleTitle,
|
||||
description: context.viewState.keyStorageToggleDescription),
|
||||
kind: .toggle($context.keyStorageEnabled))
|
||||
.onChange(of: context.keyStorageEnabled) { _, newValue in
|
||||
context.send(viewAction: .keyStorageToggled(newValue))
|
||||
}
|
||||
}
|
||||
|
||||
private var recoveryKeySection: some View {
|
||||
Section {
|
||||
switch context.viewState.recoveryState {
|
||||
@ -85,6 +82,7 @@ struct SecureBackupScreen: View {
|
||||
icon: \.key,
|
||||
iconAlignment: .top),
|
||||
kind: .navigationLink { context.send(viewAction: .recoveryKey) })
|
||||
.accessibilityIdentifier(A11yIdentifiers.secureBackupScreen.recoveryKey)
|
||||
case .disabled:
|
||||
ListRow(label: .default(title: L10n.screenChatBackupRecoveryActionSetup,
|
||||
description: L10n.screenChatBackupRecoveryActionChangeDescription,
|
||||
@ -92,10 +90,12 @@ struct SecureBackupScreen: View {
|
||||
iconAlignment: .top),
|
||||
details: .icon(BadgeView(size: 10)),
|
||||
kind: .navigationLink { context.send(viewAction: .recoveryKey) })
|
||||
.accessibilityIdentifier(A11yIdentifiers.secureBackupScreen.recoveryKey)
|
||||
case .incomplete:
|
||||
ListRow(label: .plain(title: L10n.screenChatBackupRecoveryActionConfirm),
|
||||
details: .icon(BadgeView(size: 10)),
|
||||
kind: .navigationLink { context.send(viewAction: .recoveryKey) })
|
||||
.accessibilityIdentifier(A11yIdentifiers.secureBackupScreen.recoveryKey)
|
||||
default:
|
||||
ListRow(label: .plain(title: L10n.commonLoading), details: .isWaiting(true), kind: .label)
|
||||
}
|
||||
|
@ -630,6 +630,40 @@ class MockScreen: Identifiable {
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let coordinator = PollFormScreenCoordinator(parameters: .init(mode: .new))
|
||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
return navigationStackCoordinator
|
||||
case .encryptionSettings, .encryptionSettingsOutOfSync:
|
||||
let recoveryState: SecureBackupRecoveryState = id == .encryptionSettings ? .enabled : .incomplete
|
||||
let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com", recoveryState: recoveryState))
|
||||
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
navigationStackCoordinator.setRootCoordinator(BlankFormCoordinator())
|
||||
|
||||
let coordinator = EncryptionSettingsFlowCoordinator(parameters: .init(userSession: userSession,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
navigationStackCoordinator: navigationStackCoordinator))
|
||||
retainedState.append(coordinator)
|
||||
coordinator.start()
|
||||
|
||||
return navigationStackCoordinator
|
||||
case .encryptionReset:
|
||||
let recoveryState: SecureBackupRecoveryState = id == .encryptionSettings ? .enabled : .incomplete
|
||||
let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com", recoveryState: recoveryState))
|
||||
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
|
||||
let userIndicatorController = UserIndicatorController()
|
||||
userIndicatorController.window = windowManager.overlayWindow
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
let coordinator = EncryptionResetFlowCoordinator(parameters: .init(userSession: userSession,
|
||||
userIndicatorController: userIndicatorController,
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
windowManger: windowManager))
|
||||
|
||||
retainedState.append(coordinator)
|
||||
coordinator.start()
|
||||
|
||||
return navigationStackCoordinator
|
||||
case .autoUpdatingTimeline:
|
||||
let appSettings: AppSettings = ServiceLocator.shared.settings
|
||||
|
@ -20,6 +20,9 @@ enum UITestsScreenIdentifier: String {
|
||||
case createPoll
|
||||
case createRoom
|
||||
case createRoomNoUsers
|
||||
case encryptionSettings
|
||||
case encryptionSettingsOutOfSync
|
||||
case encryptionReset
|
||||
case roomLayoutBottom
|
||||
case roomLayoutMiddle
|
||||
case roomLayoutTop
|
||||
|
37
UITests/Sources/EncryptionResetUITests.swift
Normal file
37
UITests/Sources/EncryptionResetUITests.swift
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
@MainActor
|
||||
class EncryptionResetUITests: XCTestCase {
|
||||
var app: XCUIApplication!
|
||||
|
||||
@MainActor enum Step {
|
||||
static let resetScreen = 0
|
||||
static let passwordScreen = 1
|
||||
static let resetingEncryption = 2
|
||||
}
|
||||
|
||||
func testPasswordFlow() async throws {
|
||||
app = Application.launch(.encryptionReset)
|
||||
|
||||
// Starting with the root screen.
|
||||
try await app.assertScreenshot(.encryptionReset, step: Step.resetScreen)
|
||||
|
||||
// Confirm the intent to reset.
|
||||
app.buttons[A11yIdentifiers.encryptionResetScreen.continueReset].tap()
|
||||
app.buttons[A11yIdentifiers.alertInfo.primaryButton].tap()
|
||||
try await app.assertScreenshot(.encryptionReset, step: Step.passwordScreen)
|
||||
|
||||
// Enter the password and submit.
|
||||
let passwordField = app.secureTextFields[A11yIdentifiers.encryptionResetPasswordScreen.passwordField]
|
||||
passwordField.clearAndTypeText("supersecurepassword", app: app)
|
||||
app.buttons[A11yIdentifiers.encryptionResetPasswordScreen.submit].tap()
|
||||
try await app.assertScreenshot(.encryptionReset, step: Step.resetingEncryption)
|
||||
}
|
||||
}
|
80
UITests/Sources/EncryptionSettingsUITests.swift
Normal file
80
UITests/Sources/EncryptionSettingsUITests.swift
Normal file
@ -0,0 +1,80 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
@MainActor
|
||||
class EncryptionSettingsUITests: XCTestCase {
|
||||
var app: XCUIApplication!
|
||||
|
||||
@MainActor enum Step {
|
||||
static let secureBackupScreenSetUp = 0
|
||||
static let keyBackupScreen = 1
|
||||
static let secureBackupScreenDisabled = 2
|
||||
static let setUpRecovery = 3
|
||||
static let changeRecovery = 4
|
||||
|
||||
static let secureBackupScreenOutOfSync = 5
|
||||
static let confirmRecovery = 6
|
||||
}
|
||||
|
||||
func testFlow() async throws {
|
||||
app = Application.launch(.encryptionSettings)
|
||||
|
||||
// Starting with key storage and recovery enabled.
|
||||
try await app.assertScreenshot(.encryptionSettings, step: Step.secureBackupScreenSetUp)
|
||||
|
||||
// Toggle key storage off.
|
||||
app.switches[A11yIdentifiers.secureBackupScreen.keyStorage].tap()
|
||||
try await app.assertScreenshot(.encryptionSettings, step: Step.keyBackupScreen)
|
||||
|
||||
// Confirm deletion of keys.
|
||||
app.buttons[A11yIdentifiers.secureBackupKeyBackupScreen.deleteKeyStorage].tap()
|
||||
app.buttons[A11yIdentifiers.alertInfo.primaryButton].tap()
|
||||
try await app.assertScreenshot(.encryptionSettings, step: Step.secureBackupScreenDisabled)
|
||||
|
||||
// Toggle key storage back on and set up recovery.
|
||||
app.switches[A11yIdentifiers.secureBackupScreen.keyStorage].tap()
|
||||
app.buttons[A11yIdentifiers.secureBackupScreen.recoveryKey].tap()
|
||||
try await app.assertScreenshot(.encryptionSettings, step: Step.setUpRecovery)
|
||||
|
||||
// Generate and copy a new recovery key.
|
||||
app.buttons[A11yIdentifiers.secureBackupRecoveryKeyScreen.generateRecoveryKey].tap()
|
||||
app.buttons[A11yIdentifiers.secureBackupRecoveryKeyScreen.copyRecoveryKey].tap()
|
||||
app.buttons[A11yIdentifiers.secureBackupRecoveryKeyScreen.done].tap()
|
||||
app.buttons[A11yIdentifiers.alertInfo.primaryButton].tap()
|
||||
try await app.assertScreenshot(.encryptionSettings, step: Step.secureBackupScreenSetUp)
|
||||
|
||||
// Change the recovery key.
|
||||
app.buttons[A11yIdentifiers.secureBackupScreen.recoveryKey].tap()
|
||||
try await app.assertScreenshot(.encryptionSettings, step: Step.changeRecovery)
|
||||
|
||||
// Generate and copy the updated recovery key.
|
||||
app.buttons[A11yIdentifiers.secureBackupRecoveryKeyScreen.generateRecoveryKey].tap()
|
||||
app.buttons[A11yIdentifiers.secureBackupRecoveryKeyScreen.copyRecoveryKey].tap()
|
||||
app.buttons[A11yIdentifiers.secureBackupRecoveryKeyScreen.done].tap()
|
||||
app.buttons[A11yIdentifiers.alertInfo.primaryButton].tap()
|
||||
try await app.assertScreenshot(.encryptionSettings, step: Step.secureBackupScreenSetUp)
|
||||
}
|
||||
|
||||
func testOutOfSyncFlow() async throws {
|
||||
app = Application.launch(.encryptionSettingsOutOfSync)
|
||||
|
||||
// Starting with key storage and recovery enabled.
|
||||
try await app.assertScreenshot(.encryptionSettings, step: Step.secureBackupScreenOutOfSync)
|
||||
|
||||
// Confirm the recovery key.
|
||||
app.buttons[A11yIdentifiers.secureBackupScreen.recoveryKey].tap()
|
||||
try await app.assertScreenshot(.encryptionSettings, step: Step.confirmRecovery)
|
||||
|
||||
// Enter the recovery key and submit.
|
||||
let recoveryKeyField = app.secureTextFields[A11yIdentifiers.secureBackupRecoveryKeyScreen.recoveryKeyField]
|
||||
recoveryKeyField.clearAndTypeText("sUpe RSec rEtR Ecov ERYk Ey12", app: app)
|
||||
app.buttons[A11yIdentifiers.secureBackupRecoveryKeyScreen.confirm].tap()
|
||||
try await app.assertScreenshot(.encryptionSettings, step: Step.secureBackupScreenSetUp)
|
||||
}
|
||||
}
|
BIN
UITests/Sources/__Snapshots__/Application/encryptionReset-0-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionReset-0-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionReset-0-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionReset-0-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionReset-1-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionReset-1-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionReset-1-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionReset-1-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionReset-2-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionReset-2-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionReset-2-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionReset-2-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-0-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-0-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-0-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-0-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-1-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-1-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-1-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-1-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-2-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-2-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-2-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-2-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-3-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-3-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-3-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-3-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-4-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-4-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-4-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-4-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-5-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-5-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-5-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-5-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-6-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-6-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-6-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/encryptionSettings-6-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user