mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Merge branch 'develop' into aleksandrs/6963_multi_session_logout
This commit is contained in:
commit
c02f144314
45 changed files with 509 additions and 200 deletions
3
.github/workflows/ci-ui-tests.yml
vendored
3
.github/workflows/ci-ui-tests.yml
vendored
|
@ -1,9 +1,6 @@
|
|||
name: UI Tests CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
|
||||
pull_request:
|
||||
|
||||
workflow_dispatch:
|
||||
|
|
5
.github/workflows/triage-move-labelled.yml
vendored
5
.github/workflows/triage-move-labelled.yml
vendored
|
@ -17,7 +17,8 @@ jobs:
|
|||
contains(github.event.issue.labels.*.name, 'Z-IA') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Themes-Custom') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-E2EE-Dehydration') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Tags')
|
||||
contains(github.event.issue.labels.*.name, 'A-Tags') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Rich-Text-Editor')
|
||||
steps:
|
||||
- uses: actions/github-script@v5
|
||||
with:
|
||||
|
@ -267,7 +268,7 @@ jobs:
|
|||
name: Add labelled issues to PS features team 3
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'A-Composer-WYSIWYG')
|
||||
contains(github.event.issue.labels.*.name, 'A-Rich-Text-Editor'')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
id: add_to_project
|
||||
|
|
9
Podfile
9
Podfile
|
@ -154,5 +154,14 @@ post_install do |installer|
|
|||
config.build_settings['WARNING_CFLAGS'] ||= ['$(inherited)','-Wno-nullability-completeness']
|
||||
config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['$(inherited)', '-Xcc', '-Wno-nullability-completeness']
|
||||
end
|
||||
|
||||
# Fix Xcode 14 resource bundle signing issues
|
||||
# https://github.com/CocoaPods/CocoaPods/issues/11402#issuecomment-1259231655
|
||||
if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2639,3 +2639,15 @@
|
|||
// Send Media Actions
|
||||
"wysiwyg_composer_start_action_media_picker" = "Fotobibliothek";
|
||||
"settings_labs_enable_wysiwyg_composer" = "Probiere den Rich-Text-Editor aus (bald auch mit Plain-Text-Modus)";
|
||||
"wysiwyg_composer_start_action_voice_broadcast" = "Sprachübertragung";
|
||||
"voice_broadcast_already_in_progress_message" = "Du zeichnest bereits eine Sprachübertragung auf. Bitte beende die laufende Übertragung, um eine neue zu beginnen.";
|
||||
"voice_broadcast_blocked_by_someone_else_message" = "Jemand anderes nimmt bereits eine Sprachübertragung auf. Warte auf das Ende der Übertragung, bevor du eine neue startest.";
|
||||
"voice_broadcast_permission_denied_message" = "Du hast nicht die nötigen Berechtigungen, um eine Sprachübertragung in diesem Raum zu starten. Kontaktiere einen Raumadministrator, um deine Berechtigungen anzupassen.";
|
||||
|
||||
// Mark: - Voice broadcast
|
||||
"voice_broadcast_unauthorized_title" = "Sprachübertragung kann nicht gestartet werden";
|
||||
"settings_labs_enable_voice_broadcast" = "Sprachübertragung (in aktiver Entwicklung)";
|
||||
"voice_broadcast_playback_loading_error" = "Wiedergabe der Sprachübertragung nicht möglich.";
|
||||
"deselect_all" = "Alle abwählen";
|
||||
"user_other_session_menu_select_sessions" = "Sitzungen auswählen";
|
||||
"user_other_session_selected_count" = "%@ ausgewählt";
|
||||
|
|
|
@ -2506,3 +2506,83 @@
|
|||
"authentication_qr_login_start_subtitle" = "Kasuta selle seadme kaamerat ja logi sisse teises seadmes kuvatud QR-koodi alusel:";
|
||||
"authentication_qr_login_start_title" = "Loe QR-koodi";
|
||||
"authentication_login_with_qr" = "Logi sisse QR-koodi abil";
|
||||
"wysiwyg_composer_format_action_strikethrough" = "Kasuta allajoonitud kirja";
|
||||
"wysiwyg_composer_format_action_underline" = "Kasuta läbijoonitud kirja";
|
||||
"wysiwyg_composer_format_action_italic" = "Kasuta kaldkirja";
|
||||
|
||||
// Formatting Actions
|
||||
"wysiwyg_composer_format_action_bold" = "Kasuta paksu kirja";
|
||||
"wysiwyg_composer_start_action_voice_broadcast" = "Ringhäälingukõne";
|
||||
"wysiwyg_composer_start_action_text_formatting" = "Tekstivorming";
|
||||
"wysiwyg_composer_start_action_camera" = "Kaamera";
|
||||
"wysiwyg_composer_start_action_location" = "Asukoht";
|
||||
"wysiwyg_composer_start_action_polls" = "Küsitlused";
|
||||
"wysiwyg_composer_start_action_attachments" = "Manused";
|
||||
"wysiwyg_composer_start_action_stickers" = "Kleepsud";
|
||||
|
||||
|
||||
// Mark: - WYSIWYG Composer
|
||||
|
||||
// Send Media Actions
|
||||
"wysiwyg_composer_start_action_media_picker" = "Fotode kogu";
|
||||
"user_session_details_last_activity" = "Viimati kasutusel";
|
||||
"device_type_name_unknown" = "Tundmatu seadmetüüp";
|
||||
"device_type_name_mobile" = "Mobiiltelefon";
|
||||
"device_type_name_web" = "Veebiliides";
|
||||
"device_type_name_desktop" = "Töölauarakendus";
|
||||
"user_inactive_session_item_with_date" = "Pole olnud kasutusel üle 90 päeva (%@)";
|
||||
"user_inactive_session_item" = "Pole olnud kasutusel üle 90 päeva";
|
||||
"user_session_item_details_last_activity" = "Viimati kasutusel %@";
|
||||
"user_other_session_clear_filter" = "Eemalda filter";
|
||||
"user_other_session_no_unverified_sessions" = "Verifitseerimata sessioone ei leidu.";
|
||||
"user_other_session_no_verified_sessions" = "Verifitseeritud sessioone ei leidu.";
|
||||
"user_other_session_no_inactive_sessions" = "Ei leidu sessioone, mis pole aktiivses kasutuses.";
|
||||
"user_other_session_filter_menu_inactive" = "Pole pidevas kasutuses";
|
||||
"user_other_session_filter_menu_unverified" = "Verifitseerimata";
|
||||
"user_other_session_filter_menu_verified" = "Verifitseeritud";
|
||||
"user_other_session_filter_menu_all" = "Kõik sessioonid";
|
||||
"user_other_session_filter" = "Filtreeri";
|
||||
"user_other_session_verified_sessions_header_subtitle" = "Parima turvalisuse nimel logi välja neist sessioonidest, mida sa enam ei kasuta või ei tunne ära.";
|
||||
"user_other_session_current_session_details" = "Sinu praegune sessioon";
|
||||
"user_other_session_unverified_sessions_header_subtitle" = "Turvalise sõnumvahetuse nimel verifitseeri kõik oma sessioonid ning logi neist välja, mida sa enam ei kasuta või ei tunne enam ära.";
|
||||
"user_other_session_security_recommendation_title" = "Turvalisusega seotud soovitused";
|
||||
"user_other_session_verified_additional_info" = "See sessioon on valmis turvaliseks sõnumivahetuseks.";
|
||||
"user_other_session_unverified_additional_info" = "Parima turvalisuse ja töökindluse nimel verifitseeri see sessioon või logi ta võrgust välja.";
|
||||
"user_session_verification_unknown_additional_info" = "Selle sessiooni olekut ei saa tuvastada enne kui oled ta verifitseerinud.";
|
||||
"user_session_verification_unknown_short" = "Teadmata olek";
|
||||
"user_session_verification_unknown" = "Verifitseerimise olek on määratlemata";
|
||||
"user_sessions_overview_link_device" = "Seo teise seadmega";
|
||||
|
||||
// MARK: User sessions management
|
||||
|
||||
// Parameter is the application display name (e.g. "Element")
|
||||
"user_sessions_default_session_display_name" = "%@ iOS";
|
||||
"voice_broadcast_playback_loading_error" = "Selle ringhäälingukõne esitamine ei õnnestu.";
|
||||
"voice_broadcast_already_in_progress_message" = "Sa juba salvestad ringhäälingukõnet. Uue alustamiseks palun lõpeta eelmine salvestus.";
|
||||
"voice_broadcast_blocked_by_someone_else_message" = "Keegi juba salvestab ringhäälingukõnet. Uue ringhäälingukõne salvestamiseks palun oota, kuni see teine ringhäälingukõne on lõppenud.";
|
||||
"voice_broadcast_permission_denied_message" = "Sul pole piisavalt õigusi selles jututoas ringhäälingukõne algatamiseks. Õiguste lisamiseks palun võta ühendust jututoa haldajaga.";
|
||||
|
||||
// Mark: - Voice broadcast
|
||||
"voice_broadcast_unauthorized_title" = "Uue ringhäälingukõne alustamine pole võimalik";
|
||||
"sign_out_confirmation_message" = "Kas sa oled kindel et soovid välja logida?";
|
||||
|
||||
// MARK: Sign out warning
|
||||
|
||||
"sign_out" = "Logi välja";
|
||||
"manage_session_rename" = "Muuda sessiooni nime";
|
||||
"manage_session_name_info_link" = "Lisateave";
|
||||
/* The placeholder will be replaces with manage_session_name_info_link */
|
||||
"manage_session_name_info" = "Palun arvesta, et sessioonide nimed on näha ka kõikidele osapooltele, kellega sa suhtled. %@";
|
||||
"manage_session_name_hint" = "Sinu enda kirjutatud sessiooninimede alusel on sul oma seadmeid lihtsam ära tunda.";
|
||||
"settings_labs_enable_voice_broadcast" = "Ringhäälingukõne (aktiivses arenduses)";
|
||||
"settings_labs_enable_wysiwyg_composer" = "Proovi vormindatud teksti alusel töötavat tekstitoimetit (varsti lisandub ka vormindamata teksti režiim)";
|
||||
"authentication_qr_login_failure_retry" = "Proovi uuesti";
|
||||
"authentication_qr_login_failure_request_timed_out" = "Sidumine ei lõppenud etteantud aja jooksul.";
|
||||
"authentication_qr_login_failure_request_denied" = "Teine seade lükkas päringu tagasi.";
|
||||
"authentication_qr_login_failure_invalid_qr" = "QR-kood on vigane.";
|
||||
"authentication_qr_login_failure_title" = "Seose loomine ei õnenstunud";
|
||||
"authentication_qr_login_loading_signed_in" = "Sa oled oma teises seadmes sisse loginud Matrix'i võrku.";
|
||||
"authentication_qr_login_loading_waiting_signin" = "Ootame, et teine seade logiks võrku.";
|
||||
"authentication_qr_login_loading_connecting_device" = "Loon ühendust seadmega";
|
||||
"authentication_qr_login_confirm_alert" = "Palun vaata, et sa kindlasti tead, kust see QR-kood kuvatakse. Sellisel viisil seadmete sidumisel sa annad oma kasutajakontole täiemahulise ligipääsu.";
|
||||
"authentication_qr_login_confirm_subtitle" = "Kontrolli, et järgnev kood klapib teises seadmes kuvatava koodiga:";
|
||||
|
|
|
@ -1270,3 +1270,23 @@
|
|||
"microphone_access_not_granted_for_voice_message" = "جهت ارسال پیام صوتی نیاز به دسترسی به میکروفون وجود دارد اما %@ دسترسی استفاده از آن را ندارد";
|
||||
"e2e_passphrase_too_short" = "کلمه عبور بیش از حد کوتاه است (حداقل میبایست %d کاراکتر باشد)";
|
||||
"message_reply_to_sender_sent_a_voice_message" = "یک پیام صوتی ارسال کنید.";
|
||||
"onboarding_splash_page_1_title" = "صاحب گفتگوهای خود شوید.";
|
||||
"onboarding_splash_login_button_title" = "من از قبل حساب کاربری دارم";
|
||||
|
||||
// MARK: Onboarding
|
||||
"onboarding_splash_register_button_title" = "ساخت حساب کاربری";
|
||||
"accessibility_button_label" = "دکمه";
|
||||
"saving" = "در حال ذخیره";
|
||||
|
||||
// Activities
|
||||
"loading" = "در حال بارگزاری";
|
||||
"invite_to" = "دعوت به %@";
|
||||
"confirm" = "تأیید";
|
||||
"edit" = "ویرایش";
|
||||
"suggest" = "پیشنهاد";
|
||||
"add" = "افزودن";
|
||||
"existing" = "خروج";
|
||||
"new_word" = "جدید";
|
||||
"stop" = "توقف";
|
||||
"joining" = "پیوستن";
|
||||
"enable" = "فعال";
|
||||
|
|
|
@ -2625,4 +2625,15 @@
|
|||
"authentication_qr_login_start_subtitle" = "Használd a kamerát ezen az eszközön a másik eszközödön megjelenő QR kód beolvasására:";
|
||||
"authentication_qr_login_start_title" = "QR kód beolvasása";
|
||||
"authentication_login_with_qr" = "Belépés QR kóddal";
|
||||
"settings_labs_enable_voice_broadcast" = "Hang közvetítés (aktív fejlesztés alatt). Jelenleg a hang közvetítést csak a szoba idővonalán jelezzük, egyenlőre nem lehet hangot sugározni vagy belehallgatni a közvetítésbe";
|
||||
"settings_labs_enable_voice_broadcast" = "Hang közvetítés (aktív fejlesztés alatt)";
|
||||
"wysiwyg_composer_start_action_voice_broadcast" = "Hang közvetítés";
|
||||
"voice_broadcast_playback_loading_error" = "A hang közvetítés nem játszható le.";
|
||||
"voice_broadcast_already_in_progress_message" = "Egy hang közvetítés már folyamatban van. Először fejezd be a jelenlegi közvetítést egy új indításához.";
|
||||
"voice_broadcast_blocked_by_someone_else_message" = "Valaki már elindított egy hang közvetítést. Várd meg a közvetítés végét az új indításához.";
|
||||
"voice_broadcast_permission_denied_message" = "Nincs jogosultságod hang közvetítést indítani ebben a szobában. Vedd fel a kapcsolatot a szoba adminisztrátorával a szükséges jogosultság megszerzéséhez.";
|
||||
|
||||
// Mark: - Voice broadcast
|
||||
"voice_broadcast_unauthorized_title" = "Az új hang közvetítés nem indítható el";
|
||||
"deselect_all" = "Semmit nem jelöl ki";
|
||||
"user_other_session_menu_select_sessions" = "Munkamenetek kiválasztása";
|
||||
"user_other_session_selected_count" = "%@ kiválasztva";
|
||||
|
|
|
@ -2832,3 +2832,15 @@
|
|||
"manage_session_name_info" = "Harap diketahui bahwa nama sesi juga terlihat ke orang-orang yang Anda berkomunikasi. %@";
|
||||
"manage_session_name_hint" = "Nama sesi khusus dapat membantu Anda mengenal perangkat Anda dengan lebih mudah.";
|
||||
"settings_labs_enable_wysiwyg_composer" = "Coba editor teks kaya (mode teks biasa akan datang)";
|
||||
"wysiwyg_composer_start_action_voice_broadcast" = "Siaran suara";
|
||||
"voice_broadcast_playback_loading_error" = "Tidak dapat memainkan siaran suara ini.";
|
||||
"voice_broadcast_already_in_progress_message" = "Anda saat ini merekam sebuah siaran suara. Mohon akhiri siaran suara Anda saat ini untuk memulai yang baru.";
|
||||
"voice_broadcast_blocked_by_someone_else_message" = "Ada orang lain yang saat ini merekam sebuah siaran suara. Tunggu siaran suaranya berakhir untuk memulai yang baru.";
|
||||
"voice_broadcast_permission_denied_message" = "Anda tidak memiliki izin untuk memulai sebuah siaran suara di ruangan ini. Hubungi sebuah administrator ruangan untuk meningkatkan izin Anda.";
|
||||
|
||||
// Mark: - Voice broadcast
|
||||
"voice_broadcast_unauthorized_title" = "Tidak dapat memulai sebuah siaran suara baru";
|
||||
"settings_labs_enable_voice_broadcast" = "Siaran suara (dalam pengembangan aktif)";
|
||||
"deselect_all" = "Batalkan Semua Pilihan";
|
||||
"user_other_session_menu_select_sessions" = "Pilih sesi";
|
||||
"user_other_session_selected_count" = "%@ dipilih";
|
||||
|
|
|
@ -2605,4 +2605,15 @@
|
|||
"manage_session_name_info" = "Ricorda che i nomi di sessione sono anche visibili alle persone con cui comunichi. %@";
|
||||
"manage_session_name_hint" = "I nomi di sessione personalizzati possono aiutarti a riconoscere i tuoi dispositivi più facilmente.";
|
||||
"settings_labs_enable_wysiwyg_composer" = "Prova l'editor in rich text (il testo semplice è in arrivo)";
|
||||
"settings_labs_enable_voice_broadcast" = "Broadcast voce (in sviluppo attivo). Attualmente rileviamo solo il broadcast vocale nella linea temporale della stanza, non è possibile inviare o ascoltare un vero broadcast vocale";
|
||||
"settings_labs_enable_voice_broadcast" = "Trasmissione vocale (in sviluppo attivo)";
|
||||
"wysiwyg_composer_start_action_voice_broadcast" = "Trasmissione vocale";
|
||||
"voice_broadcast_playback_loading_error" = "Impossibile avviare questa trasmissione vocale.";
|
||||
"voice_broadcast_already_in_progress_message" = "Stai già registrando una trasmissione vocale. Termina quella in corso per iniziarne una nuova.";
|
||||
"voice_broadcast_blocked_by_someone_else_message" = "Qualcun altro sta già registrando una trasmissione vocale. Aspetta che finisca prima di iniziarne una nuova.";
|
||||
"voice_broadcast_permission_denied_message" = "Non hai l'autorizzazione necessaria per iniziare un broadcast vocale in questa stanza. Contatta un amministratore della stanza per aggiornare le tue autorizzazioni.";
|
||||
|
||||
// Mark: - Voice broadcast
|
||||
"voice_broadcast_unauthorized_title" = "Impossibile iniziare una nuova trasmissione vocale";
|
||||
"deselect_all" = "Deseleziona tutti";
|
||||
"user_other_session_menu_select_sessions" = "Seleziona sessioni";
|
||||
"user_other_session_selected_count" = "%@ selezionate";
|
||||
|
|
|
@ -751,7 +751,7 @@
|
|||
"group_participants_invited_section" = "ZAPROSZONY";
|
||||
"receipt_status_read" = "Odczytano: ";
|
||||
// Media picker
|
||||
"media_picker_title" = "Selektor mediów";
|
||||
"media_picker_title" = "Biblioteka mediów";
|
||||
// Image picker
|
||||
"image_picker_action_camera" = "Zrób zdjęcie";
|
||||
"image_picker_action_library" = "Wybierz z biblioteki";
|
||||
|
@ -2569,7 +2569,7 @@
|
|||
|
||||
// Mark: - All Chats
|
||||
|
||||
"all_chats_title" = "Wszystkie rozmowy";
|
||||
"all_chats_title" = "Rozmowy";
|
||||
"spaces_subspace_creation_visibility_message" = "Utworzona przestrzeń zostanie dodana do %@.";
|
||||
"spaces_subspace_creation_visibility_title" = "Jakiego rodzaju podprzestrzeń chcesz utworzyć?";
|
||||
"spaces_explore_rooms_format" = "Przeglądaj %@";
|
||||
|
|
|
@ -2606,3 +2606,12 @@
|
|||
"manage_session_name_info" = "Por favor esteja ciente que nomes de sessões também são visíveis a pessoas com quem você se comunica. %@";
|
||||
"manage_session_name_hint" = "Nomes de sessões personalizados podem ajudar você a reconhecer seus dispositivos mais facilmente.";
|
||||
"settings_labs_enable_wysiwyg_composer" = "Experimente o editor de texto rico (modo de texto puro vindo em breve)";
|
||||
"wysiwyg_composer_start_action_voice_broadcast" = "Broadcast de voz";
|
||||
"voice_broadcast_playback_loading_error" = "Incapaz de tocar este broadcast de voz.";
|
||||
"voice_broadcast_already_in_progress_message" = "Você já está gravando um broadcast de voz. Por favor termine seu broadcast de voz atual para começar um novo.";
|
||||
"voice_broadcast_blocked_by_someone_else_message" = "Alguma outra pessoa já está gravando um broadcast de voz. Espere que o broadcast de voz dela termine para começar um novo.";
|
||||
"voice_broadcast_permission_denied_message" = "Você não tem as permissões requeridas para começar um broadcast de voz nesta sala. Contacte um(a) administrador(a) da sala para fazer upgrade de suas permissões.";
|
||||
|
||||
// Mark: - Voice broadcast
|
||||
"voice_broadcast_unauthorized_title" = "Não dá para começar um novo broadcast de voz";
|
||||
"settings_labs_enable_voice_broadcast" = "Broadcast de voz (sob desenvolvimento ativo)";
|
||||
|
|
|
@ -603,7 +603,7 @@
|
|||
"key_backup_recover_from_passphrase_passphrase_title" = "Ввод";
|
||||
"key_backup_recover_from_passphrase_passphrase_placeholder" = "Введите секретную фразу";
|
||||
"key_backup_recover_from_passphrase_recover_action" = "Разблокировать историю";
|
||||
"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Не знаете вашу секретную фразу для восстановления? Вы можете ";
|
||||
"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Не помните свою мнемоническую фразу? Вы можете ";
|
||||
"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "использовать ключ безопасности";
|
||||
"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = ".";
|
||||
"key_backup_recover_from_recovery_key_info" = "Используйте ключ безопасности для разблокировки истории безопасных сообщений";
|
||||
|
@ -624,7 +624,7 @@
|
|||
"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Ключ безопасности";
|
||||
"key_backup_setup_success_from_recovery_key_make_copy_action" = "Сделать копию";
|
||||
"key_backup_setup_success_from_recovery_key_made_copy_action" = "Я сделал копию";
|
||||
"key_backup_recover_invalid_passphrase_title" = "Неверная секретная фраза для восстановления";
|
||||
"key_backup_recover_invalid_passphrase_title" = "Неверная мнемоническая фраза";
|
||||
"key_backup_recover_invalid_recovery_key_title" = "Несоответствующий ключ безопасности";
|
||||
"key_backup_setup_banner_title" = "Не теряйте зашифрованные сообщения";
|
||||
"key_backup_setup_banner_subtitle" = "Начать использовать ключ восстановления";
|
||||
|
@ -641,7 +641,7 @@
|
|||
"key_backup_setup_intro_setup_action_with_existing_backup" = "Использовать ключ восстановления";
|
||||
"settings_key_backup_info" = "Зашифрованные сообщения защищены сквозным шифрованием. Только вы и получатель(и) имеют ключи для чтения этих сообщений.";
|
||||
"settings_key_backup_info_signout_warning" = "Сделайте резервную копию ключей перед выходом, чтобы не потерять их.";
|
||||
"key_backup_setup_passphrase_title" = "Защитите резервную копию секретной фразой";
|
||||
"key_backup_setup_passphrase_title" = "Защитите резервную копию мнемонической фразой";
|
||||
"key_backup_setup_passphrase_setup_recovery_key_info" = "Или защитите свою резервную копию с помощью ключа безопасности, сохранив ее в безопасном месте.";
|
||||
"key_backup_setup_passphrase_setup_recovery_key_action" = "(Расширенный) Настройка с ключом безопасности";
|
||||
// Success from passphrase
|
||||
|
@ -654,7 +654,7 @@
|
|||
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Зашифрованные сообщения будут утеряны";
|
||||
"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Мне не нужны мои зашифрованные сообщения";
|
||||
"sign_out_non_existing_key_backup_alert_title" = "Вы потеряете доступ к зашифрованным сообщениям если выйдете сейчас";
|
||||
"key_backup_recover_invalid_passphrase" = "Невозможно расшифровать резервную копию с помощью этой секретной фразы: убедитесь, что вы ввели верную секретную фразу для восстановления.";
|
||||
"key_backup_recover_invalid_passphrase" = "Невозможно расшифровать резервную копию с помощью этой фразы: убедитесь, что вы ввели верную мнемоническую фразу.";
|
||||
"key_backup_recover_invalid_recovery_key" = "Невозможно расшифровать резервную копию с помощью этого ключа: убедитесь, что вы ввели верный ключ безопасности.";
|
||||
"e2e_key_backup_wrong_version_button_settings" = "Настройки";
|
||||
"key_backup_setup_intro_manual_export_info" = "(Расширенный)";
|
||||
|
@ -986,7 +986,7 @@
|
|||
"secure_key_backup_setup_intro_info" = "Защитите себя от потери доступа к зашифрованным сообщениям и данным, создав резервную копию ключей шифрования на своём сервере.";
|
||||
"secure_key_backup_setup_intro_use_security_key_title" = "Используйте ключ безопасности";
|
||||
"secure_key_backup_setup_intro_use_security_key_info" = "Создайте ключ безопасности для хранения в надежном месте, например в менеджере паролей или сейфе.";
|
||||
"secure_key_backup_setup_intro_use_security_passphrase_title" = "Использовать секретную фразу";
|
||||
"secure_key_backup_setup_intro_use_security_passphrase_title" = "Использовать мнемоническую фразу";
|
||||
"secure_key_backup_setup_intro_use_security_passphrase_info" = "Введите секретную фразу, известную только вам, и создайте ключ для резервного копирования.";
|
||||
"secure_key_backup_setup_existing_backup_error_title" = "Резервная копия сообщений уже существует";
|
||||
"secure_key_backup_setup_existing_backup_error_info" = "Разблокируйте его для повторного использования в защищенной резервной копии или удалите для создания новой резервной копии сообщений в защищенной резервной копии.";
|
||||
|
@ -1024,7 +1024,7 @@
|
|||
"device_verification_self_verify_wait_information" = "Подтвердите этот сеанс на одном из других ваших сеансов, предоставив ему доступ к зашифрованным сообщениям.\n\nИспользуйте последнюю версию %@ на других ваших устройствах:";
|
||||
"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_with_passphrase" = "Используйте мнемоническую фразу или бумажный ключ";
|
||||
"device_verification_self_verify_wait_recover_secrets_additional_information" = "Если вы не можете получить доступ к существующему сеансу";
|
||||
"key_verification_verify_sas_title_emoji" = "Сравните смайлы";
|
||||
"key_verification_verify_sas_title_number" = "Сравните числа";
|
||||
|
@ -1102,17 +1102,17 @@
|
|||
"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_title" = "Мнемоническая фраза";
|
||||
"secrets_recovery_with_passphrase_information_default" = "Получите доступ к своей защищённой истории сообщений и вашей личности с кросс-подписью для проверки других сеансов, введя секретную фразу.";
|
||||
"secrets_recovery_with_passphrase_information_verify_device" = "Используйте секретную фразу, чтобы проверить это устройство.";
|
||||
"secrets_recovery_with_passphrase_information_verify_device" = "Используйте свою мнемоническую фразу, чтобы заверить эту сессию.";
|
||||
"secrets_recovery_with_passphrase_passphrase_title" = "Ввод";
|
||||
"secrets_recovery_with_passphrase_passphrase_placeholder" = "Введите секретную фразу";
|
||||
"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_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_passphrase_invalid_passphrase_message" = "Убедитесь, что вы ввели верную мнемоническую фразу.";
|
||||
"secrets_recovery_with_key_title" = "Ключ безопасности";
|
||||
"secrets_recovery_with_key_information_default" = "Получите доступ к своей защищённой истории сообщений и вашей личности с кросс-подписью для проверки других сеансов, введя ключ безопасности.";
|
||||
"secrets_recovery_with_key_information_verify_device" = "Используйте ключ безопасности, чтобы проверить это устройство.";
|
||||
|
@ -1128,11 +1128,11 @@
|
|||
"secrets_setup_recovery_key_done_action" = "Готово";
|
||||
"secrets_setup_recovery_key_storage_alert_title" = "Храните его в безопасности";
|
||||
"secrets_setup_recovery_key_storage_alert_message" = "✓ Распечатайте и храните в безопасном месте\n✓ Сохраните его на USB-носителе или резервном носителе\n✓ Скопируйте его в свое личное облачное хранилище";
|
||||
"secrets_setup_recovery_passphrase_title" = "Задайте секретную фразу";
|
||||
"secrets_setup_recovery_passphrase_title" = "Задайте мнемоническую фразу";
|
||||
"secrets_setup_recovery_passphrase_information" = "Введите секретную фразу, известную только вам, для защиты данных на вашем сервере.";
|
||||
"secrets_setup_recovery_passphrase_additional_information" = "Не используйте пароль своей учетной записи.";
|
||||
"secrets_setup_recovery_passphrase_validate_action" = "Готово";
|
||||
"secrets_setup_recovery_passphrase_confirm_information" = "Для подтверждения введите вашу секретную фразу ещё раз.";
|
||||
"secrets_setup_recovery_passphrase_confirm_information" = "Введите мнемоническую фразу ещё раз, чтобы подтвердить её.";
|
||||
"secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Подтвердить";
|
||||
"secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "Подтвердить секретную фразу";
|
||||
"cross_signing_setup_banner_title" = "Настройка шифрования";
|
||||
|
@ -1238,8 +1238,8 @@
|
|||
// MARK: - Home
|
||||
|
||||
"home_empty_view_title" = "Добро пожаловать в %@,\n%@";
|
||||
"secrets_setup_recovery_passphrase_summary_information" = "Запомните свою секретную фразу. Её можно использовать для разблокировки ваших зашифрованных сообщений и данных.";
|
||||
"secrets_setup_recovery_passphrase_summary_title" = "Сохраните вашу секретную фразу";
|
||||
"secrets_setup_recovery_passphrase_summary_information" = "Запомните свою мнемоническую фразу. Её можно использовать для разблокировки ваших зашифрованных сообщений и данных.";
|
||||
"secrets_setup_recovery_passphrase_summary_title" = "Сохраните свою мнемоническую фразу";
|
||||
"favourites_empty_view_information" = "Вы можете добавить в избранное несколькими способами - самый быстрый - просто нажать и удерживать. Нажмите на звёздочку, и они автоматически появятся здесь, и вы их навсегда сохраните.";
|
||||
|
||||
// MARK: - Favourites
|
||||
|
@ -1355,7 +1355,7 @@
|
|||
|
||||
"space_feature_unavailable_title" = "Пространств ещё нет";
|
||||
"secrets_recovery_with_key_information_unlock_secure_backup_with_key" = "Введите свой ключ безопасности, чтобы продолжить.";
|
||||
"secrets_recovery_with_key_information_unlock_secure_backup_with_phrase" = "Введите секретную фразу, чтобы продолжить.";
|
||||
"secrets_recovery_with_key_information_unlock_secure_backup_with_phrase" = "Введите мнемоническую фразу, чтобы продолжить.";
|
||||
"key_verification_verify_qr_code_scan_code_other_device_action" = "Сканирование с помощью этого устройства";
|
||||
|
||||
// Success from secure backup
|
||||
|
|
|
@ -2828,3 +2828,15 @@
|
|||
"manage_session_name_info" = "Uvedomte si, že názvy relácií sú viditeľné aj pre ľudí, s ktorými komunikujete. %@";
|
||||
"manage_session_name_hint" = "Vlastné názvy relácií vám pomôžu ľahšie rozpoznať vaše zariadenia.";
|
||||
"settings_labs_enable_wysiwyg_composer" = "Vyskúšajte rozšírený textový editor (čistý textový režim sa objaví čoskoro)";
|
||||
"wysiwyg_composer_start_action_voice_broadcast" = "Hlasové vysielanie";
|
||||
"voice_broadcast_already_in_progress_message" = "Už nahrávate hlasové vysielanie. Ukončite aktuálne hlasové vysielanie a spustite nové.";
|
||||
"voice_broadcast_blocked_by_someone_else_message" = "Niekto iný už nahráva hlasové vysielanie. Počkajte, kým sa skončí jeho hlasové vysielanie, a potom spustite nové.";
|
||||
"voice_broadcast_permission_denied_message" = "Nemáte požadované oprávnenia na spustenie hlasového vysielania v tejto miestnosti. Obráťte sa na správcu miestnosti, aby vám rozšíril oprávnenia.";
|
||||
|
||||
// Mark: - Voice broadcast
|
||||
"voice_broadcast_unauthorized_title" = "Nie je možné spustiť nové hlasové vysielanie";
|
||||
"settings_labs_enable_voice_broadcast" = "Hlasové vysielanie (v štádiu aktívneho vývoja)";
|
||||
"voice_broadcast_playback_loading_error" = "Toto hlasové vysielanie nie je možné prehrať.";
|
||||
"deselect_all" = "Zrušiť výber všetkých";
|
||||
"user_other_session_selected_count" = "%@ vybratých";
|
||||
"user_other_session_menu_select_sessions" = "Vyberte relácie";
|
||||
|
|
|
@ -2830,3 +2830,15 @@
|
|||
"manage_session_name_info" = "Зауважте, що назви сеансів також видно людям, з якими ви спілкуєтесь. %@";
|
||||
"manage_session_name_hint" = "Власні назви сеансів допоможуть вам легше розпізнавати ваші пристрої.";
|
||||
"settings_labs_enable_wysiwyg_composer" = "Спробуйте розширений текстовий редактор (незабаром з'явиться режим звичайного тексту)";
|
||||
"wysiwyg_composer_start_action_voice_broadcast" = "Голосові повідомлення";
|
||||
"voice_broadcast_playback_loading_error" = "Неможливо відтворити це голосове повідомлення.";
|
||||
"voice_broadcast_already_in_progress_message" = "Ви вже записуєте голосове повідомлення. Завершіть поточну трансляцію, щоб розпочати нову.";
|
||||
"voice_broadcast_blocked_by_someone_else_message" = "Хтось інший вже записує голосове повідомлення. Зачекайте, поки закінчиться трансляція, щоб розпочати нову.";
|
||||
"voice_broadcast_permission_denied_message" = "Ви не маєте необхідних дозволів для початку трансляції голосового повідомлення в цій кімнаті. Зверніться до адміністратора кімнати, щоб оновити ваші дозволи.";
|
||||
|
||||
// Mark: - Voice broadcast
|
||||
"voice_broadcast_unauthorized_title" = "Не вдалося розпочати трансляцію нового голосового повідомлення";
|
||||
"settings_labs_enable_voice_broadcast" = "Голосові повідомлення (в активній розробці)";
|
||||
"deselect_all" = "Скасувати вибір усіх";
|
||||
"user_other_session_menu_select_sessions" = "Вибрати сеанси";
|
||||
"user_other_session_selected_count" = "Вибрано %@";
|
||||
|
|
|
@ -329,7 +329,7 @@
|
|||
{
|
||||
if (self.mxSession.crypto)
|
||||
{
|
||||
[self.mxSession.crypto trustLevelSummaryForUserIds:@[userId] onComplete:^(MXUsersTrustLevelSummary *usersTrustLevelSummary) {
|
||||
[self.mxSession.crypto trustLevelSummaryForUserIds:@[userId] forceDownload:NO success:^(MXUsersTrustLevelSummary *usersTrustLevelSummary) {
|
||||
|
||||
UserEncryptionTrustLevel userEncryptionTrustLevel;
|
||||
double trustedDevicesPercentage = usersTrustLevelSummary.trustedDevicesProgress.fractionCompleted;
|
||||
|
@ -341,7 +341,7 @@
|
|||
else if (trustedDevicesPercentage == 0.0)
|
||||
{
|
||||
// Verify if the user has the user has cross-signing enabled
|
||||
if ([self.mxSession.crypto crossSigningKeysForUser:userId])
|
||||
if ([self.mxSession.crypto.crossSigning crossSigningKeysForUser:userId])
|
||||
{
|
||||
userEncryptionTrustLevel = UserEncryptionTrustLevelNotVerified;
|
||||
}
|
||||
|
@ -357,6 +357,9 @@
|
|||
|
||||
onComplete(userEncryptionTrustLevel);
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
MXLogErrorDetails(@"[MXRoom+Riot] Error fetching trust level summary", error);
|
||||
onComplete(UserEncryptionTrustLevelUnknown);
|
||||
}];
|
||||
}
|
||||
else
|
||||
|
|
|
@ -195,7 +195,9 @@ UINavigationControllerDelegate
|
|||
- (BOOL)presentIncomingKeyVerificationRequest:(id<MXKeyVerificationRequest>)incomingKeyVerificationRequest
|
||||
inSession:(MXSession*)session;
|
||||
|
||||
- (BOOL)presentUserVerificationForRoomMember:(MXRoomMember*)roomMember session:(MXSession*)mxSession;
|
||||
- (BOOL)presentUserVerificationForRoomMember:(MXRoomMember*)roomMember
|
||||
session:(MXSession*)mxSession
|
||||
completion:(void (^)(void))completion;
|
||||
|
||||
- (BOOL)presentCompleteSecurityForSession:(MXSession*)mxSession;
|
||||
|
||||
|
|
|
@ -128,6 +128,11 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
If any the currently displayed key verification dialog
|
||||
*/
|
||||
KeyVerificationCoordinatorBridgePresenter *keyVerificationCoordinatorBridgePresenter;
|
||||
|
||||
/**
|
||||
Completion block for the requester of key verification
|
||||
*/
|
||||
void (^keyVerificationCompletionBlock)(void);
|
||||
|
||||
/**
|
||||
Currently displayed secure backup setup
|
||||
|
@ -3700,7 +3705,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
return presented;
|
||||
}
|
||||
|
||||
- (BOOL)presentUserVerificationForRoomMember:(MXRoomMember*)roomMember session:(MXSession*)mxSession
|
||||
- (BOOL)presentUserVerificationForRoomMember:(MXRoomMember*)roomMember
|
||||
session:(MXSession*)mxSession
|
||||
completion:(void (^)(void))completion;
|
||||
{
|
||||
MXLogDebug(@"[AppDelegate][MXKeyVerification] presentUserVerificationForRoomMember: %@", roomMember);
|
||||
|
||||
|
@ -3713,6 +3720,8 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
[keyVerificationCoordinatorBridgePresenter presentFrom:self.presentedViewController roomMember:roomMember animated:YES];
|
||||
|
||||
presented = YES;
|
||||
|
||||
keyVerificationCompletionBlock = completion;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3744,7 +3753,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
|
||||
- (void)keyVerificationCoordinatorBridgePresenterDelegateDidComplete:(KeyVerificationCoordinatorBridgePresenter *)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId
|
||||
{
|
||||
MXCrypto *crypto = coordinatorBridgePresenter.session.crypto;
|
||||
id<MXCrypto> crypto = coordinatorBridgePresenter.session.crypto;
|
||||
if (!crypto.backup.hasPrivateKeyInCryptoStore || !crypto.backup.enabled)
|
||||
{
|
||||
MXLogDebug(@"[AppDelegate][MXKeyVerification] requestAllPrivateKeys: Request key backup private keys");
|
||||
|
@ -3765,6 +3774,11 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
}];
|
||||
|
||||
keyVerificationCoordinatorBridgePresenter = nil;
|
||||
|
||||
if (keyVerificationCompletionBlock) {
|
||||
keyVerificationCompletionBlock();
|
||||
}
|
||||
keyVerificationCompletionBlock = nil;
|
||||
}
|
||||
|
||||
#pragma mark - New request
|
||||
|
@ -3984,7 +3998,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
|
||||
- (void)registerUserDidSignInOnNewDeviceNotificationForSession:(MXSession*)session
|
||||
{
|
||||
MXCrossSigning *crossSigning = session.crypto.crossSigning;
|
||||
id<MXCrossSigning> crossSigning = session.crypto.crossSigning;
|
||||
|
||||
if (!crossSigning)
|
||||
{
|
||||
|
@ -4075,7 +4089,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
|
||||
- (void)registerDidChangeCrossSigningKeysNotificationForSession:(MXSession*)session
|
||||
{
|
||||
MXCrossSigning *crossSigning = session.crypto.crossSigning;
|
||||
id<MXCrossSigning> crossSigning = session.crypto.crossSigning;
|
||||
|
||||
if (!crossSigning)
|
||||
{
|
||||
|
|
|
@ -324,12 +324,8 @@ extension KeyVerificationCoordinator: KeyVerificationDataLoadingCoordinatorDeleg
|
|||
|
||||
// MARK: - DeviceVerificationStartCoordinatorDelegate
|
||||
extension KeyVerificationCoordinator: DeviceVerificationStartCoordinatorDelegate {
|
||||
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) {
|
||||
self.showVerifyBySAS(transaction: transaction, animated: true)
|
||||
}
|
||||
|
||||
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didTransactionCancelled transaction: MXSASTransaction) {
|
||||
self.didCancel()
|
||||
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, otherDidAcceptRequest request: MXKeyVerificationRequest) {
|
||||
self.showVerifyByScanning(keyVerificationRequest: request, animated: true)
|
||||
}
|
||||
|
||||
func deviceVerificationStartCoordinatorDidCancel(_ coordinator: DeviceVerificationStartCoordinatorType) {
|
||||
|
|
|
@ -63,13 +63,9 @@ extension DeviceVerificationStartCoordinator: DeviceVerificationStartViewModelCo
|
|||
func deviceVerificationStartViewModelDidUseLegacyVerification(_ viewModel: DeviceVerificationStartViewModelType) {
|
||||
self.delegate?.deviceVerificationStartCoordinatorDidCancel(self)
|
||||
}
|
||||
|
||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) {
|
||||
self.delegate?.deviceVerificationStartCoordinator(self, didCompleteWithOutgoingTransaction: transaction)
|
||||
}
|
||||
|
||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didTransactionCancelled transaction: MXSASTransaction) {
|
||||
self.delegate?.deviceVerificationStartCoordinator(self, didTransactionCancelled: transaction)
|
||||
|
||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, otherDidAcceptRequest request: MXKeyVerificationRequest) {
|
||||
self.delegate?.deviceVerificationStartCoordinator(self, otherDidAcceptRequest: request)
|
||||
}
|
||||
|
||||
func deviceVerificationStartViewModelDidCancel(_ viewModel: DeviceVerificationStartViewModelType) {
|
||||
|
|
|
@ -19,8 +19,7 @@
|
|||
import Foundation
|
||||
|
||||
protocol DeviceVerificationStartCoordinatorDelegate: AnyObject {
|
||||
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction)
|
||||
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didTransactionCancelled transaction: MXSASTransaction)
|
||||
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, otherDidAcceptRequest request: MXKeyVerificationRequest)
|
||||
|
||||
func deviceVerificationStartCoordinatorDidCancel(_ coordinator: DeviceVerificationStartCoordinatorType)
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy
|
|||
private let otherUser: MXUser
|
||||
private let otherDevice: MXDeviceInfo
|
||||
|
||||
private var transaction: MXSASTransaction!
|
||||
private var request: MXKeyVerificationRequest?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
|
@ -52,12 +52,12 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy
|
|||
case .beginVerifying:
|
||||
self.beginVerifying()
|
||||
case .verifyUsingLegacy:
|
||||
self.cancelTransaction()
|
||||
self.cancelRequest()
|
||||
self.update(viewState: .verifyUsingLegacy(self.session, self.otherDevice))
|
||||
case .verifiedUsingLegacy:
|
||||
self.coordinatorDelegate?.deviceVerificationStartViewModelDidUseLegacyVerification(self)
|
||||
case .cancel:
|
||||
self.cancelTransaction()
|
||||
self.cancelRequest()
|
||||
self.coordinatorDelegate?.deviceVerificationStartViewModelDidCancel(self)
|
||||
}
|
||||
}
|
||||
|
@ -67,30 +67,22 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy
|
|||
private func beginVerifying() {
|
||||
self.update(viewState: .loading)
|
||||
|
||||
self.verificationManager.beginKeyVerification(withUserId: self.otherUser.userId, andDeviceId: self.otherDevice.deviceId, method: MXKeyVerificationMethodSAS, success: { [weak self] (transaction) in
|
||||
|
||||
guard let sself = self else {
|
||||
return
|
||||
}
|
||||
guard let sasTransaction = transaction as? MXSASTransaction, !sasTransaction.isIncoming else {
|
||||
self.verificationManager.requestVerificationByToDevice(withUserId: otherUser.userId, deviceIds: [otherDevice.deviceId], methods: [MXKeyVerificationMethodSAS], success: { [weak self] request in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
sself.transaction = sasTransaction
|
||||
self.request = request
|
||||
|
||||
sself.update(viewState: .loaded)
|
||||
sself.registerTransactionDidStateChangeNotification(transaction: sasTransaction)
|
||||
self.update(viewState: .loaded)
|
||||
self.registerKeyVerificationRequestDidChangeNotification(for: request)
|
||||
}, failure: {[weak self] error in
|
||||
self?.update(viewState: .error(error))
|
||||
})
|
||||
}
|
||||
|
||||
private func cancelTransaction() {
|
||||
guard let transaction = self.transaction else {
|
||||
return
|
||||
}
|
||||
|
||||
transaction.cancel(with: MXTransactionCancelCode.user())
|
||||
private func cancelRequest() {
|
||||
request?.cancel(with: MXTransactionCancelCode.user(), success: nil)
|
||||
}
|
||||
|
||||
private func update(viewState: DeviceVerificationStartViewState) {
|
||||
|
@ -98,37 +90,41 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy
|
|||
}
|
||||
|
||||
|
||||
// MARK: - MXKeyVerificationTransactionDidChange
|
||||
// MARK: - MXKeyVerificationRequestDidChange
|
||||
|
||||
private func registerTransactionDidStateChangeNotification(transaction: MXSASTransaction) {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(transactionDidStateChange(notification:)), name: NSNotification.Name.MXKeyVerificationTransactionDidChange, object: transaction)
|
||||
private func registerKeyVerificationRequestDidChangeNotification(for request: MXKeyVerificationRequest) {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(requestDidStateChange(notification:)), name: .MXKeyVerificationRequestDidChange, object: request)
|
||||
}
|
||||
|
||||
private func unregisterTransactionDidStateChangeNotification() {
|
||||
NotificationCenter.default.removeObserver(self, name: .MXKeyVerificationTransactionDidChange, object: nil)
|
||||
private func unregisterKeyVerificationRequestDidChangeNotification() {
|
||||
NotificationCenter.default.removeObserver(self, name: .MXKeyVerificationRequestDidChange, object: nil)
|
||||
}
|
||||
|
||||
@objc private func transactionDidStateChange(notification: Notification) {
|
||||
guard let transaction = notification.object as? MXSASTransaction, !transaction.isIncoming else {
|
||||
|
||||
@objc private func requestDidStateChange(notification: Notification) {
|
||||
guard let request = notification.object as? MXKeyVerificationRequest, request.requestId == self.request?.requestId else {
|
||||
return
|
||||
}
|
||||
|
||||
switch transaction.state {
|
||||
case MXSASTransactionStateShowSAS:
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.coordinatorDelegate?.deviceVerificationStartViewModel(self, didCompleteWithOutgoingTransaction: transaction)
|
||||
case MXSASTransactionStateCancelled:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
switch request.state {
|
||||
case MXKeyVerificationRequestStateAccepted, MXKeyVerificationRequestStateReady:
|
||||
self.unregisterKeyVerificationRequestDidChangeNotification()
|
||||
self.coordinatorDelegate?.deviceVerificationStartViewModel(self, otherDidAcceptRequest: request)
|
||||
|
||||
case MXKeyVerificationRequestStateCancelled:
|
||||
guard let reason = request.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.unregisterKeyVerificationRequestDidChangeNotification()
|
||||
self.update(viewState: .cancelled(reason))
|
||||
case MXSASTransactionStateCancelledByMe:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
case MXKeyVerificationRequestStateCancelledByMe:
|
||||
guard let reason = request.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.unregisterKeyVerificationRequestDidChangeNotification()
|
||||
self.update(viewState: .cancelledByMe(reason))
|
||||
case MXKeyVerificationRequestStateExpired:
|
||||
self.unregisterKeyVerificationRequestDidChangeNotification()
|
||||
self.update(viewState: .error(UserVerificationStartViewModelError.keyVerificationRequestExpired))
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
|
@ -25,8 +25,7 @@ protocol DeviceVerificationStartViewModelViewDelegate: AnyObject {
|
|||
protocol DeviceVerificationStartViewModelCoordinatorDelegate: AnyObject {
|
||||
func deviceVerificationStartViewModelDidUseLegacyVerification(_ viewModel: DeviceVerificationStartViewModelType)
|
||||
|
||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction)
|
||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didTransactionCancelled transaction: MXSASTransaction)
|
||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, otherDidAcceptRequest request: MXKeyVerificationRequest)
|
||||
|
||||
func deviceVerificationStartViewModelDidCancel(_ viewModel: DeviceVerificationStartViewModelType)
|
||||
}
|
||||
|
|
|
@ -189,6 +189,7 @@ extension UserVerificationCoordinator: KeyVerificationCoordinatorDelegate {
|
|||
|
||||
func keyVerificationCoordinatorDidComplete(_ coordinator: KeyVerificationCoordinatorType, otherUserId: String, otherDeviceId: String) {
|
||||
dismissPresenter(coordinator: coordinator)
|
||||
delegate?.userVerificationCoordinatorDidComplete(self)
|
||||
}
|
||||
|
||||
func keyVerificationCoordinatorDidCancel(_ coordinator: KeyVerificationCoordinatorType) {
|
||||
|
|
|
@ -1743,8 +1743,18 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
|
|||
return;
|
||||
}
|
||||
|
||||
if (![mxSession.crypto.crossSigning isKindOfClass:[MXLegacyCrossSigning class]]) {
|
||||
MXLogFailure(@"Device dehydratation is currently only supported by legacy cross signing, add support to all implementations");
|
||||
if (failure)
|
||||
{
|
||||
failure(nil);
|
||||
}
|
||||
return;
|
||||
}
|
||||
MXLegacyCrossSigning *crossSigning = (MXLegacyCrossSigning *)mxSession.crypto.crossSigning;;
|
||||
|
||||
MXLogDebug(@"[MXKAccount] attemptDeviceDehydrationWithRetry: starting device dehydration");
|
||||
[[MXKAccountManager sharedManager].dehydrationService dehydrateDeviceWithMatrixRestClient:mxRestClient crypto:mxSession.crypto dehydrationKey:keyData success:^(NSString *deviceId) {
|
||||
[[MXKAccountManager sharedManager].dehydrationService dehydrateDeviceWithMatrixRestClient:mxRestClient crossSigning:crossSigning dehydrationKey:keyData success:^(NSString *deviceId) {
|
||||
MXLogDebug(@"[MXKAccount] attemptDeviceDehydrationWithRetry: device successfully dehydrated");
|
||||
|
||||
if (success)
|
||||
|
|
|
@ -65,9 +65,12 @@
|
|||
_event = event;
|
||||
|
||||
_displayFix = MXKRoomBubbleComponentDisplayFixNone;
|
||||
if ([event.content[@"format"] isEqualToString:kMXRoomMessageFormatHTML])
|
||||
|
||||
NSString *format = event.content[@"format"];
|
||||
if ([format isKindOfClass:[NSString class]] && [format isEqualToString:kMXRoomMessageFormatHTML])
|
||||
{
|
||||
if ([((NSString*)event.content[@"formatted_body"]) containsString:@"<blockquote"])
|
||||
NSString *formattedBody = (NSString*)event.content[@"formatted_body"];
|
||||
if ([formattedBody isKindOfClass:[NSString class]] && [formattedBody containsString:@"<blockquote"])
|
||||
{
|
||||
_displayFix |= MXKRoomBubbleComponentDisplayFixHtmlBlockquote;
|
||||
}
|
||||
|
|
|
@ -440,7 +440,9 @@
|
|||
|
||||
- (void)startUserVerification
|
||||
{
|
||||
[[AppDelegate theDelegate] presentUserVerificationForRoomMember:self.mxRoomMember session:self.mainSession];
|
||||
[[AppDelegate theDelegate] presentUserVerificationForRoomMember:self.mxRoomMember session:self.mainSession completion:^{
|
||||
[self refreshUserEncryptionTrustLevel];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)presentUserVerification
|
||||
|
@ -1332,6 +1334,7 @@
|
|||
|
||||
- (void)keyVerificationCoordinatorBridgePresenterDelegateDidComplete:(KeyVerificationCoordinatorBridgePresenter *)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId
|
||||
{
|
||||
[self refreshUserEncryptionTrustLevel];
|
||||
[self dismissKeyVerificationCoordinatorBridgePresenter];
|
||||
}
|
||||
|
||||
|
|
|
@ -806,7 +806,7 @@ static BOOL _disableLongPressGestureOnEvent;
|
|||
mimetype = bubbleData.attachment.contentInfo[@"mimetype"];
|
||||
}
|
||||
|
||||
if ([mimetype isEqualToString:@"image/gif"])
|
||||
if ([mimetype isKindOfClass:[NSString class]] && [mimetype isEqualToString:@"image/gif"])
|
||||
{
|
||||
if (_isAutoAnimatedGif)
|
||||
{
|
||||
|
|
|
@ -88,7 +88,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
|||
let subView: UIView = hostingViewController.view
|
||||
self.addSubview(subView)
|
||||
|
||||
hostingViewController.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.translatesAutoresizingMaskIntoConstraints = false
|
||||
subView.translatesAutoresizingMaskIntoConstraints = false
|
||||
heightConstraint = subView.heightAnchor.constraint(equalToConstant: height)
|
||||
NSLayoutConstraint.activate([
|
||||
|
@ -103,7 +103,13 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
|||
.sink(receiveValue: { [weak self] idealHeight in
|
||||
guard let self = self else { return }
|
||||
self.updateToolbarHeight(wysiwygHeight: idealHeight)
|
||||
})
|
||||
}),
|
||||
// Required to update the view constraints after minimise/maximise is tapped
|
||||
wysiwygViewModel.$idealHeight
|
||||
.removeDuplicates()
|
||||
.sink { [weak hostingViewController] _ in
|
||||
hostingViewController?.view.setNeedsLayout()
|
||||
}
|
||||
]
|
||||
|
||||
update(theme: ThemeService.shared().theme)
|
||||
|
|
|
@ -324,7 +324,7 @@ TableViewSectionsDelegate>
|
|||
|
||||
// Crypto sessions section
|
||||
|
||||
if (RiotSettings.shared.settingsSecurityScreenShowSessions)
|
||||
if (RiotSettings.shared.settingsSecurityScreenShowSessions && !RiotSettings.shared.enableNewSessionManager)
|
||||
{
|
||||
Section *sessionsSection = [Section sectionWithTag:SECTION_CRYPTO_SESSIONS];
|
||||
|
||||
|
@ -627,7 +627,7 @@ TableViewSectionsDelegate>
|
|||
|
||||
- (void)loadCrossSigning
|
||||
{
|
||||
MXCrossSigning *crossSigning = self.mainSession.crypto.crossSigning;
|
||||
id<MXCrossSigning> crossSigning = self.mainSession.crypto.crossSigning;
|
||||
|
||||
[crossSigning refreshStateWithSuccess:^(BOOL stateUpdated) {
|
||||
if (stateUpdated)
|
||||
|
@ -643,7 +643,7 @@ TableViewSectionsDelegate>
|
|||
{
|
||||
NSInteger numberOfRowsInCrossSigningSection;
|
||||
|
||||
MXCrossSigning *crossSigning = self.mainSession.crypto.crossSigning;
|
||||
id<MXCrossSigning> crossSigning = self.mainSession.crypto.crossSigning;
|
||||
switch (crossSigning.state)
|
||||
{
|
||||
case MXCrossSigningStateNotBootstrapped: // Action: Bootstrap
|
||||
|
@ -661,7 +661,7 @@ TableViewSectionsDelegate>
|
|||
|
||||
- (NSAttributedString*)crossSigningInformation
|
||||
{
|
||||
MXCrossSigning *crossSigning = self.mainSession.crypto.crossSigning;
|
||||
id<MXCrossSigning> crossSigning = self.mainSession.crypto.crossSigning;
|
||||
|
||||
NSString *crossSigningInformation;
|
||||
switch (crossSigning.state)
|
||||
|
@ -708,7 +708,7 @@ TableViewSectionsDelegate>
|
|||
buttonCell.mxkButton.accessibilityIdentifier = nil;
|
||||
|
||||
// And customise it
|
||||
MXCrossSigning *crossSigning = self.mainSession.crypto.crossSigning;
|
||||
id<MXCrossSigning> crossSigning = self.mainSession.crypto.crossSigning;
|
||||
switch (crossSigning.state)
|
||||
{
|
||||
case MXCrossSigningStateNotBootstrapped: // Action: Bootstrap
|
||||
|
|
|
@ -106,7 +106,11 @@ class QRLoginService: NSObject, QRLoginServiceProtocol {
|
|||
}
|
||||
|
||||
func stopScanning(destroy: Bool) {
|
||||
zxCapture.delegate = nil
|
||||
if (zxCapture.delegate != nil) {
|
||||
// Setting the zxCapture to nil without checking makes it start
|
||||
// scanning and implicitly requesting camera access
|
||||
zxCapture.delegate = nil
|
||||
}
|
||||
|
||||
guard zxCapture.running else {
|
||||
return
|
||||
|
@ -292,7 +296,7 @@ class QRLoginService: NSObject, QRLoginServiceProtocol {
|
|||
MXLog.debug("[QRLoginService] Received cross-signing details \(responsePayload)")
|
||||
|
||||
if let masterKeyFromVerifyingDevice = responsePayload.masterKey,
|
||||
let localMasterKey = session.crypto.crossSigningKeys(forUser: session.myUserId).masterKeys?.keys {
|
||||
let localMasterKey = session.crypto.crossSigning.crossSigningKeys(forUser: session.myUserId)?.masterKeys?.keys {
|
||||
guard masterKeyFromVerifyingDevice == localMasterKey else {
|
||||
MXLog.error("[QRLoginService] Received invalid master key from verifying device")
|
||||
await teardownRendezvous(state: .failed(error: .rendezvousFailed))
|
||||
|
@ -348,6 +352,7 @@ class QRLoginService: NSObject, QRLoginServiceProtocol {
|
|||
await teardownRendezvous()
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func teardownRendezvous(state: QRLoginServiceState? = nil) async {
|
||||
// Stop listening for changes, try deleting the resource
|
||||
_ = await rendezvousService?.tearDown()
|
||||
|
|
|
@ -36,6 +36,7 @@ struct ScreenList: View {
|
|||
VStack {
|
||||
TextField("Search", text: $searchQuery)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.autocorrectionDisabled()
|
||||
.padding(.horizontal)
|
||||
.accessibilityIdentifier("searchQueryTextField")
|
||||
.onChange(of: searchQuery, perform: search)
|
||||
|
|
|
@ -20,16 +20,33 @@ import XCTest
|
|||
extension XCUIApplication {
|
||||
func goToScreenWithIdentifier(_ identifier: String) {
|
||||
// Search for the screen identifier
|
||||
textFields["searchQueryTextField"].tap()
|
||||
typeText(identifier)
|
||||
|
||||
let textField = textFields["searchQueryTextField"]
|
||||
let button = buttons[identifier]
|
||||
let footer = staticTexts["footerText"]
|
||||
|
||||
while !button.isHittable, !footer.isHittable {
|
||||
tables.firstMatch.swipeUp()
|
||||
// Sometimes the search gets stuck without showing any results. Try to nudge it along
|
||||
for _ in 0...10 {
|
||||
textField.clearAndTypeText(identifier)
|
||||
if button.exists {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
button.tap()
|
||||
}
|
||||
}
|
||||
|
||||
private extension XCUIElement {
|
||||
func clearAndTypeText(_ text: String) {
|
||||
guard let stringValue = value as? String else {
|
||||
XCTFail("Tried to clear and type text into a non string value")
|
||||
return
|
||||
}
|
||||
|
||||
tap()
|
||||
|
||||
let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: stringValue.count)
|
||||
|
||||
typeText(deleteString)
|
||||
typeText(text)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,19 @@ final class ComposerUITests: MockScreenTestCase {
|
|||
wysiwygTextView.typeText("test")
|
||||
XCTAssertTrue(sendButton.exists)
|
||||
XCTAssertFalse(app.buttons["editButton"].exists)
|
||||
|
||||
let maximiseButton = app.buttons["maximiseButton"]
|
||||
let minimiseButton = app.buttons["minimiseButton"]
|
||||
XCTAssertFalse(minimiseButton.exists)
|
||||
XCTAssertTrue(maximiseButton.exists)
|
||||
|
||||
maximiseButton.tap()
|
||||
XCTAssertTrue(minimiseButton.exists)
|
||||
XCTAssertFalse(maximiseButton.exists)
|
||||
|
||||
minimiseButton.tap()
|
||||
XCTAssertFalse(minimiseButton.exists)
|
||||
XCTAssertTrue(maximiseButton.exists)
|
||||
}
|
||||
|
||||
func testReplyMode() throws {
|
||||
|
@ -56,6 +69,19 @@ final class ComposerUITests: MockScreenTestCase {
|
|||
let textViewContent = wysiwygTextView.value as! String
|
||||
XCTAssertFalse(textViewContent.isEmpty)
|
||||
XCTAssertFalse(cancelButton.exists)
|
||||
|
||||
let maximiseButton = app.buttons["maximiseButton"]
|
||||
let minimiseButton = app.buttons["minimiseButton"]
|
||||
XCTAssertFalse(minimiseButton.exists)
|
||||
XCTAssertTrue(maximiseButton.exists)
|
||||
|
||||
maximiseButton.tap()
|
||||
XCTAssertTrue(minimiseButton.exists)
|
||||
XCTAssertFalse(maximiseButton.exists)
|
||||
|
||||
minimiseButton.tap()
|
||||
XCTAssertFalse(minimiseButton.exists)
|
||||
XCTAssertTrue(maximiseButton.exists)
|
||||
}
|
||||
|
||||
func testEditMode() throws {
|
||||
|
@ -82,5 +108,18 @@ final class ComposerUITests: MockScreenTestCase {
|
|||
let textViewContent = wysiwygTextView.value as! String
|
||||
XCTAssertTrue(textViewContent.isEmpty)
|
||||
XCTAssertFalse(cancelButton.exists)
|
||||
|
||||
let maximiseButton = app.buttons["maximiseButton"]
|
||||
let minimiseButton = app.buttons["minimiseButton"]
|
||||
XCTAssertFalse(minimiseButton.exists)
|
||||
XCTAssertTrue(maximiseButton.exists)
|
||||
|
||||
maximiseButton.tap()
|
||||
XCTAssertTrue(minimiseButton.exists)
|
||||
XCTAssertFalse(maximiseButton.exists)
|
||||
|
||||
minimiseButton.tap()
|
||||
XCTAssertFalse(minimiseButton.exists)
|
||||
XCTAssertTrue(maximiseButton.exists)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,14 @@ struct Composer: View {
|
|||
viewModel.viewState.sendMode == .edit ? "editButton" : "sendButton"
|
||||
}
|
||||
|
||||
private var toggleButtonAcccessibilityIdentifier: String {
|
||||
wysiwygViewModel.maximised ? "minimiseButton" : "maximiseButton"
|
||||
}
|
||||
|
||||
private var toggleButtonImageName: String {
|
||||
wysiwygViewModel.maximised ? Asset.Images.minimiseComposer.name : Asset.Images.maximiseComposer.name
|
||||
}
|
||||
|
||||
private var borderColor: Color {
|
||||
focused ? theme.colors.quarterlyContent : theme.colors.quinaryContent
|
||||
}
|
||||
|
@ -76,8 +84,6 @@ struct Composer: View {
|
|||
var body: some View {
|
||||
VStack(spacing: 8) {
|
||||
let rect = RoundedRectangle(cornerRadius: cornerRadius)
|
||||
// TODO: Fix maximise animation bugs before re-enabling
|
||||
// ZStack(alignment: .topTrailing) {
|
||||
VStack(spacing: 12) {
|
||||
if viewModel.viewState.shouldDisplayContext {
|
||||
HStack {
|
||||
|
@ -103,36 +109,39 @@ struct Composer: View {
|
|||
.padding(.top, 8)
|
||||
.padding(.horizontal, horizontalPadding)
|
||||
}
|
||||
WysiwygComposerView(
|
||||
focused: $focused,
|
||||
content: wysiwygViewModel.content,
|
||||
replaceText: wysiwygViewModel.replaceText,
|
||||
select: wysiwygViewModel.select,
|
||||
didUpdateText: wysiwygViewModel.didUpdateText
|
||||
)
|
||||
.tintColor(theme.colors.accent)
|
||||
.placeholder(viewModel.viewState.placeholder, color: theme.colors.tertiaryContent)
|
||||
.frame(height: wysiwygViewModel.idealHeight)
|
||||
.padding(.horizontal, horizontalPadding)
|
||||
.onAppear {
|
||||
wysiwygViewModel.setup()
|
||||
HStack(alignment: .top, spacing: 0) {
|
||||
WysiwygComposerView(
|
||||
focused: $focused,
|
||||
content: wysiwygViewModel.content,
|
||||
replaceText: wysiwygViewModel.replaceText,
|
||||
select: wysiwygViewModel.select,
|
||||
didUpdateText: wysiwygViewModel.didUpdateText
|
||||
)
|
||||
.tintColor(theme.colors.accent)
|
||||
.placeholder(viewModel.viewState.placeholder, color: theme.colors.tertiaryContent)
|
||||
.frame(height: wysiwygViewModel.idealHeight)
|
||||
.onAppear {
|
||||
wysiwygViewModel.setup()
|
||||
}
|
||||
Button {
|
||||
wysiwygViewModel.maximised.toggle()
|
||||
} label: {
|
||||
Image(toggleButtonImageName)
|
||||
.resizable()
|
||||
.foregroundColor(theme.colors.tertiaryContent)
|
||||
.frame(width: 16, height: 16)
|
||||
}
|
||||
.accessibilityIdentifier(toggleButtonAcccessibilityIdentifier)
|
||||
.padding(.leading, 12)
|
||||
.padding(.trailing, 4)
|
||||
}
|
||||
// Button {
|
||||
// withAnimation(.easeInOut(duration: 0.25)) {
|
||||
// viewModel.maximised.toggle()
|
||||
// }
|
||||
// } label: {
|
||||
// Image(viewModel.maximised ? Asset.Images.minimiseComposer.name : Asset.Images.maximiseComposer.name)
|
||||
// .foregroundColor(theme.colors.tertiaryContent)
|
||||
// }
|
||||
// .padding(.top, 4)
|
||||
// .padding(.trailing, 12)
|
||||
// }
|
||||
.padding(.horizontal, horizontalPadding)
|
||||
.padding(.top, topPadding)
|
||||
.padding(.bottom, verticalPadding)
|
||||
}
|
||||
.clipShape(rect)
|
||||
.overlay(rect.stroke(borderColor, lineWidth: 1))
|
||||
.animation(.easeInOut(duration: 0.1), value: wysiwygViewModel.idealHeight)
|
||||
.padding(.horizontal, horizontalPadding)
|
||||
.padding(.top, 8)
|
||||
.onTapGesture {
|
||||
|
@ -148,7 +157,6 @@ struct Composer: View {
|
|||
.resizable()
|
||||
.foregroundColor(theme.colors.tertiaryContent)
|
||||
.frame(width: 14, height: 14)
|
||||
|
||||
}
|
||||
.frame(width: 36, height: 36)
|
||||
.background(Circle().fill(theme.colors.system))
|
||||
|
|
|
@ -24,36 +24,45 @@ class TimelinePollUITests: MockScreenTestCase {
|
|||
XCTAssert(app.staticTexts["Question"].exists)
|
||||
XCTAssert(app.staticTexts["20 votes cast"].exists)
|
||||
|
||||
XCTAssert(app.buttons["First, 10 votes"].exists)
|
||||
XCTAssertEqual(app.buttons["First, 10 votes"].value as! String, "50%")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption0Count"].label, "10 votes")
|
||||
XCTAssertEqual(app.progressIndicators["PollAnswerOption0Progress"].value as? String, "50%")
|
||||
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption1Count"].label, "5 votes")
|
||||
XCTAssertEqual(app.progressIndicators["PollAnswerOption1Progress"].value as? String, "25%")
|
||||
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption2Count"].label, "15 votes")
|
||||
XCTAssertEqual(app.progressIndicators["PollAnswerOption2Progress"].value as? String, "75%")
|
||||
|
||||
XCTAssert(app.buttons["Second, 5 votes"].exists)
|
||||
XCTAssertEqual(app.buttons["Second, 5 votes"].value as! String, "25%")
|
||||
app.buttons["PollAnswerOption0"].tap()
|
||||
|
||||
XCTAssert(app.buttons["Third, 15 votes"].exists)
|
||||
XCTAssertEqual(app.buttons["Third, 15 votes"].value as! String, "75%")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption0Count"].label, "11 votes")
|
||||
XCTAssertEqual(app.progressIndicators["PollAnswerOption0Progress"].value as? String, "55%")
|
||||
|
||||
app.buttons["First, 10 votes"].tap()
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption1Count"].label, "4 votes")
|
||||
XCTAssertEqual(app.progressIndicators["PollAnswerOption1Progress"].value as? String, "20%")
|
||||
|
||||
XCTAssert(app.buttons["First, 11 votes"].exists)
|
||||
XCTAssertEqual(app.buttons["First, 11 votes"].value as! String, "55%")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption2Count"].label, "15 votes")
|
||||
XCTAssertEqual(app.progressIndicators["PollAnswerOption2Progress"].value as? String, "75%")
|
||||
|
||||
XCTAssert(app.buttons["Second, 4 votes"].exists)
|
||||
XCTAssertEqual(app.buttons["Second, 4 votes"].value as! String, "20%")
|
||||
app.buttons["PollAnswerOption2"].tap()
|
||||
|
||||
XCTAssert(app.buttons["Third, 15 votes"].exists)
|
||||
XCTAssertEqual(app.buttons["Third, 15 votes"].value as! String, "75%")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption0Count"].label, "10 votes")
|
||||
XCTAssertEqual(app.progressIndicators["PollAnswerOption0Progress"].value as? String, "50%")
|
||||
|
||||
app.buttons["Third, 15 votes"].tap()
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption1Count"].label, "4 votes")
|
||||
XCTAssertEqual(app.progressIndicators["PollAnswerOption1Progress"].value as? String, "20%")
|
||||
|
||||
XCTAssert(app.buttons["First, 10 votes"].exists)
|
||||
XCTAssertEqual(app.buttons["First, 10 votes"].value as! String, "50%")
|
||||
|
||||
XCTAssert(app.buttons["Second, 4 votes"].exists)
|
||||
XCTAssertEqual(app.buttons["Second, 4 votes"].value as! String, "20%")
|
||||
|
||||
XCTAssert(app.buttons["Third, 16 votes"].exists)
|
||||
XCTAssertEqual(app.buttons["Third, 16 votes"].value as! String, "80%")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption2Count"].label, "16 votes")
|
||||
XCTAssertEqual(app.progressIndicators["PollAnswerOption2Progress"].value as? String, "80%")
|
||||
}
|
||||
|
||||
func testOpenUndisclosedPoll() {
|
||||
|
@ -62,29 +71,29 @@ class TimelinePollUITests: MockScreenTestCase {
|
|||
XCTAssert(app.staticTexts["Question"].exists)
|
||||
XCTAssert(app.staticTexts["20 votes cast"].exists)
|
||||
|
||||
XCTAssert(!app.buttons["First, 10 votes"].exists)
|
||||
XCTAssert(app.buttons["First"].exists)
|
||||
XCTAssertTrue((app.buttons["First"].value as! String).isEmpty)
|
||||
|
||||
XCTAssert(!app.buttons["Second, 5 votes"].exists)
|
||||
XCTAssert(app.buttons["Second"].exists)
|
||||
XCTAssertTrue((app.buttons["Second"].value as! String).isEmpty)
|
||||
|
||||
XCTAssert(!app.buttons["Third, 15 votes"].exists)
|
||||
XCTAssert(app.buttons["Third"].exists)
|
||||
XCTAssertTrue((app.buttons["Third"].value as! String).isEmpty)
|
||||
|
||||
app.buttons["First"].tap()
|
||||
|
||||
XCTAssert(app.buttons["First"].exists)
|
||||
XCTAssert(app.buttons["Second"].exists)
|
||||
XCTAssert(app.buttons["Third"].exists)
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||
XCTAssert(!app.staticTexts["PollAnswerOption0Count"].exists)
|
||||
XCTAssert(!app.progressIndicators["PollAnswerOption0Progress"].exists)
|
||||
|
||||
app.buttons["Third"].tap()
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||
XCTAssert(!app.staticTexts["PollAnswerOption1Count"].exists)
|
||||
XCTAssert(!app.progressIndicators["PollAnswerOption1Progress"].exists)
|
||||
|
||||
XCTAssert(app.buttons["First"].exists)
|
||||
XCTAssert(app.buttons["Second"].exists)
|
||||
XCTAssert(app.buttons["Third"].exists)
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||
XCTAssert(!app.staticTexts["PollAnswerOption2Count"].exists)
|
||||
XCTAssert(!app.progressIndicators["PollAnswerOption2Progress"].exists)
|
||||
|
||||
app.buttons["PollAnswerOption0"].tap()
|
||||
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||
|
||||
app.buttons["PollAnswerOption2"].tap()
|
||||
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||
}
|
||||
|
||||
func testClosedDisclosedPoll() {
|
||||
|
@ -100,25 +109,31 @@ class TimelinePollUITests: MockScreenTestCase {
|
|||
private func checkClosedPoll() {
|
||||
XCTAssert(app.staticTexts["Question"].exists)
|
||||
XCTAssert(app.staticTexts["Final results based on 20 votes"].exists)
|
||||
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption0Count"].label, "10 votes")
|
||||
XCTAssertEqual(app.progressIndicators["PollAnswerOption0Progress"].value as? String, "50%")
|
||||
|
||||
XCTAssert(app.buttons["First, 10 votes"].exists)
|
||||
XCTAssertEqual(app.buttons["First, 10 votes"].value as! String, "50%")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption1Count"].label, "5 votes")
|
||||
XCTAssertEqual(app.progressIndicators["PollAnswerOption1Progress"].value as? String, "25%")
|
||||
|
||||
XCTAssert(app.buttons["Second, 5 votes"].exists)
|
||||
XCTAssertEqual(app.buttons["Second, 5 votes"].value as! String, "25%")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption2Count"].label, "15 votes")
|
||||
XCTAssertEqual(app.progressIndicators["PollAnswerOption2Progress"].value as? String, "75%")
|
||||
|
||||
XCTAssert(app.buttons["Third, 15 votes"].exists)
|
||||
XCTAssertEqual(app.buttons["Third, 15 votes"].value as! String, "75%")
|
||||
app.buttons["PollAnswerOption0"].tap()
|
||||
|
||||
app.buttons["First, 10 votes"].tap()
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption0Count"].label, "10 votes")
|
||||
XCTAssertEqual(app.progressIndicators["PollAnswerOption0Progress"].value as? String, "50%")
|
||||
|
||||
XCTAssert(app.buttons["First, 10 votes"].exists)
|
||||
XCTAssertEqual(app.buttons["First, 10 votes"].value as! String, "50%")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption1Count"].label, "5 votes")
|
||||
XCTAssertEqual(app.progressIndicators["PollAnswerOption1Progress"].value as? String, "25%")
|
||||
|
||||
XCTAssert(app.buttons["Second, 5 votes"].exists)
|
||||
XCTAssertEqual(app.buttons["Second, 5 votes"].value as! String, "25%")
|
||||
|
||||
XCTAssert(app.buttons["Third, 15 votes"].exists)
|
||||
XCTAssertEqual(app.buttons["Third, 15 votes"].value as! String, "75%")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||
XCTAssertEqual(app.staticTexts["PollAnswerOption2Count"].label, "15 votes")
|
||||
XCTAssertEqual(app.progressIndicators["PollAnswerOption2Progress"].value as? String, "75%")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ struct TimelinePollAnswerOptionButton: View {
|
|||
.overlay(rect.stroke(borderAccentColor, lineWidth: 1.0))
|
||||
.accentColor(progressViewAccentColor)
|
||||
}
|
||||
.accessibilityIdentifier("PollAnswerOption\(optionIndex)")
|
||||
}
|
||||
|
||||
var answerOptionLabel: some View {
|
||||
|
@ -53,6 +54,7 @@ struct TimelinePollAnswerOptionButton: View {
|
|||
Text(answerOption.text)
|
||||
.font(theme.fonts.body)
|
||||
.foregroundColor(theme.colors.primaryContent)
|
||||
.accessibilityIdentifier("PollAnswerOption\(optionIndex)Label")
|
||||
|
||||
if poll.closed, answerOption.winner {
|
||||
Spacer()
|
||||
|
@ -66,11 +68,13 @@ struct TimelinePollAnswerOptionButton: View {
|
|||
total: Double(poll.totalAnswerCount))
|
||||
.progressViewStyle(LinearProgressViewStyle())
|
||||
.scaleEffect(x: 1.0, y: 1.2, anchor: .center)
|
||||
.accessibilityIdentifier("PollAnswerOption\(optionIndex)Progress")
|
||||
|
||||
if poll.shouldDiscloseResults {
|
||||
Text(answerOption.count == 1 ? VectorL10n.pollTimelineOneVote : VectorL10n.pollTimelineVotesCount(Int(answerOption.count)))
|
||||
.font(theme.fonts.footnote)
|
||||
.foregroundColor(poll.closed && answerOption.winner ? theme.colors.accent : theme.colors.secondaryContent)
|
||||
.accessibilityIdentifier("PollAnswerOption\(optionIndex)Count")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,6 +96,10 @@ struct TimelinePollAnswerOptionButton: View {
|
|||
|
||||
return answerOption.selected ? theme.colors.accent : theme.colors.quarterlyContent
|
||||
}
|
||||
|
||||
var optionIndex: Int {
|
||||
poll.answerOptions.firstIndex { $0.id == answerOption.id } ?? Int.max
|
||||
}
|
||||
}
|
||||
|
||||
struct TimelinePollAnswerOptionButton_Previews: PreviewProvider {
|
||||
|
|
|
@ -21,10 +21,7 @@ class UserSuggestionUITests: MockScreenTestCase {
|
|||
func testUserSuggestionScreen() throws {
|
||||
app.goToScreenWithIdentifier(MockUserSuggestionScreenState.multipleResults.title)
|
||||
|
||||
XCTAssert(app.tables.firstMatch.waitForExistence(timeout: 1))
|
||||
|
||||
let firstButton = app.tables.firstMatch.buttons.firstMatch
|
||||
_ = firstButton.waitForExistence(timeout: 10)
|
||||
XCTAssert(firstButton.identifier == "displayNameText-userIdText")
|
||||
let firstButton = app.buttons["displayNameText-userIdText"].firstMatch
|
||||
XCTAssert(firstButton.waitForExistence(timeout: 10))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import RiotSwiftUI
|
|||
import XCTest
|
||||
|
||||
class UserSessionDetailsUITests: MockScreenTestCase {
|
||||
func test_longPressDetailsCell_CopiesValueToClipboard() throws {
|
||||
func disabled_broken_xcode14_test_longPressDetailsCell_CopiesValueToClipboard() throws {
|
||||
app.goToScreenWithIdentifier(MockUserSessionDetailsScreenState.allSections.title)
|
||||
|
||||
UIPasteboard.general.string = ""
|
||||
|
|
|
@ -111,7 +111,7 @@ private class MockSession: MXSession {
|
|||
}
|
||||
|
||||
/// A mock `MXCrypto` that can override the `canCrossSign` state.
|
||||
private class MockCrypto: MXCrypto {
|
||||
private class MockCrypto: MXLegacyCrypto {
|
||||
let canCrossSign: Bool
|
||||
override var crossSigning: MXCrossSigning! { MockCrossSigning(canCrossSign: canCrossSign) }
|
||||
|
||||
|
@ -123,7 +123,7 @@ private class MockCrypto: MXCrypto {
|
|||
}
|
||||
|
||||
/// A mock `MXCrossSigning` with an overridden `canCrossSign` property.
|
||||
private class MockCrossSigning: MXCrossSigning {
|
||||
private class MockCrossSigning: MXLegacyCrossSigning {
|
||||
let canCrossSignMock: Bool
|
||||
override var canCrossSign: Bool { canCrossSignMock }
|
||||
|
||||
|
|
1
changelog.d/6954.change
Normal file
1
changelog.d/6954.change
Normal file
|
@ -0,0 +1 @@
|
|||
Added the maximise/minimise toggle button to the Rich Text Composer
|
1
changelog.d/pr-6937.change
Normal file
1
changelog.d/pr-6937.change
Normal file
|
@ -0,0 +1 @@
|
|||
Verification: Deprecate legacy device-to-device verification
|
1
changelog.d/pr-6943.change
Normal file
1
changelog.d/pr-6943.change
Normal file
|
@ -0,0 +1 @@
|
|||
Crypto: Define MXCrypto and MXCrossSigning as protocols
|
1
changelog.d/pr-6996.build
Normal file
1
changelog.d/pr-6996.build
Normal file
|
@ -0,0 +1 @@
|
|||
Add Z-Labs tag for rich text editor and update to the new label naming.
|
1
changelog.d/pr-6999.change
Normal file
1
changelog.d/pr-6999.change
Normal file
|
@ -0,0 +1 @@
|
|||
Hide the old session list when the new device manager is enabled.
|
|
@ -21,7 +21,7 @@ platform :ios do
|
|||
|
||||
before_all do
|
||||
# Ensure used Xcode version
|
||||
xcversion(version: "~> 13.4")
|
||||
xcversion(version: "~> 14.0.1")
|
||||
end
|
||||
|
||||
#### Public ####
|
||||
|
@ -196,7 +196,7 @@ platform :ios do
|
|||
run_tests(
|
||||
workspace: "Riot.xcworkspace",
|
||||
scheme: "RiotSwiftUITests",
|
||||
device: "iPhone 13",
|
||||
device: "iPhone 14",
|
||||
code_coverage: true,
|
||||
# Test result configuration
|
||||
result_bundle: true,
|
||||
|
|
Loading…
Reference in a new issue