From acfcb259352bf1927aa11be469c0931896115bef Mon Sep 17 00:00:00 2001 From: Fridtjof Mund Date: Wed, 27 May 2020 14:28:13 +0000 Subject: [PATCH 01/22] Translated using Weblate (German) Currently translated at 93.2% (918 of 985 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index c76ecf094..7928e89b0 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -961,7 +961,7 @@ "key_verification_tile_request_status_waiting" = "Warten…"; "key_verification_tile_request_status_expired" = "Abgelaufen"; "key_verification_tile_request_status_cancelled_by_me" = "Du hast abgebrochen"; -"key_verification_tile_request_status_cancelled" = "%s brach ab"; +"key_verification_tile_request_status_cancelled" = "%@ brach ab"; "key_verification_tile_request_status_accepted" = "Du hast akzeptiert"; "key_verification_tile_request_incoming_approval_accept" = "Akzeptieren"; "key_verification_tile_request_incoming_approval_decline" = "Ablehnen"; From 9543956c7fd6e18c5ef9b4aad001a3b8ca2a4c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Fri, 12 Jun 2020 07:31:31 +0000 Subject: [PATCH 02/22] Translated using Weblate (French) Currently translated at 100.0% (1007 of 1007 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/fr/ --- Riot/Assets/fr.lproj/Vector.strings | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index aef9b9b74..d4a3c8815 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -1101,3 +1101,25 @@ "key_verification_manually_verify_device_additional_information" = "S’ils ne correspondent pas, la sécurité de vos communications est peut-être compromise."; "key_verification_manually_verify_device_validate_action" = "Vérifier"; "user_verification_session_details_verify_action_current_user_manually" = "Vérifier manuellement avec un texte"; +"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Utiliser la clé de récupération"; +"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Utiliser la phrase secrète ou la clé de récupération"; +"device_verification_self_verify_wait_recover_secrets_additional_information" = "Si vous ne pouvez pas accéder à une session existante"; +"secrets_recovery_with_passphrase_title" = "Phrase secrète de récupération"; +"secrets_recovery_with_passphrase_information_default" = "Accédez à l’historique de vos messages sécurisés et à votre identité de signature croisée pour vérifier d’autres sessions en saisissant votre phrase secrète de récupération."; +"secrets_recovery_with_passphrase_information_verify_device" = "Utilisez votre phrase secrète de récupération pour vérifier cet appareil."; +"secrets_recovery_with_passphrase_passphrase_title" = "Saisir"; +"secrets_recovery_with_passphrase_passphrase_placeholder" = "Saisir la phrase secrète de récupération"; +"secrets_recovery_with_passphrase_recover_action" = "Utiliser la phrase secrète"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Vous ne connaissez pas votre phrase secrète de récupération ? Vous pouvez "; +"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "utiliser votre clé de récupération"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part3" = "."; +"secrets_recovery_with_passphrase_invalid_passphrase_title" = "Impossible d’accéder au coffre secret"; +"secrets_recovery_with_passphrase_invalid_passphrase_message" = "Vérifiez que vous avez saisi la bonne phrase secrète de récupération."; +"secrets_recovery_with_key_title" = "Clé de récupération"; +"secrets_recovery_with_key_information_default" = "Accédez à l’historique de vos messages sécurisés et à votre identité de signature croisée pour vérifier d’autres sessions en renseignant votre clé de récupération."; +"secrets_recovery_with_key_information_verify_device" = "Utilisez votre clé de récupération pour vérifier cet appareil."; +"secrets_recovery_with_key_recovery_key_title" = "Saisir"; +"secrets_recovery_with_key_recovery_key_placeholder" = "Saisir la clé de récupération"; +"secrets_recovery_with_key_recover_action" = "Utiliser la clé"; +"secrets_recovery_with_key_invalid_recovery_key_title" = "Impossible d’accéder au coffre secret"; +"secrets_recovery_with_key_invalid_recovery_key_message" = "Vérifiez que vous avez saisi la bonne clé de récupération."; From 24aa5bfb3969bc64c20acb7ef75d391819e98a3f Mon Sep 17 00:00:00 2001 From: Szimszon Date: Thu, 11 Jun 2020 19:29:54 +0000 Subject: [PATCH 03/22] Translated using Weblate (Hungarian) Currently translated at 100.0% (1007 of 1007 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/hu/ --- Riot/Assets/hu.lproj/Vector.strings | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index 989308d22..2a7d78066 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -1103,3 +1103,25 @@ "key_verification_manually_verify_device_additional_information" = "Ha nem egyeznek akkor a kommunikációtok biztonsága veszélyben lehet."; "key_verification_manually_verify_device_validate_action" = "Ellenőriz"; "user_verification_session_details_verify_action_current_user_manually" = "Manuális szöveges ellenőrzés"; +"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Használd a Visszaállítási Kulcsot"; +"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Használd a Visszaállítási Jelmondatot vagy Kulcsot"; +"device_verification_self_verify_wait_recover_secrets_additional_information" = "Ha nem érsz el létező munkamenetet"; +"secrets_recovery_with_passphrase_title" = "Visszaállítási Jelmondat"; +"secrets_recovery_with_passphrase_information_default" = "A visszaállítási jelmondat megadásával hozzáférhetsz a titkosított üzeneteidhez és az eszközök közötti aláírásnál használt személyazonosságodhoz hogy más munkameneteket ellenőrizhess."; +"secrets_recovery_with_passphrase_information_verify_device" = "Használd a visszaállítási jelmondatot az eszköz ellenőrzésére."; +"secrets_recovery_with_passphrase_passphrase_title" = "Bevitel"; +"secrets_recovery_with_passphrase_passphrase_placeholder" = "Visszaállítási jelmondat megadása"; +"secrets_recovery_with_passphrase_recover_action" = "Használd a jelmondatot"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Nem emlékszel a visszaállítási jelmondatodra? Használhatod a "; +"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "visszaállítási kulcs használata"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part3" = "."; +"secrets_recovery_with_passphrase_invalid_passphrase_title" = "A biztonsági tárolót nem sikerült elérni"; +"secrets_recovery_with_passphrase_invalid_passphrase_message" = "Kérlek ellenőrizd, hogy a visszaállítási jelmondatot helyesen írtad be."; +"secrets_recovery_with_key_title" = "Visszaállítási Kulcs"; +"secrets_recovery_with_key_information_default" = "A visszaállítási kulcs megadásával hozzáférhetsz a biztonságos üzeneteidhez és az eszközök közötti hitelesítéshez használt személyazonosságodhoz, hogy más munkameneteket hitelesíthess."; +"secrets_recovery_with_key_information_verify_device" = "Használd a visszaállítási kulcsot az eszköz ellenőrzésére."; +"secrets_recovery_with_key_recovery_key_title" = "Bevitel"; +"secrets_recovery_with_key_recovery_key_placeholder" = "Visszaállítási Kulcs megadása"; +"secrets_recovery_with_key_recover_action" = "Használd a kulcsot"; +"secrets_recovery_with_key_invalid_recovery_key_title" = "A biztonsági tárolót nem sikerült elérni"; +"secrets_recovery_with_key_invalid_recovery_key_message" = "Kérlek ellenőrizd, hogy a visszaállítási kulcsot helyesen írtad be."; From 3debe0148de2d8c5e79e20487fb9b18d858a82fb Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Sat, 13 Jun 2020 04:37:06 +0000 Subject: [PATCH 04/22] Translated using Weblate (Bulgarian) Currently translated at 100.0% (1007 of 1007 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/bg/ --- Riot/Assets/bg.lproj/Vector.strings | 180 +++++++++++++++++++++++++--- 1 file changed, 164 insertions(+), 16 deletions(-) diff --git a/Riot/Assets/bg.lproj/Vector.strings b/Riot/Assets/bg.lproj/Vector.strings index b3d98ba61..56c6f4e95 100644 --- a/Riot/Assets/bg.lproj/Vector.strings +++ b/Riot/Assets/bg.lproj/Vector.strings @@ -381,7 +381,7 @@ "room_details_advanced_enable_e2e_encryption" = "Включване на шифроване (не може да се изключи в последствие!)"; "room_details_advanced_e2e_encryption_enabled" = "Шифроването е включено в тази стая"; "room_details_advanced_e2e_encryption_disabled" = "Шифроването не е включено в тази стая."; -"room_details_advanced_e2e_encryption_blacklist_unverified_devices" = "Шифроване само за потвърдени устройства"; +"room_details_advanced_e2e_encryption_blacklist_unverified_devices" = "Шифроване само за потвърдени сесии"; "room_details_advanced_e2e_encryption_prompt_message" = "Шифроване от край до край е експериментално и може да не е надеждно.\n\nВсе още не трябва да се доверявате на това, че ще защити Вашите данни.\n\nУстройства все още не могат да разшифроват история от преди присъединяването към стая.\n\nВеднъж включено, шифроването в стаята не може да бъде изключено (за сега).\n\nШифровани съобщения не са видими за клиенти, които все още не поддържат шифроване."; "room_details_fail_to_update_avatar" = "Неуспешно обновяване на снимката на стаята"; "room_details_fail_to_update_room_name" = "Неуспешно обновяване на името на стаята"; @@ -457,8 +457,8 @@ // Call "call_incoming_voice_prompt" = "Входящо гласово повикване от %@"; "call_incoming_video_prompt" = "Входящо видео повикване от %@"; -"call_incoming_voice" = "Входящо повикване..."; -"call_incoming_video" = "Входящо видео повикване..."; +"call_incoming_voice" = "Входящо повикване…"; +"call_incoming_video" = "Входящо видео повикване…"; "call_already_displayed" = "В момента тече разговор."; "call_jitsi_error" = "Неуспешно присъединяване към групов разговор."; // No VoIP support @@ -477,7 +477,7 @@ "google_analytics_use_prompt" = "Искате ли да помогнете за подобрението на %@ като анонимно изпращате съобщения за грешки и данни за използване?"; // Crypto "e2e_enabling_on_app_update" = "Riot поддържа шифроване от край до край, но за да го включите трябва да влезете в профила си отново.\n\nМоже да го направите сега или по-късно от настройките на приложението."; -"e2e_need_log_in_again" = "Трябва да влезете обратно в профила си, за да се създадат ключове за шифроване от край до край за това устройство и да се изпрати публичния ключ към Home сървъра.\nТова е еднократно. Извинете за неудобството."; +"e2e_need_log_in_again" = "Трябва да влезете обратно в профила си, за да се създадат ключове за шифроване от-край-до-край за тази сесия и да се изпрати публичния ключ към Home сървъра.\nТова е еднократно. Извинете за неудобството."; "bug_crash_report_description" = "Моля, опишете какво правихте преди да възникне грешката:"; "bug_report_logs_description" = "За да се диагностицират проблемите, логовете от този клиент ще бъдат изпратени с този доклад за грешки. Ако предпочитате да изпратите само текста по-горе, моля, премахнете отметката:"; // Widget @@ -499,9 +499,9 @@ "share_extension_failed_to_encrypt" = "Неуспешно изпращане. Проверете ключовете за шифроване за тази стая в главното приложение"; // Room key request dialog "e2e_room_key_request_title" = "Заявка за ключ за шифроване"; -"e2e_room_key_request_message_new_device" = "Добавихте ново устройство '%@', което изисква ключове за шифроване."; -"e2e_room_key_request_message" = "Вашето непотвърдено устройство '%@' изисква ключове за шифроване."; -"e2e_room_key_request_start_verification" = "Започни потвърждението..."; +"e2e_room_key_request_message_new_device" = "Добавихте нова сесия '%@', която изисква ключове за шифроване."; +"e2e_room_key_request_message" = "Вашата непотвърдена сесия '%@' изисква ключове за шифроване."; +"e2e_room_key_request_start_verification" = "Започни потвърждението…"; "e2e_room_key_request_share_without_verifying" = "Сподели без потвърждение"; "e2e_room_key_request_ignore_request" = "Игнорирай поканата"; "room_warning_about_encryption" = "Шифроване от край до край е в бета версия и може да не е надеждно.\n\nВсе още не трябва да се доверявате на това, че ще защити Вашите данни.\n\nУстройства все още не могат да разшифроват история от преди присъединяването към стая.\n\nШифровани съобщения не са видими за клиенти, които все още не поддържат шифроване."; @@ -532,10 +532,10 @@ "deactivate_account_password_alert_title" = "Деактивиране на акаунт"; "deactivate_account_password_alert_message" = "За да продължите, моля въведете паролата си"; "event_formatter_rerequest_keys_part1_link" = "Изисквай повторно ключове за шифроване"; -"event_formatter_rerequest_keys_part2" = " от другите ми устройства."; +"event_formatter_rerequest_keys_part2" = " от другите ми сесии."; // Re-request confirmation dialog "rerequest_keys_alert_title" = "Заявката е изпратена"; -"rerequest_keys_alert_message" = "Моля стартирайте Riot на друго устройство можещо да разшифрова съобщението, за да може то да изпрати ключовете до това устройство."; +"rerequest_keys_alert_message" = "Моля стартирайте Riot на друго устройство можещо да разшифрова съобщението, за да може то да изпрати ключовете до тази сесия."; "room_message_reply_to_placeholder" = "Изпрати отговор (нешифрован)…"; "encrypted_room_message_reply_to_placeholder" = "Изпрати шифрован отговор…"; "room_message_reply_to_short_placeholder" = "Изпрати отговор…"; @@ -684,27 +684,27 @@ "settings_labs_message_reaction" = "Реагирай на съобщения с емоджи"; "settings_key_backup_button_connect" = "Свържи сесията към резервно копие на ключове"; "key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Свържи устройството към резервно копие на ключове"; -"key_backup_recover_connent_banner_subtitle" = "Свържи устройството към резервно копие на ключове"; +"key_backup_recover_connent_banner_subtitle" = "Свържи сесията към резервно копие на ключове"; // MARK: - Device Verification "device_verification_title" = "Потвърждение на устройство"; "device_verification_security_advice" = "За максимална сигурност, препоръчваме да правите това на живо или използвайки друг защитен начин за комуникация"; "device_verification_cancelled" = "Отсрещната страна отказа потвърждението."; "device_verification_cancelled_by_me" = "Потвърждението беше отказано. Причина: %@"; -"device_verification_error_cannot_load_device" = "Неуспешно зареждане на информация за устройството."; +"device_verification_error_cannot_load_device" = "Неуспешно зареждане на информация за сесията."; // Mark: Incoming "device_verification_incoming_title" = "Входяща заявка за потвърждение"; -"device_verification_incoming_description_1" = "Потвърдете това устройство за да го маркирате като доверено. Доверяването на устройства на партньори Ви дава допълнително спокойствие когато използвате шифроване от-край-до-край."; -"device_verification_incoming_description_2" = "Потвърждаването на устройството ще го маркира като доверено и ще маркира Вашето като доверено при партньора."; +"device_verification_incoming_description_1" = "Потвърдете тази сесия за да я маркирате като доверена. Доверяването на сесии на партньори ви дава допълнително спокойствие когато използвате шифроване от-край-до-край."; +"device_verification_incoming_description_2" = "Потвърждаването на сесията ще я маркира като доверена и ще маркира вашето като доверена при партньора."; // MARK: Start "device_verification_start_title" = "Потвърждение чрез сравняване на кратък текст"; -"device_verification_start_wait_partner" = "Изчакване на партньора да приеме..."; +"device_verification_start_wait_partner" = "Изчакване на партньора да приеме…"; "device_verification_start_use_legacy" = "Не се показва нищо? Засега не всички клиенти поддържат интерактивно потвърждение. Използвайте стария метод за потвърждение."; "device_verification_start_verify_button" = "Започни потвърждение"; "device_verification_start_use_legacy_action" = "Потвърди по стария метод"; // MARK: Verify "device_verification_verify_title_emoji" = "Потвърдете това устройство като се уверите че следните емоджита се повяват на екрана на партньора"; "device_verification_verify_title_number" = "Потвърдете това устройство като се уверите че следните числа се повяват на екрана на партньора"; -"device_verification_verify_wait_partner" = "Изчакване на потвърждение от партньора..."; +"device_verification_verify_wait_partner" = "Изчакване на потвърждение от партньора…"; // MARK: Verified "device_verification_verified_title" = "Потвърдено!"; "device_verification_verified_description_1" = "Успешно потвърдихте това устройство."; @@ -927,7 +927,7 @@ "accessibility_checkbox_label" = "отметка"; "settings_labs_dm_key_verification" = "Потвърждение на ключ чрез директно съобщение"; "settings_labs_cross_signing" = "Кръстосано-подписване"; -"widget_picker_manage_integrations" = "Управление на интеграциите..."; +"widget_picker_manage_integrations" = "Управление на интеграциите…"; // Room widget permissions "room_widget_permission_title" = "Зареждане на приспособление"; "room_widget_permission_creator_info_title" = "Приспособлението беше добавено от:"; @@ -966,3 +966,151 @@ "room_member_power_level_short_admin" = "Админ"; "room_member_power_level_short_moderator" = "Модератор"; "room_member_power_level_short_custom" = "Собствен"; +"room_participants_action_security_status_complete_security" = "Завършете сигурността"; +"security_settings_crypto_sessions_description" = "Доверете се на сесии за да им дадете достъп до съобщенията шифровани от-край-до-край. Ако не разпознавате дадена сесия, променете паролата си и нулирайте паролата за съобщения използваща се за резервни копия на съобщенията."; +"security_settings_backup" = "РЕЗЕРВНИ КОПИЯ НА СЪОБЩЕНИЯ"; +"security_settings_crosssigning" = "КРЪСТОСАНО-ПОДПИСВАНЕ"; +"security_settings_crosssigning_info_not_bootstrapped" = "Кръстосаното-подписване все още не е настроено."; +"security_settings_crosssigning_info_exists" = "Профилът ви има идентичност за кръстосано-подписване, но все още не е доверена от тази сесия. Завършете сигурността за тази сесия."; +"security_settings_crosssigning_info_trusted" = "Кръстосаното-подписване е включено. Можете да се доверявате на други потребители и други сесии на базата на кръстосано-подписване, но не можете да подписвате от тази сесия, защото нямате частни ключове за кръстосано подписване. Завършете сигурността за тази сесия."; +"security_settings_crosssigning_info_ok" = "Кръстосаното-подписване е включено."; +"security_settings_crosssigning_bootstrap" = "Настрой кръстосано-подписване"; +"security_settings_crosssigning_reset" = "Нулирай кръстосаното-подписване"; +"security_settings_crosssigning_complete_security" = "Завърши сигурността"; +"security_settings_cryptography" = "КРИПТОГРАФИЯ"; +"security_settings_export_keys_manually" = "Експортирай ключовете ръчно"; +"security_settings_advanced" = "РАЗШИРЕНИ"; +"security_settings_blacklist_unverified_devices" = "Никога не изпращай съобщения към недоверени сесии"; +"security_settings_blacklist_unverified_devices_description" = "Потвърждаване на всички потребителски сесии за да бъдат маркирани като доверени и да се изпращат съобщения до тях."; +"security_settings_complete_security_alert_title" = "Завършване на сигурността"; +"security_settings_complete_security_alert_message" = "Първо трябва да завършите сигурността на текущата си сесия."; +"security_settings_coming_soon" = "Извинете. Това действие все още не е налично в Riot-iOS. Моля използвайте друг Matrix клиент за да го настройте. Riot-iOS ще го използва след това."; +// Manage session +"manage_session_title" = "Управление на сесията"; +"manage_session_info" = "СЕСИЙНА ИНФОРМАЦИЯ"; +"manage_session_name" = "Име на сесия"; +"manage_session_trusted" = "Доверена от вас"; +"manage_session_not_trusted" = "Недоверена"; +"manage_session_sign_out" = "Излез от тази сесия"; +// Recover from private key +"key_backup_recover_from_private_key_info" = "Възстановяване на резервно копие…"; +// MARK: - Device Verification +"key_verification_other_session_title" = "Потвърждаване на сесия"; +"key_verification_new_session_title" = "Потвърждаване на новата ви сесия"; +"key_verification_this_session_title" = "Потвърждаване на тази сесия"; +"key_verification_user_title" = "Потвърждение"; +"device_verification_security_advice_emoji" = "Сравнете уникалните емоджита, подсигурявайки, че се появяват в същата последователност."; +"device_verification_security_advice_number" = "Сравнете числата, подсигурявайки, че се появяват в същата последователност."; +// New login +"device_verification_self_verify_alert_title" = "Нов вход. Вие ли бяхте?"; +"device_verification_self_verify_alert_message" = "Потвърдете входа достъпващ профила ви: %@"; +"device_verification_self_verify_alert_validate_action" = "Потвърди"; +"device_verification_self_verify_start_verify_action" = "Започни потвърждение"; +"device_verification_self_verify_start_information" = "Използвайте тази сесия за да потвърдите новата, давайки й достъп до шифрованите съобщения."; +"device_verification_self_verify_start_waiting" = "Изчакване…"; +"key_verification_self_verify_current_session_alert_title" = "Потвърждение на тази сесия"; +"key_verification_self_verify_current_session_alert_message" = "Други потребители може да не й се доверяват."; +"key_verification_self_verify_current_session_alert_validate_action" = "Потвърди"; +"key_verification_self_verify_unverified_sessions_alert_title" = "Преглед на входовете"; +"key_verification_self_verify_unverified_sessions_alert_message" = "Потвърдете всичките си сесии за да подсигурите че профила и съобщенията ви са защитени."; +"key_verification_self_verify_unverified_sessions_alert_validate_action" = "Прегледай"; +"device_verification_self_verify_wait_title" = "Завършване на сигурността"; +"device_verification_self_verify_wait_new_sign_in_title" = "Потвърждение на вход"; +"device_verification_self_verify_wait_information" = "Потвърдете тази сесия от някоя от другите ви сесии, така че да й дадете достъп до шифрованите съобщения.\n\nИзползвайте най-новия Riot на другите си устройства:"; +"device_verification_self_verify_wait_additional_information" = "или друг Matrix клиент поддържащ кръстосано-подписване"; +"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Използвай ключ за възстановяване"; +"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Използвай парола или ключ за възстановяване"; +"device_verification_self_verify_wait_recover_secrets_additional_information" = "Ако не можете да достъпите съществуваща сесия"; +"key_verification_verify_sas_title_emoji" = "Сравнение на емоджита"; +"key_verification_verify_sas_title_number" = "Сравнение на числа"; +"key_verification_verify_sas_cancel_action" = "Не съвпадат"; +"key_verification_verify_sas_validate_action" = "Съвпадат"; +"key_verification_verify_sas_additional_information" = "За най-добра сигурност, използвайте друг доверен начин за комуникация или направете това на живо."; +"key_verification_manually_verify_device_title" = "Ръчно потвърждение чрез текстово съобщение"; +"key_verification_manually_verify_device_instruction" = "Потвърдете, чрез сравнение на следното в Настройки в другата ви сесия:"; +"key_verification_manually_verify_device_name_title" = "Име на сесия"; +"key_verification_manually_verify_device_id_title" = "Идентификатор на сесия"; +"key_verification_manually_verify_device_key_title" = "Ключ на сесия"; +"key_verification_manually_verify_device_additional_information" = "Ако не съвпадат, сигурността на комуникацията ви може да е компрометирана."; +"key_verification_manually_verify_device_validate_action" = "Потвърди"; +"key_verification_verified_new_session_title" = "Новата сесия беше потвърдена!"; +"key_verification_verified_other_session_information" = "Вече може да четете защитени съобщения в другата сесия, а други потребители ще знаят да й се доверяват."; +"key_verification_verified_new_session_information" = "Вече може да четете защитени съобщения на новото си устройство, а други потребители ще знаят да му се доверяват."; +"key_verification_verified_this_session_information" = "Вече може да четете защитени съобщения на това устройство, а други потребители ще знаят да му се доверяват."; +"key_verification_verified_user_information" = "Съобщенията с този потребител са шифровани от-край-до-край и не могат да бъдат прочетени от трети страни."; +"key_verification_bootstrap_not_setup_title" = "Грешка"; +"key_verification_bootstrap_not_setup_message" = "Първо трябва да настройте кръстосано-подписване."; +"key_verification_tile_request_incoming_title" = "Заявка за потвърждение"; +"key_verification_tile_request_outgoing_title" = "Потвърждението беше изпратено"; +"key_verification_tile_request_status_data_loading" = "Зареждане на данни…"; +"key_verification_tile_request_status_waiting" = "Изчакване…"; +"key_verification_tile_request_status_expired" = "Изтекло"; +"key_verification_tile_request_status_cancelled_by_me" = "Отказахте"; +"key_verification_tile_request_status_cancelled" = "%@ отказа"; +"key_verification_tile_request_status_accepted" = "Приехте"; +"key_verification_tile_request_incoming_approval_accept" = "Приеми"; +"key_verification_tile_request_incoming_approval_decline" = "Откажи"; +"key_verification_tile_conclusion_done_title" = "Потвърдено"; +"key_verification_tile_conclusion_warning_title" = "Недоверен вход"; +"key_verification_incoming_request_incoming_alert_message" = "%@ иска да извърши потвърждение"; +"key_verification_verify_qr_code_title" = "Потвърждение чрез сканиране"; +"key_verification_verify_qr_code_information" = "Сканирайте този код за да потвърдите по сигурен начин един друг."; +"key_verification_verify_qr_code_information_other_device" = "Сканирайте кода по-долу за да потвърдите:"; +"key_verification_verify_qr_code_emoji_information" = "Потвърждение чрез сравняване на уникални емоджита."; +"key_verification_verify_qr_code_scan_code_action" = "Сканирайте кода им"; +"key_verification_verify_qr_code_cannot_scan_action" = "Не можете да сканирате?"; +"key_verification_verify_qr_code_start_emoji_action" = "Потвърждение чрез емоджи"; +"key_verification_verify_qr_code_other_scan_my_code_title" = "Другия потребител сканира ли успешно QR кода?"; +"key_verification_verify_qr_code_scan_other_code_success_title" = "Кодът беше валидиран!"; +"key_verification_verify_qr_code_scan_other_code_success_message" = "QR кодът беше валидиран успешно."; +// Scanning +"key_verification_scan_confirmation_scanning_title" = "Почти приключихте! Изчакване на потвърждение…"; +"key_verification_scan_confirmation_scanning_user_waiting_other" = "Изчакване на %@…"; +"key_verification_scan_confirmation_scanning_device_waiting_other" = "Изчакване на другото устройство…"; +// Scanned +"key_verification_scan_confirmation_scanned_title" = "Почти приключихте!"; +"key_verification_scan_confirmation_scanned_user_information" = "Показва ли се същия щит при %@?"; +"key_verification_scan_confirmation_scanned_device_information" = "Показва ли се същия щит на другото устройство?"; +"user_verification_start_verify_action" = "Започнете потвърждение"; +"user_verification_start_information_part1" = "За допълнителна сигурност, потвърдете "; +"user_verification_start_information_part2" = " чрез проверяване на еднократен код на двете устройства."; +"user_verification_start_waiting_partner" = "Изчакване на %@…"; +"user_verification_start_additional_information" = "За да е сигурно, направете това на живо или чрез използване на друг начин за комуникация."; +"user_verification_sessions_list_user_trust_level_trusted_title" = "Доверено"; +"user_verification_sessions_list_user_trust_level_warning_title" = "Внимание"; +"user_verification_sessions_list_user_trust_level_unknown_title" = "Непознато"; +"user_verification_sessions_list_information" = "Съобщенията с този потребител в тази стая са шифровани от-край-до-край и не могат да бъдат прочетени от трети страни."; +"user_verification_sessions_list_table_title" = "Сесии"; +"user_verification_sessions_list_session_trusted" = "Доверена"; +"user_verification_sessions_list_session_untrusted" = "Недоверена"; +"user_verification_session_details_trusted_title" = "Доверена"; +"user_verification_session_details_untrusted_title" = "Недоверена"; +"user_verification_session_details_information_trusted_current_user" = "Тази сесия е доверена за сигурна комуникация, защото сте я потвърдили:"; +"user_verification_session_details_information_trusted_other_user_part1" = "Тази сесия е доверена за сигурна комуникация, защото "; +"user_verification_session_details_information_trusted_other_user_part2" = " я е потвърдил:"; +"user_verification_session_details_information_untrusted_current_user" = "Потвърдете тази сесия за да я маркирате като доверена и да й дадете достъп до шифрованите съобщения:"; +"user_verification_session_details_information_untrusted_other_user" = " влезе използвайки нова сесия:"; +"user_verification_session_details_additional_information_untrusted_other_user" = "Докато този потребител се довери на тази сесия, съобщенията изпратени от и към нея ще бъдат маркирани с предупреждения. Друго решение е да я потвърдите ръчно."; +"user_verification_session_details_additional_information_untrusted_current_user" = "Ако не сте се вписвали в тази сесия, профилът ви може да е бил компрометиран."; +"user_verification_session_details_verify_action_current_user" = "Потвърди интерактивно"; +"user_verification_session_details_verify_action_current_user_manually" = "Потвърди ръчно чрез текстово съобщение"; +"user_verification_session_details_verify_action_other_user" = "Потвърди ръчно"; +"secrets_recovery_with_passphrase_title" = "Парола за възстановяване"; +"secrets_recovery_with_passphrase_information_default" = "Достъпете защитената история на съобщенията и идентичността си за кръстосано-подписване (за потвърждение на други сесии), чрез въвеждане на паролата за възстановяване."; +"secrets_recovery_with_passphrase_information_verify_device" = "Използвайте паролата за възстановяване за да потвърдите това устройство."; +"secrets_recovery_with_passphrase_passphrase_title" = "Въвеждане"; +"secrets_recovery_with_passphrase_passphrase_placeholder" = "Въведете паролата за възстановяване"; +"secrets_recovery_with_passphrase_recover_action" = "Използвай парола"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Не знаете паролата си за възстановяване? Можете да "; +"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "използвате ключа за възстановяване"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part3" = "."; +"secrets_recovery_with_passphrase_invalid_passphrase_title" = "Неуспешен достъп до секретното складиране"; +"secrets_recovery_with_passphrase_invalid_passphrase_message" = "Потвърдете, че сте въвели правилната парола за възстановяване."; +"secrets_recovery_with_key_title" = "Ключ за възстановяване"; +"secrets_recovery_with_key_information_default" = "Достъпете защитената история на съобщенията и самоличността за кръстосано-подписване (за потвърждение на други сесии), чрез въвеждане на ключа за възстановяване."; +"secrets_recovery_with_key_information_verify_device" = "Използвайте ключа за възстановяване за потвърждение на това устройство."; +"secrets_recovery_with_key_recovery_key_title" = "Въвеждане"; +"secrets_recovery_with_key_recovery_key_placeholder" = "Въвеждане на ключ за възстановяване"; +"secrets_recovery_with_key_recover_action" = "Използвай ключа"; +"secrets_recovery_with_key_invalid_recovery_key_title" = "Неуспешно достъпване на секретното складиране"; +"secrets_recovery_with_key_invalid_recovery_key_message" = "Потвърдете, че сте въвели правилния ключ за възстановяване."; From 1c0a6276894fddd63791b3d9648e2839c9a703c1 Mon Sep 17 00:00:00 2001 From: random Date: Fri, 12 Jun 2020 13:51:13 +0000 Subject: [PATCH 05/22] Translated using Weblate (Italian) Currently translated at 100.0% (1007 of 1007 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/it/ --- Riot/Assets/it.lproj/Vector.strings | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index 00e66ecda..f09045b3a 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -1075,3 +1075,25 @@ "key_verification_manually_verify_device_additional_information" = "Se non corrispondono, la sicurezza delle tue comunicazioni potrebbe essere compromessa."; "key_verification_manually_verify_device_validate_action" = "Verifica"; "user_verification_session_details_verify_action_current_user_manually" = "Verifica manualmente con testo"; +"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Usa chiave di recupero"; +"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Usa password o chiave di recupero"; +"device_verification_self_verify_wait_recover_secrets_additional_information" = "Se non puoi accedere a una sessione esistente"; +"secrets_recovery_with_passphrase_title" = "Password di recupero"; +"secrets_recovery_with_passphrase_information_default" = "Accedi alla tua cronologia dei messaggi sicuri e all'identità della firma incrociata per verificare altre sessioni inserendo la password di recupero."; +"secrets_recovery_with_passphrase_information_verify_device" = "Usa la password di recupero per verificare questo dispositivo."; +"secrets_recovery_with_passphrase_passphrase_title" = "Inserisci"; +"secrets_recovery_with_passphrase_passphrase_placeholder" = "Inserisci la password di recupero"; +"secrets_recovery_with_passphrase_recover_action" = "Usa password"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Non conosci la tua password di recupero? Puoi "; +"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "usare la chiave di recupero"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part3" = "."; +"secrets_recovery_with_passphrase_invalid_passphrase_title" = "Impossibile accedere all'archivio segreto"; +"secrets_recovery_with_passphrase_invalid_passphrase_message" = "Controlla di avere inserito la password di recupero corretta."; +"secrets_recovery_with_key_title" = "Chiave di recupero"; +"secrets_recovery_with_key_information_default" = "Accedi alla tua cronologia dei messaggi sicuri e all'identità della firma incrociata per verificare altre sessioni inserendo la chiave di recupero."; +"secrets_recovery_with_key_information_verify_device" = "Usa la chiave di recupero per verificare questo dispositivo."; +"secrets_recovery_with_key_recovery_key_title" = "Inserisci"; +"secrets_recovery_with_key_recovery_key_placeholder" = "Inserisci chiave di recupero"; +"secrets_recovery_with_key_recover_action" = "Usa chiave"; +"secrets_recovery_with_key_invalid_recovery_key_title" = "Impossibile accedere all'archivio segreto"; +"secrets_recovery_with_key_invalid_recovery_key_message" = "Controlla di avere inserito la chiave di recupero corretta."; From 29de7ed9018f413b4a4458da91ecb3de4f515485 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Sun, 14 Jun 2020 10:04:28 +0000 Subject: [PATCH 06/22] Translated using Weblate (Albanian) Currently translated at 98.6% (993 of 1007 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index 27ec55974..f61a3babe 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -1091,3 +1091,17 @@ "key_verification_manually_verify_device_additional_information" = "Nëse s’përputhen, siguria e komunikimeve tuaja mund të jetë komprometuar."; "key_verification_manually_verify_device_validate_action" = "Verifikoje"; "user_verification_session_details_verify_action_current_user_manually" = "Verifikojeni Dorazi përmes Teksti"; +"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Përdor Kyç Rimarrjesh"; +"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Përdor Frazëkalim ose Kyç Rimarrjesh"; +"device_verification_self_verify_wait_recover_secrets_additional_information" = "Nëse s’hyni dot në një sesion ekzistues"; +"secrets_recovery_with_passphrase_title" = "Frazëkalim Rimarrjesh"; +"secrets_recovery_with_passphrase_information_default" = "Hyni në historikun tuaj të mesazheve të sigurt dhe përdorni identitetin tuaj “cross-signing” për të verifikuar sesione të tjerë duke dhënë frazëkalimin tuaj të rimarrjeve."; +"secrets_recovery_with_passphrase_information_verify_device" = "Që të verifikoni këtë pajisje, përdorni Frazëkalimin tuaj të Rimarrjeve."; +"secrets_recovery_with_passphrase_passphrase_placeholder" = "Jepni Frazëkalim Rimarrjesh"; +"secrets_recovery_with_passphrase_recover_action" = "Përdor Frazëkalim"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "S’e din frazëkalimin tuaj të rimarrjeve? Mundeni të "; +"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "përdorni kyçin tuaj të rimarrjeve"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part3" = "."; +"secrets_recovery_with_passphrase_invalid_passphrase_title" = "S’arrihet të hyhet në depo të fshehtë"; +"secrets_recovery_with_passphrase_invalid_passphrase_message" = "Ju lutemi, verifikoni që keni dhënë frazëkalimin e saktë të rimarrjeve."; +"secrets_recovery_with_key_title" = "Kyç Rimarrjesh"; From cb6ba11c2087a16aa5a790d9c0b02df00db1a531 Mon Sep 17 00:00:00 2001 From: Osoitz Date: Sun, 14 Jun 2020 14:41:06 +0000 Subject: [PATCH 07/22] Translated using Weblate (Basque) Currently translated at 100.0% (1007 of 1007 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/eu/ --- Riot/Assets/eu.lproj/Vector.strings | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Riot/Assets/eu.lproj/Vector.strings b/Riot/Assets/eu.lproj/Vector.strings index a7c72266d..99a43fde7 100644 --- a/Riot/Assets/eu.lproj/Vector.strings +++ b/Riot/Assets/eu.lproj/Vector.strings @@ -1086,3 +1086,25 @@ "key_verification_verify_qr_code_emoji_information" = "Egiaztatu emoji bakanak konparatuz."; "key_verification_verify_qr_code_start_emoji_action" = "Egiaztatu emoji bidez"; "user_verification_session_details_verify_action_current_user_manually" = "Egiaztatu eskuz testu bidez"; +"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Erabili berreskuratze gakoa"; +"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Erabili berreskuratze pasa-esaldia edo gakoa"; +"device_verification_self_verify_wait_recover_secrets_additional_information" = "Ezin baduzu dagoen saio bat erabili"; +"secrets_recovery_with_passphrase_title" = "Berreskuratze pasa-esaldia"; +"secrets_recovery_with_passphrase_information_default" = "Atzitu zure mezu seguruen historiala eta zeharkako sinatzerako identitatea beste saioak egiaztatzeko zure berreskuratze pasa-esaldia sartuz."; +"secrets_recovery_with_passphrase_information_verify_device" = "Erabili berreskuratze pasa-esaldia gailu hau egiaztatzeko."; +"secrets_recovery_with_passphrase_passphrase_title" = "Sartu"; +"secrets_recovery_with_passphrase_passphrase_placeholder" = "Sartu berreskuratze pasa-esaldia"; +"secrets_recovery_with_passphrase_recover_action" = "Erabili pasa-esaldia"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Ez duzu berreskuratze pasa-esaldia gogoratzen? "; +"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "erabili berreskuratze-gakoa"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part3" = "."; +"secrets_recovery_with_passphrase_invalid_passphrase_title" = "Ezin izan da biltegi sekretua atzitu"; +"secrets_recovery_with_passphrase_invalid_passphrase_message" = "Egiaztatu berreskuratze pasa-esaldi zuzena sartu duzula."; +"secrets_recovery_with_key_title" = "Berreskuratze gakoa"; +"secrets_recovery_with_key_information_default" = "Atzitu zure mezu seguruen historiala eta zeharkako sinatzerako identitatea beste saioak egiaztatzeko zure berreskuratze gakoa sartuz."; +"secrets_recovery_with_key_information_verify_device" = "Erabili berreskuratze gakoa gailu hau egiaztatzeko."; +"secrets_recovery_with_key_recovery_key_title" = "Sartu"; +"secrets_recovery_with_key_recovery_key_placeholder" = "Sartu berreskuratze-gakoa"; +"secrets_recovery_with_key_recover_action" = "Erabili gakoa"; +"secrets_recovery_with_key_invalid_recovery_key_title" = "Ezin izan da biltegi sekretua atzitu"; +"secrets_recovery_with_key_invalid_recovery_key_message" = "Egiaztatu berreskuratze gako zuzena sartu duzula."; From 77dbd665f3b4b1db42b34a1ed25b9e57f018c4d3 Mon Sep 17 00:00:00 2001 From: vicdorke Date: Wed, 17 Jun 2020 01:39:47 +0000 Subject: [PATCH 08/22] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (1007 of 1007 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/zh_Hans/ --- Riot/Assets/zh_Hans.lproj/Vector.strings | 105 ++++++++++++++++++++++- 1 file changed, 101 insertions(+), 4 deletions(-) diff --git a/Riot/Assets/zh_Hans.lproj/Vector.strings b/Riot/Assets/zh_Hans.lproj/Vector.strings index ef13e2874..8bd9d6a1d 100644 --- a/Riot/Assets/zh_Hans.lproj/Vector.strings +++ b/Riot/Assets/zh_Hans.lproj/Vector.strings @@ -298,7 +298,7 @@ "settings_password_updated" = "您的密码已经更新"; "settings_crypto_device_name" = "会话名称: "; "settings_crypto_device_id" = "\n会话ID: "; -"settings_crypto_device_key" = "\n会话密钥:\n"; +"settings_crypto_device_key" = "\n会话密钥:\n"; "settings_crypto_export" = "导出密钥"; "settings_crypto_blacklist_unverified_devices" = "只加密到已验证的会话"; // Room Details @@ -841,7 +841,7 @@ "key_backup_recover_connent_banner_subtitle" = "关联此会话到“密钥备份”"; // MARK: - Device Verification "device_verification_title" = "验证会话"; -"key_verification_user_title" = "验证用户"; +"key_verification_user_title" = "验证它们"; "device_verification_security_advice" = "为了最大化安全性,我们建议你们当面或者使用其他可信任的通讯手段来进行此操作"; "device_verification_cancelled" = "另一方取消了验证。"; "device_verification_cancelled_by_me" = "此验证被取消。原因:%@"; @@ -976,7 +976,7 @@ "user_verification_sessions_list_session_trusted" = "已信任"; "user_verification_sessions_list_session_untrusted" = "不信任"; "user_verification_session_details_trusted_title" = "已信任"; -"user_verification_session_details_untrusted_title" = "警告"; +"user_verification_session_details_untrusted_title" = "不受信任"; "user_verification_session_details_information_trusted_current_user" = "此会话已被信任用于安全消息,因为你已经验证过它:"; "user_verification_session_details_information_trusted_other_user_part1" = "此会话已被信任用于安全消息,因为: "; "user_verification_session_details_information_trusted_other_user_part2" = " 已验证过它:"; @@ -984,7 +984,7 @@ "user_verification_session_details_information_untrusted_other_user" = " 用新会话登录:"; "user_verification_session_details_additional_information_untrusted_other_user" = "在此用户信任这个会话之前,发往和收到它的消息会有警告标签。或者,你可以手动验证它。"; "user_verification_session_details_additional_information_untrusted_current_user" = "如果你没有登录到这个会话,你的账号可能在被盗用。"; -"user_verification_session_details_verify_action_current_user" = "验证"; +"user_verification_session_details_verify_action_current_user" = "交互式验证"; "user_verification_session_details_verify_action_other_user" = "手动验证"; // MARK: Clients "client_desktop_name" = "Riot 桌面版"; @@ -1000,3 +1000,100 @@ "security_settings_crosssigning" = "交叉签名"; "security_settings_crosssigning_info_not_bootstrapped" = "交叉签名还没有被设置。"; "security_settings_crosssigning_info_exists" = "您的帐户有一个交叉签名身份,但是还没有被这个会话信任。完全安全的会话。"; +"skip" = "跳过"; +"room_member_power_level_custom_in" = "自定义(%@) 到%@"; +"room_member_power_level_short_custom" = "自定义"; +"security_settings_crosssigning_info_trusted" = "已启用交叉签名。您可以基于交叉签名信任其他用户和其他会话,但不能从此会话交叉签名,因为它没有交叉签名私钥。此会话完全安全。"; +"security_settings_crosssigning_info_ok" = "交叉签名还没有被设置。"; +"security_settings_crosssigning_bootstrap" = "引导交叉签名"; +"security_settings_crosssigning_reset" = "重置交叉签名"; +"security_settings_crosssigning_complete_security" = "完整安全性"; +"security_settings_cryptography" = "加密"; +"security_settings_complete_security_alert_title" = "绝对安全"; +"security_settings_complete_security_alert_message" = "您应该先完成当前会话的安全防护。"; +"security_settings_coming_soon" = "抱歉的。Riot-iOS上尚未提供此操作。请使用其他Matrix客户端进行设置。Riot-iOS将使用它。"; +// Recover from private key +"key_backup_recover_from_private_key_info" = "备份恢复中…"; +// MARK: - Device Verification +"key_verification_other_session_title" = "验证会话"; +"key_verification_new_session_title" = "验证你的新会话"; +"key_verification_this_session_title" = "验证此会话"; +"device_verification_security_advice_emoji" = "比较独特的emoji,确保它们以相同的顺序出现。"; +"device_verification_security_advice_number" = "比较数字,确保它们以相同的顺序出现。"; +// New login +"device_verification_self_verify_alert_title" = "新登录。这是你吗?"; +"device_verification_self_verify_alert_message" = "验证访问您的帐户的新登录名:%@"; +"device_verification_self_verify_alert_validate_action" = "验证"; +"device_verification_self_verify_start_verify_action" = "开始验证"; +"device_verification_self_verify_start_information" = "使用此会话验证您的新会话,并授予其访问加密信息的权限。"; +"device_verification_self_verify_start_waiting" = "等待中……"; +"key_verification_self_verify_current_session_alert_title" = "验证此会话"; +"key_verification_self_verify_current_session_alert_message" = "其他用户可能不信任它。"; +"key_verification_self_verify_current_session_alert_validate_action" = "验证"; +"key_verification_self_verify_unverified_sessions_alert_title" = "查看您的登陆位置"; +"key_verification_self_verify_unverified_sessions_alert_message" = "验证您的所有会话以确保您的帐户和消息的安全。"; +"key_verification_self_verify_unverified_sessions_alert_validate_action" = "检查"; +"device_verification_self_verify_wait_title" = "绝对安全"; +"device_verification_self_verify_wait_new_sign_in_title" = "验证此登录名"; +"device_verification_self_verify_wait_information" = "从您的其他会话之一验证此会话,并授予其对加密信息的访问权限。\n\n在您的其他设备上使用最新的Riot:"; +"device_verification_self_verify_wait_additional_information" = "或另一个支持交叉签名的Matrix客户端"; +"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "使用恢复密钥"; +"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "使用恢复密码或密钥"; +"device_verification_self_verify_wait_recover_secrets_additional_information" = "如果您无法访问现有会话"; +"key_verification_verify_sas_title_emoji" = "比较emoji"; +"key_verification_verify_sas_title_number" = "比较数字"; +"key_verification_verify_sas_cancel_action" = "它们不相配"; +"key_verification_verify_sas_validate_action" = "它们相配"; +"key_verification_verify_sas_additional_information" = "为了最终的安全,请使用另一种可信的通信方式或亲自执行此操作。"; +"key_verification_manually_verify_device_title" = "通过文本手动验证"; +"key_verification_manually_verify_device_instruction" = "通过将以下设置与其他会话中的用户设置进行比较来确认:"; +"key_verification_manually_verify_device_name_title" = "会话名称"; +"key_verification_manually_verify_device_id_title" = "会话 ID"; +"key_verification_manually_verify_device_key_title" = "会话密钥"; +"key_verification_manually_verify_device_additional_information" = "如果它们不匹配,您的通信安全可能会受到威胁。"; +"key_verification_manually_verify_device_validate_action" = "验证"; +"key_verification_verified_new_session_title" = "新会话已验证!"; +"key_verification_verified_other_session_information" = "您现在可以阅读其他会话上的安全信息,其他用户将知道他们可以信任它。"; +"key_verification_verified_new_session_information" = "您现在可以在新设备上阅读安全信息,其他用户将知道他们可以信任它。"; +"key_verification_verified_this_session_information" = "您现在可以在此设备上阅读安全信息,其他用户将知道他们可以信任它。"; +"key_verification_verified_user_information" = "此用户的信息是端到端加密的,第三方无法读取。"; +"key_verification_bootstrap_not_setup_title" = "错误"; +"key_verification_bootstrap_not_setup_message" = "您需要先启动交叉签名。"; +"key_verification_verify_qr_code_title" = "通过扫描进行验证"; +"key_verification_verify_qr_code_information" = "扫描代码以安全地相互验证。"; +"key_verification_verify_qr_code_information_other_device" = "扫描以下代码以验证:"; +"key_verification_verify_qr_code_emoji_information" = "通过比较唯一的表情符号进行验证。"; +"key_verification_verify_qr_code_scan_code_action" = "扫描他们的代码"; +"key_verification_verify_qr_code_cannot_scan_action" = "不能扫描吗?"; +"key_verification_verify_qr_code_start_emoji_action" = "通过表情符号验证"; +"key_verification_verify_qr_code_other_scan_my_code_title" = "其他用户是否成功扫描了二维码?"; +"key_verification_verify_qr_code_scan_other_code_success_title" = "代码已验证!"; +"key_verification_verify_qr_code_scan_other_code_success_message" = "二维码已成功验证。"; +// Scanning +"key_verification_scan_confirmation_scanning_title" = "快到了!\n正在等待确认…"; +"key_verification_scan_confirmation_scanning_user_waiting_other" = "等待中%@…"; +"key_verification_scan_confirmation_scanning_device_waiting_other" = "正在等待其他设备…"; +// Scanned +"key_verification_scan_confirmation_scanned_title" = "快到了!"; +"key_verification_scan_confirmation_scanned_user_information" = "是否%@显示相同的盾牌?"; +"key_verification_scan_confirmation_scanned_device_information" = "另一台设备是否显示相同的盾牌?"; +"user_verification_session_details_verify_action_current_user_manually" = "通过文本手动验证"; +"secrets_recovery_with_passphrase_title" = "恢复密码"; +"secrets_recovery_with_passphrase_information_default" = "通过输入恢复密码,访问您的安全信息历史记录和交叉签名身份,以验证其他会话。"; +"secrets_recovery_with_passphrase_information_verify_device" = "使用您的恢复密码验证此设备。"; +"secrets_recovery_with_passphrase_passphrase_title" = "输入"; +"secrets_recovery_with_passphrase_passphrase_placeholder" = "输入恢复密码"; +"secrets_recovery_with_passphrase_recover_action" = "使用密码短语"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "忘记了您的恢复密码?您可以 "; +"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "使用您的恢复密钥"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part3" = "."; +"secrets_recovery_with_passphrase_invalid_passphrase_title" = "无法访问机密存储"; +"secrets_recovery_with_passphrase_invalid_passphrase_message" = "请验证您输入的恢复密码是否正确。"; +"secrets_recovery_with_key_title" = "恢复密钥"; +"secrets_recovery_with_key_information_default" = "通过输入恢复密钥访问安全信息历史记录和交叉签名身份,以验证其他会话。"; +"secrets_recovery_with_key_information_verify_device" = "使用您的恢复密码验证此设备。"; +"secrets_recovery_with_key_recovery_key_title" = "输入"; +"secrets_recovery_with_key_recovery_key_placeholder" = "输入恢复密钥"; +"secrets_recovery_with_key_recover_action" = "使用密钥"; +"secrets_recovery_with_key_invalid_recovery_key_title" = "无法访问机密存储"; +"secrets_recovery_with_key_invalid_recovery_key_message" = "请验证您输入的恢复密钥是否正确。"; From aa8db7eaf62a960cb6d552315b44ed6828a0b8ec Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 23 Jun 2020 17:37:44 +0200 Subject: [PATCH 09/22] Secrets setup: Add images. --- .../Contents.json | 0 .../Secrets/Recovery/Contents.json | 6 +++++ .../Contents.json | 0 .../secrets_recovery_key.png | Bin .../secrets_recovery_key@2x.png | Bin .../secrets_recovery_key@3x.png | Bin .../Contents.json | 0 .../secrets_recovery_passphrase.png | Bin .../secrets_recovery_passphrase@2x.png | Bin .../secrets_recovery_passphrase@3x.png | Bin .../Secrets/Setup/Contents.json | 6 +++++ .../secrets_setup_key.imageset/Contents.json | 23 ++++++++++++++++++ .../secrets_setup_key.png | Bin 0 -> 4747 bytes .../secrets_setup_key@2x.png | Bin 0 -> 7466 bytes .../secrets_setup_key@3x.png | Bin 0 -> 3927 bytes .../Contents.json | 23 ++++++++++++++++++ .../secrets_setup_passphrase.png | Bin 0 -> 4757 bytes .../secrets_setup_passphrase@2x.png | Bin 0 -> 7420 bytes .../secrets_setup_passphrase@3x.png | Bin 0 -> 3960 bytes Riot/Generated/Images.swift | 2 ++ 20 files changed, 60 insertions(+) rename Riot/Assets/Images.xcassets/{SecretsRecovery => Secrets}/Contents.json (100%) create mode 100644 Riot/Assets/Images.xcassets/Secrets/Recovery/Contents.json rename Riot/Assets/Images.xcassets/{SecretsRecovery => Secrets/Recovery}/secrets_recovery_key.imageset/Contents.json (100%) rename Riot/Assets/Images.xcassets/{SecretsRecovery => Secrets/Recovery}/secrets_recovery_key.imageset/secrets_recovery_key.png (100%) rename Riot/Assets/Images.xcassets/{SecretsRecovery => Secrets/Recovery}/secrets_recovery_key.imageset/secrets_recovery_key@2x.png (100%) rename Riot/Assets/Images.xcassets/{SecretsRecovery => Secrets/Recovery}/secrets_recovery_key.imageset/secrets_recovery_key@3x.png (100%) rename Riot/Assets/Images.xcassets/{SecretsRecovery => Secrets/Recovery}/secrets_recovery_passphrase.imageset/Contents.json (100%) rename Riot/Assets/Images.xcassets/{SecretsRecovery => Secrets/Recovery}/secrets_recovery_passphrase.imageset/secrets_recovery_passphrase.png (100%) rename Riot/Assets/Images.xcassets/{SecretsRecovery => Secrets/Recovery}/secrets_recovery_passphrase.imageset/secrets_recovery_passphrase@2x.png (100%) rename Riot/Assets/Images.xcassets/{SecretsRecovery => Secrets/Recovery}/secrets_recovery_passphrase.imageset/secrets_recovery_passphrase@3x.png (100%) create mode 100644 Riot/Assets/Images.xcassets/Secrets/Setup/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_key.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_key.imageset/secrets_setup_key.png create mode 100644 Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_key.imageset/secrets_setup_key@2x.png create mode 100644 Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_key.imageset/secrets_setup_key@3x.png create mode 100644 Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_passphrase.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_passphrase.imageset/secrets_setup_passphrase.png create mode 100644 Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_passphrase.imageset/secrets_setup_passphrase@2x.png create mode 100644 Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_passphrase.imageset/secrets_setup_passphrase@3x.png diff --git a/Riot/Assets/Images.xcassets/SecretsRecovery/Contents.json b/Riot/Assets/Images.xcassets/Secrets/Contents.json similarity index 100% rename from Riot/Assets/Images.xcassets/SecretsRecovery/Contents.json rename to Riot/Assets/Images.xcassets/Secrets/Contents.json diff --git a/Riot/Assets/Images.xcassets/Secrets/Recovery/Contents.json b/Riot/Assets/Images.xcassets/Secrets/Recovery/Contents.json new file mode 100644 index 000000000..da4a164c9 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Secrets/Recovery/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_key.imageset/Contents.json b/Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_key.imageset/Contents.json similarity index 100% rename from Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_key.imageset/Contents.json rename to Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_key.imageset/Contents.json diff --git a/Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_key.imageset/secrets_recovery_key.png b/Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_key.imageset/secrets_recovery_key.png similarity index 100% rename from Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_key.imageset/secrets_recovery_key.png rename to Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_key.imageset/secrets_recovery_key.png diff --git a/Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_key.imageset/secrets_recovery_key@2x.png b/Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_key.imageset/secrets_recovery_key@2x.png similarity index 100% rename from Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_key.imageset/secrets_recovery_key@2x.png rename to Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_key.imageset/secrets_recovery_key@2x.png diff --git a/Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_key.imageset/secrets_recovery_key@3x.png b/Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_key.imageset/secrets_recovery_key@3x.png similarity index 100% rename from Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_key.imageset/secrets_recovery_key@3x.png rename to Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_key.imageset/secrets_recovery_key@3x.png diff --git a/Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_passphrase.imageset/Contents.json b/Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_passphrase.imageset/Contents.json similarity index 100% rename from Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_passphrase.imageset/Contents.json rename to Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_passphrase.imageset/Contents.json diff --git a/Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_passphrase.imageset/secrets_recovery_passphrase.png b/Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_passphrase.imageset/secrets_recovery_passphrase.png similarity index 100% rename from Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_passphrase.imageset/secrets_recovery_passphrase.png rename to Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_passphrase.imageset/secrets_recovery_passphrase.png diff --git a/Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_passphrase.imageset/secrets_recovery_passphrase@2x.png b/Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_passphrase.imageset/secrets_recovery_passphrase@2x.png similarity index 100% rename from Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_passphrase.imageset/secrets_recovery_passphrase@2x.png rename to Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_passphrase.imageset/secrets_recovery_passphrase@2x.png diff --git a/Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_passphrase.imageset/secrets_recovery_passphrase@3x.png b/Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_passphrase.imageset/secrets_recovery_passphrase@3x.png similarity index 100% rename from Riot/Assets/Images.xcassets/SecretsRecovery/secrets_recovery_passphrase.imageset/secrets_recovery_passphrase@3x.png rename to Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_passphrase.imageset/secrets_recovery_passphrase@3x.png diff --git a/Riot/Assets/Images.xcassets/Secrets/Setup/Contents.json b/Riot/Assets/Images.xcassets/Secrets/Setup/Contents.json new file mode 100644 index 000000000..da4a164c9 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Secrets/Setup/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_key.imageset/Contents.json b/Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_key.imageset/Contents.json new file mode 100644 index 000000000..58e5ed869 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_key.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "secrets_setup_key.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "secrets_setup_key@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "secrets_setup_key@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_key.imageset/secrets_setup_key.png b/Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_key.imageset/secrets_setup_key.png new file mode 100644 index 0000000000000000000000000000000000000000..db27047e7212e5786920449a43f2fc804eb15a10 GIT binary patch literal 4747 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 Lb@>`uXy|_d&&C8O literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_key.imageset/secrets_setup_key@2x.png b/Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_key.imageset/secrets_setup_key@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4e9c7e1bf99a9e820de99761b3a7a135e5d420b2 GIT binary patch literal 7466 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 Apa1{> literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_passphrase.imageset/Contents.json b/Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_passphrase.imageset/Contents.json new file mode 100644 index 000000000..c99ab2f3d --- /dev/null +++ b/Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_passphrase.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "secrets_setup_passphrase.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "secrets_setup_passphrase@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "secrets_setup_passphrase@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_passphrase.imageset/secrets_setup_passphrase.png b/Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_passphrase.imageset/secrets_setup_passphrase.png new file mode 100644 index 0000000000000000000000000000000000000000..1002f668f08a8aa5da582b168f67a238a31f64ee GIT binary patch literal 4757 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 literal 0 HcmV?d00001 diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift index a49bd2bbb..0fd4a093b 100644 --- a/Riot/Generated/Images.swift +++ b/Riot/Generated/Images.swift @@ -106,6 +106,8 @@ internal enum Asset { internal static let searchIcon = ImageAsset(name: "search_icon") internal static let secretsRecoveryKey = ImageAsset(name: "secrets_recovery_key") internal static let secretsRecoveryPassphrase = ImageAsset(name: "secrets_recovery_passphrase") + internal static let secretsSetupKey = ImageAsset(name: "secrets_setup_key") + internal static let secretsSetupPassphrase = ImageAsset(name: "secrets_setup_passphrase") internal static let removeIconPink = ImageAsset(name: "remove_icon_pink") internal static let settingsIcon = ImageAsset(name: "settings_icon") internal static let tabFavourites = ImageAsset(name: "tab_favourites") From a5aa761986e903864be4576f07481403ddfc4fbf Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 23 Jun 2020 17:40:58 +0200 Subject: [PATCH 10/22] Secure key backup: Add string localizations. --- Riot/Assets/en.lproj/Vector.strings | 47 +++++++++++++++ Riot/Generated/Strings.swift | 88 +++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index eb2d3e589..a2fad040a 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -919,6 +919,25 @@ "rerequest_keys_alert_title" = "Request Sent"; "rerequest_keys_alert_message" = "Please launch Riot on another device that can decrypt the message so it can send the keys to this session."; +// MARK: Secure Key backup setup + +// Intro + +"secure_key_backup_setup_intro_title" = "Secure Backup"; +"secure_key_backup_setup_intro_info" = "Safe guard against losing access to encrypted messages & data by backing up encryption keys on your server."; + +"secure_key_backup_setup_intro_use_security_key_title" = "Use a Security Key"; +"secure_key_backup_setup_intro_use_security_key_info" = "Generate a security key to store somewhere safe like a password manager or a safe."; + +"secure_key_backup_setup_intro_use_security_passphrase_title" = "Use a Security Passphrase"; +"secure_key_backup_setup_intro_use_security_passphrase_info" = "Enter a secret phrase only you know, and generate a key for backup."; + +// Cancel + +"secure_key_backup_setup_cancel_alert_title" = "Are your sure?"; +"secure_key_backup_setup_cancel_alert_message" = "If you cancel now, you may lose encrypted messages & data if you lose access to your logins.\n\nYou can also set up Secure Backup & manage your keys in Settings."; + + // MARK: Key backup setup "key_backup_setup_title" = "Key Backup"; @@ -1342,3 +1361,31 @@ "secrets_recovery_with_key_invalid_recovery_key_title" = "Unable to access secret storage"; "secrets_recovery_with_key_invalid_recovery_key_message" = "Please verify that you entered the correct recovery key."; + +// MARK: - Secrets set up + +// Recovery Key + +"secrets_setup_recovery_key_title" = "Save your Security Key"; +"secrets_setup_recovery_key_information" = "Store your Recovery Key somewhere safe. It can be used to unlock your encrypted messages & data."; +"secrets_setup_recovery_key_loading" = "Loading…"; +"secrets_setup_recovery_key_export_action" = "Save"; +"secrets_setup_recovery_key_done_action" = "Done"; + +"secrets_setup_recovery_key_storage_alert_title" = "Keep it safe"; +"secrets_setup_recovery_key_storage_alert_message" = "✓ Print it and store it somewhere safe\n✓ Save it on a USB key or backup drive\n✓ Copy it to your personal cloud storage"; + +// Recovery passphrase + +"secrets_setup_recovery_passphrase_title" = "Set a Security Phrase"; +"secrets_setup_recovery_passphrase_information" = "Enter a security phrase only you know, used to secure secrets on your server."; +"secrets_setup_recovery_passphrase_additional_information" = "Don't use your account password."; +"secrets_setup_recovery_passphrase_validate_action" = "Done"; + +"secrets_setup_recovery_passphrase_confirm_information" = "Enter your Security Phrase again to confirm it."; +"secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Confirm"; +"secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "Confirm passphrase"; + +"key_backup_setup_passphrase_confirm_passphrase_valid" = "Great!"; +"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Passphrase doesn’t match"; + diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index c5b8032ec..992125e76 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -3002,6 +3002,94 @@ internal enum VectorL10n { internal static var secretsRecoveryWithPassphraseTitle: String { return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_title") } + /// Done + internal static var secretsSetupRecoveryKeyDoneAction: String { + return VectorL10n.tr("Vector", "secrets_setup_recovery_key_done_action") + } + /// Save + internal static var secretsSetupRecoveryKeyExportAction: String { + return VectorL10n.tr("Vector", "secrets_setup_recovery_key_export_action") + } + /// Store your Recovery Key somewhere safe. It can be used to unlock your encrypted messages & data. + internal static var secretsSetupRecoveryKeyInformation: String { + return VectorL10n.tr("Vector", "secrets_setup_recovery_key_information") + } + /// Loading… + internal static var secretsSetupRecoveryKeyLoading: String { + return VectorL10n.tr("Vector", "secrets_setup_recovery_key_loading") + } + /// ✓ Print it and store it somewhere safe\n✓ Save it on a USB key or backup drive\n✓ Copy it to your personal cloud storage + internal static var secretsSetupRecoveryKeyStorageAlertMessage: String { + return VectorL10n.tr("Vector", "secrets_setup_recovery_key_storage_alert_message") + } + /// Keep it safe + internal static var secretsSetupRecoveryKeyStorageAlertTitle: String { + return VectorL10n.tr("Vector", "secrets_setup_recovery_key_storage_alert_title") + } + /// Save your Security Key + internal static var secretsSetupRecoveryKeyTitle: String { + return VectorL10n.tr("Vector", "secrets_setup_recovery_key_title") + } + /// Don't use your account password. + internal static var secretsSetupRecoveryPassphraseAdditionalInformation: String { + return VectorL10n.tr("Vector", "secrets_setup_recovery_passphrase_additional_information") + } + /// Enter your Security Phrase again to confirm it. + internal static var secretsSetupRecoveryPassphraseConfirmInformation: String { + return VectorL10n.tr("Vector", "secrets_setup_recovery_passphrase_confirm_information") + } + /// Confirm passphrase + internal static var secretsSetupRecoveryPassphraseConfirmPassphrasePlaceholder: String { + return VectorL10n.tr("Vector", "secrets_setup_recovery_passphrase_confirm_passphrase_placeholder") + } + /// Confirm + internal static var secretsSetupRecoveryPassphraseConfirmPassphraseTitle: String { + return VectorL10n.tr("Vector", "secrets_setup_recovery_passphrase_confirm_passphrase_title") + } + /// Enter a security phrase only you know, used to secure secrets on your server. + internal static var secretsSetupRecoveryPassphraseInformation: String { + return VectorL10n.tr("Vector", "secrets_setup_recovery_passphrase_information") + } + /// Set a Security Phrase + internal static var secretsSetupRecoveryPassphraseTitle: String { + return VectorL10n.tr("Vector", "secrets_setup_recovery_passphrase_title") + } + /// Done + internal static var secretsSetupRecoveryPassphraseValidateAction: String { + return VectorL10n.tr("Vector", "secrets_setup_recovery_passphrase_validate_action") + } + /// If you cancel now, you may lose encrypted messages & data if you lose access to your logins.\n\nYou can also set up Secure Backup & manage your keys in Settings. + internal static var secureKeyBackupSetupCancelAlertMessage: String { + return VectorL10n.tr("Vector", "secure_key_backup_setup_cancel_alert_message") + } + /// Are your sure? + internal static var secureKeyBackupSetupCancelAlertTitle: String { + return VectorL10n.tr("Vector", "secure_key_backup_setup_cancel_alert_title") + } + /// Safe guard against losing access to encrypted messages & data by backing up encryption keys on your server. + internal static var secureKeyBackupSetupIntroInfo: String { + return VectorL10n.tr("Vector", "secure_key_backup_setup_intro_info") + } + /// Secure Backup + internal static var secureKeyBackupSetupIntroTitle: String { + return VectorL10n.tr("Vector", "secure_key_backup_setup_intro_title") + } + /// Generate a security key to store somewhere safe like a password manager or a safe. + internal static var secureKeyBackupSetupIntroUseSecurityKeyInfo: String { + return VectorL10n.tr("Vector", "secure_key_backup_setup_intro_use_security_key_info") + } + /// Use a Security Key + internal static var secureKeyBackupSetupIntroUseSecurityKeyTitle: String { + return VectorL10n.tr("Vector", "secure_key_backup_setup_intro_use_security_key_title") + } + /// Enter a secret phrase only you know, and generate a key for backup. + internal static var secureKeyBackupSetupIntroUseSecurityPassphraseInfo: String { + return VectorL10n.tr("Vector", "secure_key_backup_setup_intro_use_security_passphrase_info") + } + /// Use a Security Passphrase + internal static var secureKeyBackupSetupIntroUseSecurityPassphraseTitle: String { + return VectorL10n.tr("Vector", "secure_key_backup_setup_intro_use_security_passphrase_title") + } /// ADVANCED internal static var securitySettingsAdvanced: String { return VectorL10n.tr("Vector", "security_settings_advanced") From ffdf54754f5265da0ac6806c9c01face3ba5d5c5 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 23 Jun 2020 17:45:50 +0200 Subject: [PATCH 11/22] Secrets recover: Move files. --- .../RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift | 0 .../RecoverWithKey/SecretsRecoveryWithKeyCoordinatorType.swift | 0 .../RecoverWithKey/SecretsRecoveryWithKeyViewAction.swift | 0 .../SecretsRecoveryWithKeyViewController.storyboard | 0 .../RecoverWithKey/SecretsRecoveryWithKeyViewController.swift | 0 .../Recover}/RecoverWithKey/SecretsRecoveryWithKeyViewModel.swift | 0 .../RecoverWithKey/SecretsRecoveryWithKeyViewModelType.swift | 0 .../Recover}/RecoverWithKey/SecretsRecoveryWithKeyViewState.swift | 0 .../SecretsRecoveryWithPassphraseCoordinator.swift | 0 .../SecretsRecoveryWithPassphraseCoordinatorType.swift | 0 .../SecretsRecoveryWithPassphraseViewAction.swift | 0 .../SecretsRecoveryWithPassphraseViewController.storyboard | 0 .../SecretsRecoveryWithPassphraseViewController.swift | 0 .../SecretsRecoveryWithPassphraseViewModel.swift | 0 .../SecretsRecoveryWithPassphraseViewModelType.swift | 0 .../SecretsRecoveryWithPassphraseViewState.swift | 0 .../Recover}/SecretsRecoveryCoordinator.swift | 0 .../Recover}/SecretsRecoveryCoordinatorType.swift | 0 .../Recover}/SecretsRecoveryGoal.swift | 0 .../Recover}/SecretsRecoveryMode.swift | 0 20 files changed, 0 insertions(+), 0 deletions(-) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithKey/SecretsRecoveryWithKeyCoordinatorType.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithKey/SecretsRecoveryWithKeyViewAction.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithKey/SecretsRecoveryWithKeyViewController.storyboard (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithKey/SecretsRecoveryWithKeyViewModel.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithKey/SecretsRecoveryWithKeyViewModelType.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithKey/SecretsRecoveryWithKeyViewState.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinatorType.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewAction.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.storyboard (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModelType.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewState.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/SecretsRecoveryCoordinator.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/SecretsRecoveryCoordinatorType.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/SecretsRecoveryGoal.swift (100%) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/SecretsRecoveryMode.swift (100%) diff --git a/Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift rename to Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift diff --git a/Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyCoordinatorType.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinatorType.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyCoordinatorType.swift rename to Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinatorType.swift diff --git a/Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyViewAction.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewAction.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyViewAction.swift rename to Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewAction.swift diff --git a/Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyViewController.storyboard b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.storyboard similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyViewController.storyboard rename to Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.storyboard diff --git a/Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift rename to Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift diff --git a/Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyViewModel.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModel.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyViewModel.swift rename to Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModel.swift diff --git a/Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyViewModelType.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModelType.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyViewModelType.swift rename to Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModelType.swift diff --git a/Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyViewState.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewState.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithKey/SecretsRecoveryWithKeyViewState.swift rename to Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewState.swift diff --git a/Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift rename to Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift diff --git a/Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinatorType.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinatorType.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinatorType.swift rename to Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinatorType.swift diff --git a/Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewAction.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewAction.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewAction.swift rename to Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewAction.swift diff --git a/Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.storyboard b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.storyboard similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.storyboard rename to Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.storyboard diff --git a/Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift rename to Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift diff --git a/Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift rename to Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift diff --git a/Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModelType.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModelType.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModelType.swift rename to Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModelType.swift diff --git a/Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewState.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewState.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewState.swift rename to Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewState.swift diff --git a/Riot/Modules/SecretsRecovery/SecretsRecoveryCoordinator.swift b/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/SecretsRecoveryCoordinator.swift rename to Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift diff --git a/Riot/Modules/SecretsRecovery/SecretsRecoveryCoordinatorType.swift b/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinatorType.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/SecretsRecoveryCoordinatorType.swift rename to Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinatorType.swift diff --git a/Riot/Modules/SecretsRecovery/SecretsRecoveryGoal.swift b/Riot/Modules/Secrets/Recover/SecretsRecoveryGoal.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/SecretsRecoveryGoal.swift rename to Riot/Modules/Secrets/Recover/SecretsRecoveryGoal.swift diff --git a/Riot/Modules/SecretsRecovery/SecretsRecoveryMode.swift b/Riot/Modules/Secrets/Recover/SecretsRecoveryMode.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/SecretsRecoveryMode.swift rename to Riot/Modules/Secrets/Recover/SecretsRecoveryMode.swift From 90ed345679ab14d79cf335c8de9b37c2d46e87a5 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 23 Jun 2020 17:47:26 +0200 Subject: [PATCH 12/22] UIImage: Add convenient method to set the alpha. --- Riot/Categories/UIImage.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Riot/Categories/UIImage.swift b/Riot/Categories/UIImage.swift index 9c6847645..63a35d9da 100644 --- a/Riot/Categories/UIImage.swift +++ b/Riot/Categories/UIImage.swift @@ -47,5 +47,13 @@ extension UIImage { let tintedImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return tintedImage - } + } + + func vc_withAlpha(_ alpha: CGFloat) -> UIImage? { + UIGraphicsBeginImageContextWithOptions(size, false, scale) + draw(at: .zero, blendMode: .normal, alpha: alpha) + let newImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return newImage + } } From 0f251aee4bde725b6d44819f294f388cb21efd4d Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 23 Jun 2020 17:52:00 +0200 Subject: [PATCH 13/22] Secure key backup: Handle setup introduction screen. --- .../Intro/SecureKeyBackupSetupIntroCell.swift | 157 ++++++++++++++++++ .../Intro/SecureKeyBackupSetupIntroCell.xib | 102 ++++++++++++ ...yBackupSetupIntroViewController.storyboard | 132 +++++++++++++++ ...ureKeyBackupSetupIntroViewController.swift | 133 +++++++++++++++ 4 files changed, 524 insertions(+) create mode 100644 Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroCell.swift create mode 100644 Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroCell.xib create mode 100644 Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroViewController.storyboard create mode 100644 Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroViewController.swift diff --git a/Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroCell.swift b/Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroCell.swift new file mode 100644 index 000000000..73961e2eb --- /dev/null +++ b/Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroCell.swift @@ -0,0 +1,157 @@ +/* + Copyright 2020 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 UIKit +import Reusable + +final class SecureKeyBackupSetupIntroCell: UIView, NibOwnerLoadable, Themable { + + // MARK: - Constants + + private enum ImageAlpha { + static let highlighted: CGFloat = 0.3 + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var backgroundView: UIView! + @IBOutlet private weak var imageView: UIImageView! + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var informationLabel: UILabel! + @IBOutlet private weak var accessoryImageView: UIImageView! + @IBOutlet private weak var separatorView: UIView! + + // MARK: Private + + private var theme: Theme? + + private var isHighlighted: Bool = false { + didSet { + self.updateView() + } + } + + // MARK: Public + + var action: (() -> Void)? + + // MARK: Setup + + private func commonInit() { + self.setupGestureRecognizer() + + let accessoryTemplateImage = Asset.Images.disclosureIcon.image.withRenderingMode(.alwaysTemplate) + self.accessoryImageView.image = accessoryTemplateImage + self.accessoryImageView.highlightedImage = accessoryTemplateImage.vc_withAlpha(ImageAlpha.highlighted) + } + + convenience init() { + self.init(frame: CGRect.zero) + self.commonInit() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.loadNibContent() + self.commonInit() + } + + override init(frame: CGRect) { + super.init(frame: frame) + self.loadNibContent() + self.commonInit() + } + + // MARK: - Public + + func update(theme: Theme) { + self.theme = theme + + self.backgroundView.backgroundColor = theme.backgroundColor + self.imageView.tintColor = theme.textPrimaryColor + self.titleLabel.textColor = theme.tintColor + self.informationLabel.textColor = theme.textSecondaryColor + self.accessoryImageView.tintColor = theme.textSecondaryColor + self.separatorView.backgroundColor = theme.lineBreakColor + + self.updateView() + } + + func fill(title: String, information: String, image: UIImage) { + let templateImage = image.withRenderingMode(.alwaysTemplate) + + self.imageView.image = templateImage + self.imageView.highlightedImage = templateImage.vc_withAlpha(ImageAlpha.highlighted) + + self.titleLabel.text = title + self.informationLabel.text = information + + self.setupAccessibility(title: title, isEnabled: true) + self.updateView() + } + + // MARK: - Private + + private func setupAccessibility(title: String, isEnabled: Bool) { + self.isAccessibilityElement = true + self.accessibilityLabel = title + self.accessibilityTraits = .button + if !isEnabled { + self.accessibilityTraits.insert(.notEnabled) + } + } + + private func setupGestureRecognizer() { + let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(buttonAction(_:))) + gestureRecognizer.minimumPressDuration = 0 + self.addGestureRecognizer(gestureRecognizer) + } + + private func updateView() { + + if let theme = self.theme { + self.backgroundView.backgroundColor = self.isHighlighted ? theme.overlayBackgroundColor : theme.backgroundColor + } + + self.imageView.isHighlighted = self.isHighlighted + self.accessoryImageView.isHighlighted = self.isHighlighted + } + + // MARK: - Actions + + @objc private func buttonAction(_ sender: UILongPressGestureRecognizer) { + + let isBackgroundViewTouched = sender.vc_isTouchingInside() + + switch sender.state { + case .began, .changed: + self.isHighlighted = isBackgroundViewTouched + case .ended: + self.isHighlighted = false + + if isBackgroundViewTouched { + self.action?() + } + case .cancelled: + self.isHighlighted = false + default: + break + } + } + +} diff --git a/Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroCell.xib b/Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroCell.xib new file mode 100644 index 000000000..3b29fac64 --- /dev/null +++ b/Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroCell.xib @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroViewController.storyboard b/Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroViewController.storyboard new file mode 100644 index 000000000..0f6dd3102 --- /dev/null +++ b/Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroViewController.storyboard @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroViewController.swift b/Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroViewController.swift new file mode 100644 index 000000000..9213b3024 --- /dev/null +++ b/Riot/Modules/KeyBackup/SecureSetup/Intro/SecureKeyBackupSetupIntroViewController.swift @@ -0,0 +1,133 @@ +/* + Copyright 2020 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 UIKit + +protocol SecureKeyBackupSetupIntroViewControllerDelegate: class { + func secureKeyBackupSetupIntroViewControllerDidTapUseKey(_ secureKeyBackupSetupIntroViewController: SecureKeyBackupSetupIntroViewController) + func secureKeyBackupSetupIntroViewControllerDidTapUsePassphrase(_ secureKeyBackupSetupIntroViewController: SecureKeyBackupSetupIntroViewController) + func secureKeyBackupSetupIntroViewControllerDidCancel(_ secureKeyBackupSetupIntroViewController: SecureKeyBackupSetupIntroViewController) +} + +@objcMembers +final class SecureKeyBackupSetupIntroViewController: UIViewController { + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var informationLabel: UILabel! + + @IBOutlet private weak var topSeparatorView: UIView! + @IBOutlet private weak var secureKeyCell: SecureKeyBackupSetupIntroCell! + @IBOutlet private weak var securePassphraseCell: SecureKeyBackupSetupIntroCell! + + // MARK: Private + + private var theme: Theme! + + // MARK: Public + + weak var delegate: SecureKeyBackupSetupIntroViewControllerDelegate? + + // MARK: - Setup + + class func instantiate() -> SecureKeyBackupSetupIntroViewController { + let viewController = StoryboardScene.SecureKeyBackupSetupIntroViewController.initialScene.instantiate() + viewController.theme = ThemeService.shared().theme + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.vc_removeBackTitle() + + self.setupViews() + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func setupViews() { + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in + guard let self = self else { + return + } + self.delegate?.secureKeyBackupSetupIntroViewControllerDidCancel(self) + } + self.navigationItem.rightBarButtonItem = cancelBarButtonItem + + self.title = VectorL10n.secureKeyBackupSetupIntroTitle + + self.informationLabel.text = VectorL10n.secureKeyBackupSetupIntroInfo + + self.secureKeyCell.fill(title: VectorL10n.secureKeyBackupSetupIntroUseSecurityKeyTitle, + information: VectorL10n.secureKeyBackupSetupIntroUseSecurityKeyInfo, + image: Asset.Images.secretsSetupKey.image) + + self.secureKeyCell.action = { [weak self] in + guard let self = self else { + return + } + self.delegate?.secureKeyBackupSetupIntroViewControllerDidTapUseKey(self) + } + + self.securePassphraseCell.fill(title: VectorL10n.secureKeyBackupSetupIntroUseSecurityPassphraseTitle, + information: VectorL10n.secureKeyBackupSetupIntroUseSecurityPassphraseInfo, + image: Asset.Images.secretsSetupPassphrase.image) + + self.securePassphraseCell.action = { [weak self] in + guard let self = self else { + return + } + self.delegate?.secureKeyBackupSetupIntroViewControllerDidTapUsePassphrase(self) + } + } + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + self.informationLabel.textColor = theme.textPrimaryColor + + self.topSeparatorView.backgroundColor = theme.lineBreakColor + self.secureKeyCell.update(theme: theme) + self.securePassphraseCell.update(theme: theme) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } +} From 95f628f1c0668ff13cc392e8c335d7c0f730e1df Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 23 Jun 2020 18:15:26 +0200 Subject: [PATCH 14/22] Secrets setup: Handle set up recovery key screen. --- .../SecretsSetupRecoveryKeyCoordinator.swift | 72 ++++++ ...cretsSetupRecoveryKeyCoordinatorType.swift | 30 +++ .../SecretsSetupRecoveryKeyViewAction.swift | 27 +++ ...sSetupRecoveryKeyViewController.storyboard | 163 +++++++++++++ ...ecretsSetupRecoveryKeyViewController.swift | 225 ++++++++++++++++++ .../SecretsSetupRecoveryKeyViewModel.swift | 72 ++++++ ...SecretsSetupRecoveryKeyViewModelType.swift | 38 +++ .../SecretsSetupRecoveryKeyViewState.swift | 26 ++ 8 files changed, 653 insertions(+) create mode 100644 Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift create mode 100644 Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinatorType.swift create mode 100644 Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewAction.swift create mode 100644 Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewController.storyboard create mode 100644 Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewController.swift create mode 100644 Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift create mode 100644 Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModelType.swift create mode 100644 Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewState.swift diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift new file mode 100644 index 000000000..076065aff --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift @@ -0,0 +1,72 @@ +// File created from ScreenTemplate +// $ createScreen.sh SecretsSetupRecoveryKey SecretsSetupRecoveryKey +/* + Copyright 2020 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 +import UIKit + +final class SecretsSetupRecoveryKeyCoordinator: SecretsSetupRecoveryKeyCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private var secretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelType + private let secretsSetupRecoveryKeyViewController: SecretsSetupRecoveryKeyViewController + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: SecretsSetupRecoveryKeyCoordinatorDelegate? + + // MARK: - Setup + + init(recoveryService: MXRecoveryService, passphrase: String?) { + let secretsSetupRecoveryKeyViewModel = SecretsSetupRecoveryKeyViewModel(recoveryService: recoveryService, passphrase: passphrase) + let secretsSetupRecoveryKeyViewController = SecretsSetupRecoveryKeyViewController.instantiate(with: secretsSetupRecoveryKeyViewModel) + self.secretsSetupRecoveryKeyViewModel = secretsSetupRecoveryKeyViewModel + self.secretsSetupRecoveryKeyViewController = secretsSetupRecoveryKeyViewController + } + + // MARK: - Public methods + + func start() { + self.secretsSetupRecoveryKeyViewModel.coordinatorDelegate = self + } + + func toPresentable() -> UIViewController { + return self.secretsSetupRecoveryKeyViewController + } +} + +// MARK: - SecretsSetupRecoveryKeyViewModelCoordinatorDelegate +extension SecretsSetupRecoveryKeyCoordinator: SecretsSetupRecoveryKeyViewModelCoordinatorDelegate { + + func secretsSetupRecoveryKeyViewModelDidComplete(_ viewModel: SecretsSetupRecoveryKeyViewModelType) { + self.delegate?.secretsSetupRecoveryKeyCoordinatorDidComplete(self) + } + + func secretsSetupRecoveryKeyViewModelDidFailed(_ viewModel: SecretsSetupRecoveryKeyViewModelType) { + self.delegate?.secretsSetupRecoveryKeyCoordinatorDidFailed(self) + } + + func secretsSetupRecoveryKeyViewModelDidCancel(_ viewModel: SecretsSetupRecoveryKeyViewModelType) { + self.delegate?.secretsSetupRecoveryKeyCoordinatorDidCancel(self) + } +} diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinatorType.swift b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinatorType.swift new file mode 100644 index 000000000..0c2002813 --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinatorType.swift @@ -0,0 +1,30 @@ +// File created from ScreenTemplate +// $ createScreen.sh SecretsSetupRecoveryKey SecretsSetupRecoveryKey +/* + Copyright 2020 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 + +protocol SecretsSetupRecoveryKeyCoordinatorDelegate: class { + func secretsSetupRecoveryKeyCoordinatorDidComplete(_ coordinator: SecretsSetupRecoveryKeyCoordinatorType) + func secretsSetupRecoveryKeyCoordinatorDidFailed(_ coordinator: SecretsSetupRecoveryKeyCoordinatorType) + func secretsSetupRecoveryKeyCoordinatorDidCancel(_ coordinator: SecretsSetupRecoveryKeyCoordinatorType) +} + +/// `SecretsSetupRecoveryKeyCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. +protocol SecretsSetupRecoveryKeyCoordinatorType: Coordinator, Presentable { + var delegate: SecretsSetupRecoveryKeyCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewAction.swift b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewAction.swift new file mode 100644 index 000000000..3139a14e1 --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewAction.swift @@ -0,0 +1,27 @@ +// File created from ScreenTemplate +// $ createScreen.sh SecretsSetupRecoveryKey SecretsSetupRecoveryKey +/* + Copyright 2020 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 + +/// SecretsSetupRecoveryKeyViewController view actions exposed to view model +enum SecretsSetupRecoveryKeyViewAction { + case loadData + case done + case errorAlertOk + case cancel +} diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewController.storyboard b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewController.storyboard new file mode 100644 index 000000000..11ee58eaa --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewController.storyboard @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewController.swift b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewController.swift new file mode 100644 index 000000000..cafb5b48a --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewController.swift @@ -0,0 +1,225 @@ +// File created from ScreenTemplate +// $ createScreen.sh SecretsSetupRecoveryKey SecretsSetupRecoveryKey +/* + Copyright 2020 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 UIKit + +final class SecretsSetupRecoveryKeyViewController: UIViewController { + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var secureKeyImageView: UIImageView! + @IBOutlet private weak var informationLabel: UILabel! + @IBOutlet private weak var recoveryKeyLabel: UILabel! + @IBOutlet private weak var exportButton: RoundedButton! + @IBOutlet private weak var doneButton: RoundedButton! + + // MARK: Private + + private var viewModel: SecretsSetupRecoveryKeyViewModelType! + private var theme: Theme! + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + + private var recoveryKey: String? + private var hasSavedRecoveryKey: Bool = false + + // MARK: - Setup + + class func instantiate(with viewModel: SecretsSetupRecoveryKeyViewModelType) -> SecretsSetupRecoveryKeyViewController { + let viewController = StoryboardScene.SecretsSetupRecoveryKeyViewController.initialScene.instantiate() + viewController.viewModel = viewModel + viewController.theme = ThemeService.shared().theme + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.setupViews() + self.activityPresenter = ActivityIndicatorPresenter() + self.errorPresenter = MXKErrorAlertPresentation() + + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + + self.viewModel.viewDelegate = self + + self.viewModel.process(viewAction: .loadData) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + // Hide back button + self.navigationItem.setHidesBackButton(true, animated: animated) + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + self.secureKeyImageView.tintColor = theme.textPrimaryColor + self.informationLabel.textColor = theme.textPrimaryColor + self.recoveryKeyLabel.textColor = theme.textSecondaryColor + + self.exportButton.update(theme: theme) + self.doneButton.update(theme: theme) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + private func setupViews() { + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in + self?.cancelButtonAction() + } + + self.navigationItem.rightBarButtonItem = cancelBarButtonItem + + self.vc_removeBackTitle() + + self.title = VectorL10n.secretsSetupRecoveryKeyTitle + + self.secureKeyImageView.image = Asset.Images.secretsSetupKey.image.withRenderingMode(.alwaysTemplate) + self.informationLabel.text = VectorL10n.secretsSetupRecoveryKeyInformation + self.recoveryKeyLabel.text = VectorL10n.secretsSetupRecoveryKeyLoading + + self.exportButton.setTitle(VectorL10n.secretsSetupRecoveryKeyExportAction, for: .normal) + self.exportButton.isEnabled = false + self.doneButton.setTitle(VectorL10n.continue, for: .normal) + + self.updateDoneButton() + } + + private func render(viewState: SecretsSetupRecoveryKeyViewState) { + switch viewState { + case .loading: + self.renderLoading() + case .loaded(let recoveryKey): + self.renderLoaded(recoveryKey: recoveryKey) + case .error(let error): + self.render(error: error) + } + } + + private func renderLoading() { + self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) + } + + private func renderLoaded(recoveryKey: String) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + self.exportButton.isEnabled = true + self.recoveryKey = recoveryKey + self.recoveryKeyLabel.text = recoveryKey + } + + private func render(error: Error) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + self.errorPresenter.presentError(from: self, forError: error, animated: true) { + self.viewModel.process(viewAction: .errorAlertOk) + } + } + + private func updateDoneButton() { + self.doneButton.isEnabled = self.hasSavedRecoveryKey + } + + private func presentKeepSafeAlert() { + let alertController = UIAlertController(title: VectorL10n.secretsSetupRecoveryKeyStorageAlertTitle, + message: VectorL10n.secretsSetupRecoveryKeyStorageAlertMessage, + preferredStyle: .alert) + + alertController.addAction(UIAlertAction(title: VectorL10n.continue, style: .cancel, handler: { action in + self.viewModel.process(viewAction: .done) + })) + + self.present(alertController, animated: true, completion: nil) + } + + private func shareRecoveryKey() { + guard let recoveryKey = self.recoveryKey else { + return + } + + // Set up activity view controller + let activityItems: [Any] = [ recoveryKey ] + let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil) + + activityViewController.completionWithItemsHandler = { (activityType, completed, returnedItems, error) in + + // Enable made copy button only if user has selected an activity item and has setup recovery key without passphrase + if completed { + self.hasSavedRecoveryKey = true + self.updateDoneButton() + } + } + + // Configure source view when activity view controller is presented with a popover + if let popoverPresentationController = activityViewController.popoverPresentationController { + popoverPresentationController.sourceView = self.exportButton + popoverPresentationController.sourceRect = self.exportButton.bounds + popoverPresentationController.permittedArrowDirections = [.down, .up] + } + + self.present(activityViewController, animated: true) + } + + // MARK: - Actions + + @IBAction private func exportButtonAction(_ sender: Any) { + self.shareRecoveryKey() + } + + @IBAction private func doneButtonAction(_ sender: Any) { + self.presentKeepSafeAlert() + } + + private func cancelButtonAction() { + self.viewModel.process(viewAction: .cancel) + } +} + +// MARK: - SecretsSetupRecoveryKeyViewModelViewDelegate +extension SecretsSetupRecoveryKeyViewController: SecretsSetupRecoveryKeyViewModelViewDelegate { + + func secretsSetupRecoveryKeyViewModel(_ viewModel: SecretsSetupRecoveryKeyViewModelType, didUpdateViewState viewSate: SecretsSetupRecoveryKeyViewState) { + self.render(viewState: viewSate) + } +} diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift new file mode 100644 index 000000000..5ca915323 --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift @@ -0,0 +1,72 @@ +// File created from ScreenTemplate +// $ createScreen.sh SecretsSetupRecoveryKey SecretsSetupRecoveryKey +/* + Copyright 2020 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 + +final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelType { + + // MARK: - Properties + + // MARK: Private + + private let recoveryService: MXRecoveryService + private let passphrase: String? + + // MARK: Public + + weak var viewDelegate: SecretsSetupRecoveryKeyViewModelViewDelegate? + weak var coordinatorDelegate: SecretsSetupRecoveryKeyViewModelCoordinatorDelegate? + + // MARK: - Setup + + init(recoveryService: MXRecoveryService, passphrase: String?) { + self.recoveryService = recoveryService + self.passphrase = passphrase + } + + // MARK: - Public + + func process(viewAction: SecretsSetupRecoveryKeyViewAction) { + switch viewAction { + case .loadData: + self.createSecureKey() + case .done: + self.coordinatorDelegate?.secretsSetupRecoveryKeyViewModelDidComplete(self) + case .errorAlertOk: + self.coordinatorDelegate?.secretsSetupRecoveryKeyViewModelDidFailed(self) + case .cancel: + self.coordinatorDelegate?.secretsSetupRecoveryKeyViewModelDidCancel(self) + } + } + + // MARK: - Private + + private func createSecureKey() { + self.update(viewState: .loading) + + self.recoveryService.createRecovery(forSecrets: nil, withPassphrase: self.passphrase, success: { secretStorageKeyCreationInfo in + self.update(viewState: .loaded(secretStorageKeyCreationInfo.recoveryKey)) + }, failure: { error in + self.update(viewState: .error(error)) + }) + } + + private func update(viewState: SecretsSetupRecoveryKeyViewState) { + self.viewDelegate?.secretsSetupRecoveryKeyViewModel(self, didUpdateViewState: viewState) + } +} diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModelType.swift b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModelType.swift new file mode 100644 index 000000000..3cf3843d2 --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModelType.swift @@ -0,0 +1,38 @@ +// File created from ScreenTemplate +// $ createScreen.sh SecretsSetupRecoveryKey SecretsSetupRecoveryKey +/* + Copyright 2020 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 + +protocol SecretsSetupRecoveryKeyViewModelViewDelegate: class { + func secretsSetupRecoveryKeyViewModel(_ viewModel: SecretsSetupRecoveryKeyViewModelType, didUpdateViewState viewSate: SecretsSetupRecoveryKeyViewState) +} + +protocol SecretsSetupRecoveryKeyViewModelCoordinatorDelegate: class { + func secretsSetupRecoveryKeyViewModelDidComplete(_ viewModel: SecretsSetupRecoveryKeyViewModelType) + func secretsSetupRecoveryKeyViewModelDidFailed(_ viewModel: SecretsSetupRecoveryKeyViewModelType) + func secretsSetupRecoveryKeyViewModelDidCancel(_ viewModel: SecretsSetupRecoveryKeyViewModelType) +} + +/// Protocol describing the view model used by `SecretsSetupRecoveryKeyViewController` +protocol SecretsSetupRecoveryKeyViewModelType { + + var viewDelegate: SecretsSetupRecoveryKeyViewModelViewDelegate? { get set } + var coordinatorDelegate: SecretsSetupRecoveryKeyViewModelCoordinatorDelegate? { get set } + + func process(viewAction: SecretsSetupRecoveryKeyViewAction) +} diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewState.swift b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewState.swift new file mode 100644 index 000000000..c9e18e1d2 --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewState.swift @@ -0,0 +1,26 @@ +// File created from ScreenTemplate +// $ createScreen.sh SecretsSetupRecoveryKey SecretsSetupRecoveryKey +/* + Copyright 2020 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 + +/// SecretsSetupRecoveryKeyViewController view state +enum SecretsSetupRecoveryKeyViewState { + case loading + case loaded(_ recoveryKey: String) + case error(Error) +} From 43598c95932459f60876d0f431c05c57648abc61 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 23 Jun 2020 18:17:38 +0200 Subject: [PATCH 15/22] Secrets setup: Handle set up recovery passphrase screen. --- ...tsSetupRecoveryPassphraseCoordinator.swift | 73 ++++ ...tupRecoveryPassphraseCoordinatorType.swift | 30 ++ ...retsSetupRecoveryPassphraseInputMode.swift | 24 ++ ...etsSetupRecoveryPassphraseViewAction.swift | 27 ++ ...ecoveryPassphraseViewController.storyboard | 285 ++++++++++++++++ ...etupRecoveryPassphraseViewController.swift | 321 ++++++++++++++++++ ...retsSetupRecoveryPassphraseViewModel.swift | 126 +++++++ ...SetupRecoveryPassphraseViewModelType.swift | 38 +++ ...retsSetupRecoveryPassphraseViewState.swift | 36 ++ 9 files changed, 960 insertions(+) create mode 100644 Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseCoordinator.swift create mode 100644 Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseCoordinatorType.swift create mode 100644 Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseInputMode.swift create mode 100644 Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewAction.swift create mode 100644 Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewController.storyboard create mode 100644 Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewController.swift create mode 100644 Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewModel.swift create mode 100644 Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewModelType.swift create mode 100644 Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewState.swift diff --git a/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseCoordinator.swift b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseCoordinator.swift new file mode 100644 index 000000000..2f67abb74 --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseCoordinator.swift @@ -0,0 +1,73 @@ +// File created from ScreenTemplate +// $ createScreen.sh Test SecretsSetupRecoveryPassphrase +/* + Copyright 2020 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 +import UIKit + +final class SecretsSetupRecoveryPassphraseCoordinator: SecretsSetupRecoveryPassphraseCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private var secretsSetupRecoveryPassphraseViewModel: SecretsSetupRecoveryPassphraseViewModelType + private let secretsSetupRecoveryPassphraseViewController: SecretsSetupRecoveryPassphraseViewController + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: SecretsSetupRecoveryPassphraseCoordinatorDelegate? + + // MARK: - Setup + + init(passphraseInput: SecretsSetupRecoveryPassphraseInput) { + + let secretsSetupRecoveryPassphraseViewModel = SecretsSetupRecoveryPassphraseViewModel(passphraseInput: passphraseInput) + let secretsSetupRecoveryPassphraseViewController = SecretsSetupRecoveryPassphraseViewController.instantiate(with: secretsSetupRecoveryPassphraseViewModel) + self.secretsSetupRecoveryPassphraseViewModel = secretsSetupRecoveryPassphraseViewModel + self.secretsSetupRecoveryPassphraseViewController = secretsSetupRecoveryPassphraseViewController + } + + // MARK: - Public methods + + func start() { + self.secretsSetupRecoveryPassphraseViewModel.coordinatorDelegate = self + } + + func toPresentable() -> UIViewController { + return self.secretsSetupRecoveryPassphraseViewController + } +} + +// MARK: - SecretsSetupRecoveryPassphraseViewModelCoordinatorDelegate +extension SecretsSetupRecoveryPassphraseCoordinator: SecretsSetupRecoveryPassphraseViewModelCoordinatorDelegate { + + func secretsSetupRecoveryPassphraseViewModel(_ viewModel: SecretsSetupRecoveryPassphraseViewModelType, didEnterNewPassphrase passphrase: String) { + self.delegate?.secretsSetupRecoveryPassphraseCoordinator(self, didEnterNewPassphrase: passphrase) + } + + func secretsSetupRecoveryPassphraseViewModel(_ viewModel: SecretsSetupRecoveryPassphraseViewModelType, didConfirmPassphrase passphrase: String) { + self.delegate?.secretsSetupRecoveryPassphraseCoordinator(self, didConfirmPassphrase: passphrase) + } + + func secretsSetupRecoveryPassphraseViewModelDidCancel(_ viewModel: SecretsSetupRecoveryPassphraseViewModelType) { + self.delegate?.secretsSetupRecoveryPassphraseCoordinatorDidCancel(self) + } +} diff --git a/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseCoordinatorType.swift b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseCoordinatorType.swift new file mode 100644 index 000000000..4318c4414 --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseCoordinatorType.swift @@ -0,0 +1,30 @@ +// File created from ScreenTemplate +// $ createScreen.sh Test SecretsSetupRecoveryPassphrase +/* + Copyright 2020 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 + +protocol SecretsSetupRecoveryPassphraseCoordinatorDelegate: class { + func secretsSetupRecoveryPassphraseCoordinator(_ coordinator: SecretsSetupRecoveryPassphraseCoordinatorType, didEnterNewPassphrase passphrase: String) + func secretsSetupRecoveryPassphraseCoordinator(_ coordinator: SecretsSetupRecoveryPassphraseCoordinatorType, didConfirmPassphrase passphrase: String) + func secretsSetupRecoveryPassphraseCoordinatorDidCancel(_ coordinator: SecretsSetupRecoveryPassphraseCoordinatorType) +} + +/// `SecretsSetupRecoveryPassphraseCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. +protocol SecretsSetupRecoveryPassphraseCoordinatorType: Coordinator, Presentable { + var delegate: SecretsSetupRecoveryPassphraseCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseInputMode.swift b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseInputMode.swift new file mode 100644 index 000000000..142984280 --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseInputMode.swift @@ -0,0 +1,24 @@ +// File created from ScreenTemplate +// $ createScreen.sh Test SecretsSetupRecoveryPassphrase +/* + Copyright 2020 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 SecretsSetupRecoveryPassphraseInput { + case new + case confirm(_ passphrase: String) +} diff --git a/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewAction.swift b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewAction.swift new file mode 100644 index 000000000..f090bc98e --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewAction.swift @@ -0,0 +1,27 @@ +// File created from ScreenTemplate +// $ createScreen.sh Test SecretsSetupRecoveryPassphrase +/* + Copyright 2020 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 + +/// SecretsSetupRecoveryPassphraseViewController view actions exposed to view model +enum SecretsSetupRecoveryPassphraseViewAction { + case loadData + case updatePassphrase(_ passphrase: String?) + case validate + case cancel +} diff --git a/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewController.storyboard b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewController.storyboard new file mode 100644 index 000000000..b6fc256be --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewController.storyboard @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewController.swift b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewController.swift new file mode 100644 index 000000000..1de85b994 --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewController.swift @@ -0,0 +1,321 @@ +// File created from ScreenTemplate +// $ createScreen.sh Test SecretsSetupRecoveryPassphrase +/* + Copyright 2020 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 UIKit + +final class SecretsSetupRecoveryPassphraseViewController: UIViewController { + + // MARK: - Constants + + private enum Constants { + static let animationDuration: TimeInterval = 0.3 + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var scrollView: UIScrollView! + + @IBOutlet private weak var securePassphraseImageView: UIImageView! + @IBOutlet private weak var informationLabel: UILabel! + + @IBOutlet private weak var formBackgroundView: UIView! + + @IBOutlet private weak var passphraseTitleLabel: UILabel! + @IBOutlet private weak var passphraseTextField: UITextField! + @IBOutlet private weak var passphraseVisibilityButton: UIButton! + + @IBOutlet private weak var passphraseAdditionalInfoView: UIView! + @IBOutlet private weak var passphraseStrengthContainerView: UIView! + @IBOutlet private weak var passphraseStrengthView: PasswordStrengthView! + @IBOutlet private weak var passphraseAdditionalLabel: UILabel! + + @IBOutlet private weak var additionalInformationLabel: UILabel! + + @IBOutlet private weak var validateButton: RoundedButton! + + // MARK: Private + + private var viewModel: SecretsSetupRecoveryPassphraseViewModelType! + private var theme: Theme! + private var keyboardAvoider: KeyboardAvoider? + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + private var isFirstViewAppearing: Bool = true + private var isPassphraseTextFieldEditedOnce: Bool = false + + private var currentViewData: SecretsSetupRecoveryPassphraseViewData? + + // MARK: - Setup + + class func instantiate(with viewModel: SecretsSetupRecoveryPassphraseViewModelType) -> SecretsSetupRecoveryPassphraseViewController { + let viewController = StoryboardScene.SecretsSetupRecoveryPassphraseViewController.initialScene.instantiate() + viewController.viewModel = viewModel + viewController.theme = ThemeService.shared().theme + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.setupViews() + self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView) + self.activityPresenter = ActivityIndicatorPresenter() + self.errorPresenter = MXKErrorAlertPresentation() + + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + + self.viewModel.viewDelegate = self + + self.viewModel.process(viewAction: .loadData) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + self.keyboardAvoider?.startAvoiding() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if self.isFirstViewAppearing { + self.isFirstViewAppearing = false + } + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + self.view.endEditing(true) + self.keyboardAvoider?.stopAvoiding() + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func setupViews() { + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in + self?.cancelButtonAction() + } + + self.navigationItem.rightBarButtonItem = cancelBarButtonItem + + self.vc_removeBackTitle() + + self.title = VectorL10n.secretsSetupRecoveryPassphraseTitle + + self.scrollView.keyboardDismissMode = .interactive + + self.securePassphraseImageView.image = Asset.Images.secretsSetupPassphrase.image.withRenderingMode(.alwaysTemplate) + + self.passphraseTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged) + self.passphraseAdditionalInfoView.isHidden = true + + let visibilityImage = Asset.Images.revealPasswordButton.image.withRenderingMode(.alwaysTemplate) + self.passphraseVisibilityButton.setImage(visibilityImage, for: .normal) + + self.additionalInformationLabel.text = VectorL10n.secretsSetupRecoveryPassphraseAdditionalInformation + } + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + self.securePassphraseImageView.tintColor = theme.textPrimaryColor + + self.informationLabel.textColor = theme.textPrimaryColor + + self.formBackgroundView.backgroundColor = theme.backgroundColor + self.passphraseTitleLabel.textColor = theme.textPrimaryColor + theme.applyStyle(onTextField: self.passphraseTextField) + + let passphraseTitle: String + + if let viewData = self.currentViewData, case .confimPassphrase = viewData.mode { + passphraseTitle = VectorL10n.secretsSetupRecoveryPassphraseConfirmPassphrasePlaceholder + } else { + passphraseTitle = VectorL10n.keyBackupSetupPassphrasePassphrasePlaceholder + } + + self.passphraseTextField.attributedPlaceholder = NSAttributedString(string: passphraseTitle, + attributes: [.foregroundColor: theme.placeholderTextColor]) + self.passphraseVisibilityButton.tintColor = theme.tintColor + + self.additionalInformationLabel.textColor = theme.textSecondaryColor + + self.validateButton.update(theme: theme) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + private func render(viewState: SecretsSetupRecoveryPassphraseViewState) { + switch viewState { + case .loaded(let viewData): + self.renderLoaded(viewData: viewData) + case .formUpdated(let viewData): + self.renderFormUpdated(viewData: viewData) + case .error(let error): + self.render(error: error) + } + } + + private func renderLoaded(viewData: SecretsSetupRecoveryPassphraseViewData) { + + self.currentViewData = viewData + + let informationText: String + let passphraseTitle: String + let showPasswordStrength: Bool + + switch viewData.mode { + case .newPassphrase(strength: let strength): + informationText = VectorL10n.secretsSetupRecoveryPassphraseInformation + passphraseTitle = VectorL10n.keyBackupSetupPassphrasePassphraseTitle + showPasswordStrength = true + self.passphraseStrengthView.strength = strength + case .confimPassphrase: + informationText = VectorL10n.secretsSetupRecoveryPassphraseConfirmInformation + passphraseTitle = VectorL10n.secretsSetupRecoveryPassphraseConfirmPassphraseTitle + showPasswordStrength = false + } + + self.informationLabel.text = informationText + self.passphraseTitleLabel.text = passphraseTitle + + self.passphraseStrengthContainerView.isHidden = !showPasswordStrength + + self.update(theme: self.theme) + } + + private func renderFormUpdated(viewData: SecretsSetupRecoveryPassphraseViewData) { + self.currentViewData = viewData + + if case .newPassphrase(strength: let strength) = viewData.mode { + self.passphraseStrengthView.strength = strength + } + + self.validateButton.isEnabled = viewData.isFormValid + self.updatePassphraseAdditionalLabel(viewData: viewData) + + // Show passphrase additional info at first character entered + if self.isPassphraseTextFieldEditedOnce == false, self.passphraseTextField.text?.isEmpty == false { + self.isPassphraseTextFieldEditedOnce = true + self.showPassphraseAdditionalInfo(animated: true) + } + } + + private func render(error: Error) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil) + } + + private func showPassphraseAdditionalInfo(animated: Bool) { + guard self.passphraseAdditionalInfoView.isHidden else { + return + } + + // Workaround to layout passphraseStrengthView corner radius + self.passphraseStrengthView.setNeedsLayout() + + UIView.animate(withDuration: Constants.animationDuration) { + self.passphraseAdditionalInfoView.isHidden = false + } + } + + private func updatePassphraseAdditionalLabel(viewData: SecretsSetupRecoveryPassphraseViewData) { + + let text: String + let textColor: UIColor + + if viewData.isFormValid { + switch viewData.mode { + case .newPassphrase: + text = VectorL10n.keyBackupSetupPassphrasePassphraseValid + case .confimPassphrase: + text = VectorL10n.keyBackupSetupPassphraseConfirmPassphraseValid + } + + textColor = self.theme.tintColor + } else { + switch viewData.mode { + case .newPassphrase: + text = VectorL10n.keyBackupSetupPassphrasePassphraseInvalid + case .confimPassphrase: + text = VectorL10n.keyBackupSetupPassphraseConfirmPassphraseInvalid + } + + textColor = self.theme.noticeColor + } + + self.passphraseAdditionalLabel.text = text + self.passphraseAdditionalLabel.textColor = textColor + } + + // MARK: - Actions + + @IBAction private func passphraseVisibilityButtonAction(_ sender: Any) { + guard self.isPassphraseTextFieldEditedOnce else { + return + } + self.passphraseTextField.isSecureTextEntry.toggle() + } + + @objc private func textFieldDidChange(_ textField: UITextField) { + guard textField == self.passphraseTextField else { + return + } + self.viewModel.process(viewAction: .updatePassphrase(textField.text)) + } + + @IBAction private func validateButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .validate) + } + + private func cancelButtonAction() { + self.viewModel.process(viewAction: .cancel) + } +} + +// MARK: - SecretsSetupRecoveryPassphraseViewModelViewDelegate +extension SecretsSetupRecoveryPassphraseViewController: SecretsSetupRecoveryPassphraseViewModelViewDelegate { + + func secretsSetupRecoveryPassphraseViewModel(_ viewModel: SecretsSetupRecoveryPassphraseViewModelType, didUpdateViewState viewSate: SecretsSetupRecoveryPassphraseViewState) { + self.render(viewState: viewSate) + } +} diff --git a/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewModel.swift b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewModel.swift new file mode 100644 index 000000000..1ae003e9c --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewModel.swift @@ -0,0 +1,126 @@ +// File created from ScreenTemplate +// $ createScreen.sh Test SecretsSetupRecoveryPassphrase +/* + Copyright 2020 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 + +final class SecretsSetupRecoveryPassphraseViewModel: SecretsSetupRecoveryPassphraseViewModelType { + + // MARK: - Properties + + // MARK: Private + + private let passphraseInput: SecretsSetupRecoveryPassphraseInput + private let passwordStrengthManager: PasswordStrengthManager + + private var currentViewData: SecretsSetupRecoveryPassphraseViewData? + private var passphrase: String? + + // MARK: Public + + weak var viewDelegate: SecretsSetupRecoveryPassphraseViewModelViewDelegate? + weak var coordinatorDelegate: SecretsSetupRecoveryPassphraseViewModelCoordinatorDelegate? + + // MARK: - Setup + + init(passphraseInput: SecretsSetupRecoveryPassphraseInput) { + self.passphraseInput = passphraseInput + self.passwordStrengthManager = PasswordStrengthManager() + } + + // MARK: - Public + + func process(viewAction: SecretsSetupRecoveryPassphraseViewAction) { + switch viewAction { + case .loadData: + self.loadData() + case .updatePassphrase(let passphrase): + self.updatePassphrase(passphrase) + case .validate: + self.validate() + case .cancel: + self.coordinatorDelegate?.secretsSetupRecoveryPassphraseViewModelDidCancel(self) + } + } + + // MARK: - Private + + private func loadData() { + + let viewDataMode: SecretsSetupRecoveryPassphraseViewDataMode + + switch self.passphraseInput { + case .new: + viewDataMode = .newPassphrase(strength: .tooGuessable) + case .confirm: + viewDataMode = .confimPassphrase + } + + let viewData = SecretsSetupRecoveryPassphraseViewData(mode: viewDataMode, isFormValid: false) + + self.update(viewState: .loaded(viewData)) + } + + private func update(viewState: SecretsSetupRecoveryPassphraseViewState) { + self.viewDelegate?.secretsSetupRecoveryPassphraseViewModel(self, didUpdateViewState: viewState) + } + + private func updatePassphrase(_ passphrase: String?) { + + let viewDataMode: SecretsSetupRecoveryPassphraseViewDataMode + let isFormValid: Bool + + switch self.passphraseInput { + case .new: + let passphraseStrength = self.passwordStrength(for: passphrase) + viewDataMode = .newPassphrase(strength: passphraseStrength) + isFormValid = passphraseStrength == .veryUnguessable + case .confirm(let passphraseToConfirm): + viewDataMode = .confimPassphrase + isFormValid = passphrase == passphraseToConfirm + } + + let viewData = SecretsSetupRecoveryPassphraseViewData(mode: viewDataMode, isFormValid: isFormValid) + + self.passphrase = passphrase + self.currentViewData = viewData + + self.update(viewState: .formUpdated(viewData)) + } + + private func validate() { + guard let viewData = self.currentViewData, + viewData.isFormValid, + let passphrase = self.passphrase else { + return + } + + switch self.passphraseInput { + case .new: + self.coordinatorDelegate?.secretsSetupRecoveryPassphraseViewModel(self, didEnterNewPassphrase: passphrase) + case .confirm: + self.coordinatorDelegate?.secretsSetupRecoveryPassphraseViewModel(self, didConfirmPassphrase: passphrase) + } + } + + private func passwordStrength(for password: String?) -> PasswordStrength { + guard let password = password else { + return .tooGuessable + } + return self.passwordStrengthManager.passwordStrength(for: password) + } +} diff --git a/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewModelType.swift b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewModelType.swift new file mode 100644 index 000000000..a7f27ad22 --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewModelType.swift @@ -0,0 +1,38 @@ +// File created from ScreenTemplate +// $ createScreen.sh Test SecretsSetupRecoveryPassphrase +/* + Copyright 2020 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 + +protocol SecretsSetupRecoveryPassphraseViewModelViewDelegate: class { + func secretsSetupRecoveryPassphraseViewModel(_ viewModel: SecretsSetupRecoveryPassphraseViewModelType, didUpdateViewState viewSate: SecretsSetupRecoveryPassphraseViewState) +} + +protocol SecretsSetupRecoveryPassphraseViewModelCoordinatorDelegate: class { + func secretsSetupRecoveryPassphraseViewModel(_ viewModel: SecretsSetupRecoveryPassphraseViewModelType, didEnterNewPassphrase passphrase: String) + func secretsSetupRecoveryPassphraseViewModel(_ viewModel: SecretsSetupRecoveryPassphraseViewModelType, didConfirmPassphrase passphrase: String) + func secretsSetupRecoveryPassphraseViewModelDidCancel(_ viewModel: SecretsSetupRecoveryPassphraseViewModelType) +} + +/// Protocol describing the view model used by `SecretsSetupRecoveryPassphraseViewController` +protocol SecretsSetupRecoveryPassphraseViewModelType { + + var viewDelegate: SecretsSetupRecoveryPassphraseViewModelViewDelegate? { get set } + var coordinatorDelegate: SecretsSetupRecoveryPassphraseViewModelCoordinatorDelegate? { get set } + + func process(viewAction: SecretsSetupRecoveryPassphraseViewAction) +} diff --git a/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewState.swift b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewState.swift new file mode 100644 index 000000000..72ece5367 --- /dev/null +++ b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewState.swift @@ -0,0 +1,36 @@ +// File created from ScreenTemplate +// $ createScreen.sh Test SecretsSetupRecoveryPassphrase +/* + Copyright 2020 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 SecretsSetupRecoveryPassphraseViewDataMode { + case newPassphrase(strength: PasswordStrength) + case confimPassphrase +} + +struct SecretsSetupRecoveryPassphraseViewData { + let mode: SecretsSetupRecoveryPassphraseViewDataMode + let isFormValid: Bool +} + +/// SecretsSetupRecoveryPassphraseViewController view state +enum SecretsSetupRecoveryPassphraseViewState { + case loaded(_ viewData: SecretsSetupRecoveryPassphraseViewData) + case formUpdated(_ viewData: SecretsSetupRecoveryPassphraseViewData) + case error(Error) +} From af43b42d344a3da9181c7b01d25d82d46369fc69 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 23 Jun 2020 18:18:32 +0200 Subject: [PATCH 16/22] Secure key backup: Handle secure key backup flow. --- .../SecureKeyBackupSetupCoordinator.swift | 171 ++++++++++++++++++ ...ackupSetupCoordinatorBridgePresenter.swift | 88 +++++++++ .../SecureKeyBackupSetupCoordinatorType.swift | 29 +++ 3 files changed, 288 insertions(+) create mode 100644 Riot/Modules/KeyBackup/SecureSetup/SecureKeyBackupSetupCoordinator.swift create mode 100644 Riot/Modules/KeyBackup/SecureSetup/SecureKeyBackupSetupCoordinatorBridgePresenter.swift create mode 100644 Riot/Modules/KeyBackup/SecureSetup/SecureKeyBackupSetupCoordinatorType.swift diff --git a/Riot/Modules/KeyBackup/SecureSetup/SecureKeyBackupSetupCoordinator.swift b/Riot/Modules/KeyBackup/SecureSetup/SecureKeyBackupSetupCoordinator.swift new file mode 100644 index 000000000..c3328a6e5 --- /dev/null +++ b/Riot/Modules/KeyBackup/SecureSetup/SecureKeyBackupSetupCoordinator.swift @@ -0,0 +1,171 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh KeyBackupSetup/SecureSetup SecureKeyBackupSetup +/* + Copyright 2020 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 UIKit + +@objcMembers +final class SecureKeyBackupSetupCoordinator: SecureKeyBackupSetupCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let navigationRouter: NavigationRouterType + private let recoveryService: MXRecoveryService + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: SecureKeyBackupSetupCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController()) + self.recoveryService = session.crypto.recoveryService + } + + // MARK: - Public methods + + func start() { + let rootViewController = self.createIntro() + self.navigationRouter.setRootModule(rootViewController) + } + + func toPresentable() -> UIViewController { + return self.navigationRouter.toPresentable() + } + + // MARK: - Private methods + + private func createIntro() -> SecureKeyBackupSetupIntroViewController { + let introViewController = SecureKeyBackupSetupIntroViewController.instantiate() + introViewController.delegate = self + return introViewController + } + + private func showSetupKey(passphrase: String? = nil) { + let coordinator = SecretsSetupRecoveryKeyCoordinator(recoveryService: self.recoveryService, passphrase: passphrase) + coordinator.delegate = self + coordinator.start() + + self.add(childCoordinator: coordinator) + self.navigationRouter.push(coordinator, animated: true) { [weak self] in + self?.remove(childCoordinator: coordinator) + } + } + + private func showSetupPassphrase() { + let coordinator = SecretsSetupRecoveryPassphraseCoordinator(passphraseInput: .new) + coordinator.delegate = self + coordinator.start() + + self.add(childCoordinator: coordinator) + self.navigationRouter.push(coordinator, animated: true) { [weak self] in + self?.remove(childCoordinator: coordinator) + } + } + + private func showSetupPassphraseConfirmation(with passphrase: String) { + let coordinator = SecretsSetupRecoveryPassphraseCoordinator(passphraseInput: .confirm(passphrase)) + coordinator.delegate = self + coordinator.start() + + self.add(childCoordinator: coordinator) + self.navigationRouter.push(coordinator, animated: true) { [weak self] in + self?.remove(childCoordinator: coordinator) + } + } + + private func showCancelAlert() { + let alertController = UIAlertController(title: VectorL10n.secureKeyBackupSetupCancelAlertTitle, + message: VectorL10n.secureKeyBackupSetupCancelAlertMessage, + preferredStyle: .alert) + + alertController.addAction(UIAlertAction(title: VectorL10n.continue, style: .cancel, handler: { action in + })) + + alertController.addAction(UIAlertAction(title: VectorL10n.keyBackupSetupSkipAlertSkipAction, style: .default, handler: { action in + self.delegate?.secureKeyBackupSetupCoordinatorDidCancel(self) + })) + + self.navigationRouter.present(alertController, animated: true) + } + + private func didCancel(showSkipAlert: Bool = true) { + if showSkipAlert { + self.showCancelAlert() + } else { + self.delegate?.secureKeyBackupSetupCoordinatorDidCancel(self) + } + } + + private func didComplete() { + self.delegate?.secureKeyBackupSetupCoordinatorDidComplete(self) + } +} + +// MARK: - SecureKeyBackupSetupIntroViewControllerDelegate +extension SecureKeyBackupSetupCoordinator: SecureKeyBackupSetupIntroViewControllerDelegate { + + func secureKeyBackupSetupIntroViewControllerDidTapUseKey(_ secureKeyBackupSetupIntroViewController: SecureKeyBackupSetupIntroViewController) { + self.showSetupKey() + } + + func secureKeyBackupSetupIntroViewControllerDidTapUsePassphrase(_ secureKeyBackupSetupIntroViewController: SecureKeyBackupSetupIntroViewController) { + self.showSetupPassphrase() + } + + func secureKeyBackupSetupIntroViewControllerDidCancel(_ secureKeyBackupSetupIntroViewController: SecureKeyBackupSetupIntroViewController) { + self.didCancel() + } +} + +// MARK: - SecretsSetupRecoveryKeyCoordinatorDelegate +extension SecureKeyBackupSetupCoordinator: SecretsSetupRecoveryKeyCoordinatorDelegate { + + func secretsSetupRecoveryKeyCoordinatorDidComplete(_ coordinator: SecretsSetupRecoveryKeyCoordinatorType) { + self.didComplete() + } + + func secretsSetupRecoveryKeyCoordinatorDidFailed(_ coordinator: SecretsSetupRecoveryKeyCoordinatorType) { + self.didCancel(showSkipAlert: false) + } + + func secretsSetupRecoveryKeyCoordinatorDidCancel(_ coordinator: SecretsSetupRecoveryKeyCoordinatorType) { + self.didCancel() + } +} + +// MARK: - SecretsSetupRecoveryPassphraseCoordinatorDelegate +extension SecureKeyBackupSetupCoordinator: SecretsSetupRecoveryPassphraseCoordinatorDelegate { + + func secretsSetupRecoveryPassphraseCoordinator(_ coordinator: SecretsSetupRecoveryPassphraseCoordinatorType, didEnterNewPassphrase passphrase: String) { + self.showSetupPassphraseConfirmation(with: passphrase) + } + + func secretsSetupRecoveryPassphraseCoordinator(_ coordinator: SecretsSetupRecoveryPassphraseCoordinatorType, didConfirmPassphrase passphrase: String) { + self.showSetupKey(passphrase: passphrase) + } + + func secretsSetupRecoveryPassphraseCoordinatorDidCancel(_ coordinator: SecretsSetupRecoveryPassphraseCoordinatorType) { + self.didCancel() + } +} diff --git a/Riot/Modules/KeyBackup/SecureSetup/SecureKeyBackupSetupCoordinatorBridgePresenter.swift b/Riot/Modules/KeyBackup/SecureSetup/SecureKeyBackupSetupCoordinatorBridgePresenter.swift new file mode 100644 index 000000000..4e69e3a8d --- /dev/null +++ b/Riot/Modules/KeyBackup/SecureSetup/SecureKeyBackupSetupCoordinatorBridgePresenter.swift @@ -0,0 +1,88 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh KeyBackupSetup/SecureSetup SecureKeyBackupSetup +/* + Copyright 2020 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 + +@objc protocol SecureKeyBackupSetupCoordinatorBridgePresenterDelegate { + func secureKeyBackupSetupCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: SecureKeyBackupSetupCoordinatorBridgePresenter) + func secureKeyBackupSetupCoordinatorBridgePresenterDelegateDidCancel(_ coordinatorBridgePresenter: SecureKeyBackupSetupCoordinatorBridgePresenter) +} + +/// SecureKeyBackupSetupCoordinatorBridgePresenter enables to start SecureKeyBackupSetupCoordinator from a view controller. +/// This bridge is used while waiting for global usage of coordinator pattern. +@objcMembers +final class SecureKeyBackupSetupCoordinatorBridgePresenter: NSObject { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private var coordinator: SecureKeyBackupSetupCoordinator? + + // MARK: Public + + weak var delegate: SecureKeyBackupSetupCoordinatorBridgePresenterDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.session = session + super.init() + } + + // MARK: - Public + + // NOTE: Default value feature is not compatible with Objective-C. + // func present(from viewController: UIViewController, animated: Bool) { + // self.present(from: viewController, animated: animated) + // } + + func present(from viewController: UIViewController, animated: Bool) { + let secureKeyBackupSetupCoordinator = SecureKeyBackupSetupCoordinator(session: self.session) + secureKeyBackupSetupCoordinator.delegate = self + viewController.present(secureKeyBackupSetupCoordinator.toPresentable(), animated: animated, completion: nil) + secureKeyBackupSetupCoordinator.start() + + self.coordinator = secureKeyBackupSetupCoordinator + } + + func dismiss(animated: Bool, completion: (() -> Void)?) { + guard let coordinator = self.coordinator else { + return + } + coordinator.toPresentable().dismiss(animated: animated) { + self.coordinator = nil + + if let completion = completion { + completion() + } + } + } +} + +// MARK: - SecureKeyBackupSetupCoordinatorDelegate +extension SecureKeyBackupSetupCoordinatorBridgePresenter: SecureKeyBackupSetupCoordinatorDelegate { + func secureKeyBackupSetupCoordinatorDidComplete(_ coordinator: SecureKeyBackupSetupCoordinatorType) { + self.delegate?.secureKeyBackupSetupCoordinatorBridgePresenterDelegateDidComplete(self) + } + + func secureKeyBackupSetupCoordinatorDidCancel(_ coordinator: SecureKeyBackupSetupCoordinatorType) { + self.delegate?.secureKeyBackupSetupCoordinatorBridgePresenterDelegateDidCancel(self) + } +} diff --git a/Riot/Modules/KeyBackup/SecureSetup/SecureKeyBackupSetupCoordinatorType.swift b/Riot/Modules/KeyBackup/SecureSetup/SecureKeyBackupSetupCoordinatorType.swift new file mode 100644 index 000000000..0c51a497d --- /dev/null +++ b/Riot/Modules/KeyBackup/SecureSetup/SecureKeyBackupSetupCoordinatorType.swift @@ -0,0 +1,29 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh KeyBackupSetup/SecureSetup SecureKeyBackupSetup +/* + Copyright 2020 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 + +protocol SecureKeyBackupSetupCoordinatorDelegate: class { + func secureKeyBackupSetupCoordinatorDidComplete(_ coordinator: SecureKeyBackupSetupCoordinatorType) + func secureKeyBackupSetupCoordinatorDidCancel(_ coordinator: SecureKeyBackupSetupCoordinatorType) +} + +/// `SecureKeyBackupSetupCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow. +protocol SecureKeyBackupSetupCoordinatorType: Coordinator, Presentable { + var delegate: SecureKeyBackupSetupCoordinatorDelegate? { get } +} From 2ee7dee519753b209045c3f95e804b43a4a01137 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 23 Jun 2020 18:33:48 +0200 Subject: [PATCH 17/22] Fix rebase issue. --- Riot.xcodeproj/project.pbxproj | 163 +++++++++++++++++++++++++++++-- Riot/Generated/Storyboards.swift | 15 +++ 2 files changed, 168 insertions(+), 10 deletions(-) diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index d1a122ce6..107c2c645 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -249,6 +249,15 @@ B157FAA523264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B157FA9D23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModel.swift */; }; B157FAA623264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B157FA9E23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.swift */; }; B157FAA823264BED00EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B157FAA723264BED00EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter.swift */; }; + B15F076924A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15F076124A0FBA3005E26A1 /* SecretsSetupRecoveryPassphraseViewAction.swift */; }; + B15F076A24A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15F076224A0FBA4005E26A1 /* SecretsSetupRecoveryPassphraseViewController.swift */; }; + B15F076B24A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B15F076324A0FBA4005E26A1 /* SecretsSetupRecoveryPassphraseViewController.storyboard */; }; + B15F076C24A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15F076424A0FBA4005E26A1 /* SecretsSetupRecoveryPassphraseCoordinatorType.swift */; }; + B15F076D24A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15F076524A0FBA5005E26A1 /* SecretsSetupRecoveryPassphraseViewState.swift */; }; + B15F076E24A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15F076624A0FBA5005E26A1 /* SecretsSetupRecoveryPassphraseViewModel.swift */; }; + B15F076F24A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15F076724A0FBA6005E26A1 /* SecretsSetupRecoveryPassphraseViewModelType.swift */; }; + B15F077024A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15F076824A0FBA6005E26A1 /* SecretsSetupRecoveryPassphraseCoordinator.swift */; }; + B15F077224A1F315005E26A1 /* SecretsSetupRecoveryPassphraseInputMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15F077124A1F314005E26A1 /* SecretsSetupRecoveryPassphraseInputMode.swift */; }; B1664BC520F4E67600808783 /* FallbackViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1664BAD20F4E67500808783 /* FallbackViewController.xib */; }; B1664BC620F4E67600808783 /* FallbackViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1664BAE20F4E67500808783 /* FallbackViewController.m */; }; B1664BC720F4E67600808783 /* SharePresentingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1664BB220F4E67500808783 /* SharePresentingViewController.m */; }; @@ -730,6 +739,21 @@ B1DCC63922E85E9A00625807 /* EmojiMartStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63822E85E9A00625807 /* EmojiMartStore.swift */; }; B1DCC63B22E85EF800625807 /* EmojiMartCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63A22E85EF800625807 /* EmojiMartCategory.swift */; }; B1DCC63F22E9A3AE00625807 /* EmojiItem+EmojiMart.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63E22E9A3AE00625807 /* EmojiItem+EmojiMart.swift */; }; + B1DE85E7249A5733006454AF /* SecureKeyBackupSetupCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DE85E4249A5732006454AF /* SecureKeyBackupSetupCoordinatorType.swift */; }; + B1DE85E8249A5733006454AF /* SecureKeyBackupSetupCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DE85E5249A5732006454AF /* SecureKeyBackupSetupCoordinator.swift */; }; + B1DE85E9249A5733006454AF /* SecureKeyBackupSetupCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DE85E6249A5732006454AF /* SecureKeyBackupSetupCoordinatorBridgePresenter.swift */; }; + B1DE85EC249A5819006454AF /* SecureKeyBackupSetupIntroViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DE85EB249A5819006454AF /* SecureKeyBackupSetupIntroViewController.swift */; }; + B1DE85EE249A5981006454AF /* SecureKeyBackupSetupIntroViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1DE85ED249A5981006454AF /* SecureKeyBackupSetupIntroViewController.storyboard */; }; + B1DE8608249A5C4B006454AF /* SecretsSetupRecoveryKeyViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DE8600249A5C4A006454AF /* SecretsSetupRecoveryKeyViewAction.swift */; }; + B1DE8609249A5C4B006454AF /* SecretsSetupRecoveryKeyViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DE8601249A5C4A006454AF /* SecretsSetupRecoveryKeyViewState.swift */; }; + B1DE860A249A5C4B006454AF /* SecretsSetupRecoveryKeyCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DE8602249A5C4A006454AF /* SecretsSetupRecoveryKeyCoordinator.swift */; }; + B1DE860B249A5C4B006454AF /* SecretsSetupRecoveryKeyCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DE8603249A5C4A006454AF /* SecretsSetupRecoveryKeyCoordinatorType.swift */; }; + B1DE860C249A5C4B006454AF /* SecretsSetupRecoveryKeyViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DE8604249A5C4A006454AF /* SecretsSetupRecoveryKeyViewModelType.swift */; }; + B1DE860D249A5C4B006454AF /* SecretsSetupRecoveryKeyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DE8605249A5C4B006454AF /* SecretsSetupRecoveryKeyViewModel.swift */; }; + B1DE860E249A5C4B006454AF /* SecretsSetupRecoveryKeyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DE8606249A5C4B006454AF /* SecretsSetupRecoveryKeyViewController.swift */; }; + B1DE860F249A5C4B006454AF /* SecretsSetupRecoveryKeyViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1DE8607249A5C4B006454AF /* SecretsSetupRecoveryKeyViewController.storyboard */; }; + B1DE8611249BB448006454AF /* SecureKeyBackupSetupIntroCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DE8610249BB448006454AF /* SecureKeyBackupSetupIntroCell.swift */; }; + B1DE8613249BB470006454AF /* SecureKeyBackupSetupIntroCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1DE8612249BB470006454AF /* SecureKeyBackupSetupIntroCell.xib */; }; B1E5368921FB1E20001F3AFF /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E5368821FB1E20001F3AFF /* UIButton.swift */; }; B1E5368D21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E5368C21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift */; }; B1E5368F21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1E5368E21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard */; }; @@ -1076,6 +1100,15 @@ B157FA9D23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryThreePidDetailsViewModel.swift; sourceTree = ""; }; B157FA9E23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryThreePidDetailsViewController.swift; sourceTree = ""; }; B157FAA723264BED00EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter.swift; sourceTree = ""; }; + B15F076124A0FBA3005E26A1 /* SecretsSetupRecoveryPassphraseViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsSetupRecoveryPassphraseViewAction.swift; sourceTree = ""; }; + B15F076224A0FBA4005E26A1 /* SecretsSetupRecoveryPassphraseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsSetupRecoveryPassphraseViewController.swift; sourceTree = ""; }; + B15F076324A0FBA4005E26A1 /* SecretsSetupRecoveryPassphraseViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = SecretsSetupRecoveryPassphraseViewController.storyboard; sourceTree = ""; }; + B15F076424A0FBA4005E26A1 /* SecretsSetupRecoveryPassphraseCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsSetupRecoveryPassphraseCoordinatorType.swift; sourceTree = ""; }; + B15F076524A0FBA5005E26A1 /* SecretsSetupRecoveryPassphraseViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsSetupRecoveryPassphraseViewState.swift; sourceTree = ""; }; + B15F076624A0FBA5005E26A1 /* SecretsSetupRecoveryPassphraseViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsSetupRecoveryPassphraseViewModel.swift; sourceTree = ""; }; + B15F076724A0FBA6005E26A1 /* SecretsSetupRecoveryPassphraseViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsSetupRecoveryPassphraseViewModelType.swift; sourceTree = ""; }; + B15F076824A0FBA6005E26A1 /* SecretsSetupRecoveryPassphraseCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsSetupRecoveryPassphraseCoordinator.swift; sourceTree = ""; }; + B15F077124A1F314005E26A1 /* SecretsSetupRecoveryPassphraseInputMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretsSetupRecoveryPassphraseInputMode.swift; sourceTree = ""; }; B1664BAD20F4E67500808783 /* FallbackViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FallbackViewController.xib; sourceTree = ""; }; B1664BAE20F4E67500808783 /* FallbackViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FallbackViewController.m; sourceTree = ""; }; B1664BAF20F4E67500808783 /* FallbackViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FallbackViewController.h; sourceTree = ""; }; @@ -1742,6 +1775,21 @@ B1DCC63822E85E9A00625807 /* EmojiMartStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiMartStore.swift; sourceTree = ""; }; B1DCC63A22E85EF800625807 /* EmojiMartCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiMartCategory.swift; sourceTree = ""; }; B1DCC63E22E9A3AE00625807 /* EmojiItem+EmojiMart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EmojiItem+EmojiMart.swift"; sourceTree = ""; }; + B1DE85E4249A5732006454AF /* SecureKeyBackupSetupCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecureKeyBackupSetupCoordinatorType.swift; sourceTree = ""; }; + B1DE85E5249A5732006454AF /* SecureKeyBackupSetupCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecureKeyBackupSetupCoordinator.swift; sourceTree = ""; }; + B1DE85E6249A5732006454AF /* SecureKeyBackupSetupCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecureKeyBackupSetupCoordinatorBridgePresenter.swift; sourceTree = ""; }; + B1DE85EB249A5819006454AF /* SecureKeyBackupSetupIntroViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureKeyBackupSetupIntroViewController.swift; sourceTree = ""; }; + B1DE85ED249A5981006454AF /* SecureKeyBackupSetupIntroViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SecureKeyBackupSetupIntroViewController.storyboard; sourceTree = ""; }; + B1DE8600249A5C4A006454AF /* SecretsSetupRecoveryKeyViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsSetupRecoveryKeyViewAction.swift; sourceTree = ""; }; + B1DE8601249A5C4A006454AF /* SecretsSetupRecoveryKeyViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsSetupRecoveryKeyViewState.swift; sourceTree = ""; }; + B1DE8602249A5C4A006454AF /* SecretsSetupRecoveryKeyCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsSetupRecoveryKeyCoordinator.swift; sourceTree = ""; }; + B1DE8603249A5C4A006454AF /* SecretsSetupRecoveryKeyCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsSetupRecoveryKeyCoordinatorType.swift; sourceTree = ""; }; + B1DE8604249A5C4A006454AF /* SecretsSetupRecoveryKeyViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsSetupRecoveryKeyViewModelType.swift; sourceTree = ""; }; + B1DE8605249A5C4B006454AF /* SecretsSetupRecoveryKeyViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsSetupRecoveryKeyViewModel.swift; sourceTree = ""; }; + B1DE8606249A5C4B006454AF /* SecretsSetupRecoveryKeyViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsSetupRecoveryKeyViewController.swift; sourceTree = ""; }; + B1DE8607249A5C4B006454AF /* SecretsSetupRecoveryKeyViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = SecretsSetupRecoveryKeyViewController.storyboard; sourceTree = ""; }; + B1DE8610249BB448006454AF /* SecureKeyBackupSetupIntroCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureKeyBackupSetupIntroCell.swift; sourceTree = ""; }; + B1DE8612249BB470006454AF /* SecureKeyBackupSetupIntroCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SecureKeyBackupSetupIntroCell.xib; sourceTree = ""; }; B1E5368821FB1E20001F3AFF /* UIButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = ""; }; B1E5368C21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseViewController.swift; sourceTree = ""; }; B1E5368E21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = KeyBackupRecoverFromPassphraseViewController.storyboard; sourceTree = ""; }; @@ -2251,6 +2299,7 @@ B1098BE921ECFE64000DDA48 /* KeyBackup */ = { isa = PBXGroup; children = ( + B1DE85E3249A51F7006454AF /* SecureSetup */, B1098C0221ECFEAF000DDA48 /* Setup */, B1FDF56421F726AD00BA3834 /* Recover */, B1107ECB2201BE800038014B /* Banners */, @@ -2506,6 +2555,22 @@ path = ThreePidDetails; sourceTree = ""; }; + B15F075C24A0A196005E26A1 /* RecoveryPassphrase */ = { + isa = PBXGroup; + children = ( + B15F077124A1F314005E26A1 /* SecretsSetupRecoveryPassphraseInputMode.swift */, + B15F076424A0FBA4005E26A1 /* SecretsSetupRecoveryPassphraseCoordinatorType.swift */, + B15F076824A0FBA6005E26A1 /* SecretsSetupRecoveryPassphraseCoordinator.swift */, + B15F076124A0FBA3005E26A1 /* SecretsSetupRecoveryPassphraseViewAction.swift */, + B15F076524A0FBA5005E26A1 /* SecretsSetupRecoveryPassphraseViewState.swift */, + B15F076724A0FBA6005E26A1 /* SecretsSetupRecoveryPassphraseViewModelType.swift */, + B15F076624A0FBA5005E26A1 /* SecretsSetupRecoveryPassphraseViewModel.swift */, + B15F076224A0FBA4005E26A1 /* SecretsSetupRecoveryPassphraseViewController.swift */, + B15F076324A0FBA4005E26A1 /* SecretsSetupRecoveryPassphraseViewController.storyboard */, + ); + path = RecoveryPassphrase; + sourceTree = ""; + }; B1664BAB20F4E67500808783 /* Modules */ = { isa = PBXGroup; children = ( @@ -2818,18 +2883,13 @@ path = Riot/Modules/Common/CollectionView; sourceTree = SOURCE_ROOT; }; - B19C4E6A248E98B9009A423F /* SecretsRecovery */ = { + B19C4E6A248E98B9009A423F /* Secrets */ = { isa = PBXGroup; children = ( - B1CA93732493BDDF00575122 /* SecretsRecoveryCoordinatorBridgePresenter.swift */, - B19C4E6D248F79EE009A423F /* SecretsRecoveryCoordinatorType.swift */, - B19C4E6E248F79EF009A423F /* SecretsRecoveryCoordinator.swift */, - B19C4E9324922403009A423F /* SecretsRecoveryMode.swift */, - B19C4E91249223D0009A423F /* SecretsRecoveryGoal.swift */, - B19C4E6B248E993F009A423F /* RecoverWithPassphrase */, - B19C4E6C248E994D009A423F /* RecoverWithKey */, + B1DE85E1249A5007006454AF /* Setup */, + B1B408EE249A4F39004A331C /* Recover */, ); - path = SecretsRecovery; + path = Secrets; sourceTree = ""; }; B19C4E6B248E993F009A423F /* RecoverWithPassphrase */ = { @@ -2937,6 +2997,19 @@ path = SelfVerifyStart; sourceTree = ""; }; + B1B408EE249A4F39004A331C /* Recover */ = { + isa = PBXGroup; + children = ( + B19C4E6D248F79EE009A423F /* SecretsRecoveryCoordinatorType.swift */, + B19C4E6E248F79EF009A423F /* SecretsRecoveryCoordinator.swift */, + B19C4E9324922403009A423F /* SecretsRecoveryMode.swift */, + B19C4E91249223D0009A423F /* SecretsRecoveryGoal.swift */, + B19C4E6B248E993F009A423F /* RecoverWithPassphrase */, + B19C4E6C248E994D009A423F /* RecoverWithKey */, + ); + path = Recover; + sourceTree = ""; + }; B1B5567620EE6C4C00210D55 /* Modules */ = { isa = PBXGroup; children = ( @@ -2965,7 +3038,7 @@ B1B556B020EE6C4C00210D55 /* BugReport */, B1098BE921ECFE64000DDA48 /* KeyBackup */, B1550FCF242148FA00CE097B /* KeyVerification */, - B19C4E6A248E98B9009A423F /* SecretsRecovery */, + B19C4E6A248E98B9009A423F /* Secrets */, B1A6C10523881ECB002882FD /* SlidingModal */, 32DB556722FDADE50016329E /* ServiceTerms */, B1550FC52420E8F400CE097B /* QRCode */, @@ -4289,6 +4362,52 @@ path = Store; sourceTree = ""; }; + B1DE85E1249A5007006454AF /* Setup */ = { + isa = PBXGroup; + children = ( + B1DE85E2249A5034006454AF /* RecoveryKey */, + B15F075C24A0A196005E26A1 /* RecoveryPassphrase */, + ); + path = Setup; + sourceTree = ""; + }; + B1DE85E2249A5034006454AF /* RecoveryKey */ = { + isa = PBXGroup; + children = ( + B1DE8603249A5C4A006454AF /* SecretsSetupRecoveryKeyCoordinatorType.swift */, + B1DE8602249A5C4A006454AF /* SecretsSetupRecoveryKeyCoordinator.swift */, + B1DE8600249A5C4A006454AF /* SecretsSetupRecoveryKeyViewAction.swift */, + B1DE8601249A5C4A006454AF /* SecretsSetupRecoveryKeyViewState.swift */, + B1DE8604249A5C4A006454AF /* SecretsSetupRecoveryKeyViewModelType.swift */, + B1DE8605249A5C4B006454AF /* SecretsSetupRecoveryKeyViewModel.swift */, + B1DE8606249A5C4B006454AF /* SecretsSetupRecoveryKeyViewController.swift */, + B1DE8607249A5C4B006454AF /* SecretsSetupRecoveryKeyViewController.storyboard */, + ); + path = RecoveryKey; + sourceTree = ""; + }; + B1DE85E3249A51F7006454AF /* SecureSetup */ = { + isa = PBXGroup; + children = ( + B1DE85E6249A5732006454AF /* SecureKeyBackupSetupCoordinatorBridgePresenter.swift */, + B1DE85E4249A5732006454AF /* SecureKeyBackupSetupCoordinatorType.swift */, + B1DE85E5249A5732006454AF /* SecureKeyBackupSetupCoordinator.swift */, + B1DE85EA249A5737006454AF /* Intro */, + ); + path = SecureSetup; + sourceTree = ""; + }; + B1DE85EA249A5737006454AF /* Intro */ = { + isa = PBXGroup; + children = ( + B1DE85EB249A5819006454AF /* SecureKeyBackupSetupIntroViewController.swift */, + B1DE85ED249A5981006454AF /* SecureKeyBackupSetupIntroViewController.storyboard */, + B1DE8610249BB448006454AF /* SecureKeyBackupSetupIntroCell.swift */, + B1DE8612249BB470006454AF /* SecureKeyBackupSetupIntroCell.xib */, + ); + path = Intro; + sourceTree = ""; + }; B1E5368A21FB6FC0001F3AFF /* Passphrase */ = { isa = PBXGroup; children = ( @@ -4767,10 +4886,12 @@ F083BDF21E7009ED00A9B29C /* GoogleService-Info.plist in Resources */, 329E746622CD02EA006F9797 /* BubbleReactionActionViewCell.xib in Resources */, B1B558E320EF768F00210D55 /* RoomEmptyBubbleCell.xib in Resources */, + B1DE85EE249A5981006454AF /* SecureKeyBackupSetupIntroViewController.storyboard in Resources */, B1E5368F21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard in Resources */, B1B5590420EF768F00210D55 /* RoomOutgoingAttachmentBubbleCell.xib in Resources */, B1B558F120EF768F00210D55 /* RoomIncomingAttachmentWithPaginationTitleBubbleCell.xib in Resources */, B1B557CB20EF5D8000210D55 /* DirectoryServerTableViewCell.xib in Resources */, + B1DE860F249A5C4B006454AF /* SecretsSetupRecoveryKeyViewController.storyboard in Resources */, B1B558EC20EF768F00210D55 /* RoomMembershipCollapsedBubbleCell.xib in Resources */, B1B558D720EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.xib in Resources */, B1B5590820EF768F00210D55 /* RoomMembershipWithPaginationTitleBubbleCell.xib in Resources */, @@ -4817,6 +4938,7 @@ F083BE061E7009ED00A9B29C /* Riot-Defaults.plist in Resources */, B1B558D120EF768F00210D55 /* RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.xib in Resources */, B1B558FE20EF768F00210D55 /* RoomMembershipExpandedWithPaginationTitleBubbleCell.xib in Resources */, + B15F076B24A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseViewController.storyboard in Resources */, B1B5581D20EF625800210D55 /* RoomAvatarTitleView.xib in Resources */, B1BEE74923E093260003A4CB /* UserVerificationSessionStatusViewController.storyboard in Resources */, B1B5590B20EF768F00210D55 /* RoomMembershipExpandedBubbleCell.xib in Resources */, @@ -4855,6 +4977,7 @@ B105778F2213052A00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.storyboard in Resources */, B19C4E90248F7A0E009A423F /* SecretsRecoveryWithPassphraseViewController.storyboard in Resources */, B1B5590F20EF782800210D55 /* TableViewCellWithPhoneNumberTextField.xib in Resources */, + B1DE8613249BB470006454AF /* SecureKeyBackupSetupIntroCell.xib in Resources */, B19C4E80248F79FD009A423F /* SecretsRecoveryWithKeyViewController.storyboard in Resources */, B1B5578520EF564900210D55 /* GroupTableViewCellWithSwitch.xib in Resources */, B1B557B320EF5AEF00210D55 /* EventDetailsView.xib in Resources */, @@ -5084,6 +5207,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B1DE8611249BB448006454AF /* SecureKeyBackupSetupIntroCell.swift in Sources */, B1B557D120EF5E3500210D55 /* MediaAlbumTableCell.m in Sources */, 32607D71243E0A55006674CC /* KeyBackupRecoverFromPrivateKeyViewState.swift in Sources */, 324A2053225FC571004FE8B0 /* DeviceVerificationIncomingViewModel.swift in Sources */, @@ -5109,6 +5233,7 @@ B1B5581C20EF625800210D55 /* RoomAvatarTitleView.m in Sources */, B169330820F3CA0E00746532 /* ContactsDataSource.m in Sources */, B1B5574B20EE6C4D00210D55 /* MediaAlbumContentViewController.m in Sources */, + B1DE85E9249A5733006454AF /* SecureKeyBackupSetupCoordinatorBridgePresenter.swift in Sources */, B1B5598820EFC3E000210D55 /* WidgetManager.m in Sources */, B1DB4F0E22316FFF0065DBFA /* UserNameColorGenerator.swift in Sources */, 6E6F1CB524506FA40068B78B /* UITableView.swift in Sources */, @@ -5143,6 +5268,7 @@ B1B558E820EF768F00210D55 /* RoomIncomingAttachmentWithPaginationTitleBubbleCell.m in Sources */, B1B558F320EF768F00210D55 /* RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m in Sources */, B12D79FE23E2462200FACEDC /* UserVerificationStartViewController.swift in Sources */, + B15F077224A1F315005E26A1 /* SecretsSetupRecoveryPassphraseInputMode.swift in Sources */, 32607D73243E0A55006674CC /* KeyBackupRecoverFromPrivateKeyCoordinator.swift in Sources */, B1B557BD20EF5B4500210D55 /* KeyboardGrowingTextView.m in Sources */, B1A68593229E807A00D6C09A /* RoomBubbleCellLayout.swift in Sources */, @@ -5156,6 +5282,7 @@ B1C960F02458308D00C5704B /* RoundedButton.swift in Sources */, B1CE83D52422817200D07506 /* KeyVerificationVerifyByScanningViewController.swift in Sources */, B19C4E8C248F7A0E009A423F /* SecretsRecoveryWithPassphraseViewState.swift in Sources */, + B1DE860C249A5C4B006454AF /* SecretsSetupRecoveryKeyViewModelType.swift in Sources */, 3232ABA3225730E100AD6A5C /* DeviceVerificationStartCoordinatorType.swift in Sources */, 3232AB4D2256558300AD6A5C /* TemplateScreenCoordinatorType.swift in Sources */, B1B5581720EF625800210D55 /* PreviewRoomTitleView.m in Sources */, @@ -5173,6 +5300,7 @@ B1B558CB20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */, B11291EA238D35590077B478 /* SlidingModalPresentable.swift in Sources */, B157FAA823264BED00EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter.swift in Sources */, + B1DE85E8249A5733006454AF /* SecureKeyBackupSetupCoordinator.swift in Sources */, B169330B20F3CA3A00746532 /* Contact.m in Sources */, B1A5B33E227ADF2A004CBA85 /* UIImage.swift in Sources */, 6E6F1CB324506EC50068B78B /* UITableViewCell.swift in Sources */, @@ -5193,6 +5321,7 @@ B1CE83DC2422817200D07506 /* KeyVerificationVerifyByScanningViewState.swift in Sources */, B139C21B21FE5B9200BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift in Sources */, B1C45A8C232A8C2600165425 /* SettingsIdentityServerViewAction.swift in Sources */, + B1DE85E7249A5733006454AF /* SecureKeyBackupSetupCoordinatorType.swift in Sources */, 32A6001E22C661100042C1D9 /* EditHistoryCoordinatorBridgePresenter.swift in Sources */, B1B5574A20EE6C4D00210D55 /* MediaPickerViewController.m in Sources */, B1BEE74623E093260003A4CB /* UserVerificationSessionStatusViewState.swift in Sources */, @@ -5215,6 +5344,7 @@ B157A7BA2445BD86008A5504 /* KeyVerificationScanConfirmationCoordinator.swift in Sources */, B125FE1D231D5DE400B72806 /* SettingsDiscoveryViewModel.swift in Sources */, 32863A5A2384070300D07C4A /* RiotSharedSettings.swift in Sources */, + B1DE860E249A5C4B006454AF /* SecretsSetupRecoveryKeyViewController.swift in Sources */, B1B5594720EF7BD000210D55 /* RoomCollectionViewCell.m in Sources */, B14B17652462C69000C2751E /* KeyVerificationManuallyVerifyViewModel.swift in Sources */, B14B17662462C69000C2751E /* KeyVerificationManuallyVerifyCoordinatorType.swift in Sources */, @@ -5222,6 +5352,7 @@ B1A6C10B23882B6C002882FD /* SlidingModalPresentationAnimator.swift in Sources */, B1B558C120EF768F00210D55 /* RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.m in Sources */, B1B5573E20EE6C4D00210D55 /* RiotNavigationController.m in Sources */, + B15F076C24A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseCoordinatorType.swift in Sources */, B1B5593B20EF7BAC00210D55 /* TableViewCellWithCheckBoxAndLabel.m in Sources */, B1B5581A20EF625800210D55 /* ExpandedRoomTitleView.m in Sources */, B1107EC82200B0720038014B /* KeyBackupRecoverSuccessViewController.swift in Sources */, @@ -5241,6 +5372,7 @@ 32DB557F22FDADE50016329E /* ServiceTermsModalScreenCoordinator.swift in Sources */, B157A7B72445BD86008A5504 /* KeyVerificationScanConfirmationViewModel.swift in Sources */, B1098C1121ED07E4000DDA48 /* NavigationRouterType.swift in Sources */, + B1DE860D249A5C4B006454AF /* SecretsSetupRecoveryKeyViewModel.swift in Sources */, B1B5573D20EE6C4D00210D55 /* WebViewViewController.m in Sources */, 3209451221F1C1430088CAA2 /* BlackTheme.swift in Sources */, B1B5572720EE6C4D00210D55 /* RoomSearchViewController.m in Sources */, @@ -5292,6 +5424,7 @@ B1B558E420EF768F00210D55 /* RoomMembershipWithPaginationTitleBubbleCell.m in Sources */, B18DEDD8243377C10075FEF7 /* KeyVerificationSelfVerifyWaitCoordinatorType.swift in Sources */, B1B5573620EE6C4D00210D55 /* GroupsViewController.m in Sources */, + B15F077024A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseCoordinator.swift in Sources */, B125FE21231D5E1D00B72806 /* SettingsDiscoveryViewAction.swift in Sources */, B108932323AB908A00802670 /* KeyVerificationRequestStatusViewData.swift in Sources */, B19C4E70248F79EF009A423F /* SecretsRecoveryCoordinator.swift in Sources */, @@ -5311,6 +5444,7 @@ B12D7A0123E2462200FACEDC /* UserVerificationStartViewModel.swift in Sources */, B12C56EF2396CB5E00FAC6DE /* RoomMessageURLParser.swift in Sources */, B1BEE73623DF44A60003A4CB /* UserVerificationSessionsStatusCoordinatorType.swift in Sources */, + B1DE8608249A5C4B006454AF /* SecretsSetupRecoveryKeyViewAction.swift in Sources */, B1C45A86232A8C2600165425 /* SettingsIdentityServerViewModelType.swift in Sources */, F083BE031E7009ED00A9B29C /* EventFormatter.m in Sources */, B157FAA623264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.swift in Sources */, @@ -5334,12 +5468,14 @@ F0D2ADA11F6AA5FD00A7097D /* MXRoomSummary+Riot.m in Sources */, B19C4E7C248F79FD009A423F /* SecretsRecoveryWithKeyViewModel.swift in Sources */, B1CE83B92422815C00D07506 /* KeyVerificationKind.swift in Sources */, + B15F076E24A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseViewModel.swift in Sources */, B1BEE71423DF2ACF0003A4CB /* UserVerificationCoordinatorType.swift in Sources */, B1B5596F20EFA85D00210D55 /* EncryptionInfoView.m in Sources */, B1B5573820EE6C4D00210D55 /* GroupParticipantsViewController.m in Sources */, 3232ABAB225730E100AD6A5C /* KeyVerificationCoordinator.swift in Sources */, B1BEE73B23DF44A60003A4CB /* UserVerificationSessionsStatusCoordinator.swift in Sources */, B1B5583E20EF6E7F00210D55 /* GroupRoomTableViewCell.m in Sources */, + B15F076924A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseViewAction.swift in Sources */, B14F143522144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift in Sources */, B1DCC61E22E5E17100625807 /* EmojiPickerViewModel.swift in Sources */, B1B5574F20EE6C4D00210D55 /* RoomsViewController.m in Sources */, @@ -5454,6 +5590,7 @@ 32607D6E243E0A55006674CC /* KeyBackupRecoverFromPrivateKeyViewModelType.swift in Sources */, B1B558D020EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */, B1B558CF20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.m in Sources */, + B1DE8609249A5C4B006454AF /* SecretsSetupRecoveryKeyViewState.swift in Sources */, B140B4A221F87F7100E3F5FE /* OperationQueue.swift in Sources */, B14B17612462C69000C2751E /* KeyVerificationManuallyVerifyCoordinator.swift in Sources */, B183226C23F59F810035B2E8 /* CloseButton.swift in Sources */, @@ -5500,6 +5637,7 @@ B1098BFF21ECFE65000DDA48 /* PasswordStrengthView.swift in Sources */, B1B558D220EF768F00210D55 /* RoomEncryptedDataBubbleCell.m in Sources */, B1B558FA20EF768F00210D55 /* RoomMembershipBubbleCell.m in Sources */, + B1DE860B249A5C4B006454AF /* SecretsSetupRecoveryKeyCoordinatorType.swift in Sources */, B157FAA223264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewAction.swift in Sources */, B1CE83D72422817200D07506 /* KeyVerificationVerifyByScanningViewModelType.swift in Sources */, 3232ABA1225730E100AD6A5C /* KeyVerificationCoordinatorType.swift in Sources */, @@ -5520,6 +5658,7 @@ 324A2050225FC571004FE8B0 /* DeviceVerificationIncomingViewController.swift in Sources */, B1098C0D21ED07E4000DDA48 /* NavigationRouter.swift in Sources */, B110872321F098F0003554A5 /* ActivityIndicatorPresenterType.swift in Sources */, + B1DE860A249A5C4B006454AF /* SecretsSetupRecoveryKeyCoordinator.swift in Sources */, B139C22321FF01B200BB68EC /* KeyBackupRecoverFromPassphraseCoordinatorType.swift in Sources */, B14084CE23BFA0990010F692 /* KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.swift in Sources */, B14F143222144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinator.swift in Sources */, @@ -5527,6 +5666,7 @@ B1CE83E02422817200D07506 /* KeyVerificationVerifyBySASViewController.swift in Sources */, B19EFA3921F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift in Sources */, B1C3360322F1ED600021BA8D /* MediaPickerCoordinator.swift in Sources */, + B15F076D24A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseViewState.swift in Sources */, B1E5368D21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift in Sources */, B1963B3822933BC800CBA17F /* AutosizedCollectionView.swift in Sources */, B12D79FB23E2462200FACEDC /* UserVerificationStartCoordinator.swift in Sources */, @@ -5590,6 +5730,7 @@ B1CE83DA2422817200D07506 /* KeyVerificationVerifyByScanningViewModel.swift in Sources */, 32242F0921E8B05F00725742 /* UIColor.swift in Sources */, B16932E720F3C37100746532 /* HomeMessagesSearchDataSource.m in Sources */, + B1DE85EC249A5819006454AF /* SecureKeyBackupSetupIntroViewController.swift in Sources */, B12D79FF23E2462200FACEDC /* UserVerificationStartViewState.swift in Sources */, B1B558CE20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentBubbleCell.m in Sources */, B14B17672462C69000C2751E /* KeyVerificationManuallyVerifyViewState.swift in Sources */, @@ -5608,11 +5749,13 @@ B1B9DEEE22EB34EF0065E677 /* ReactionHistoryViewAction.swift in Sources */, B1C543A4239E98E400DCA1FA /* KeyVerificationCellInnerContentView.swift in Sources */, B1CE83B62422812100D07506 /* KeyVerificationCoordinatorBridgePresenter.swift in Sources */, + B15F076F24A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseViewModelType.swift in Sources */, 32B94DFA228EC26400716A26 /* ReactionsMenuButton.swift in Sources */, B1B9DEEC22EB34EF0065E677 /* ReactionHistoryViewModelType.swift in Sources */, B157FAA523264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModel.swift in Sources */, B1C562E8228C7CF20037F12A /* ContextualMenuItemView.swift in Sources */, B14F143022144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift in Sources */, + B15F076A24A0FBA7005E26A1 /* SecretsSetupRecoveryPassphraseViewController.swift in Sources */, B1E5368921FB1E20001F3AFF /* UIButton.swift in Sources */, B1DCC63422E72C1B00625807 /* UISearchBar.swift in Sources */, 32A6001622C661100042C1D9 /* EditHistoryViewState.swift in Sources */, diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift index 9a1e6c994..c3e8b4fb6 100644 --- a/Riot/Generated/Storyboards.swift +++ b/Riot/Generated/Storyboards.swift @@ -137,6 +137,21 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: SecretsRecoveryWithPassphraseViewController.self) } + internal enum SecretsSetupRecoveryKeyViewController: StoryboardType { + internal static let storyboardName = "SecretsSetupRecoveryKeyViewController" + + internal static let initialScene = InitialSceneType(storyboard: SecretsSetupRecoveryKeyViewController.self) + } + internal enum SecretsSetupRecoveryPassphraseViewController: StoryboardType { + internal static let storyboardName = "SecretsSetupRecoveryPassphraseViewController" + + internal static let initialScene = InitialSceneType(storyboard: SecretsSetupRecoveryPassphraseViewController.self) + } + internal enum SecureKeyBackupSetupIntroViewController: StoryboardType { + internal static let storyboardName = "SecureKeyBackupSetupIntroViewController" + + internal static let initialScene = InitialSceneType(storyboard: SecureKeyBackupSetupIntroViewController.self) + } internal enum ServiceTermsModalScreenViewController: StoryboardType { internal static let storyboardName = "ServiceTermsModalScreenViewController" From e54d8b10dee8e77e51b7f3cf6099796531c3e377 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 23 Jun 2020 18:40:30 +0200 Subject: [PATCH 18/22] Fix rebase. --- Riot.xcodeproj/project.pbxproj | 7 ++++--- .../SecretsRecoveryCoordinatorBridgePresenter.swift | 0 2 files changed, 4 insertions(+), 3 deletions(-) rename Riot/Modules/{SecretsRecovery => Secrets/Recover}/SecretsRecoveryCoordinatorBridgePresenter.swift (100%) diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 107c2c645..3c6cba965 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -302,6 +302,7 @@ B183226623F55D6B0035B2E8 /* CameraAccessManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B183226523F55D6B0035B2E8 /* CameraAccessManager.swift */; }; B183226823F561380035B2E8 /* CameraAccessAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B183226723F561380035B2E8 /* CameraAccessAlertPresenter.swift */; }; B183226C23F59F810035B2E8 /* CloseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B183226B23F59F810035B2E8 /* CloseButton.swift */; }; + B185AF4F24A2672D00EE7D30 /* SecretsRecoveryCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B185AF4E24A2672D00EE7D30 /* SecretsRecoveryCoordinatorBridgePresenter.swift */; }; B18DEDD4243377C10075FEF7 /* KeyVerificationSelfVerifyWaitViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18DEDCC243377C00075FEF7 /* KeyVerificationSelfVerifyWaitViewModelType.swift */; }; B18DEDD5243377C10075FEF7 /* KeyVerificationSelfVerifyWaitViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18DEDCD243377C00075FEF7 /* KeyVerificationSelfVerifyWaitViewModel.swift */; }; B18DEDD6243377C10075FEF7 /* KeyVerificationSelfVerifyWaitCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18DEDCE243377C00075FEF7 /* KeyVerificationSelfVerifyWaitCoordinator.swift */; }; @@ -671,7 +672,6 @@ B1C960F02458308D00C5704B /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C960EF2458308D00C5704B /* RoundedButton.swift */; }; B1CA3A2721EF6914000D1D89 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA3A2621EF6913000D1D89 /* UIViewController.swift */; }; B1CA3A2921EF692B000D1D89 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA3A2821EF692B000D1D89 /* UIView.swift */; }; - B1CA93742493BDDF00575122 /* SecretsRecoveryCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA93732493BDDF00575122 /* SecretsRecoveryCoordinatorBridgePresenter.swift */; }; B1CA93762493CBF200575122 /* MXRecoveryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA93752493CBF200575122 /* MXRecoveryService.swift */; }; B1CE83B62422812100D07506 /* KeyVerificationCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CE83B52422812000D07506 /* KeyVerificationCoordinatorBridgePresenter.swift */; }; B1CE83B92422815C00D07506 /* KeyVerificationKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CE83B72422815900D07506 /* KeyVerificationKind.swift */; }; @@ -1201,6 +1201,7 @@ B183226523F55D6B0035B2E8 /* CameraAccessManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraAccessManager.swift; sourceTree = ""; }; B183226723F561380035B2E8 /* CameraAccessAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraAccessAlertPresenter.swift; sourceTree = ""; }; B183226B23F59F810035B2E8 /* CloseButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseButton.swift; sourceTree = ""; }; + B185AF4E24A2672D00EE7D30 /* SecretsRecoveryCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsRecoveryCoordinatorBridgePresenter.swift; sourceTree = ""; }; B18DEDCC243377C00075FEF7 /* KeyVerificationSelfVerifyWaitViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationSelfVerifyWaitViewModelType.swift; sourceTree = ""; }; B18DEDCD243377C00075FEF7 /* KeyVerificationSelfVerifyWaitViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationSelfVerifyWaitViewModel.swift; sourceTree = ""; }; B18DEDCE243377C00075FEF7 /* KeyVerificationSelfVerifyWaitCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationSelfVerifyWaitCoordinator.swift; sourceTree = ""; }; @@ -1710,7 +1711,6 @@ B1C960EF2458308D00C5704B /* RoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; B1CA3A2621EF6913000D1D89 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; B1CA3A2821EF692B000D1D89 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; - B1CA93732493BDDF00575122 /* SecretsRecoveryCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretsRecoveryCoordinatorBridgePresenter.swift; sourceTree = ""; }; B1CA93752493CBF200575122 /* MXRecoveryService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRecoveryService.swift; sourceTree = ""; }; B1CE83B52422812000D07506 /* KeyVerificationCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationCoordinatorBridgePresenter.swift; sourceTree = ""; }; B1CE83B72422815900D07506 /* KeyVerificationKind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationKind.swift; sourceTree = ""; }; @@ -3000,6 +3000,7 @@ B1B408EE249A4F39004A331C /* Recover */ = { isa = PBXGroup; children = ( + B185AF4E24A2672D00EE7D30 /* SecretsRecoveryCoordinatorBridgePresenter.swift */, B19C4E6D248F79EE009A423F /* SecretsRecoveryCoordinatorType.swift */, B19C4E6E248F79EF009A423F /* SecretsRecoveryCoordinator.swift */, B19C4E9324922403009A423F /* SecretsRecoveryMode.swift */, @@ -5403,7 +5404,6 @@ 3232ABC2225B996200AD6A5C /* Themable.swift in Sources */, 32A6001B22C661100042C1D9 /* EditHistoryViewAction.swift in Sources */, 3232ABA7225730E100AD6A5C /* DeviceVerificationStartCoordinator.swift in Sources */, - B1CA93742493BDDF00575122 /* SecretsRecoveryCoordinatorBridgePresenter.swift in Sources */, 6E75C3E32458797D00AF497D /* UniversalLink.m in Sources */, B1D4752721EE4E630067973F /* KeyboardAvoider.swift in Sources */, B1D4752821EE4E630067973F /* KeyboardNotification.swift in Sources */, @@ -5494,6 +5494,7 @@ B183226623F55D6B0035B2E8 /* CameraAccessManager.swift in Sources */, B1B5571D20EE6C4D00210D55 /* HomeViewController.m in Sources */, B1CE83DD2422817200D07506 /* KeyVerificationVerifyBySASViewAction.swift in Sources */, + B185AF4F24A2672D00EE7D30 /* SecretsRecoveryCoordinatorBridgePresenter.swift in Sources */, B1C45A84232A8C2600165425 /* SettingsIdentityServerCoordinatorType.swift in Sources */, B1DCC63722E8541700625807 /* EmojiStore.swift in Sources */, 3232ABA6225730E100AD6A5C /* DeviceVerificationStartViewController.swift in Sources */, diff --git a/Riot/Modules/SecretsRecovery/SecretsRecoveryCoordinatorBridgePresenter.swift b/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinatorBridgePresenter.swift similarity index 100% rename from Riot/Modules/SecretsRecovery/SecretsRecoveryCoordinatorBridgePresenter.swift rename to Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinatorBridgePresenter.swift From a6bdaa2907b9987f35f8326cd5506696be2abb72 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 23 Jun 2020 18:43:51 +0200 Subject: [PATCH 19/22] Update changes --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 2ad63348c..c37ce8bfe 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,7 @@ Improvements: * Timeline: Hide encrypted history (pre-invite) (#3239). * Complete security: Add recovery from 4S (#3304). * Key backup: Connect/restore backup created with SSSS (#3124). + * Key backup: Add secure backup creation flow (#3344). Bug fix: * CallVC: Declined calls now properly reset call view controller, thanks to @Legi429 (#2877). From 315913165168ae9d18c7da6859d03fb026157bb0 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 24 Jun 2020 17:08:14 +0200 Subject: [PATCH 20/22] E2E by default: Disable it if the HS admin disabled it #3305 --- CHANGES.rst | 1 + Riot/AppDelegate.m | 2 +- Riot/Categories/MXSession+Riot.h | 18 ++++++++++++ Riot/Categories/MXSession+Riot.m | 29 +++++++++++++++++++ .../Details/ContactDetailsViewController.m | 2 +- .../StartChat/StartChatViewController.m | 2 +- Tools/Rageshakes/getlogs.sh | 25 ++++++++++++++++ 7 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 Tools/Rageshakes/getlogs.sh diff --git a/CHANGES.rst b/CHANGES.rst index 2ad63348c..b785f41cd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,7 @@ Improvements: * Timeline: Hide encrypted history (pre-invite) (#3239). * Complete security: Add recovery from 4S (#3304). * Key backup: Connect/restore backup created with SSSS (#3124). + * E2E by default: Disable it if the HS admin disabled it (#3305). Bug fix: * CallVC: Declined calls now properly reset call view controller, thanks to @Legi429 (#2877). diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 04f03c5d4..9b2767c2b 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -2962,7 +2962,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni } }; - [mxSession canEnableE2EByDefaultInNewRoomWithUsers:invite success:^(BOOL canEnableE2E) { + [mxSession riot_canEnableE2EByDefaultInNewRoomWithUsers:invite success:^(BOOL canEnableE2E) { MXRoomCreationParameters *roomCreationParameters = [MXRoomCreationParameters new]; roomCreationParameters.visibility = kMXRoomDirectoryVisibilityPrivate; diff --git a/Riot/Categories/MXSession+Riot.h b/Riot/Categories/MXSession+Riot.h index 9bc296a98..60be762a2 100644 --- a/Riot/Categories/MXSession+Riot.h +++ b/Riot/Categories/MXSession+Riot.h @@ -25,4 +25,22 @@ */ - (NSUInteger)riot_missedDiscussionsCount; +/** + Check if E2E by default is welcomed on the user's HS. + The default value is YES. + + HS admins can disable it in /.well-known/matrix/client by returning: + "im.vector.riot.e2ee": { + "default": false + } + */ +- (BOOL)riot_isE2EByDefaultEnabledByHSAdmin; + +/** + Riot version of [MXSession canEnableE2EByDefaultInNewRoomWithUsers:] + */ +- (MXHTTPOperation*)riot_canEnableE2EByDefaultInNewRoomWithUsers:(NSArray*)userIds + success:(void (^)(BOOL canEnableE2E))success + failure:(void (^)(NSError *error))failure; + @end diff --git a/Riot/Categories/MXSession+Riot.m b/Riot/Categories/MXSession+Riot.m index 0f2f9782d..0f0f193fa 100644 --- a/Riot/Categories/MXSession+Riot.m +++ b/Riot/Categories/MXSession+Riot.m @@ -49,4 +49,33 @@ return missedDiscussionsCount; } +- (BOOL)riot_isE2EByDefaultEnabledByHSAdmin +{ + BOOL isE2EByDefaultEnabledByHSAdmin = YES; + + MXWellKnown *wellKnown = self.homeserverWellknown; + + if (wellKnown.JSONDictionary[@"im.vector.riot.e2ee"][@"default"]) + { + MXJSONModelSetBoolean(isE2EByDefaultEnabledByHSAdmin, wellKnown.JSONDictionary[@"im.vector.riot.e2ee"][@"default"]); + } + + return isE2EByDefaultEnabledByHSAdmin; +} + +- (MXHTTPOperation*)riot_canEnableE2EByDefaultInNewRoomWithUsers:(NSArray*)userIds + success:(void (^)(BOOL canEnableE2E))success + failure:(void (^)(NSError *error))failure; +{ + if (self.riot_isE2EByDefaultEnabledByHSAdmin) + { + return [self canEnableE2EByDefaultInNewRoomWithUsers:userIds success:success failure:failure]; + } + else + { + success(NO); + return [MXHTTPOperation new]; + } +} + @end diff --git a/Riot/Modules/Contacts/Details/ContactDetailsViewController.m b/Riot/Modules/Contacts/Details/ContactDetailsViewController.m index a6c968496..6fc16ad99 100644 --- a/Riot/Modules/Contacts/Details/ContactDetailsViewController.m +++ b/Riot/Modules/Contacts/Details/ContactDetailsViewController.m @@ -1058,7 +1058,7 @@ // Create a new room - [self.mainSession canEnableE2EByDefaultInNewRoomWithUsers:inviteArray success:^(BOOL canEnableE2E) { + [self.mainSession riot_canEnableE2EByDefaultInNewRoomWithUsers:inviteArray success:^(BOOL canEnableE2E) { MXStrongifyAndReturnIfNil(self); MXRoomCreationParameters *roomCreationParameters = [MXRoomCreationParameters new]; diff --git a/Riot/Modules/StartChat/StartChatViewController.m b/Riot/Modules/StartChat/StartChatViewController.m index f18c383d0..aab731c1a 100644 --- a/Riot/Modules/StartChat/StartChatViewController.m +++ b/Riot/Modules/StartChat/StartChatViewController.m @@ -564,7 +564,7 @@ [[AppDelegate theDelegate] showErrorAsAlert:error]; }; - [self.mainSession canEnableE2EByDefaultInNewRoomWithUsers:inviteArray success:^(BOOL canEnableE2E) { + [self.mainSession riot_canEnableE2EByDefaultInNewRoomWithUsers:inviteArray success:^(BOOL canEnableE2E) { MXStrongifyAndReturnIfNil(self); // Create new room diff --git a/Tools/Rageshakes/getlogs.sh b/Tools/Rageshakes/getlogs.sh new file mode 100644 index 000000000..1dc7a855b --- /dev/null +++ b/Tools/Rageshakes/getlogs.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +# ./getlogs.sh https://riot.im/bugreports/listing/2020-06-07/104229/ + +if [ ! $# -eq 1 ]; then + echo "Usage: ./getLogs.sh [http link]" + exit 1 +fi + +LOGS_URL=$1 + +ID=$( basename $LOGS_URL ) + +echo $ID +mkdir $ID +cd $ID + +wget -r -nd --user=matrix --password=a^njerkoo=les $LOGS_URL + +for f in *.log.gz; do + mv -- "$f" "${f%.log.gz}.log" +done + +rm *.html + From 612fd7268de16b5bf811caeba6cb92a71406e1d4 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 24 Jun 2020 17:09:23 +0200 Subject: [PATCH 21/22] E2E by default: Do not bootstrap cross-signing automatically if hs setting is disabled --- .../AuthenticationViewController.m | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/Riot/Modules/Authentication/AuthenticationViewController.m b/Riot/Modules/Authentication/AuthenticationViewController.m index 3d51bbb22..ad5dd8d8b 100644 --- a/Riot/Modules/Authentication/AuthenticationViewController.m +++ b/Riot/Modules/Authentication/AuthenticationViewController.m @@ -20,6 +20,7 @@ #import "AppDelegate.h" #import "Riot-Swift.h" +#import "MXSession+Riot.h" #import "AuthInputsView.h" #import "ForgotPasswordInputsView.h" @@ -1154,32 +1155,42 @@ case MXCrossSigningStateNotBootstrapped: { #ifdef NEW_CROSS_SIGNING_FLOW - // Bootstrap cross-signing on user's account - // We do it for both registration and new login as long as cross-signing does not exist yet - if (self.authInputsView.password.length) + // TODO: This is still not sure we want to disable the automatic cross-signing bootstrap + // if the admin disabled e2e by default. + // Do like riot-web for the moment + if (session.riot_isE2EByDefaultEnabledByHSAdmin) +#else + if (0) +#endif { - NSLog(@"[AuthenticationVC] sessionStateDidChange: Bootstrap with password"); - - [session.crypto.crossSigning bootstrapWithPassword:self.authInputsView.password success:^{ - NSLog(@"[AuthenticationVC] sessionStateDidChange: Bootstrap succeeded"); - [self dismiss]; - } failure:^(NSError * _Nonnull error) { - NSLog(@"[AuthenticationVC] sessionStateDidChange: Bootstrap failed. Error: %@", error); + // Bootstrap cross-signing on user's account + // We do it for both registration and new login as long as cross-signing does not exist yet + if (self.authInputsView.password.length) + { + NSLog(@"[AuthenticationVC] sessionStateDidChange: Bootstrap with password"); + + [session.crypto.crossSigning bootstrapWithPassword:self.authInputsView.password success:^{ + NSLog(@"[AuthenticationVC] sessionStateDidChange: Bootstrap succeeded"); + [self dismiss]; + } failure:^(NSError * _Nonnull error) { + NSLog(@"[AuthenticationVC] sessionStateDidChange: Bootstrap failed. Error: %@", error); + [session.crypto setOutgoingKeyRequestsEnabled:YES onComplete:nil]; + [self dismiss]; + }]; + } + else + { + NSLog(@"[AuthenticationVC] sessionStateDidChange: Do not know how to bootstrap cross-signing. Skip it."); + [session.crypto setOutgoingKeyRequestsEnabled:YES onComplete:nil]; [self dismiss]; - }]; + } } else { - NSLog(@"[AuthenticationVC] sessionStateDidChange: Do not know how to bootstrap cross-signing. Skip it."); - [session.crypto setOutgoingKeyRequestsEnabled:YES onComplete:nil]; [self dismiss]; } -#else - [session.crypto setOutgoingKeyRequestsEnabled:YES onComplete:nil]; - [self dismiss]; -#endif break; } case MXCrossSigningStateCrossSigningExists: From cd8c353972c8a389d65f586894c7a3f1ba5b7bb0 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 24 Jun 2020 18:29:52 +0200 Subject: [PATCH 22/22] Fix Nad's remarks. --- Riot/Assets/en.lproj/Vector.strings | 2 +- Riot/Generated/Strings.swift | 2 +- .../SecretsSetupRecoveryKeyViewController.storyboard | 2 +- .../SecretsSetupRecoveryPassphraseViewController.swift | 2 ++ 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index a2fad040a..a7c8250e5 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -924,7 +924,7 @@ // Intro "secure_key_backup_setup_intro_title" = "Secure Backup"; -"secure_key_backup_setup_intro_info" = "Safe guard against losing access to encrypted messages & data by backing up encryption keys on your server."; +"secure_key_backup_setup_intro_info" = "Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server."; "secure_key_backup_setup_intro_use_security_key_title" = "Use a Security Key"; "secure_key_backup_setup_intro_use_security_key_info" = "Generate a security key to store somewhere safe like a password manager or a safe."; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 992125e76..28a08579b 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1394,7 +1394,7 @@ internal enum VectorL10n { internal static var keyBackupRecoverTitle: String { return VectorL10n.tr("Vector", "key_backup_recover_title") } - /// Start using Key Backup + /// Safeguard against losing access to encrypted messages & data internal static var keyBackupSetupBannerSubtitle: String { return VectorL10n.tr("Vector", "key_backup_setup_banner_subtitle") } diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewController.storyboard b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewController.storyboard index 11ee58eaa..81b6b145c 100644 --- a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewController.storyboard +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewController.storyboard @@ -73,7 +73,7 @@ - + diff --git a/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewController.swift b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewController.swift index 1de85b994..0cb5bd2d2 100644 --- a/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewController.swift +++ b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewController.swift @@ -140,6 +140,8 @@ final class SecretsSetupRecoveryPassphraseViewController: UIViewController { self.passphraseVisibilityButton.setImage(visibilityImage, for: .normal) self.additionalInformationLabel.text = VectorL10n.secretsSetupRecoveryPassphraseAdditionalInformation + + self.validateButton.setTitle(VectorL10n.continue, for: .normal) } private func update(theme: Theme) {