mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-29 07:42:40 +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
|
name: UI Tests CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
|
||||||
branches: [ develop ]
|
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
workflow_dispatch:
|
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, 'Z-IA') ||
|
||||||
contains(github.event.issue.labels.*.name, 'A-Themes-Custom') ||
|
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-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:
|
steps:
|
||||||
- uses: actions/github-script@v5
|
- uses: actions/github-script@v5
|
||||||
with:
|
with:
|
||||||
|
@ -267,7 +268,7 @@ jobs:
|
||||||
name: Add labelled issues to PS features team 3
|
name: Add labelled issues to PS features team 3
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'A-Composer-WYSIWYG')
|
contains(github.event.issue.labels.*.name, 'A-Rich-Text-Editor'')
|
||||||
steps:
|
steps:
|
||||||
- uses: octokit/graphql-action@v2.x
|
- uses: octokit/graphql-action@v2.x
|
||||||
id: add_to_project
|
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['WARNING_CFLAGS'] ||= ['$(inherited)','-Wno-nullability-completeness']
|
||||||
config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['$(inherited)', '-Xcc', '-Wno-nullability-completeness']
|
config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['$(inherited)', '-Xcc', '-Wno-nullability-completeness']
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -2639,3 +2639,15 @@
|
||||||
// Send Media Actions
|
// Send Media Actions
|
||||||
"wysiwyg_composer_start_action_media_picker" = "Fotobibliothek";
|
"wysiwyg_composer_start_action_media_picker" = "Fotobibliothek";
|
||||||
"settings_labs_enable_wysiwyg_composer" = "Probiere den Rich-Text-Editor aus (bald auch mit Plain-Text-Modus)";
|
"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_subtitle" = "Kasuta selle seadme kaamerat ja logi sisse teises seadmes kuvatud QR-koodi alusel:";
|
||||||
"authentication_qr_login_start_title" = "Loe QR-koodi";
|
"authentication_qr_login_start_title" = "Loe QR-koodi";
|
||||||
"authentication_login_with_qr" = "Logi sisse QR-koodi abil";
|
"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" = "جهت ارسال پیام صوتی نیاز به دسترسی به میکروفون وجود دارد اما %@ دسترسی استفاده از آن را ندارد";
|
"microphone_access_not_granted_for_voice_message" = "جهت ارسال پیام صوتی نیاز به دسترسی به میکروفون وجود دارد اما %@ دسترسی استفاده از آن را ندارد";
|
||||||
"e2e_passphrase_too_short" = "کلمه عبور بیش از حد کوتاه است (حداقل میبایست %d کاراکتر باشد)";
|
"e2e_passphrase_too_short" = "کلمه عبور بیش از حد کوتاه است (حداقل میبایست %d کاراکتر باشد)";
|
||||||
"message_reply_to_sender_sent_a_voice_message" = "یک پیام صوتی ارسال کنید.";
|
"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_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_qr_login_start_title" = "QR kód beolvasása";
|
||||||
"authentication_login_with_qr" = "Belépés QR kóddal";
|
"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_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.";
|
"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)";
|
"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_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.";
|
"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_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";
|
"group_participants_invited_section" = "ZAPROSZONY";
|
||||||
"receipt_status_read" = "Odczytano: ";
|
"receipt_status_read" = "Odczytano: ";
|
||||||
// Media picker
|
// Media picker
|
||||||
"media_picker_title" = "Selektor mediów";
|
"media_picker_title" = "Biblioteka mediów";
|
||||||
// Image picker
|
// Image picker
|
||||||
"image_picker_action_camera" = "Zrób zdjęcie";
|
"image_picker_action_camera" = "Zrób zdjęcie";
|
||||||
"image_picker_action_library" = "Wybierz z biblioteki";
|
"image_picker_action_library" = "Wybierz z biblioteki";
|
||||||
|
@ -2569,7 +2569,7 @@
|
||||||
|
|
||||||
// Mark: - All Chats
|
// 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_message" = "Utworzona przestrzeń zostanie dodana do %@.";
|
||||||
"spaces_subspace_creation_visibility_title" = "Jakiego rodzaju podprzestrzeń chcesz utworzyć?";
|
"spaces_subspace_creation_visibility_title" = "Jakiego rodzaju podprzestrzeń chcesz utworzyć?";
|
||||||
"spaces_explore_rooms_format" = "Przeglądaj %@";
|
"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_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.";
|
"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)";
|
"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_title" = "Ввод";
|
||||||
"key_backup_recover_from_passphrase_passphrase_placeholder" = "Введите секретную фразу";
|
"key_backup_recover_from_passphrase_passphrase_placeholder" = "Введите секретную фразу";
|
||||||
"key_backup_recover_from_passphrase_recover_action" = "Разблокировать историю";
|
"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_part2" = "использовать ключ безопасности";
|
||||||
"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = ".";
|
"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = ".";
|
||||||
"key_backup_recover_from_recovery_key_info" = "Используйте ключ безопасности для разблокировки истории безопасных сообщений";
|
"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_recovery_key_title" = "Ключ безопасности";
|
||||||
"key_backup_setup_success_from_recovery_key_make_copy_action" = "Сделать копию";
|
"key_backup_setup_success_from_recovery_key_make_copy_action" = "Сделать копию";
|
||||||
"key_backup_setup_success_from_recovery_key_made_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_recover_invalid_recovery_key_title" = "Несоответствующий ключ безопасности";
|
||||||
"key_backup_setup_banner_title" = "Не теряйте зашифрованные сообщения";
|
"key_backup_setup_banner_title" = "Не теряйте зашифрованные сообщения";
|
||||||
"key_backup_setup_banner_subtitle" = "Начать использовать ключ восстановления";
|
"key_backup_setup_banner_subtitle" = "Начать использовать ключ восстановления";
|
||||||
|
@ -641,7 +641,7 @@
|
||||||
"key_backup_setup_intro_setup_action_with_existing_backup" = "Использовать ключ восстановления";
|
"key_backup_setup_intro_setup_action_with_existing_backup" = "Использовать ключ восстановления";
|
||||||
"settings_key_backup_info" = "Зашифрованные сообщения защищены сквозным шифрованием. Только вы и получатель(и) имеют ключи для чтения этих сообщений.";
|
"settings_key_backup_info" = "Зашифрованные сообщения защищены сквозным шифрованием. Только вы и получатель(и) имеют ключи для чтения этих сообщений.";
|
||||||
"settings_key_backup_info_signout_warning" = "Сделайте резервную копию ключей перед выходом, чтобы не потерять их.";
|
"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_info" = "Или защитите свою резервную копию с помощью ключа безопасности, сохранив ее в безопасном месте.";
|
||||||
"key_backup_setup_passphrase_setup_recovery_key_action" = "(Расширенный) Настройка с ключом безопасности";
|
"key_backup_setup_passphrase_setup_recovery_key_action" = "(Расширенный) Настройка с ключом безопасности";
|
||||||
// Success from passphrase
|
// Success from passphrase
|
||||||
|
@ -654,7 +654,7 @@
|
||||||
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Зашифрованные сообщения будут утеряны";
|
"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_discard_key_backup_action" = "Мне не нужны мои зашифрованные сообщения";
|
||||||
"sign_out_non_existing_key_backup_alert_title" = "Вы потеряете доступ к зашифрованным сообщениям если выйдете сейчас";
|
"sign_out_non_existing_key_backup_alert_title" = "Вы потеряете доступ к зашифрованным сообщениям если выйдете сейчас";
|
||||||
"key_backup_recover_invalid_passphrase" = "Невозможно расшифровать резервную копию с помощью этой секретной фразы: убедитесь, что вы ввели верную секретную фразу для восстановления.";
|
"key_backup_recover_invalid_passphrase" = "Невозможно расшифровать резервную копию с помощью этой фразы: убедитесь, что вы ввели верную мнемоническую фразу.";
|
||||||
"key_backup_recover_invalid_recovery_key" = "Невозможно расшифровать резервную копию с помощью этого ключа: убедитесь, что вы ввели верный ключ безопасности.";
|
"key_backup_recover_invalid_recovery_key" = "Невозможно расшифровать резервную копию с помощью этого ключа: убедитесь, что вы ввели верный ключ безопасности.";
|
||||||
"e2e_key_backup_wrong_version_button_settings" = "Настройки";
|
"e2e_key_backup_wrong_version_button_settings" = "Настройки";
|
||||||
"key_backup_setup_intro_manual_export_info" = "(Расширенный)";
|
"key_backup_setup_intro_manual_export_info" = "(Расширенный)";
|
||||||
|
@ -986,7 +986,7 @@
|
||||||
"secure_key_backup_setup_intro_info" = "Защитите себя от потери доступа к зашифрованным сообщениям и данным, создав резервную копию ключей шифрования на своём сервере.";
|
"secure_key_backup_setup_intro_info" = "Защитите себя от потери доступа к зашифрованным сообщениям и данным, создав резервную копию ключей шифрования на своём сервере.";
|
||||||
"secure_key_backup_setup_intro_use_security_key_title" = "Используйте ключ безопасности";
|
"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_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_intro_use_security_passphrase_info" = "Введите секретную фразу, известную только вам, и создайте ключ для резервного копирования.";
|
||||||
"secure_key_backup_setup_existing_backup_error_title" = "Резервная копия сообщений уже существует";
|
"secure_key_backup_setup_existing_backup_error_title" = "Резервная копия сообщений уже существует";
|
||||||
"secure_key_backup_setup_existing_backup_error_info" = "Разблокируйте его для повторного использования в защищенной резервной копии или удалите для создания новой резервной копии сообщений в защищенной резервной копии.";
|
"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_information" = "Подтвердите этот сеанс на одном из других ваших сеансов, предоставив ему доступ к зашифрованным сообщениям.\n\nИспользуйте последнюю версию %@ на других ваших устройствах:";
|
||||||
"device_verification_self_verify_wait_additional_information" = "Это работает с %@ и другими клиентами Matrix с поддержкой кросс-подписи.";
|
"device_verification_self_verify_wait_additional_information" = "Это работает с %@ и другими клиентами Matrix с поддержкой кросс-подписи.";
|
||||||
"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Используйте ключ безопасности";
|
"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" = "Если вы не можете получить доступ к существующему сеансу";
|
"device_verification_self_verify_wait_recover_secrets_additional_information" = "Если вы не можете получить доступ к существующему сеансу";
|
||||||
"key_verification_verify_sas_title_emoji" = "Сравните смайлы";
|
"key_verification_verify_sas_title_emoji" = "Сравните смайлы";
|
||||||
"key_verification_verify_sas_title_number" = "Сравните числа";
|
"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" = "Интерактивная проверка";
|
||||||
"user_verification_session_details_verify_action_current_user_manually" = "Ручная проверка с помощью текста";
|
"user_verification_session_details_verify_action_current_user_manually" = "Ручная проверка с помощью текста";
|
||||||
"user_verification_session_details_verify_action_other_user" = "Подтверждение вручную";
|
"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_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_title" = "Ввод";
|
||||||
"secrets_recovery_with_passphrase_passphrase_placeholder" = "Введите секретную фразу";
|
"secrets_recovery_with_passphrase_passphrase_placeholder" = "Введите мнемоническую фразу";
|
||||||
"secrets_recovery_with_passphrase_recover_action" = "Использовать секретную фразу";
|
"secrets_recovery_with_passphrase_recover_action" = "Использовать секретную фразу";
|
||||||
"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Не знаете вашу секретную фразу? Вы можете ";
|
"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Не помните свою мнемоническую фразу? Вы можете ";
|
||||||
"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "использовать ключ безопасности";
|
"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "использовать бумажный ключ";
|
||||||
"secrets_recovery_with_passphrase_lost_passphrase_action_part3" = ".";
|
"secrets_recovery_with_passphrase_lost_passphrase_action_part3" = ".";
|
||||||
"secrets_recovery_with_passphrase_invalid_passphrase_title" = "Невозможно получить доступ к секретному хранилищу";
|
"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_title" = "Ключ безопасности";
|
||||||
"secrets_recovery_with_key_information_default" = "Получите доступ к своей защищённой истории сообщений и вашей личности с кросс-подписью для проверки других сеансов, введя ключ безопасности.";
|
"secrets_recovery_with_key_information_default" = "Получите доступ к своей защищённой истории сообщений и вашей личности с кросс-подписью для проверки других сеансов, введя ключ безопасности.";
|
||||||
"secrets_recovery_with_key_information_verify_device" = "Используйте ключ безопасности, чтобы проверить это устройство.";
|
"secrets_recovery_with_key_information_verify_device" = "Используйте ключ безопасности, чтобы проверить это устройство.";
|
||||||
|
@ -1128,11 +1128,11 @@
|
||||||
"secrets_setup_recovery_key_done_action" = "Готово";
|
"secrets_setup_recovery_key_done_action" = "Готово";
|
||||||
"secrets_setup_recovery_key_storage_alert_title" = "Храните его в безопасности";
|
"secrets_setup_recovery_key_storage_alert_title" = "Храните его в безопасности";
|
||||||
"secrets_setup_recovery_key_storage_alert_message" = "✓ Распечатайте и храните в безопасном месте\n✓ Сохраните его на USB-носителе или резервном носителе\n✓ Скопируйте его в свое личное облачное хранилище";
|
"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_information" = "Введите секретную фразу, известную только вам, для защиты данных на вашем сервере.";
|
||||||
"secrets_setup_recovery_passphrase_additional_information" = "Не используйте пароль своей учетной записи.";
|
"secrets_setup_recovery_passphrase_additional_information" = "Не используйте пароль своей учетной записи.";
|
||||||
"secrets_setup_recovery_passphrase_validate_action" = "Готово";
|
"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_title" = "Подтвердить";
|
||||||
"secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "Подтвердить секретную фразу";
|
"secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "Подтвердить секретную фразу";
|
||||||
"cross_signing_setup_banner_title" = "Настройка шифрования";
|
"cross_signing_setup_banner_title" = "Настройка шифрования";
|
||||||
|
@ -1238,8 +1238,8 @@
|
||||||
// MARK: - Home
|
// MARK: - Home
|
||||||
|
|
||||||
"home_empty_view_title" = "Добро пожаловать в %@,\n%@";
|
"home_empty_view_title" = "Добро пожаловать в %@,\n%@";
|
||||||
"secrets_setup_recovery_passphrase_summary_information" = "Запомните свою секретную фразу. Её можно использовать для разблокировки ваших зашифрованных сообщений и данных.";
|
"secrets_setup_recovery_passphrase_summary_information" = "Запомните свою мнемоническую фразу. Её можно использовать для разблокировки ваших зашифрованных сообщений и данных.";
|
||||||
"secrets_setup_recovery_passphrase_summary_title" = "Сохраните вашу секретную фразу";
|
"secrets_setup_recovery_passphrase_summary_title" = "Сохраните свою мнемоническую фразу";
|
||||||
"favourites_empty_view_information" = "Вы можете добавить в избранное несколькими способами - самый быстрый - просто нажать и удерживать. Нажмите на звёздочку, и они автоматически появятся здесь, и вы их навсегда сохраните.";
|
"favourites_empty_view_information" = "Вы можете добавить в избранное несколькими способами - самый быстрый - просто нажать и удерживать. Нажмите на звёздочку, и они автоматически появятся здесь, и вы их навсегда сохраните.";
|
||||||
|
|
||||||
// MARK: - Favourites
|
// MARK: - Favourites
|
||||||
|
@ -1355,7 +1355,7 @@
|
||||||
|
|
||||||
"space_feature_unavailable_title" = "Пространств ещё нет";
|
"space_feature_unavailable_title" = "Пространств ещё нет";
|
||||||
"secrets_recovery_with_key_information_unlock_secure_backup_with_key" = "Введите свой ключ безопасности, чтобы продолжить.";
|
"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" = "Сканирование с помощью этого устройства";
|
"key_verification_verify_qr_code_scan_code_other_device_action" = "Сканирование с помощью этого устройства";
|
||||||
|
|
||||||
// Success from secure backup
|
// 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_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.";
|
"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)";
|
"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_info" = "Зауважте, що назви сеансів також видно людям, з якими ви спілкуєтесь. %@";
|
||||||
"manage_session_name_hint" = "Власні назви сеансів допоможуть вам легше розпізнавати ваші пристрої.";
|
"manage_session_name_hint" = "Власні назви сеансів допоможуть вам легше розпізнавати ваші пристрої.";
|
||||||
"settings_labs_enable_wysiwyg_composer" = "Спробуйте розширений текстовий редактор (незабаром з'явиться режим звичайного тексту)";
|
"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)
|
if (self.mxSession.crypto)
|
||||||
{
|
{
|
||||||
[self.mxSession.crypto trustLevelSummaryForUserIds:@[userId] onComplete:^(MXUsersTrustLevelSummary *usersTrustLevelSummary) {
|
[self.mxSession.crypto trustLevelSummaryForUserIds:@[userId] forceDownload:NO success:^(MXUsersTrustLevelSummary *usersTrustLevelSummary) {
|
||||||
|
|
||||||
UserEncryptionTrustLevel userEncryptionTrustLevel;
|
UserEncryptionTrustLevel userEncryptionTrustLevel;
|
||||||
double trustedDevicesPercentage = usersTrustLevelSummary.trustedDevicesProgress.fractionCompleted;
|
double trustedDevicesPercentage = usersTrustLevelSummary.trustedDevicesProgress.fractionCompleted;
|
||||||
|
@ -341,7 +341,7 @@
|
||||||
else if (trustedDevicesPercentage == 0.0)
|
else if (trustedDevicesPercentage == 0.0)
|
||||||
{
|
{
|
||||||
// Verify if the user has the user has cross-signing enabled
|
// 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;
|
userEncryptionTrustLevel = UserEncryptionTrustLevelNotVerified;
|
||||||
}
|
}
|
||||||
|
@ -357,6 +357,9 @@
|
||||||
|
|
||||||
onComplete(userEncryptionTrustLevel);
|
onComplete(userEncryptionTrustLevel);
|
||||||
|
|
||||||
|
} failure:^(NSError *error) {
|
||||||
|
MXLogErrorDetails(@"[MXRoom+Riot] Error fetching trust level summary", error);
|
||||||
|
onComplete(UserEncryptionTrustLevelUnknown);
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -195,7 +195,9 @@ UINavigationControllerDelegate
|
||||||
- (BOOL)presentIncomingKeyVerificationRequest:(id<MXKeyVerificationRequest>)incomingKeyVerificationRequest
|
- (BOOL)presentIncomingKeyVerificationRequest:(id<MXKeyVerificationRequest>)incomingKeyVerificationRequest
|
||||||
inSession:(MXSession*)session;
|
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;
|
- (BOOL)presentCompleteSecurityForSession:(MXSession*)mxSession;
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,11 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
||||||
*/
|
*/
|
||||||
KeyVerificationCoordinatorBridgePresenter *keyVerificationCoordinatorBridgePresenter;
|
KeyVerificationCoordinatorBridgePresenter *keyVerificationCoordinatorBridgePresenter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Completion block for the requester of key verification
|
||||||
|
*/
|
||||||
|
void (^keyVerificationCompletionBlock)(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Currently displayed secure backup setup
|
Currently displayed secure backup setup
|
||||||
*/
|
*/
|
||||||
|
@ -3700,7 +3705,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
||||||
return presented;
|
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);
|
MXLogDebug(@"[AppDelegate][MXKeyVerification] presentUserVerificationForRoomMember: %@", roomMember);
|
||||||
|
|
||||||
|
@ -3713,6 +3720,8 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
||||||
[keyVerificationCoordinatorBridgePresenter presentFrom:self.presentedViewController roomMember:roomMember animated:YES];
|
[keyVerificationCoordinatorBridgePresenter presentFrom:self.presentedViewController roomMember:roomMember animated:YES];
|
||||||
|
|
||||||
presented = YES;
|
presented = YES;
|
||||||
|
|
||||||
|
keyVerificationCompletionBlock = completion;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3744,7 +3753,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
||||||
|
|
||||||
- (void)keyVerificationCoordinatorBridgePresenterDelegateDidComplete:(KeyVerificationCoordinatorBridgePresenter *)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId
|
- (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)
|
if (!crypto.backup.hasPrivateKeyInCryptoStore || !crypto.backup.enabled)
|
||||||
{
|
{
|
||||||
MXLogDebug(@"[AppDelegate][MXKeyVerification] requestAllPrivateKeys: Request key backup private keys");
|
MXLogDebug(@"[AppDelegate][MXKeyVerification] requestAllPrivateKeys: Request key backup private keys");
|
||||||
|
@ -3765,6 +3774,11 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
||||||
}];
|
}];
|
||||||
|
|
||||||
keyVerificationCoordinatorBridgePresenter = nil;
|
keyVerificationCoordinatorBridgePresenter = nil;
|
||||||
|
|
||||||
|
if (keyVerificationCompletionBlock) {
|
||||||
|
keyVerificationCompletionBlock();
|
||||||
|
}
|
||||||
|
keyVerificationCompletionBlock = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - New request
|
#pragma mark - New request
|
||||||
|
@ -3984,7 +3998,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
||||||
|
|
||||||
- (void)registerUserDidSignInOnNewDeviceNotificationForSession:(MXSession*)session
|
- (void)registerUserDidSignInOnNewDeviceNotificationForSession:(MXSession*)session
|
||||||
{
|
{
|
||||||
MXCrossSigning *crossSigning = session.crypto.crossSigning;
|
id<MXCrossSigning> crossSigning = session.crypto.crossSigning;
|
||||||
|
|
||||||
if (!crossSigning)
|
if (!crossSigning)
|
||||||
{
|
{
|
||||||
|
@ -4075,7 +4089,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
||||||
|
|
||||||
- (void)registerDidChangeCrossSigningKeysNotificationForSession:(MXSession*)session
|
- (void)registerDidChangeCrossSigningKeysNotificationForSession:(MXSession*)session
|
||||||
{
|
{
|
||||||
MXCrossSigning *crossSigning = session.crypto.crossSigning;
|
id<MXCrossSigning> crossSigning = session.crypto.crossSigning;
|
||||||
|
|
||||||
if (!crossSigning)
|
if (!crossSigning)
|
||||||
{
|
{
|
||||||
|
|
|
@ -324,12 +324,8 @@ extension KeyVerificationCoordinator: KeyVerificationDataLoadingCoordinatorDeleg
|
||||||
|
|
||||||
// MARK: - DeviceVerificationStartCoordinatorDelegate
|
// MARK: - DeviceVerificationStartCoordinatorDelegate
|
||||||
extension KeyVerificationCoordinator: DeviceVerificationStartCoordinatorDelegate {
|
extension KeyVerificationCoordinator: DeviceVerificationStartCoordinatorDelegate {
|
||||||
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) {
|
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, otherDidAcceptRequest request: MXKeyVerificationRequest) {
|
||||||
self.showVerifyBySAS(transaction: transaction, animated: true)
|
self.showVerifyByScanning(keyVerificationRequest: request, animated: true)
|
||||||
}
|
|
||||||
|
|
||||||
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didTransactionCancelled transaction: MXSASTransaction) {
|
|
||||||
self.didCancel()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func deviceVerificationStartCoordinatorDidCancel(_ coordinator: DeviceVerificationStartCoordinatorType) {
|
func deviceVerificationStartCoordinatorDidCancel(_ coordinator: DeviceVerificationStartCoordinatorType) {
|
||||||
|
|
|
@ -64,12 +64,8 @@ extension DeviceVerificationStartCoordinator: DeviceVerificationStartViewModelCo
|
||||||
self.delegate?.deviceVerificationStartCoordinatorDidCancel(self)
|
self.delegate?.deviceVerificationStartCoordinatorDidCancel(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) {
|
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, otherDidAcceptRequest request: MXKeyVerificationRequest) {
|
||||||
self.delegate?.deviceVerificationStartCoordinator(self, didCompleteWithOutgoingTransaction: transaction)
|
self.delegate?.deviceVerificationStartCoordinator(self, otherDidAcceptRequest: request)
|
||||||
}
|
|
||||||
|
|
||||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didTransactionCancelled transaction: MXSASTransaction) {
|
|
||||||
self.delegate?.deviceVerificationStartCoordinator(self, didTransactionCancelled: transaction)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func deviceVerificationStartViewModelDidCancel(_ viewModel: DeviceVerificationStartViewModelType) {
|
func deviceVerificationStartViewModelDidCancel(_ viewModel: DeviceVerificationStartViewModelType) {
|
||||||
|
|
|
@ -19,8 +19,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
protocol DeviceVerificationStartCoordinatorDelegate: AnyObject {
|
protocol DeviceVerificationStartCoordinatorDelegate: AnyObject {
|
||||||
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction)
|
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, otherDidAcceptRequest request: MXKeyVerificationRequest)
|
||||||
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didTransactionCancelled transaction: MXSASTransaction)
|
|
||||||
|
|
||||||
func deviceVerificationStartCoordinatorDidCancel(_ coordinator: DeviceVerificationStartCoordinatorType)
|
func deviceVerificationStartCoordinatorDidCancel(_ coordinator: DeviceVerificationStartCoordinatorType)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy
|
||||||
private let otherUser: MXUser
|
private let otherUser: MXUser
|
||||||
private let otherDevice: MXDeviceInfo
|
private let otherDevice: MXDeviceInfo
|
||||||
|
|
||||||
private var transaction: MXSASTransaction!
|
private var request: MXKeyVerificationRequest?
|
||||||
|
|
||||||
// MARK: Public
|
// MARK: Public
|
||||||
|
|
||||||
|
@ -52,12 +52,12 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy
|
||||||
case .beginVerifying:
|
case .beginVerifying:
|
||||||
self.beginVerifying()
|
self.beginVerifying()
|
||||||
case .verifyUsingLegacy:
|
case .verifyUsingLegacy:
|
||||||
self.cancelTransaction()
|
self.cancelRequest()
|
||||||
self.update(viewState: .verifyUsingLegacy(self.session, self.otherDevice))
|
self.update(viewState: .verifyUsingLegacy(self.session, self.otherDevice))
|
||||||
case .verifiedUsingLegacy:
|
case .verifiedUsingLegacy:
|
||||||
self.coordinatorDelegate?.deviceVerificationStartViewModelDidUseLegacyVerification(self)
|
self.coordinatorDelegate?.deviceVerificationStartViewModelDidUseLegacyVerification(self)
|
||||||
case .cancel:
|
case .cancel:
|
||||||
self.cancelTransaction()
|
self.cancelRequest()
|
||||||
self.coordinatorDelegate?.deviceVerificationStartViewModelDidCancel(self)
|
self.coordinatorDelegate?.deviceVerificationStartViewModelDidCancel(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,30 +67,22 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy
|
||||||
private func beginVerifying() {
|
private func beginVerifying() {
|
||||||
self.update(viewState: .loading)
|
self.update(viewState: .loading)
|
||||||
|
|
||||||
self.verificationManager.beginKeyVerification(withUserId: self.otherUser.userId, andDeviceId: self.otherDevice.deviceId, method: MXKeyVerificationMethodSAS, success: { [weak self] (transaction) in
|
self.verificationManager.requestVerificationByToDevice(withUserId: otherUser.userId, deviceIds: [otherDevice.deviceId], methods: [MXKeyVerificationMethodSAS], success: { [weak self] request in
|
||||||
|
guard let self = self else {
|
||||||
guard let sself = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard let sasTransaction = transaction as? MXSASTransaction, !sasTransaction.isIncoming else {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sself.transaction = sasTransaction
|
self.request = request
|
||||||
|
|
||||||
sself.update(viewState: .loaded)
|
self.update(viewState: .loaded)
|
||||||
sself.registerTransactionDidStateChangeNotification(transaction: sasTransaction)
|
self.registerKeyVerificationRequestDidChangeNotification(for: request)
|
||||||
}, failure: {[weak self] error in
|
}, failure: {[weak self] error in
|
||||||
self?.update(viewState: .error(error))
|
self?.update(viewState: .error(error))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private func cancelTransaction() {
|
private func cancelRequest() {
|
||||||
guard let transaction = self.transaction else {
|
request?.cancel(with: MXTransactionCancelCode.user(), success: nil)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
transaction.cancel(with: MXTransactionCancelCode.user())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func update(viewState: DeviceVerificationStartViewState) {
|
private func update(viewState: DeviceVerificationStartViewState) {
|
||||||
|
@ -98,37 +90,41 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: - MXKeyVerificationTransactionDidChange
|
// MARK: - MXKeyVerificationRequestDidChange
|
||||||
|
|
||||||
private func registerTransactionDidStateChangeNotification(transaction: MXSASTransaction) {
|
private func registerKeyVerificationRequestDidChangeNotification(for request: MXKeyVerificationRequest) {
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(transactionDidStateChange(notification:)), name: NSNotification.Name.MXKeyVerificationTransactionDidChange, object: transaction)
|
NotificationCenter.default.addObserver(self, selector: #selector(requestDidStateChange(notification:)), name: .MXKeyVerificationRequestDidChange, object: request)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func unregisterTransactionDidStateChangeNotification() {
|
private func unregisterKeyVerificationRequestDidChangeNotification() {
|
||||||
NotificationCenter.default.removeObserver(self, name: .MXKeyVerificationTransactionDidChange, object: nil)
|
NotificationCenter.default.removeObserver(self, name: .MXKeyVerificationRequestDidChange, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func transactionDidStateChange(notification: Notification) {
|
@objc private func requestDidStateChange(notification: Notification) {
|
||||||
guard let transaction = notification.object as? MXSASTransaction, !transaction.isIncoming else {
|
guard let request = notification.object as? MXKeyVerificationRequest, request.requestId == self.request?.requestId else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch transaction.state {
|
switch request.state {
|
||||||
case MXSASTransactionStateShowSAS:
|
case MXKeyVerificationRequestStateAccepted, MXKeyVerificationRequestStateReady:
|
||||||
self.unregisterTransactionDidStateChangeNotification()
|
self.unregisterKeyVerificationRequestDidChangeNotification()
|
||||||
self.coordinatorDelegate?.deviceVerificationStartViewModel(self, didCompleteWithOutgoingTransaction: transaction)
|
self.coordinatorDelegate?.deviceVerificationStartViewModel(self, otherDidAcceptRequest: request)
|
||||||
case MXSASTransactionStateCancelled:
|
|
||||||
guard let reason = transaction.reasonCancelCode else {
|
case MXKeyVerificationRequestStateCancelled:
|
||||||
|
guard let reason = request.reasonCancelCode else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.unregisterTransactionDidStateChangeNotification()
|
self.unregisterKeyVerificationRequestDidChangeNotification()
|
||||||
self.update(viewState: .cancelled(reason))
|
self.update(viewState: .cancelled(reason))
|
||||||
case MXSASTransactionStateCancelledByMe:
|
case MXKeyVerificationRequestStateCancelledByMe:
|
||||||
guard let reason = transaction.reasonCancelCode else {
|
guard let reason = request.reasonCancelCode else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.unregisterTransactionDidStateChangeNotification()
|
self.unregisterKeyVerificationRequestDidChangeNotification()
|
||||||
self.update(viewState: .cancelledByMe(reason))
|
self.update(viewState: .cancelledByMe(reason))
|
||||||
|
case MXKeyVerificationRequestStateExpired:
|
||||||
|
self.unregisterKeyVerificationRequestDidChangeNotification()
|
||||||
|
self.update(viewState: .error(UserVerificationStartViewModelError.keyVerificationRequestExpired))
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,7 @@ protocol DeviceVerificationStartViewModelViewDelegate: AnyObject {
|
||||||
protocol DeviceVerificationStartViewModelCoordinatorDelegate: AnyObject {
|
protocol DeviceVerificationStartViewModelCoordinatorDelegate: AnyObject {
|
||||||
func deviceVerificationStartViewModelDidUseLegacyVerification(_ viewModel: DeviceVerificationStartViewModelType)
|
func deviceVerificationStartViewModelDidUseLegacyVerification(_ viewModel: DeviceVerificationStartViewModelType)
|
||||||
|
|
||||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction)
|
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, otherDidAcceptRequest request: MXKeyVerificationRequest)
|
||||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didTransactionCancelled transaction: MXSASTransaction)
|
|
||||||
|
|
||||||
func deviceVerificationStartViewModelDidCancel(_ viewModel: DeviceVerificationStartViewModelType)
|
func deviceVerificationStartViewModelDidCancel(_ viewModel: DeviceVerificationStartViewModelType)
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,6 +189,7 @@ extension UserVerificationCoordinator: KeyVerificationCoordinatorDelegate {
|
||||||
|
|
||||||
func keyVerificationCoordinatorDidComplete(_ coordinator: KeyVerificationCoordinatorType, otherUserId: String, otherDeviceId: String) {
|
func keyVerificationCoordinatorDidComplete(_ coordinator: KeyVerificationCoordinatorType, otherUserId: String, otherDeviceId: String) {
|
||||||
dismissPresenter(coordinator: coordinator)
|
dismissPresenter(coordinator: coordinator)
|
||||||
|
delegate?.userVerificationCoordinatorDidComplete(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
func keyVerificationCoordinatorDidCancel(_ coordinator: KeyVerificationCoordinatorType) {
|
func keyVerificationCoordinatorDidCancel(_ coordinator: KeyVerificationCoordinatorType) {
|
||||||
|
|
|
@ -1743,8 +1743,18 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
|
||||||
return;
|
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");
|
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");
|
MXLogDebug(@"[MXKAccount] attemptDeviceDehydrationWithRetry: device successfully dehydrated");
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
|
|
|
@ -65,9 +65,12 @@
|
||||||
_event = event;
|
_event = event;
|
||||||
|
|
||||||
_displayFix = MXKRoomBubbleComponentDisplayFixNone;
|
_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;
|
_displayFix |= MXKRoomBubbleComponentDisplayFixHtmlBlockquote;
|
||||||
}
|
}
|
||||||
|
|
|
@ -440,7 +440,9 @@
|
||||||
|
|
||||||
- (void)startUserVerification
|
- (void)startUserVerification
|
||||||
{
|
{
|
||||||
[[AppDelegate theDelegate] presentUserVerificationForRoomMember:self.mxRoomMember session:self.mainSession];
|
[[AppDelegate theDelegate] presentUserVerificationForRoomMember:self.mxRoomMember session:self.mainSession completion:^{
|
||||||
|
[self refreshUserEncryptionTrustLevel];
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)presentUserVerification
|
- (void)presentUserVerification
|
||||||
|
@ -1332,6 +1334,7 @@
|
||||||
|
|
||||||
- (void)keyVerificationCoordinatorBridgePresenterDelegateDidComplete:(KeyVerificationCoordinatorBridgePresenter *)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId
|
- (void)keyVerificationCoordinatorBridgePresenterDelegateDidComplete:(KeyVerificationCoordinatorBridgePresenter *)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId
|
||||||
{
|
{
|
||||||
|
[self refreshUserEncryptionTrustLevel];
|
||||||
[self dismissKeyVerificationCoordinatorBridgePresenter];
|
[self dismissKeyVerificationCoordinatorBridgePresenter];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -806,7 +806,7 @@ static BOOL _disableLongPressGestureOnEvent;
|
||||||
mimetype = bubbleData.attachment.contentInfo[@"mimetype"];
|
mimetype = bubbleData.attachment.contentInfo[@"mimetype"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([mimetype isEqualToString:@"image/gif"])
|
if ([mimetype isKindOfClass:[NSString class]] && [mimetype isEqualToString:@"image/gif"])
|
||||||
{
|
{
|
||||||
if (_isAutoAnimatedGif)
|
if (_isAutoAnimatedGif)
|
||||||
{
|
{
|
||||||
|
|
|
@ -88,7 +88,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
||||||
let subView: UIView = hostingViewController.view
|
let subView: UIView = hostingViewController.view
|
||||||
self.addSubview(subView)
|
self.addSubview(subView)
|
||||||
|
|
||||||
hostingViewController.view.translatesAutoresizingMaskIntoConstraints = false
|
self.translatesAutoresizingMaskIntoConstraints = false
|
||||||
subView.translatesAutoresizingMaskIntoConstraints = false
|
subView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
heightConstraint = subView.heightAnchor.constraint(equalToConstant: height)
|
heightConstraint = subView.heightAnchor.constraint(equalToConstant: height)
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
|
@ -103,7 +103,13 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
||||||
.sink(receiveValue: { [weak self] idealHeight in
|
.sink(receiveValue: { [weak self] idealHeight in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.updateToolbarHeight(wysiwygHeight: idealHeight)
|
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)
|
update(theme: ThemeService.shared().theme)
|
||||||
|
|
|
@ -324,7 +324,7 @@ TableViewSectionsDelegate>
|
||||||
|
|
||||||
// Crypto sessions section
|
// Crypto sessions section
|
||||||
|
|
||||||
if (RiotSettings.shared.settingsSecurityScreenShowSessions)
|
if (RiotSettings.shared.settingsSecurityScreenShowSessions && !RiotSettings.shared.enableNewSessionManager)
|
||||||
{
|
{
|
||||||
Section *sessionsSection = [Section sectionWithTag:SECTION_CRYPTO_SESSIONS];
|
Section *sessionsSection = [Section sectionWithTag:SECTION_CRYPTO_SESSIONS];
|
||||||
|
|
||||||
|
@ -627,7 +627,7 @@ TableViewSectionsDelegate>
|
||||||
|
|
||||||
- (void)loadCrossSigning
|
- (void)loadCrossSigning
|
||||||
{
|
{
|
||||||
MXCrossSigning *crossSigning = self.mainSession.crypto.crossSigning;
|
id<MXCrossSigning> crossSigning = self.mainSession.crypto.crossSigning;
|
||||||
|
|
||||||
[crossSigning refreshStateWithSuccess:^(BOOL stateUpdated) {
|
[crossSigning refreshStateWithSuccess:^(BOOL stateUpdated) {
|
||||||
if (stateUpdated)
|
if (stateUpdated)
|
||||||
|
@ -643,7 +643,7 @@ TableViewSectionsDelegate>
|
||||||
{
|
{
|
||||||
NSInteger numberOfRowsInCrossSigningSection;
|
NSInteger numberOfRowsInCrossSigningSection;
|
||||||
|
|
||||||
MXCrossSigning *crossSigning = self.mainSession.crypto.crossSigning;
|
id<MXCrossSigning> crossSigning = self.mainSession.crypto.crossSigning;
|
||||||
switch (crossSigning.state)
|
switch (crossSigning.state)
|
||||||
{
|
{
|
||||||
case MXCrossSigningStateNotBootstrapped: // Action: Bootstrap
|
case MXCrossSigningStateNotBootstrapped: // Action: Bootstrap
|
||||||
|
@ -661,7 +661,7 @@ TableViewSectionsDelegate>
|
||||||
|
|
||||||
- (NSAttributedString*)crossSigningInformation
|
- (NSAttributedString*)crossSigningInformation
|
||||||
{
|
{
|
||||||
MXCrossSigning *crossSigning = self.mainSession.crypto.crossSigning;
|
id<MXCrossSigning> crossSigning = self.mainSession.crypto.crossSigning;
|
||||||
|
|
||||||
NSString *crossSigningInformation;
|
NSString *crossSigningInformation;
|
||||||
switch (crossSigning.state)
|
switch (crossSigning.state)
|
||||||
|
@ -708,7 +708,7 @@ TableViewSectionsDelegate>
|
||||||
buttonCell.mxkButton.accessibilityIdentifier = nil;
|
buttonCell.mxkButton.accessibilityIdentifier = nil;
|
||||||
|
|
||||||
// And customise it
|
// And customise it
|
||||||
MXCrossSigning *crossSigning = self.mainSession.crypto.crossSigning;
|
id<MXCrossSigning> crossSigning = self.mainSession.crypto.crossSigning;
|
||||||
switch (crossSigning.state)
|
switch (crossSigning.state)
|
||||||
{
|
{
|
||||||
case MXCrossSigningStateNotBootstrapped: // Action: Bootstrap
|
case MXCrossSigningStateNotBootstrapped: // Action: Bootstrap
|
||||||
|
|
|
@ -106,7 +106,11 @@ class QRLoginService: NSObject, QRLoginServiceProtocol {
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopScanning(destroy: Bool) {
|
func stopScanning(destroy: Bool) {
|
||||||
|
if (zxCapture.delegate != nil) {
|
||||||
|
// Setting the zxCapture to nil without checking makes it start
|
||||||
|
// scanning and implicitly requesting camera access
|
||||||
zxCapture.delegate = nil
|
zxCapture.delegate = nil
|
||||||
|
}
|
||||||
|
|
||||||
guard zxCapture.running else {
|
guard zxCapture.running else {
|
||||||
return
|
return
|
||||||
|
@ -292,7 +296,7 @@ class QRLoginService: NSObject, QRLoginServiceProtocol {
|
||||||
MXLog.debug("[QRLoginService] Received cross-signing details \(responsePayload)")
|
MXLog.debug("[QRLoginService] Received cross-signing details \(responsePayload)")
|
||||||
|
|
||||||
if let masterKeyFromVerifyingDevice = responsePayload.masterKey,
|
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 {
|
guard masterKeyFromVerifyingDevice == localMasterKey else {
|
||||||
MXLog.error("[QRLoginService] Received invalid master key from verifying device")
|
MXLog.error("[QRLoginService] Received invalid master key from verifying device")
|
||||||
await teardownRendezvous(state: .failed(error: .rendezvousFailed))
|
await teardownRendezvous(state: .failed(error: .rendezvousFailed))
|
||||||
|
@ -348,6 +352,7 @@ class QRLoginService: NSObject, QRLoginServiceProtocol {
|
||||||
await teardownRendezvous()
|
await teardownRendezvous()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
private func teardownRendezvous(state: QRLoginServiceState? = nil) async {
|
private func teardownRendezvous(state: QRLoginServiceState? = nil) async {
|
||||||
// Stop listening for changes, try deleting the resource
|
// Stop listening for changes, try deleting the resource
|
||||||
_ = await rendezvousService?.tearDown()
|
_ = await rendezvousService?.tearDown()
|
||||||
|
|
|
@ -36,6 +36,7 @@ struct ScreenList: View {
|
||||||
VStack {
|
VStack {
|
||||||
TextField("Search", text: $searchQuery)
|
TextField("Search", text: $searchQuery)
|
||||||
.textFieldStyle(.roundedBorder)
|
.textFieldStyle(.roundedBorder)
|
||||||
|
.autocorrectionDisabled()
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
.accessibilityIdentifier("searchQueryTextField")
|
.accessibilityIdentifier("searchQueryTextField")
|
||||||
.onChange(of: searchQuery, perform: search)
|
.onChange(of: searchQuery, perform: search)
|
||||||
|
|
|
@ -20,16 +20,33 @@ import XCTest
|
||||||
extension XCUIApplication {
|
extension XCUIApplication {
|
||||||
func goToScreenWithIdentifier(_ identifier: String) {
|
func goToScreenWithIdentifier(_ identifier: String) {
|
||||||
// Search for the screen identifier
|
// Search for the screen identifier
|
||||||
textFields["searchQueryTextField"].tap()
|
let textField = textFields["searchQueryTextField"]
|
||||||
typeText(identifier)
|
|
||||||
|
|
||||||
let button = buttons[identifier]
|
let button = buttons[identifier]
|
||||||
let footer = staticTexts["footerText"]
|
|
||||||
|
|
||||||
while !button.isHittable, !footer.isHittable {
|
// Sometimes the search gets stuck without showing any results. Try to nudge it along
|
||||||
tables.firstMatch.swipeUp()
|
for _ in 0...10 {
|
||||||
|
textField.clearAndTypeText(identifier)
|
||||||
|
if button.exists {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button.tap()
|
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")
|
wysiwygTextView.typeText("test")
|
||||||
XCTAssertTrue(sendButton.exists)
|
XCTAssertTrue(sendButton.exists)
|
||||||
XCTAssertFalse(app.buttons["editButton"].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 {
|
func testReplyMode() throws {
|
||||||
|
@ -56,6 +69,19 @@ final class ComposerUITests: MockScreenTestCase {
|
||||||
let textViewContent = wysiwygTextView.value as! String
|
let textViewContent = wysiwygTextView.value as! String
|
||||||
XCTAssertFalse(textViewContent.isEmpty)
|
XCTAssertFalse(textViewContent.isEmpty)
|
||||||
XCTAssertFalse(cancelButton.exists)
|
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 {
|
func testEditMode() throws {
|
||||||
|
@ -82,5 +108,18 @@ final class ComposerUITests: MockScreenTestCase {
|
||||||
let textViewContent = wysiwygTextView.value as! String
|
let textViewContent = wysiwygTextView.value as! String
|
||||||
XCTAssertTrue(textViewContent.isEmpty)
|
XCTAssertTrue(textViewContent.isEmpty)
|
||||||
XCTAssertFalse(cancelButton.exists)
|
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"
|
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 {
|
private var borderColor: Color {
|
||||||
focused ? theme.colors.quarterlyContent : theme.colors.quinaryContent
|
focused ? theme.colors.quarterlyContent : theme.colors.quinaryContent
|
||||||
}
|
}
|
||||||
|
@ -76,8 +84,6 @@ struct Composer: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 8) {
|
VStack(spacing: 8) {
|
||||||
let rect = RoundedRectangle(cornerRadius: cornerRadius)
|
let rect = RoundedRectangle(cornerRadius: cornerRadius)
|
||||||
// TODO: Fix maximise animation bugs before re-enabling
|
|
||||||
// ZStack(alignment: .topTrailing) {
|
|
||||||
VStack(spacing: 12) {
|
VStack(spacing: 12) {
|
||||||
if viewModel.viewState.shouldDisplayContext {
|
if viewModel.viewState.shouldDisplayContext {
|
||||||
HStack {
|
HStack {
|
||||||
|
@ -103,6 +109,7 @@ struct Composer: View {
|
||||||
.padding(.top, 8)
|
.padding(.top, 8)
|
||||||
.padding(.horizontal, horizontalPadding)
|
.padding(.horizontal, horizontalPadding)
|
||||||
}
|
}
|
||||||
|
HStack(alignment: .top, spacing: 0) {
|
||||||
WysiwygComposerView(
|
WysiwygComposerView(
|
||||||
focused: $focused,
|
focused: $focused,
|
||||||
content: wysiwygViewModel.content,
|
content: wysiwygViewModel.content,
|
||||||
|
@ -113,26 +120,28 @@ struct Composer: View {
|
||||||
.tintColor(theme.colors.accent)
|
.tintColor(theme.colors.accent)
|
||||||
.placeholder(viewModel.viewState.placeholder, color: theme.colors.tertiaryContent)
|
.placeholder(viewModel.viewState.placeholder, color: theme.colors.tertiaryContent)
|
||||||
.frame(height: wysiwygViewModel.idealHeight)
|
.frame(height: wysiwygViewModel.idealHeight)
|
||||||
.padding(.horizontal, horizontalPadding)
|
|
||||||
.onAppear {
|
.onAppear {
|
||||||
wysiwygViewModel.setup()
|
wysiwygViewModel.setup()
|
||||||
}
|
}
|
||||||
// Button {
|
Button {
|
||||||
// withAnimation(.easeInOut(duration: 0.25)) {
|
wysiwygViewModel.maximised.toggle()
|
||||||
// viewModel.maximised.toggle()
|
} label: {
|
||||||
// }
|
Image(toggleButtonImageName)
|
||||||
// } label: {
|
.resizable()
|
||||||
// Image(viewModel.maximised ? Asset.Images.minimiseComposer.name : Asset.Images.maximiseComposer.name)
|
.foregroundColor(theme.colors.tertiaryContent)
|
||||||
// .foregroundColor(theme.colors.tertiaryContent)
|
.frame(width: 16, height: 16)
|
||||||
// }
|
}
|
||||||
// .padding(.top, 4)
|
.accessibilityIdentifier(toggleButtonAcccessibilityIdentifier)
|
||||||
// .padding(.trailing, 12)
|
.padding(.leading, 12)
|
||||||
// }
|
.padding(.trailing, 4)
|
||||||
|
}
|
||||||
|
.padding(.horizontal, horizontalPadding)
|
||||||
.padding(.top, topPadding)
|
.padding(.top, topPadding)
|
||||||
.padding(.bottom, verticalPadding)
|
.padding(.bottom, verticalPadding)
|
||||||
}
|
}
|
||||||
.clipShape(rect)
|
.clipShape(rect)
|
||||||
.overlay(rect.stroke(borderColor, lineWidth: 1))
|
.overlay(rect.stroke(borderColor, lineWidth: 1))
|
||||||
|
.animation(.easeInOut(duration: 0.1), value: wysiwygViewModel.idealHeight)
|
||||||
.padding(.horizontal, horizontalPadding)
|
.padding(.horizontal, horizontalPadding)
|
||||||
.padding(.top, 8)
|
.padding(.top, 8)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
@ -148,7 +157,6 @@ struct Composer: View {
|
||||||
.resizable()
|
.resizable()
|
||||||
.foregroundColor(theme.colors.tertiaryContent)
|
.foregroundColor(theme.colors.tertiaryContent)
|
||||||
.frame(width: 14, height: 14)
|
.frame(width: 14, height: 14)
|
||||||
|
|
||||||
}
|
}
|
||||||
.frame(width: 36, height: 36)
|
.frame(width: 36, height: 36)
|
||||||
.background(Circle().fill(theme.colors.system))
|
.background(Circle().fill(theme.colors.system))
|
||||||
|
|
|
@ -24,36 +24,45 @@ class TimelinePollUITests: MockScreenTestCase {
|
||||||
XCTAssert(app.staticTexts["Question"].exists)
|
XCTAssert(app.staticTexts["Question"].exists)
|
||||||
XCTAssert(app.staticTexts["20 votes cast"].exists)
|
XCTAssert(app.staticTexts["20 votes cast"].exists)
|
||||||
|
|
||||||
XCTAssert(app.buttons["First, 10 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||||
XCTAssertEqual(app.buttons["First, 10 votes"].value as! String, "50%")
|
XCTAssertEqual(app.staticTexts["PollAnswerOption0Count"].label, "10 votes")
|
||||||
|
XCTAssertEqual(app.progressIndicators["PollAnswerOption0Progress"].value as? String, "50%")
|
||||||
|
|
||||||
XCTAssert(app.buttons["Second, 5 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||||
XCTAssertEqual(app.buttons["Second, 5 votes"].value as! String, "25%")
|
XCTAssertEqual(app.staticTexts["PollAnswerOption1Count"].label, "5 votes")
|
||||||
|
XCTAssertEqual(app.progressIndicators["PollAnswerOption1Progress"].value as? String, "25%")
|
||||||
|
|
||||||
XCTAssert(app.buttons["Third, 15 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||||
XCTAssertEqual(app.buttons["Third, 15 votes"].value as! String, "75%")
|
XCTAssertEqual(app.staticTexts["PollAnswerOption2Count"].label, "15 votes")
|
||||||
|
XCTAssertEqual(app.progressIndicators["PollAnswerOption2Progress"].value as? String, "75%")
|
||||||
|
|
||||||
app.buttons["First, 10 votes"].tap()
|
app.buttons["PollAnswerOption0"].tap()
|
||||||
|
|
||||||
XCTAssert(app.buttons["First, 11 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||||
XCTAssertEqual(app.buttons["First, 11 votes"].value as! String, "55%")
|
XCTAssertEqual(app.staticTexts["PollAnswerOption0Count"].label, "11 votes")
|
||||||
|
XCTAssertEqual(app.progressIndicators["PollAnswerOption0Progress"].value as? String, "55%")
|
||||||
|
|
||||||
XCTAssert(app.buttons["Second, 4 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||||
XCTAssertEqual(app.buttons["Second, 4 votes"].value as! String, "20%")
|
XCTAssertEqual(app.staticTexts["PollAnswerOption1Count"].label, "4 votes")
|
||||||
|
XCTAssertEqual(app.progressIndicators["PollAnswerOption1Progress"].value as? String, "20%")
|
||||||
|
|
||||||
XCTAssert(app.buttons["Third, 15 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||||
XCTAssertEqual(app.buttons["Third, 15 votes"].value as! String, "75%")
|
XCTAssertEqual(app.staticTexts["PollAnswerOption2Count"].label, "15 votes")
|
||||||
|
XCTAssertEqual(app.progressIndicators["PollAnswerOption2Progress"].value as? String, "75%")
|
||||||
|
|
||||||
app.buttons["Third, 15 votes"].tap()
|
app.buttons["PollAnswerOption2"].tap()
|
||||||
|
|
||||||
XCTAssert(app.buttons["First, 10 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||||
XCTAssertEqual(app.buttons["First, 10 votes"].value as! String, "50%")
|
XCTAssertEqual(app.staticTexts["PollAnswerOption0Count"].label, "10 votes")
|
||||||
|
XCTAssertEqual(app.progressIndicators["PollAnswerOption0Progress"].value as? String, "50%")
|
||||||
|
|
||||||
XCTAssert(app.buttons["Second, 4 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||||
XCTAssertEqual(app.buttons["Second, 4 votes"].value as! String, "20%")
|
XCTAssertEqual(app.staticTexts["PollAnswerOption1Count"].label, "4 votes")
|
||||||
|
XCTAssertEqual(app.progressIndicators["PollAnswerOption1Progress"].value as? String, "20%")
|
||||||
|
|
||||||
XCTAssert(app.buttons["Third, 16 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||||
XCTAssertEqual(app.buttons["Third, 16 votes"].value as! String, "80%")
|
XCTAssertEqual(app.staticTexts["PollAnswerOption2Count"].label, "16 votes")
|
||||||
|
XCTAssertEqual(app.progressIndicators["PollAnswerOption2Progress"].value as? String, "80%")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testOpenUndisclosedPoll() {
|
func testOpenUndisclosedPoll() {
|
||||||
|
@ -62,29 +71,29 @@ class TimelinePollUITests: MockScreenTestCase {
|
||||||
XCTAssert(app.staticTexts["Question"].exists)
|
XCTAssert(app.staticTexts["Question"].exists)
|
||||||
XCTAssert(app.staticTexts["20 votes cast"].exists)
|
XCTAssert(app.staticTexts["20 votes cast"].exists)
|
||||||
|
|
||||||
XCTAssert(!app.buttons["First, 10 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||||
XCTAssert(app.buttons["First"].exists)
|
XCTAssert(!app.staticTexts["PollAnswerOption0Count"].exists)
|
||||||
XCTAssertTrue((app.buttons["First"].value as! String).isEmpty)
|
XCTAssert(!app.progressIndicators["PollAnswerOption0Progress"].exists)
|
||||||
|
|
||||||
XCTAssert(!app.buttons["Second, 5 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||||
XCTAssert(app.buttons["Second"].exists)
|
XCTAssert(!app.staticTexts["PollAnswerOption1Count"].exists)
|
||||||
XCTAssertTrue((app.buttons["Second"].value as! String).isEmpty)
|
XCTAssert(!app.progressIndicators["PollAnswerOption1Progress"].exists)
|
||||||
|
|
||||||
XCTAssert(!app.buttons["Third, 15 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||||
XCTAssert(app.buttons["Third"].exists)
|
XCTAssert(!app.staticTexts["PollAnswerOption2Count"].exists)
|
||||||
XCTAssertTrue((app.buttons["Third"].value as! String).isEmpty)
|
XCTAssert(!app.progressIndicators["PollAnswerOption2Progress"].exists)
|
||||||
|
|
||||||
app.buttons["First"].tap()
|
app.buttons["PollAnswerOption0"].tap()
|
||||||
|
|
||||||
XCTAssert(app.buttons["First"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||||
XCTAssert(app.buttons["Second"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||||
XCTAssert(app.buttons["Third"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||||
|
|
||||||
app.buttons["Third"].tap()
|
app.buttons["PollAnswerOption2"].tap()
|
||||||
|
|
||||||
XCTAssert(app.buttons["First"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||||
XCTAssert(app.buttons["Second"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||||
XCTAssert(app.buttons["Third"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testClosedDisclosedPoll() {
|
func testClosedDisclosedPoll() {
|
||||||
|
@ -101,24 +110,30 @@ class TimelinePollUITests: MockScreenTestCase {
|
||||||
XCTAssert(app.staticTexts["Question"].exists)
|
XCTAssert(app.staticTexts["Question"].exists)
|
||||||
XCTAssert(app.staticTexts["Final results based on 20 votes"].exists)
|
XCTAssert(app.staticTexts["Final results based on 20 votes"].exists)
|
||||||
|
|
||||||
XCTAssert(app.buttons["First, 10 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||||
XCTAssertEqual(app.buttons["First, 10 votes"].value as! String, "50%")
|
XCTAssertEqual(app.staticTexts["PollAnswerOption0Count"].label, "10 votes")
|
||||||
|
XCTAssertEqual(app.progressIndicators["PollAnswerOption0Progress"].value as? String, "50%")
|
||||||
|
|
||||||
XCTAssert(app.buttons["Second, 5 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||||
XCTAssertEqual(app.buttons["Second, 5 votes"].value as! String, "25%")
|
XCTAssertEqual(app.staticTexts["PollAnswerOption1Count"].label, "5 votes")
|
||||||
|
XCTAssertEqual(app.progressIndicators["PollAnswerOption1Progress"].value as? String, "25%")
|
||||||
|
|
||||||
XCTAssert(app.buttons["Third, 15 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||||
XCTAssertEqual(app.buttons["Third, 15 votes"].value as! String, "75%")
|
XCTAssertEqual(app.staticTexts["PollAnswerOption2Count"].label, "15 votes")
|
||||||
|
XCTAssertEqual(app.progressIndicators["PollAnswerOption2Progress"].value as? String, "75%")
|
||||||
|
|
||||||
app.buttons["First, 10 votes"].tap()
|
app.buttons["PollAnswerOption0"].tap()
|
||||||
|
|
||||||
XCTAssert(app.buttons["First, 10 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption0Label"].label, "First")
|
||||||
XCTAssertEqual(app.buttons["First, 10 votes"].value as! String, "50%")
|
XCTAssertEqual(app.staticTexts["PollAnswerOption0Count"].label, "10 votes")
|
||||||
|
XCTAssertEqual(app.progressIndicators["PollAnswerOption0Progress"].value as? String, "50%")
|
||||||
|
|
||||||
XCTAssert(app.buttons["Second, 5 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption1Label"].label, "Second")
|
||||||
XCTAssertEqual(app.buttons["Second, 5 votes"].value as! String, "25%")
|
XCTAssertEqual(app.staticTexts["PollAnswerOption1Count"].label, "5 votes")
|
||||||
|
XCTAssertEqual(app.progressIndicators["PollAnswerOption1Progress"].value as? String, "25%")
|
||||||
|
|
||||||
XCTAssert(app.buttons["Third, 15 votes"].exists)
|
XCTAssertEqual(app.staticTexts["PollAnswerOption2Label"].label, "Third")
|
||||||
XCTAssertEqual(app.buttons["Third, 15 votes"].value as! String, "75%")
|
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))
|
.overlay(rect.stroke(borderAccentColor, lineWidth: 1.0))
|
||||||
.accentColor(progressViewAccentColor)
|
.accentColor(progressViewAccentColor)
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier("PollAnswerOption\(optionIndex)")
|
||||||
}
|
}
|
||||||
|
|
||||||
var answerOptionLabel: some View {
|
var answerOptionLabel: some View {
|
||||||
|
@ -53,6 +54,7 @@ struct TimelinePollAnswerOptionButton: View {
|
||||||
Text(answerOption.text)
|
Text(answerOption.text)
|
||||||
.font(theme.fonts.body)
|
.font(theme.fonts.body)
|
||||||
.foregroundColor(theme.colors.primaryContent)
|
.foregroundColor(theme.colors.primaryContent)
|
||||||
|
.accessibilityIdentifier("PollAnswerOption\(optionIndex)Label")
|
||||||
|
|
||||||
if poll.closed, answerOption.winner {
|
if poll.closed, answerOption.winner {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
@ -66,11 +68,13 @@ struct TimelinePollAnswerOptionButton: View {
|
||||||
total: Double(poll.totalAnswerCount))
|
total: Double(poll.totalAnswerCount))
|
||||||
.progressViewStyle(LinearProgressViewStyle())
|
.progressViewStyle(LinearProgressViewStyle())
|
||||||
.scaleEffect(x: 1.0, y: 1.2, anchor: .center)
|
.scaleEffect(x: 1.0, y: 1.2, anchor: .center)
|
||||||
|
.accessibilityIdentifier("PollAnswerOption\(optionIndex)Progress")
|
||||||
|
|
||||||
if poll.shouldDiscloseResults {
|
if poll.shouldDiscloseResults {
|
||||||
Text(answerOption.count == 1 ? VectorL10n.pollTimelineOneVote : VectorL10n.pollTimelineVotesCount(Int(answerOption.count)))
|
Text(answerOption.count == 1 ? VectorL10n.pollTimelineOneVote : VectorL10n.pollTimelineVotesCount(Int(answerOption.count)))
|
||||||
.font(theme.fonts.footnote)
|
.font(theme.fonts.footnote)
|
||||||
.foregroundColor(poll.closed && answerOption.winner ? theme.colors.accent : theme.colors.secondaryContent)
|
.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
|
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 {
|
struct TimelinePollAnswerOptionButton_Previews: PreviewProvider {
|
||||||
|
|
|
@ -21,10 +21,7 @@ class UserSuggestionUITests: MockScreenTestCase {
|
||||||
func testUserSuggestionScreen() throws {
|
func testUserSuggestionScreen() throws {
|
||||||
app.goToScreenWithIdentifier(MockUserSuggestionScreenState.multipleResults.title)
|
app.goToScreenWithIdentifier(MockUserSuggestionScreenState.multipleResults.title)
|
||||||
|
|
||||||
XCTAssert(app.tables.firstMatch.waitForExistence(timeout: 1))
|
let firstButton = app.buttons["displayNameText-userIdText"].firstMatch
|
||||||
|
XCTAssert(firstButton.waitForExistence(timeout: 10))
|
||||||
let firstButton = app.tables.firstMatch.buttons.firstMatch
|
|
||||||
_ = firstButton.waitForExistence(timeout: 10)
|
|
||||||
XCTAssert(firstButton.identifier == "displayNameText-userIdText")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import RiotSwiftUI
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class UserSessionDetailsUITests: MockScreenTestCase {
|
class UserSessionDetailsUITests: MockScreenTestCase {
|
||||||
func test_longPressDetailsCell_CopiesValueToClipboard() throws {
|
func disabled_broken_xcode14_test_longPressDetailsCell_CopiesValueToClipboard() throws {
|
||||||
app.goToScreenWithIdentifier(MockUserSessionDetailsScreenState.allSections.title)
|
app.goToScreenWithIdentifier(MockUserSessionDetailsScreenState.allSections.title)
|
||||||
|
|
||||||
UIPasteboard.general.string = ""
|
UIPasteboard.general.string = ""
|
||||||
|
|
|
@ -111,7 +111,7 @@ private class MockSession: MXSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A mock `MXCrypto` that can override the `canCrossSign` state.
|
/// A mock `MXCrypto` that can override the `canCrossSign` state.
|
||||||
private class MockCrypto: MXCrypto {
|
private class MockCrypto: MXLegacyCrypto {
|
||||||
let canCrossSign: Bool
|
let canCrossSign: Bool
|
||||||
override var crossSigning: MXCrossSigning! { MockCrossSigning(canCrossSign: canCrossSign) }
|
override var crossSigning: MXCrossSigning! { MockCrossSigning(canCrossSign: canCrossSign) }
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ private class MockCrypto: MXCrypto {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A mock `MXCrossSigning` with an overridden `canCrossSign` property.
|
/// A mock `MXCrossSigning` with an overridden `canCrossSign` property.
|
||||||
private class MockCrossSigning: MXCrossSigning {
|
private class MockCrossSigning: MXLegacyCrossSigning {
|
||||||
let canCrossSignMock: Bool
|
let canCrossSignMock: Bool
|
||||||
override var canCrossSign: Bool { canCrossSignMock }
|
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
|
before_all do
|
||||||
# Ensure used Xcode version
|
# Ensure used Xcode version
|
||||||
xcversion(version: "~> 13.4")
|
xcversion(version: "~> 14.0.1")
|
||||||
end
|
end
|
||||||
|
|
||||||
#### Public ####
|
#### Public ####
|
||||||
|
@ -196,7 +196,7 @@ platform :ios do
|
||||||
run_tests(
|
run_tests(
|
||||||
workspace: "Riot.xcworkspace",
|
workspace: "Riot.xcworkspace",
|
||||||
scheme: "RiotSwiftUITests",
|
scheme: "RiotSwiftUITests",
|
||||||
device: "iPhone 13",
|
device: "iPhone 14",
|
||||||
code_coverage: true,
|
code_coverage: true,
|
||||||
# Test result configuration
|
# Test result configuration
|
||||||
result_bundle: true,
|
result_bundle: true,
|
||||||
|
|
Loading…
Reference in a new issue