Reset Recovery Key Screen (#2648)

Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com>
This commit is contained in:
Mauro 2024-04-04 18:14:22 +02:00 committed by GitHub
parent 97d9189077
commit 023b7fc608
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 458 additions and 37 deletions

View File

@ -253,6 +253,7 @@
3C549A0BF39F8A854D45D9FD /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 0DD568A494247444A4B56031 /* Kingfisher */; };
3C73442084BF8A6939F0F80B /* AnalyticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5445FCE0CE15E634FDC1A2E2 /* AnalyticsService.swift */; };
3CE4C5071B6D2576E2473989 /* OrderedSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62B07B296D7A9D2F09120853 /* OrderedSet.swift */; };
3D2E3E1B01D4389DCBE8F0E8 /* ResetRecoveryKeyScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E995E3F726488F8938F0BCD1 /* ResetRecoveryKeyScreenCoordinator.swift */; };
3DA57CA0D609A6B37CA1DC2F /* BugReportService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DC38E64A5ED3FDB201029A /* BugReportService.swift */; };
3DAD62988F072607441CB7A5 /* PollFormScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F8664F1FB95AF3C4202478 /* PollFormScreenCoordinator.swift */; };
3DAF325D8AE461F7CDB282BD /* StartChatScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6861FE915C7B5466E6962BBA /* StartChatScreen.swift */; };
@ -424,6 +425,7 @@
67C05C50AD734283374605E3 /* MatrixEntityRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */; };
67D6E0700A9C1E676F6231F8 /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = AD544C0FA48DFFB080920061 /* Collections */; };
67E9926C4572C54F59FCA91A /* AuthenticationFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B069D7772DDF6513E0F1B8 /* AuthenticationFlowCoordinator.swift */; };
680062C402ECB8FCAAE85A5C /* ResetRecoveryKeyScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99637028A8BD2843A35A92D4 /* ResetRecoveryKeyScreenViewModelProtocol.swift */; };
6817EAD73DC1FFD8B943B5B9 /* HomeScreenRoomTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B73587C2E3CF5998361AE516 /* HomeScreenRoomTests.swift */; };
68184EF36396424FE19A727D /* MediaLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AFCE895ECFFA53FEE64D62B /* MediaLoader.swift */; };
6832733838C57A7D3FE8FEB5 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 78A5A8DE1E2B09C978C7F3B0 /* KeychainAccess */; };
@ -643,6 +645,7 @@
9BEA56957B3AF954E7321658 /* ComposerToolbarViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E44928D844E16EE48A311FCA /* ComposerToolbarViewModelProtocol.swift */; };
9C4EC28A921486B1775D7F8C /* IdentityConfirmedScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 307702DD66E7DDCDD9214784 /* IdentityConfirmedScreen.swift */; };
9C55746D8F6A3E35CFCF4A7A /* AuthenticationStartLogo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 598F01EBD0C4CC550C644418 /* AuthenticationStartLogo.swift */; };
9C9838B68C00C980A498050C /* ResetRecoveryKeyScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DC30DEC0097B9D217493007 /* ResetRecoveryKeyScreen.swift */; };
9D2E03DB175A6AB14589076D /* AnalyticsEvents in Frameworks */ = {isa = PBXBuildFile; productRef = 2A3F7BCCB18C15B30CCA39A9 /* AnalyticsEvents */; };
9D79B94493FB32249F7E472F /* PlaceholderAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */; };
9D9690D2FD4CD26FF670620F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75EF87651B00A176AB08E97 /* AppDelegate.swift */; };
@ -913,6 +916,7 @@
DFCA89C4EC2A5332ED6B441F /* DataProtectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4959CECEC984B3995616F427 /* DataProtectionManager.swift */; };
DFD5AA8688A34C72D48AF3B1 /* StaticLocationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5311C989EC15B4C2D699025 /* StaticLocationScreenViewModel.swift */; };
DFF7D6A6C26DDD40D00AE579 /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = F012CB5EE3F2B67359F6CC52 /* target.yml */; };
E07ABB9FD1C87EBBDDE81DC5 /* ResetRecoveryKeyScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C63616A8920EC6948B31EA1B /* ResetRecoveryKeyScreenViewModel.swift */; };
E0A4DCA633D174EB43AD599F /* BackgroundTaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CA028DCD4157F9A1F999827 /* BackgroundTaskProtocol.swift */; };
E0B6A569AC3E81D233B43D60 /* SettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E625B0EB2F86B37C14EF7E6 /* SettingsScreenViewModel.swift */; };
E0FB26262689F04D66A949D7 /* TestablePreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E227F34BE43B08E098796E /* TestablePreview.swift */; };
@ -967,6 +971,7 @@
EBDB339A7C127F068B6E52E5 /* VoiceMessageRecordingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A634D8DD1E10D858CF7995D /* VoiceMessageRecordingView.swift */; };
EBE13FAB4E29738AC41BD3E5 /* InfoPlistReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A580295A56B55A856CC4084 /* InfoPlistReader.swift */; };
EC280623A42904341363EAAF /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = A20EA00CCB9DBE0FFB17DD09 /* Collections */; };
EC65AF0D9240A248DC9917BB /* ResetRecoveryKeyScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76911C322BC4CD117A8A0AF1 /* ResetRecoveryKeyScreenModels.swift */; };
ECA636DAF071C611FDC2BB57 /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; };
ED564C8C7C43CF5F67000368 /* PlatformViewVersionPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D26813CCE39221FE30BF22CD /* PlatformViewVersionPredicate.swift */; };
ED90A59F068FD0CA27E602ED /* UserProfileListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10DA51DBC8C7E1460DBCCBD /* UserProfileListRow.swift */; };
@ -1521,6 +1526,7 @@
6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestsScreenIdentifier.swift; sourceTree = "<group>"; };
6D0A27607AB09784C8501B5C /* DeveloperOptionsScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenViewModelTests.swift; sourceTree = "<group>"; };
6D4777F0142E330A75C46FE4 /* SessionVerificationUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationUITests.swift; sourceTree = "<group>"; };
6DC30DEC0097B9D217493007 /* ResetRecoveryKeyScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetRecoveryKeyScreen.swift; sourceTree = "<group>"; };
6DF438EAFC732D2D95D34BF6 /* StartChatViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatViewModelTests.swift; sourceTree = "<group>"; };
6DF81D7F2A6BA9DE3F6F8D9D /* InvitesScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenViewModel.swift; sourceTree = "<group>"; };
6DFCAA239095A116976E32C4 /* BackgroundTaskTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskTests.swift; sourceTree = "<group>"; };
@ -1557,6 +1563,7 @@
7509AB72755DCC4B4E721B36 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/SAS.strings; sourceTree = "<group>"; };
752A0EB49BF5BCEA37EDF7A3 /* Signposter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signposter.swift; sourceTree = "<group>"; };
76310030C831D4610A705603 /* URLComponentsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLComponentsTests.swift; sourceTree = "<group>"; };
76911C322BC4CD117A8A0AF1 /* ResetRecoveryKeyScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetRecoveryKeyScreenModels.swift; sourceTree = "<group>"; };
772334731A8BF8E6D90B194D /* LocationRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRoomTimelineView.swift; sourceTree = "<group>"; };
7773CBFDBD458E0B7E270507 /* PillView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillView.swift; sourceTree = "<group>"; };
780258F1B9D15E30549FF4BE /* NotificationSettingsEditScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreenViewModel.swift; sourceTree = "<group>"; };
@ -1685,6 +1692,7 @@
9873076F224E4CE09D8BD47D /* TemplateScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenUITests.swift; sourceTree = "<group>"; };
989D7380D9C86B3A10D30B13 /* AppLockSetupPINScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenViewModelTests.swift; sourceTree = "<group>"; };
98A2932515EA11D3DD8A3506 /* TimelineItemBubbledStylerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemBubbledStylerView.swift; sourceTree = "<group>"; };
99637028A8BD2843A35A92D4 /* ResetRecoveryKeyScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetRecoveryKeyScreenViewModelProtocol.swift; sourceTree = "<group>"; };
9A008E57D52B07B78DFAD1BB /* RoomFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomFlowCoordinator.swift; sourceTree = "<group>"; };
9A22A05E472533ED3C5A31B3 /* NavigationModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationModule.swift; sourceTree = "<group>"; };
9A2AC7BE17C05CF7D2A22338 /* landscape_test_video.mov */ = {isa = PBXFileReference; lastKnownFileType = video.quicktime; path = landscape_test_video.mov; sourceTree = "<group>"; };
@ -1863,6 +1871,7 @@
C5F06F2F09B2EDD067DC2174 /* NotificationSettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsScreen.swift; sourceTree = "<group>"; };
C616D90B1E2F033CAA325439 /* StaticLocationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLocationScreenViewModelProtocol.swift; sourceTree = "<group>"; };
C618CA2B6C8758B06C88013C /* CreateRoomCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomCoordinator.swift; sourceTree = "<group>"; };
C63616A8920EC6948B31EA1B /* ResetRecoveryKeyScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetRecoveryKeyScreenViewModel.swift; sourceTree = "<group>"; };
C687844F60BFF532D49A994C /* AnalyticsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsTests.swift; sourceTree = "<group>"; };
C6A9F49B3EE59147AF2F70BB /* SeparatorRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorRoomTimelineItem.swift; sourceTree = "<group>"; };
C6FEA87EA3752203065ECE27 /* BugReportUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportUITests.swift; sourceTree = "<group>"; };
@ -2008,6 +2017,7 @@
E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIFont+AttributedStringBuilder.m"; sourceTree = "<group>"; };
E96ED747FF90332EA1333C22 /* RoomTimelineItemFixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFixtures.swift; sourceTree = "<group>"; };
E992D7B8BE54B2AB454613AF /* XCUIElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCUIElement.swift; sourceTree = "<group>"; };
E995E3F726488F8938F0BCD1 /* ResetRecoveryKeyScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetRecoveryKeyScreenCoordinator.swift; sourceTree = "<group>"; };
E9A3D3CFA199FA7897364547 /* CallInviteRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallInviteRoomTimelineItem.swift; sourceTree = "<group>"; };
E9D059BFE329BE09B6D96A9F /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ro; path = ro.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
EA4D639E27D5882A6A71AECF /* GlobalSearchScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSearchScreenViewModelTests.swift; sourceTree = "<group>"; };
@ -2199,6 +2209,14 @@
path = VoiceMessage;
sourceTree = "<group>";
};
01DF58ADE41080A419AD424A /* View */ = {
isa = PBXGroup;
children = (
6DC30DEC0097B9D217493007 /* ResetRecoveryKeyScreen.swift */,
);
path = View;
sourceTree = "<group>";
};
0210F4932B59277E2EEEF7BC /* RoomNotificationSettingsScreen */ = {
isa = PBXGroup;
children = (
@ -2468,6 +2486,7 @@
2565414373E6F68005966B8E /* SecureBackup */ = {
isa = PBXGroup;
children = (
EE703B39AC7A2862CFDCA12C /* ResetKeyScreen */,
B1FD4FD6CEB987AE274AEEE5 /* SecureBackupKeyBackupScreen */,
63E514D74481A3D9556DFFC3 /* SecureBackupLogoutConfirmationScreen */,
6E8F16377AD462BBD4951271 /* SecureBackupRecoveryKeyScreen */,
@ -4909,6 +4928,18 @@
path = StartChatScreen;
sourceTree = "<group>";
};
EE703B39AC7A2862CFDCA12C /* ResetKeyScreen */ = {
isa = PBXGroup;
children = (
E995E3F726488F8938F0BCD1 /* ResetRecoveryKeyScreenCoordinator.swift */,
76911C322BC4CD117A8A0AF1 /* ResetRecoveryKeyScreenModels.swift */,
C63616A8920EC6948B31EA1B /* ResetRecoveryKeyScreenViewModel.swift */,
99637028A8BD2843A35A92D4 /* ResetRecoveryKeyScreenViewModelProtocol.swift */,
01DF58ADE41080A419AD424A /* View */,
);
path = ResetKeyScreen;
sourceTree = "<group>";
};
EFD4F7FCAAAB3EF45EE7A067 /* BlockedUsersScreen */ = {
isa = PBXGroup;
children = (
@ -6117,6 +6148,11 @@
46A261AA898344A1F3C406B1 /* ReportContentScreenModels.swift in Sources */,
42A5A42ACF063EEE6B1980D2 /* ReportContentScreenViewModel.swift in Sources */,
8285FF4B2C2331758C437FF7 /* ReportContentScreenViewModelProtocol.swift in Sources */,
9C9838B68C00C980A498050C /* ResetRecoveryKeyScreen.swift in Sources */,
3D2E3E1B01D4389DCBE8F0E8 /* ResetRecoveryKeyScreenCoordinator.swift in Sources */,
EC65AF0D9240A248DC9917BB /* ResetRecoveryKeyScreenModels.swift in Sources */,
E07ABB9FD1C87EBBDDE81DC5 /* ResetRecoveryKeyScreenViewModel.swift in Sources */,
680062C402ECB8FCAAE85A5C /* ResetRecoveryKeyScreenViewModelProtocol.swift in Sources */,
A494741843F087881299ACF0 /* RestorationToken.swift in Sources */,
1772AFA97DDA51CF1B293A78 /* RoomAttachmentPicker.swift in Sources */,
F8C87130FD999F7F1076208C /* RoomChangePermissionsScreen.swift in Sources */,

View File

@ -156,7 +156,7 @@
"common_privacy_policy" = "Privacy policy";
"common_reaction" = "Reaction";
"common_reactions" = "Reactions";
"common_recovery_key" = "Recovery key";
"common_recovery_key" = "Recovery key or passcode";
"common_refreshing" = "Refreshing…";
"common_replying_to" = "Replying to %1$@";
"common_report_a_bug" = "Report a bug";
@ -374,6 +374,13 @@
"screen_chat_backup_recovery_action_confirm_description" = "Your chat backup is currently out of sync.";
"screen_chat_backup_recovery_action_setup" = "Set up recovery";
"screen_chat_backup_recovery_action_setup_description" = "Get access to your encrypted messages if you lose all your devices or are signed out of %1$@ everywhere.";
"screen_create_new_recovery_key_list_item_1" = "Open Element in a desktop device";
"screen_create_new_recovery_key_list_item_2" = "Sign into your account again";
"screen_create_new_recovery_key_list_item_3" = "When asked to verify your device, select %1$@";
"screen_create_new_recovery_key_list_item_3_reset_all" = "“Reset all”";
"screen_create_new_recovery_key_list_item_4" = "Follow the instructions to create a new recovery key";
"screen_create_new_recovery_key_list_item_5" = "Save your new recovery key in a password manager or encrypted note";
"screen_create_new_recovery_key_title" = "Reset the encryption for your account using another device";
"screen_create_poll_add_option_btn" = "Add option";
"screen_create_poll_anonymous_desc" = "Show results only after poll ends";
"screen_create_poll_anonymous_headline" = "Hide votes";
@ -398,6 +405,7 @@
"screen_edit_profile_error_title" = "Unable to update profile";
"screen_edit_profile_title" = "Edit profile";
"screen_edit_profile_updating_details" = "Updating profile…";
"screen_identity_confirmation_create_new_recovery_key" = "Create a new recovery key";
"screen_identity_confirmation_subtitle" = "Verify this device to set up secure messaging.";
"screen_identity_confirmation_title" = "Confirm that it's you";
"screen_identity_confirmed_subtitle" = "Now you can read or send messages securely, and anyone you chat with can also trust this device.";
@ -473,13 +481,14 @@
"screen_recovery_key_change_generate_key_description" = "Make sure you can store your recovery key somewhere safe";
"screen_recovery_key_change_success" = "Recovery key changed";
"screen_recovery_key_change_title" = "Change recovery key?";
"screen_recovery_key_confirm_description" = "Enter your recovery key to confirm access to your chat backup.";
"screen_recovery_key_confirm_create_new_recovery_key" = "Create new recovery key";
"screen_recovery_key_confirm_description" = "Make sure nobody can see this screen!";
"screen_recovery_key_confirm_error_content" = "Please try again to confirm access to your chat backup.";
"screen_recovery_key_confirm_error_title" = "Incorrect recovery key";
"screen_recovery_key_confirm_key_description" = "Enter the 48 character code.";
"screen_recovery_key_confirm_key_description" = "If you have a recovery passphrase or secret passphrase/key, this will work too.";
"screen_recovery_key_confirm_key_placeholder" = "Enter…";
"screen_recovery_key_confirm_success" = "Recovery key confirmed";
"screen_recovery_key_confirm_title" = "Enter your recovery key";
"screen_recovery_key_confirm_title" = "Enter your recovery key or passcode";
"screen_recovery_key_copied_to_clipboard" = "Copied recovery key";
"screen_recovery_key_generating_key" = "Generating…";
"screen_recovery_key_save_action" = "Save recovery key";

View File

@ -258,6 +258,8 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
case .recoveryFixed:
appSettings.hasRunIdentityConfirmationOnboarding = true
stateMachine.tryEvent(.next)
case .showResetKeyInfo:
presentResetRecoveryKeyScreen()
default:
fatalError("Other flows shouldn't be possible")
}
@ -267,6 +269,18 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
presentCoordinator(coordinator)
}
private func presentResetRecoveryKeyScreen() {
let coordinator = ResetRecoveryKeyScreenCoordinator()
coordinator.actionsPublisher.sink { [weak self] action in
switch action {
case .cancel:
self?.navigationStackCoordinator.setSheetCoordinator(nil)
}
}
.store(in: &cancellables)
navigationStackCoordinator.setSheetCoordinator(coordinator)
}
private func presentIdentityConfirmedScreen() {
let coordinator = IdentityConfirmedScreenCoordinator(parameters: .init())
coordinator.actionsPublisher

View File

@ -364,7 +364,7 @@ internal enum L10n {
internal static var commonReaction: String { return L10n.tr("Localizable", "common_reaction") }
/// Reactions
internal static var commonReactions: String { return L10n.tr("Localizable", "common_reactions") }
/// Recovery key
/// Recovery key or passcode
internal static var commonRecoveryKey: String { return L10n.tr("Localizable", "common_recovery_key") }
/// Refreshing
internal static var commonRefreshing: String { return L10n.tr("Localizable", "common_refreshing") }
@ -919,6 +919,22 @@ internal enum L10n {
internal static func screenChatBackupRecoveryActionSetupDescription(_ p1: Any) -> String {
return L10n.tr("Localizable", "screen_chat_backup_recovery_action_setup_description", String(describing: p1))
}
/// Open Element in a desktop device
internal static var screenCreateNewRecoveryKeyListItem1: String { return L10n.tr("Localizable", "screen_create_new_recovery_key_list_item_1") }
/// Sign into your account again
internal static var screenCreateNewRecoveryKeyListItem2: String { return L10n.tr("Localizable", "screen_create_new_recovery_key_list_item_2") }
/// When asked to verify your device, select %1$@
internal static func screenCreateNewRecoveryKeyListItem3(_ p1: Any) -> String {
return L10n.tr("Localizable", "screen_create_new_recovery_key_list_item_3", String(describing: p1))
}
/// Reset all
internal static var screenCreateNewRecoveryKeyListItem3ResetAll: String { return L10n.tr("Localizable", "screen_create_new_recovery_key_list_item_3_reset_all") }
/// Follow the instructions to create a new recovery key
internal static var screenCreateNewRecoveryKeyListItem4: String { return L10n.tr("Localizable", "screen_create_new_recovery_key_list_item_4") }
/// Save your new recovery key in a password manager or encrypted note
internal static var screenCreateNewRecoveryKeyListItem5: String { return L10n.tr("Localizable", "screen_create_new_recovery_key_list_item_5") }
/// Reset the encryption for your account using another device
internal static var screenCreateNewRecoveryKeyTitle: String { return L10n.tr("Localizable", "screen_create_new_recovery_key_title") }
/// Add option
internal static var screenCreatePollAddOptionBtn: String { return L10n.tr("Localizable", "screen_create_poll_add_option_btn") }
/// Show results only after poll ends
@ -991,6 +1007,8 @@ internal enum L10n {
internal static var screenEditProfileTitle: String { return L10n.tr("Localizable", "screen_edit_profile_title") }
/// Updating profile
internal static var screenEditProfileUpdatingDetails: String { return L10n.tr("Localizable", "screen_edit_profile_updating_details") }
/// Create a new recovery key
internal static var screenIdentityConfirmationCreateNewRecoveryKey: String { return L10n.tr("Localizable", "screen_identity_confirmation_create_new_recovery_key") }
/// Verify this device to set up secure messaging.
internal static var screenIdentityConfirmationSubtitle: String { return L10n.tr("Localizable", "screen_identity_confirmation_subtitle") }
/// Confirm that it's you
@ -1159,19 +1177,21 @@ internal enum L10n {
internal static var screenRecoveryKeyChangeSuccess: String { return L10n.tr("Localizable", "screen_recovery_key_change_success") }
/// Change recovery key?
internal static var screenRecoveryKeyChangeTitle: String { return L10n.tr("Localizable", "screen_recovery_key_change_title") }
/// Enter your recovery key to confirm access to your chat backup.
/// Create new recovery key
internal static var screenRecoveryKeyConfirmCreateNewRecoveryKey: String { return L10n.tr("Localizable", "screen_recovery_key_confirm_create_new_recovery_key") }
/// Make sure nobody can see this screen!
internal static var screenRecoveryKeyConfirmDescription: String { return L10n.tr("Localizable", "screen_recovery_key_confirm_description") }
/// Please try again to confirm access to your chat backup.
internal static var screenRecoveryKeyConfirmErrorContent: String { return L10n.tr("Localizable", "screen_recovery_key_confirm_error_content") }
/// Incorrect recovery key
internal static var screenRecoveryKeyConfirmErrorTitle: String { return L10n.tr("Localizable", "screen_recovery_key_confirm_error_title") }
/// Enter the 48 character code.
/// If you have a recovery passphrase or secret passphrase/key, this will work too.
internal static var screenRecoveryKeyConfirmKeyDescription: String { return L10n.tr("Localizable", "screen_recovery_key_confirm_key_description") }
/// Enter
internal static var screenRecoveryKeyConfirmKeyPlaceholder: String { return L10n.tr("Localizable", "screen_recovery_key_confirm_key_placeholder") }
/// Recovery key confirmed
internal static var screenRecoveryKeyConfirmSuccess: String { return L10n.tr("Localizable", "screen_recovery_key_confirm_success") }
/// Enter your recovery key
/// Enter your recovery key or passcode
internal static var screenRecoveryKeyConfirmTitle: String { return L10n.tr("Localizable", "screen_recovery_key_confirm_title") }
/// Copied recovery key
internal static var screenRecoveryKeyCopiedToClipboard: String { return L10n.tr("Localizable", "screen_recovery_key_copied_to_clipboard") }

View File

@ -24,6 +24,7 @@ struct HeroImage: View {
enum Style {
case normal
case positive
case subtle
var foregroundColor: Color {
switch self {
@ -31,6 +32,8 @@ struct HeroImage: View {
return .compound.iconPrimary
case .positive:
return .compound.iconSuccessPrimary
case .subtle:
return .compound.iconSecondary
}
}
@ -40,6 +43,8 @@ struct HeroImage: View {
return .compound.bgSubtleSecondary
case .positive:
return .compound.bgSuccessSubtle
case .subtle:
return .compound.bgSubtlePrimary
}
}
}

View File

@ -0,0 +1,56 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// periphery:ignore:all - this is just a resetKey remove this comment once generating the final file
import Combine
import SwiftUI
enum ResetRecoveryKeyScreenCoordinatorAction {
case cancel
}
final class ResetRecoveryKeyScreenCoordinator: CoordinatorProtocol {
private let viewModel: ResetRecoveryKeyScreenViewModelProtocol
private var cancellables = Set<AnyCancellable>()
private let actionsSubject: PassthroughSubject<ResetRecoveryKeyScreenCoordinatorAction, Never> = .init()
var actionsPublisher: AnyPublisher<ResetRecoveryKeyScreenCoordinatorAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
init() {
viewModel = ResetRecoveryKeyScreenViewModel()
}
func start() {
viewModel.actionsPublisher.sink { [weak self] action in
MXLog.info("Coordinator: received view model action: \(action)")
guard let self else { return }
switch action {
case .cancel:
self.actionsSubject.send(.cancel)
}
}
.store(in: &cancellables)
}
func toPresentable() -> AnyView {
AnyView(ResetRecoveryKeyScreen(context: viewModel.context))
}
}

View File

@ -0,0 +1,36 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
enum ResetRecoveryKeyScreenViewModelAction {
case cancel
}
struct ResetRecoveryKeyScreenViewState: BindableState {
let listItem3AttributedText = {
let boldPlaceholder = "{bold}"
var finalString = AttributedString(L10n.screenCreateNewRecoveryKeyListItem3(boldPlaceholder))
var boldString = AttributedString(L10n.screenCreateNewRecoveryKeyListItem3ResetAll)
boldString.bold()
finalString.replace(boldPlaceholder, with: boldString)
return finalString
}()
}
enum ResetRecoveryKeyScreenViewAction {
case cancel
}

View File

@ -0,0 +1,40 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Combine
import SwiftUI
typealias ResetRecoveryKeyScreenViewModelType = StateStoreViewModel<ResetRecoveryKeyScreenViewState, ResetRecoveryKeyScreenViewAction>
class ResetRecoveryKeyScreenViewModel: ResetRecoveryKeyScreenViewModelType, ResetRecoveryKeyScreenViewModelProtocol {
private let actionsSubject: PassthroughSubject<ResetRecoveryKeyScreenViewModelAction, Never> = .init()
var actionsPublisher: AnyPublisher<ResetRecoveryKeyScreenViewModelAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
init() {
super.init(initialViewState: ResetRecoveryKeyScreenViewState())
}
// MARK: - Public
override func process(viewAction: ResetRecoveryKeyScreenViewAction) {
switch viewAction {
case .cancel:
actionsSubject.send(.cancel)
}
}
}

View File

@ -0,0 +1,23 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Combine
@MainActor
protocol ResetRecoveryKeyScreenViewModelProtocol {
var actionsPublisher: AnyPublisher<ResetRecoveryKeyScreenViewModelAction, Never> { get }
var context: ResetRecoveryKeyScreenViewModelType.Context { get }
}

View File

@ -0,0 +1,134 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Compound
import SwiftUI
struct ResetRecoveryKeyScreen: View {
@ObservedObject var context: ResetRecoveryKeyScreenViewModel.Context
var body: some View {
NavigationStack {
FullscreenDialog {
mainContent
} bottomContent: {
EmptyView()
}
.toolbar { toolbar }
.toolbar(.visible, for: .navigationBar)
.background()
.environment(\.backgroundStyle, AnyShapeStyle(Color.compound.bgSubtleSecondary))
.interactiveDismissDisabled()
}
}
private var mainContent: some View {
VStack(spacing: 40) {
header
list
}
}
private var header: some View {
VStack(spacing: 16) {
HeroImage(icon: \.computer, style: .subtle)
Text(L10n.screenCreateNewRecoveryKeyTitle)
.foregroundColor(.compound.textPrimary)
.font(.compound.headingMDBold)
.multilineTextAlignment(.center)
}
}
private var list: some View {
VStack(alignment: .leading, spacing: 24) {
Label {
Text(L10n.screenCreateNewRecoveryKeyListItem1)
} icon: {
Image(systemSymbol: ._1Circle)
.imageScale(.large)
.fontWeight(.light)
.foregroundColor(.compound.textPlaceholder)
}
.foregroundColor(.compound.textPrimary)
.font(.compound.bodyMD)
Label {
Text(L10n.screenCreateNewRecoveryKeyListItem2)
} icon: {
Image(systemSymbol: ._2Circle)
.imageScale(.large)
.fontWeight(.light)
.foregroundColor(.compound.textPlaceholder)
}
.foregroundColor(.compound.textPrimary)
.font(.compound.bodyMD)
Label {
Text(context.viewState.listItem3AttributedText)
} icon: {
Image(systemSymbol: ._3Circle)
.imageScale(.large)
.fontWeight(.light)
.foregroundColor(.compound.textPlaceholder)
}
.foregroundColor(.compound.textPrimary)
.font(.compound.bodyMD)
Label {
Text(L10n.screenCreateNewRecoveryKeyListItem4)
} icon: {
Image(systemSymbol: ._4Circle)
.imageScale(.large)
.fontWeight(.light)
.foregroundColor(.compound.textPlaceholder)
}
.foregroundColor(.compound.textPrimary)
.font(.compound.bodyMD)
Label {
Text(L10n.screenCreateNewRecoveryKeyListItem5)
} icon: {
Image(systemSymbol: ._5Circle)
.imageScale(.large)
.fontWeight(.light)
.foregroundColor(.compound.textPlaceholder)
}
.foregroundColor(.compound.textPrimary)
.font(.compound.bodyMD)
}
}
@ToolbarContentBuilder
private var toolbar: some ToolbarContent {
ToolbarItem(placement: .cancellationAction) {
Button(L10n.actionCancel) {
context.send(viewAction: .cancel)
}
}
}
}
// MARK: - Previews
struct ResetRecoveryKeyScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = ResetRecoveryKeyScreenViewModel()
static var previews: some View {
NavigationStack {
ResetRecoveryKeyScreen(context: viewModel.context)
}
}
}

View File

@ -28,6 +28,7 @@ enum SecureBackupRecoveryKeyScreenCoordinatorAction {
case recoverySetUp
case recoveryChanged
case recoveryFixed
case showResetKeyInfo
}
final class SecureBackupRecoveryKeyScreenCoordinator: CoordinatorProtocol {
@ -62,6 +63,8 @@ final class SecureBackupRecoveryKeyScreenCoordinator: CoordinatorProtocol {
case .fixRecovery:
self.actionsSubject.send(.recoveryFixed)
}
case .showResetKeyInfo:
self.actionsSubject.send(.showResetKeyInfo)
}
}
.store(in: &cancellables)

View File

@ -19,6 +19,7 @@ import Foundation
enum SecureBackupRecoveryKeyScreenViewModelAction {
case done(mode: SecureBackupRecoveryKeyScreenViewMode)
case cancel
case showResetKeyInfo
}
enum SecureBackupRecoveryKeyScreenViewMode {
@ -82,6 +83,7 @@ enum SecureBackupRecoveryKeyScreenViewAction {
case copyKey
case keySaved
case confirmKey
case resetKey
case done
case cancel
}

View File

@ -100,6 +100,8 @@ class SecureBackupRecoveryKeyScreenViewModel: SecureBackupRecoveryKeyScreenViewM
guard let self else { return }
actionsSubject.send(.done(mode: context.viewState.mode))
}))
case .resetKey:
actionsSubject.send(.showResetKeyInfo)
}
}
}

View File

@ -64,6 +64,12 @@ struct SecureBackupRecoveryKeyScreen: View {
case .setupRecovery, .changeRecovery:
recoveryCreatedActionButtons
case .fixRecovery:
incompleteVerificationActionButtons
}
}
private var incompleteVerificationActionButtons: some View {
VStack(spacing: 16) {
Button {
context.send(viewAction: .confirmKey)
} label: {
@ -71,6 +77,14 @@ struct SecureBackupRecoveryKeyScreen: View {
}
.buttonStyle(.compound(.primary))
.disabled(context.confirmationRecoveryKey.isEmpty)
Button {
context.send(viewAction: .resetKey)
} label: {
Text(L10n.screenIdentityConfirmationCreateNewRecoveryKey)
.padding(.vertical, 14)
}
.buttonStyle(.compound(.plain))
}
}
@ -127,7 +141,7 @@ struct SecureBackupRecoveryKeyScreen: View {
VStack(alignment: .leading, spacing: 8) {
Text(L10n.commonRecoveryKey)
.foregroundColor(.compound.textPrimary)
.font(.compound.bodySM)
.font(.compound.bodySMSemibold)
Group {
if context.viewState.recoveryKey == nil {
@ -180,7 +194,7 @@ struct SecureBackupRecoveryKeyScreen: View {
VStack(alignment: .leading, spacing: 8) {
Text(L10n.commonRecoveryKey)
.foregroundColor(.compound.textPrimary)
.font(.compound.bodySM)
.font(.compound.bodySMSemibold)
SecureField(L10n.screenRecoveryKeyConfirmKeyPlaceholder, text: $context.confirmationRecoveryKey)
.textContentType(.password) // Not ideal but stops random suggestions

View File

@ -51,18 +51,20 @@ final class SecureBackupScreenCoordinator: CoordinatorProtocol {
recoveryKeyCoordinator.actions.sink { [weak self] action in
guard let self else { return }
parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
switch action {
case .cancel:
break
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 .showResetKeyInfo:
showResetRecoveryKeyScreen(navigationStackCoordinator: navigationStackCoordinator)
}
}
.store(in: &cancellables)
@ -105,4 +107,16 @@ final class SecureBackupScreenCoordinator: CoordinatorProtocol {
iconName: "checkmark",
persistent: false))
}
private func showResetRecoveryKeyScreen(navigationStackCoordinator: NavigationStackCoordinator) {
let coordinator = ResetRecoveryKeyScreenCoordinator()
coordinator.actionsPublisher.sink { action in
switch action {
case .cancel:
navigationStackCoordinator.setSheetCoordinator(nil)
}
}
.store(in: &cancellables)
navigationStackCoordinator.setSheetCoordinator(coordinator)
}
}

Binary file not shown.

Binary file not shown.

1
changelog.d/2647.feature Normal file
View File

@ -0,0 +1 @@
Added a view that explains how to reset your recovery key in case the user forgot it.