diff --git a/CHANGES.md b/CHANGES.md index d2e289625..25f3abef4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,39 @@ +## Changes in 1.11.1 (2023-08-29) + +✨ Features + +- New settings cell to manage your account through MAS if the home server allows it. ([#7653](https://github.com/vector-im/element-ios/issues/7653)) + +🙌 Improvements + +- Upgrade MatrixSDK version ([v0.27.1](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.27.1)). + +🐛 Bugfixes + +- Prevent mention crashes when room members are missing display names (objc interop) ([#7649](https://github.com/vector-im/element-ios/pull/7649)) +- Add email UI is hidden if the 3 pid changes capability is disabled. ([#7645](https://github.com/vector-im/element-ios/issues/7645)) +- You can now log out from other sessions using MAS on supported OIDC home servers. ([#7646](https://github.com/vector-im/element-ios/issues/7646)) +- Deactivate account is hidden for servers with OIDC auth. ([#7648](https://github.com/vector-im/element-ios/issues/7648)) +- Prevent pill crashes when room members are missing display names (objc interop) ([#7651](https://github.com/vector-im/element-ios/issues/7651)) + + +## Changes in 1.11.0 (2023-08-15) + +✨ Features + +- Integrate Device Dehydration v2 through the Crypto SDK ([#7630](https://github.com/vector-im/element-ios/pull/7630)) + +🙌 Improvements + +- Upgrade MatrixSDK version ([v0.27.0](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.27.0)). +- Disable Siri, Share and Reply (from NSE) Extensions. ([#7618](https://github.com/vector-im/element-ios/issues/7618)) + +🐛 Bugfixes + +- Fix bug in SSO URL generation that was non-compliant with the spec. ([#7639](https://github.com/vector-im/element-ios/pull/7639)) +- Fix a crash when opening v11 rooms. ([#7633](https://github.com/vector-im/element-ios/issues/7633)) + + ## Changes in 1.10.14 (2023-06-21) 🙌 Improvements diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index 44c008e0b..417da0c49 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -192,7 +192,7 @@ final class BuildSettings: NSObject { #else /// The configuration to use for analytics. Set `isEnabled` to false to disable analytics. static let analyticsConfiguration = AnalyticsConfiguration(isEnabled: BuildSettings.baseBundleIdentifier.starts(with: "im.vector.app"), - host: "https://posthog.hss.element.io", + host: "https://posthog.element.io", apiKey: "phc_Jzsm6DTm6V2705zeU5dcNvQDlonOR68XvX2sh1sEOHO", termsURL: URL(string: "https://element.io/cookie-policy")!) #endif diff --git a/Podfile b/Podfile index 4485d490e..67ce61163 100644 --- a/Podfile +++ b/Podfile @@ -16,7 +16,7 @@ use_frameworks! # - `{ :specHash => {sdk spec hash}` to depend on specific pod options (:git => …, :podspec => …) for MatrixSDK repo. Used by Fastfile during CI # # Warning: our internal tooling depends on the name of this variable name, so be sure not to change it -$matrixSDKVersion = '= 0.26.12' +$matrixSDKVersion = '= 0.27.1' # $matrixSDKVersion = :local # $matrixSDKVersion = { :branch => 'develop'} # $matrixSDKVersion = { :specHash => { git: 'https://git.io/fork123', branch: 'fix' } } @@ -178,11 +178,11 @@ abstract_target 'RiotPods' do target "RiotSwiftUI" do import_SwiftUI_pods - end + end target "RiotSwiftUITests" do import_SwiftUI_pods - end + end target "RiotNSE" do import_MatrixSDK @@ -193,6 +193,18 @@ abstract_target 'RiotPods' do import_MatrixSDK end + # Disabled due to crypto corruption issues. + # https://github.com/vector-im/element-ios/issues/7618 + # target "RiotShareExtension" do + # import_MatrixSDK + # import_MatrixKit_pods + # end + # + # target "SiriIntents" do + # import_MatrixSDK + # import_MatrixKit_pods + # end + end post_install do |installer| diff --git a/Podfile.lock b/Podfile.lock index bdbbb2d00..2a6591aa6 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -26,7 +26,7 @@ PODS: - GBDeviceInfo/Core (= 7.1.0) - GBDeviceInfo/Core (7.1.0) - GZIP (1.3.0) - - Introspect (0.1.4) + - Introspect (0.11.0) - JitsiMeetSDKLite (8.1.2-lite): - JitsiWebRTC (~> 111.0) - JitsiWebRTC (111.0.2) @@ -39,20 +39,20 @@ PODS: - LoggerAPI (1.9.200): - Logging (~> 1.1) - Logging (1.4.0) - - MatrixSDK (0.26.12): - - MatrixSDK/Core (= 0.26.12) - - MatrixSDK/Core (0.26.12): + - MatrixSDK (0.27.1): + - MatrixSDK/Core (= 0.27.1) + - MatrixSDK/Core (0.27.1): - AFNetworking (~> 4.0.0) - GZIP (~> 1.3.0) - libbase58 (~> 0.1.4) - - MatrixSDKCrypto (= 0.3.4) + - MatrixSDKCrypto (= 0.3.12) - OLMKit (~> 3.2.5) - Realm (= 10.27.0) - SwiftyBeaver (= 1.9.5) - - MatrixSDK/JingleCallStack (0.26.12): + - MatrixSDK/JingleCallStack (0.27.1): - JitsiMeetSDKLite (= 8.1.2-lite) - MatrixSDK/Core - - MatrixSDKCrypto (0.3.4) + - MatrixSDKCrypto (0.3.12) - OLMKit (3.2.12): - OLMKit/olmc (= 3.2.12) - OLMKit/olmcpp (= 3.2.12) @@ -102,8 +102,8 @@ DEPENDENCIES: - KeychainAccess (~> 4.2.2) - KTCenterFlowLayout (~> 1.3.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixSDK (= 0.26.12) - - MatrixSDK/JingleCallStack (= 0.26.12) + - MatrixSDK (= 0.27.1) + - MatrixSDK/JingleCallStack (= 0.27.1) - OLMKit - PostHog (~> 2.0.0) - ReadMoreTextView (~> 3.0.1) @@ -177,7 +177,7 @@ SPEC CHECKSUMS: FlowCommoniOS: ca92071ab526dc89905495a37844fd7e78d1a7f2 GBDeviceInfo: 5d62fa85bdcce3ed288d83c28789adf1173e4376 GZIP: 416858efbe66b41b206895ac6dfd5493200d95b3 - Introspect: b62c4dd2063072327c21d618ef2bedc3c87bc366 + Introspect: 4cc1e4c34dd016540c8d86a591c231c09dafbee3 JitsiMeetSDKLite: 895213158cf62342069a10634a41d2f1c00057f7 JitsiWebRTC: 80f62908fcf2a1160e0d14b584323fb6e6be630b KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51 @@ -187,8 +187,8 @@ SPEC CHECKSUMS: libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75 LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d Logging: beeb016c9c80cf77042d62e83495816847ef108b - MatrixSDK: 0af737bc461b82d0ec9edd6fdf8f70b02771ebd3 - MatrixSDKCrypto: ac805c22c24f79f349cdbfa065855c73a4c81b51 + MatrixSDK: f6c197ca06aab29ff69d1105965a57d277dfcd9d + MatrixSDKCrypto: 25929a40733b4ab54f659aaf6a730552a0a06504 OLMKit: da115f16582e47626616874e20f7bb92222c7a51 PostHog: 660ec6c9d80cec17b685e148f17f6785a88b597d ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d @@ -208,6 +208,6 @@ SPEC CHECKSUMS: zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: 0e7e10f516d40d9df60cb874170b91603c632118 +PODFILE CHECKSUM: ce6afe3dea7ea9b073a7ad0406b2cc5615646746 COCOAPODS: 1.11.3 diff --git a/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme b/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme index e1775adc4..28a1eae52 100644 --- a/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme +++ b/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme @@ -1,7 +1,7 @@ + LastUpgradeVersion = "1430" + version = "1.7"> diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index d6a80382a..448447190 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -406,8 +406,8 @@ "settings_ui_language" = "Sprache"; // Events formatter "event_formatter_member_updates" = "%tu Änderungen der Mitgliedschaft"; -"contacts_user_directory_section" = "NUTZER VERZEICHNIS"; -"contacts_user_directory_offline_section" = "NUTZER VERZEICHNIS (offline)"; +"contacts_user_directory_section" = "NUTZERVERZEICHNIS"; +"contacts_user_directory_offline_section" = "NUTZERVERZEICHNIS (offline)"; "auth_home_server_placeholder" = "URL (z.B. https://matrix.org)"; "auth_identity_server_placeholder" = "URL (z. B. https://vector.im)"; "room_ongoing_conference_call_close" = "Schließen"; @@ -2396,12 +2396,12 @@ "room_suggestion_settings_screen_nav_title" = "Raum vorschlagen"; "room_access_space_chooser_other_spaces_section_info" = "Diese sind vermutlich Dinge, zu denen andere Admins von %@ gehören."; "room_access_space_chooser_other_spaces_section" = "Andere Spaces oder Räume"; -"room_access_settings_screen_setting_room_access" = "Lege Raumzugriff fest"; -"room_access_settings_screen_upgrade_alert_upgrading" = "Raum upgraden"; +"room_access_settings_screen_setting_room_access" = "Raumzutritt festlegen"; +"room_access_settings_screen_upgrade_alert_upgrading" = "Raum aktualisieren"; "room_access_settings_screen_upgrade_alert_upgrade_button" = "Aktualisieren"; "room_access_settings_screen_upgrade_alert_auto_invite_switch" = "Mitglieder automatisch zu neuem Raum einladen"; -"room_access_settings_screen_upgrade_alert_message" = "Jeder in %@ kann diesen Raum finden und ihm beitreten - jeden manuell einzuladen ist nicht nötig. Du kannst diese Einstellung jederzeit ändern."; -"room_access_settings_screen_upgrade_alert_title" = "Raum upgraden"; +"room_access_settings_screen_upgrade_alert_message" = "Jeder in %@ kann diesen Raum finden und ihm beitreten – jeden manuell einzuladen ist nicht nötig. Du kannst diese Einstellung jederzeit ändern."; +"room_access_settings_screen_upgrade_alert_title" = "Raum aktualisieren"; "room_access_settings_screen_edit_spaces" = "Spaces bearbeiten"; "room_access_settings_screen_upgrade_required" = "Upgrade erforderlich"; "room_access_settings_screen_message" = "Lege fest, wer %@ finden und beitreten kann."; @@ -2461,13 +2461,13 @@ "threads_discourage_information_1" = "Dein Heimserver unterstützt aktuell keine Threads, weshalb diese Funktion unzuverlässig sein könnte. Manche Thread-Nachrichten könnten nicht zuverlässig verfügbar sein. "; "all_chats_nothing_found_placeholder_title" = "Nichts gefunden."; "spaces_create_subspace_title" = "Sub-Space erstellen"; -"room_access_settings_screen_upgrade_alert_note" = "Bitte beachte, dass das Upgrade eine neue Version dieses Raums erstellt. Alle aktuellen Nachrichten bleiben in diesem archivierten Raum."; +"room_access_settings_screen_upgrade_alert_note" = "Bitte beachte, dass die Aktualisierung eine neue Version dieses Raums erstellt. Alle aktuellen Nachrichten bleiben in diesem archivierten Raum."; "invite_to" = "Zu %@ einladen"; "all_chats_empty_unreads_placeholder_message" = "Hier werden deine ungelesenen Nachrichten erscheinen, wenn du welche hast."; "all_chats_edit_layout_show_recents" = "Historie anzeigen"; "all_chats_empty_list_placeholder_title" = "Du bist auf dem neuesten Stand."; "spaces_explore_rooms_format" = "%@ erkunden"; -"room_access_settings_screen_upgrade_alert_message_no_param" = "Jeder in einem übergeordneten Space kann diesen Raum finden und ihm beitreten - jeden manuell einzuladen ist nicht nötig. Du kannst diese Einstellung jederzeit ändern."; +"room_access_settings_screen_upgrade_alert_message_no_param" = "Jeder in einem übergeordneten Space kann diesen Raum finden und ihm beitreten – jeden manuell einzuladen ist nicht nötig. Du kannst diese Einstellung jederzeit ändern."; "room_access_settings_screen_public_message" = "Sichtbar und zugänglich für jeden."; "room_access_settings_screen_restricted_message" = "Sichtbar und betretbar für jeden Nutzer in einem Space.\nDu wählst, für welche Spaces dies gilt."; "room_access_settings_screen_private_message" = "Nur sichtbar und betretbar für eingeladene Personen."; @@ -2771,3 +2771,8 @@ "notice_display_name_changed_to" = "%@ hat den Anzeigenamen zu %@ geändert"; "poll_timeline_loading" = "Lade …"; "room_command_discard_session_description" = "Erzwingt das Verferfen der aktuell ausgehende Gruppensitzung in einem verschlüsseltem Raum"; +"room_creation_user_not_found_prompt_title" = "Bestätigung"; +"room_creation_user_not_found_prompt_message" = "Wir konnten kein Profil für diese Matrix-ID finden. Möchtest du dennoch eine Direktnachricht beginnen?"; +"room_creation_user_not_found_prompt_invite_action" = "Dennoch DM beginnen"; +"room_participants_invite_unknown_participant_prompt_to_msg" = "Wir konnten kein Profil für diese Matrix-ID finden. Möchtest du wirklich %@ nach %@ einladen?"; +"room_participants_invite_anyway" = "Dennoch einladen"; diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index e22420771..8c867408e 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -748,6 +748,9 @@ Tap the + to start adding people."; "settings_three_pids_management_information_part1" = "Manage which email addresses or phone numbers you can use to log in or recover your account here. Control who can find you in "; "settings_three_pids_management_information_part2" = "Discovery"; "settings_three_pids_management_information_part3" = "."; +"settings_manage_account_title" = "Account"; +"settings_manage_account_action" = "Manage account"; +"settings_manage_account_description" = "Manage your account at %@"; "settings_confirm_media_size" = "Confirm size when sending"; "settings_confirm_media_size_description" = "When this is on, you’ll be asked to confirm what size images and videos will be sent as."; @@ -964,6 +967,8 @@ Tap the + to start adding people."; "manage_session_trusted" = "Trusted by you"; "manage_session_not_trusted" = "Not trusted"; "manage_session_sign_out" = "Sign out of this session"; +"manage_session_redirect" = "You will be redirected to your server's authentication provider to complete sign out."; +"manage_session_redirect_error" = "Functionality currently unavailable. Please contact your homeserver admin"; "manage_session_rename" = "Rename session"; "manage_session_sign_out_other_sessions" = "Sign out of all other sessions"; // User sessions management diff --git a/Riot/Assets/eo.lproj/Vector.strings b/Riot/Assets/eo.lproj/Vector.strings index 2e91114ed..a09965843 100644 --- a/Riot/Assets/eo.lproj/Vector.strings +++ b/Riot/Assets/eo.lproj/Vector.strings @@ -1968,7 +1968,7 @@ "notice_encrypted_message" = "Ĉifrita mesaĝo"; "notice_room_related_groups" = "Grupoj rilataj al ĉi tiu ĉambro estas: %@"; "notice_room_aliases_for_dm" = "La kromnomoj estas: %@"; -"notice_room_aliases" = "Kromnomoj de la ĉamrbo estas: %@"; +"notice_room_aliases" = "Kromnomoj de la ĉambro estas: %@"; "notice_room_power_level_event_requirement" = "La minimumaj povniveloj rilataj al okazoj estas:"; "notice_room_power_level_acting_requirement" = "La minimuma povnivelo, kiun uzanto bezonas antaŭ agi, estas:"; "notice_room_power_level_intro_for_dm" = "La povniveloj de ĉambranoj estas:"; diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 21f95be98..0cad547d7 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -2709,3 +2709,8 @@ "room_command_change_display_name_description" = "Muudab sinu kuvatavat nime"; "notice_display_name_changed_to" = "%@ muutis oma kuvatavaks nimeks %@"; "poll_timeline_loading" = "Laadin..."; +"room_creation_user_not_found_prompt_title" = "Kinnitus"; +"room_creation_user_not_found_prompt_invite_action" = "Ikkagi alusta vestlust"; +"room_creation_user_not_found_prompt_message" = "Sellele Matrix'i kasutajatunnuse profiili ei leidu. Kas sa ikkagi tahaksid temaga vestlust alustada?"; +"room_participants_invite_unknown_participant_prompt_to_msg" = "Sellele Matrix'i kasutajatunnuse profiili ei leidu. Kas sa ikkagi tahaksid saata kutset kasutajale %@ jututuppa %@?"; +"room_participants_invite_anyway" = "Kutsu siiski"; diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index 81f489410..d3c3cf5c5 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -274,7 +274,7 @@ "room_title_members" = "%@ membri"; "room_title_one_member" = "1 membro"; // Room Preview -"room_preview_invitation_format" = "Sei stato invitato ad entrare in questa stanza da %@"; +"room_preview_invitation_format" = "Sei stato/a invitato/a ad entrare in questa stanza da %@"; "room_preview_subtitle" = "Questa è l'anteprima della stanza. Le interazioni sono disabilitate."; "room_preview_unlinked_email_warning" = "Questo invito è stato spedito a %@, che non è associato a questo account. Puoi aggiungere questa email al tuo account o provare ad accedere con un account differente."; "room_preview_try_join_an_unknown_room" = "Stai provando ad accedere a %@. Desideri entrare per partecipare alla discussione?"; @@ -457,7 +457,7 @@ "group_home_multi_members_format" = "%tu membri"; "group_home_one_room_format" = "1 stanza"; "group_home_multi_rooms_format" = "%tu stanze"; -"group_invitation_format" = "%@ ti ha invitato ad unirti a questa comunità"; +"group_invitation_format" = "%@ ti ha invitato/a ad unirti a questa comunità"; // Group participants "group_participants_add_participant" = "Aggiungi membri"; "group_participants_leave_prompt_title" = "Lascia la stanza"; @@ -649,7 +649,7 @@ "sign_out_key_backup_in_progress_alert_cancel_action" = "Attendo"; "close" = "Chiudi"; "auth_forgot_password_error_no_configured_identity_server" = "Non è stato configurato alcun server d'identità: aggiungine uno per ripristinare la password dell'account Matrix."; -"auth_softlogout_signed_out" = "Sei uscito"; +"auth_softlogout_signed_out" = "Sei disconnesso/a"; "auth_softlogout_sign_in" = "Accedi"; "auth_softlogout_reason" = "L'amministratore dell'Home Server (%1$@) ti ha disconnesso dal tuo account %2$@ (%3$@)."; "auth_softlogout_recover_encryption_keys" = "Accedi per recuperare le chiavi crittografiche archiviate su questo dispositivo. Le chiavi ti servono per poter leggere i tuoi messaggi cifrati su altri dispositivi."; @@ -1164,7 +1164,7 @@ "biometrics_cant_unlocked_alert_message_login" = "Riaccedi"; "biometrics_cant_unlocked_alert_message_retry" = "Riprova"; "device_verification_self_verify_wait_recover_secrets_checking_availability" = "Controllo di altre possibilità di verifica..."; -"joined" = "Entrato"; +"joined" = "Entrato/a"; "switch" = "Cambia"; "more" = "Altro"; "pin_protection_choose_pin_welcome_after_login" = "Bentornato/a."; @@ -1832,7 +1832,7 @@ "room_error_cannot_load_timeline" = "Caricamento storico dei messaggi fallito"; "room_error_timeline_event_not_found_title" = "Caricamento della posizione nello storico fallito"; "room_error_timeline_event_not_found" = "L'applicazione ha cercato di caricare un punto specifico dello storico dei messaggi in questo canale, ma non è riuscita a trovarlo"; -"room_left" = "Sei uscito dalla stanza"; +"room_left" = "Sei uscito/a dalla stanza"; "room_no_power_to_create_conference_call" = "Hai bisogno del permesso per invitare a iniziare una conferenza in questo canale"; "room_no_conference_call_in_encrypted_rooms" = "Le chiamate in conferenza non sono supportate nei canali criptati"; // Reply to message @@ -1906,7 +1906,7 @@ // Language picker "language_picker_title" = "Scegli una lingua"; "language_picker_default_language" = "Predefinito (%@)"; -"notice_room_invite" = "%@ invitato %@"; +"notice_room_invite" = "%@ ha invitato %@"; "notice_room_third_party_invite" = "%@ ha invitato %@ a unirsi al canale"; "notice_room_third_party_registered_invite" = "%@ ha accettato l'invito per %@"; "notice_room_third_party_revoked_invite" = "%@ ha ritirato l'invito per %@ a unirsi al canale"; @@ -1944,7 +1944,7 @@ "login" = "Entra"; "create_account" = "Crea utente"; "membership_invite" = "Invitati"; -"membership_leave" = "Uscito"; +"membership_leave" = "Uscito/a"; "membership_ban" = "Espulso"; "num_members_one" = "%@ utente"; "num_members_other" = "%@ utenti"; @@ -1979,7 +1979,7 @@ "notification_settings_contain_my_user_name" = "Notifica con un suono i messaggi che contengono il mio nome utente"; "notification_settings_contain_my_display_name" = "Notifica con un suono i messaggi che contengono il mio nome completo"; "notification_settings_just_sent_to_me" = "Notifica con un suono i messaggi inviati solo a me"; -"notification_settings_invite_to_a_new_room" = "Notifica quando sono invitato in un nuovo canale"; +"notification_settings_invite_to_a_new_room" = "Avvisami quando sono invitato/a in una nuova stanza"; "notification_settings_people_join_leave_rooms" = "Notifica quando gli utenti entrano o escono dai canali"; "notification_settings_receive_a_call" = "Notifica quando ricevo una chiamata"; "notification_settings_suppress_from_bots" = "Sopprimi le notifiche dai bot"; @@ -2009,12 +2009,12 @@ "notice_encryption_enabled_unknown_algorithm" = "%1$@ ha attivato la crittografia end-to-end (algoritmo %2$@ non riconosciuto)."; // Notice Events with "You" "notice_room_invite_by_you" = "Hai invitato %@"; -"notice_room_invite_you" = "%@ ti ha invitato"; +"notice_room_invite_you" = "%@ ti ha invitato/a"; "notice_room_third_party_invite_by_you" = "Hai mandato un invito a %@ a unirsi alla stanza"; "notice_room_third_party_registered_invite_by_you" = "Hai accettato l'invito per %@"; "notice_room_third_party_revoked_invite_by_you" = "Hai revocato l'invito per %@ a unirsi alla stanza"; -"notice_room_join_by_you" = "Sei entrato"; -"notice_room_leave_by_you" = "Sei uscito"; +"notice_room_join_by_you" = "Sei entrato/a"; +"notice_room_leave_by_you" = "Sei uscito/a"; "notice_room_reject_by_you" = "Hai rifiutato l'invito"; "notice_room_kick_by_you" = "Hai rimosso %@"; "notice_room_unban_by_you" = "Hai riammesso %@"; @@ -2049,7 +2049,7 @@ "notice_room_join_rule_public" = "%@ ha reso la stanza pubblica."; "notice_room_join_rule_public_by_you" = "Hai reso la stanza pubblica."; "notice_room_name_removed_for_dm" = "%@ ha rimosso il nome"; -"notice_room_created_for_dm" = "%@ è entrato."; +"notice_room_created_for_dm" = "%@ è entrato/a."; "notice_room_join_rule_invite_for_dm" = "%@ l'ha resa solo su invito."; "notice_room_join_rule_invite_by_you_for_dm" = "L'hai resa solo su invito."; "notice_room_join_rule_public_for_dm" = "%@ l'ha resa pubblica."; @@ -2059,7 +2059,7 @@ "notice_room_history_visible_to_members_for_dm" = "%@ ha reso visibili i messaggi futuri a tutti i membri della stanza."; "notice_room_history_visible_to_members_from_invited_point_for_dm" = "%@ ha reso visibili i messaggi futuri a chiunque, dal momento dell'invito."; "notice_room_history_visible_to_members_from_joined_point_for_dm" = "%@ ha reso visibili i messaggi futuri a chiunque, dal momento dell'entrata."; -"room_left_for_dm" = "Sei uscito"; +"room_left_for_dm" = "Sei uscito/a"; "notice_room_third_party_invite_for_dm" = "%@ ha invitato %@"; "notice_room_third_party_revoked_invite_for_dm" = "%@ ha revocato l'invito per %@"; "notice_room_name_changed_for_dm" = "%@ ha cambiato il nome in %@."; @@ -2067,7 +2067,7 @@ "notice_room_third_party_revoked_invite_by_you_for_dm" = "Hai revocato l'invito per %@"; "notice_room_name_changed_by_you_for_dm" = "Hai cambiato il nome in %@."; "notice_room_name_removed_by_you_for_dm" = "Hai rimosso il nome"; -"notice_room_created_by_you_for_dm" = "Sei entrato."; +"notice_room_created_by_you_for_dm" = "Sei entrato/a."; "notice_room_history_visible_to_members_by_you_for_dm" = "Hai reso visibili i messaggi futuri a tutti i membri della stanza."; "notice_room_history_visible_to_members_from_invited_point_by_you_for_dm" = "Hai reso visibili i messaggi futuri a chiunque, dal momento dell'invito."; "notice_room_history_visible_to_members_from_joined_point_by_you_for_dm" = "Hai reso visibili i messaggi futuri a chiunque, dal momento dell'entrata."; @@ -2096,7 +2096,7 @@ "attachment_small_with_resolution" = "Piccolo %@ (~%@)"; "attachment_size_prompt_message" = "Puoi disattivarlo nelle impostazioni."; "attachment_size_prompt_title" = "Conferma dimensione da inviare"; -"room_displayname_all_other_members_left" = "%@ (Uscito)"; +"room_displayname_all_other_members_left" = "%@ (Uscito/a)"; "attachment_unsupported_preview_message" = "Questo tipo di file non è supportato."; "attachment_unsupported_preview_title" = "Anteprima non disponibile"; "message_reply_to_sender_sent_their_location" = "ha condiviso la sua posizione."; @@ -2273,7 +2273,7 @@ "location_sharing_live_list_item_last_update_invalid" = "Ultimo aggiornamento sconosciuto"; "location_sharing_live_list_item_last_update" = "Aggiornato %@ fa"; "location_sharing_live_list_item_sharing_expired" = "Condivisione scaduta"; -"location_sharing_live_list_item_time_left" = "%@ è uscito"; +"location_sharing_live_list_item_time_left" = "%@ è uscito/a"; "location_sharing_live_viewer_title" = "Posizione"; "location_sharing_live_map_callout_title" = "Condividi posizione"; "bug_report_logs_description" = "Per diagnosticare i problemi, i registri di questo client verranno inviati con questo rapporto di errore. Se preferisci inviare solo il testo soprastante, deseleziona:"; @@ -2737,3 +2737,8 @@ "room_command_change_display_name_description" = "Cambia il tuo nome visualizzato"; "notice_display_name_changed_to" = "%@ ha cambiato il suo nome visualizzato in %@"; "poll_timeline_loading" = "Caricamento..."; +"room_creation_user_not_found_prompt_title" = "Conferma"; +"room_creation_user_not_found_prompt_invite_action" = "Inizia il messaggio lo stesso"; +"room_participants_invite_anyway" = "Invita lo stesso"; +"room_participants_invite_unknown_participant_prompt_to_msg" = "Impossibile trovare profili con questo ID Matrix. Vuoi davvero invitare %@ in %@?"; +"room_creation_user_not_found_prompt_message" = "Impossibile trovare profili con questo ID Matrix. Vuoi comunque iniziare un messaggio diretto?"; diff --git a/Riot/Assets/ru.lproj/Vector.strings b/Riot/Assets/ru.lproj/Vector.strings index 2f016c5fd..3e96f0ad3 100644 --- a/Riot/Assets/ru.lproj/Vector.strings +++ b/Riot/Assets/ru.lproj/Vector.strings @@ -2265,3 +2265,91 @@ // Room Access Settings "room_access_settings_screen_nav_title" = "Доступ к комнате"; "spaces_coming_soon_detail" = "Эта функция еще не реализована здесь, но она в разработке. На данный момент вы можете сделать это с помощью %@ на своем компьютере."; + +// Unverified sessions +"key_verification_alert_title" = "У вас есть не подтвержденные сессии"; +"key_verification_scan_qr_code_title" = "Сканируйте QR-код"; +"home_context_menu_favourite" = "Любимые"; +"spaces_add_space_title" = "Создать пространство"; +"room_details_promote_room_title" = "Продвигать комнату"; +"room_suggestion_settings_screen_title" = "Сделать комнату предлагаемой в пространстве"; +"analytics_prompt_terms_link_new_user" = "здесь"; +"create_room_suggest_room" = "Предложения для пользователей пространства"; +"room_info_back_button_title" = "Информация о комнате"; +"home_context_menu_make_dm" = "Перейти к людям"; +"create_room_processing" = "Создание комнаты"; +"home_context_menu_unmute" = "Снять заглушку"; + +// MARK: - Room invite + +"room_invite_to_space_option_title" = "К %@"; +"analytics_prompt_yes" = "Да, все классно"; +"create_room_section_footer_type_private" = "Только приглашенные люди могут искать и вступать."; +"home_context_menu_make_room" = "Перейти к комнатам"; +"create_room_suggest_room_footer" = "Предлагаемые комнаты выдвинуты для участников как лучший выбор для вступления."; +"home_context_menu_notifications" = "Уведомления"; +"analytics_prompt_terms_link_upgrade" = "здесь"; +"device_verification_self_verify_open_on_other_device_title" = "Откройте %@ на другом вашем устройстве"; +"create_room_section_footer_type_public" = "Только приглашенные люди могут искать и вступать, не только люди из пространства."; +"home_context_menu_unfavourite" = "Удалить из любимых"; +"home_context_menu_normal_priority" = "Нормальный приоритет"; +"home_context_menu_mute" = "Заглушить"; +"room_access_settings_screen_upgrade_alert_upgrading" = "Улучшение комнаты"; +"room_access_settings_screen_upgrade_alert_upgrade_button" = "Улучшить"; +"key_backup_recover_from_private_key_progress" = "%@%% закончено"; +"room_access_settings_screen_upgrade_alert_title" = "Улучшить комнату"; +"share_extension_low_quality_video_message" = "Отправьте в %@ для лучшего качества или отправьте в пониженном качестве."; +"settings_acceptable_use" = "Принимаемые политики пользования"; +"room_suggestion_settings_screen_message" = "Предлагаемые комнаты рекламируются среди участников пространства как подходящие для присоединения."; +/* Note: The placeholder is for the contents of analytics_prompt_terms_link_new_user */ +"analytics_prompt_terms_new_user" = "Вы можете прочитать все наши условия %@."; +"key_verification_alert_body" = "Перепроверьте надежность сохранности аккаунта."; + +// MARK: - Launch loading + +"launch_loading_generic" = "Синхронизация ваших разговоров"; +"home_context_menu_mark_as_unread" = "Пометить как непрочитанное"; + +// MARK: Sign out warning + +"sign_out" = "Выйти"; +"room_command_unban_user_description" = "Разблокирует пользователя с указанным ID"; +"device_verification_self_verify_open_on_other_device_information" = "Вам нужно подтвердить эту сессию для того, чтобы прочитать защищенную историю сообщений\n\nОткройте Element на одном из ваших прочих устройств и действуйте по инструкции."; +"room_command_change_room_topic_description" = "Указывает тему комнаты"; +"room_access_settings_screen_upgrade_required" = "Требуется улучшение"; +"key_verification_scan_qr_code_information_other_user" = "Наведите камеру на QR-код, отображенном на вашем устройстве для подтверждения ваших сессий"; + +// Room suggestion Settings +"room_suggestion_settings_screen_nav_title" = "Предложить комнату"; +/* Note: The placeholder is for the contents of analytics_prompt_terms_link_upgrade */ +"analytics_prompt_terms_upgrade" = "Прочитать все наши условия %@. Они подходят?"; +"key_verification_scan_qr_code_information_other_session" = "Наведите камеру на QR-код, отображенном на другом вашем устройстве для подтверждения вашей сессии"; +"key_verification_scan_qr_code_information_other_device" = "Наведите камеру на QR-код, отображенном на другом вашем устройстве для подтверждения этой сессии"; +"create_room_type_restricted" = "Участники пространства"; +"home_context_menu_low_priority" = "Низкий приоритет"; +"share_invite_link_space_text" = "Хэй, вступай в это пространство на %@"; +"create_room_promotion_header" = "ПРОДВИЖЕНИЕ"; +"create_room_show_in_directory_footer" = "Это может помочь людям искать и вступать."; +"space_invite_not_enough_permission" = "У вас нет разрешения для приглашения людей в это пространство"; + +// Room commands descriptions +"room_command_change_display_name_description" = "Изменяет отображаемое имя пользователя"; +"room_command_emote_description" = "Отображает действие"; +"room_command_join_room_description" = "Присоединяться к комнате с указанным адресом"; +"room_command_part_room_description" = "Покинуть комнату"; +"room_command_invite_user_description" = "Добавляет пользователя с указанным ID в текущую комнату"; +"room_command_kick_user_description" = "Удаляет пользователя с указанным ID из этой комнаты"; +"room_command_ban_user_description" = "Блокирует пользователя с указанным ID"; +"room_command_set_user_power_level_description" = "Назначает уровень силы пользователя"; +"room_command_reset_user_power_level_description" = "Разжалует пользователя с указанным ID"; +"room_command_discard_session_description" = "Принудительно отбрасывает текущий исходящий групповой сеанс в зашифрованной комнате"; +"room_command_error_unknown_command" = "Некорректная или необработанная команда"; + +// Legacy to Rust security upgrade + +"key_verification_self_verify_security_upgrade_alert_title" = "Приложение обновлено"; +"key_verification_self_verify_security_upgrade_alert_message" = "Безопасное общение может быть улучшено с новым обновлением. Пожалуйста, проверьте ваше устройство."; +"device_verification_self_verify_wait_recover_secrets_additional_help" = "Нет доступа к существующей %@ сессии?"; +"key_verification_scan_qr_code_information_new_session" = "Наведите камеру на QR-код, отображенном на другом вашем устройстве для подтверждения вашей новой сессии"; +"create_room_section_footer_type_restricted" = "Все в пространстве могут искать и вступать."; +"launch_loading_delay_warning" = "Это может занимать больше времени.\nСпасибо за терпение."; diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index 91e52cee0..061991f16 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -2960,3 +2960,8 @@ "room_command_change_display_name_description" = "Mení vaše zobrazované meno / prezývku"; "notice_display_name_changed_to" = "%@ zmenil/a svoje zobrazované meno na %@"; "poll_timeline_loading" = "Načítavanie…"; +"room_creation_user_not_found_prompt_title" = "Potvrdenie"; +"room_creation_user_not_found_prompt_invite_action" = "Spustiť konverzáciu aj tak"; +"room_participants_invite_anyway" = "Napriek tomu pozvať"; +"room_creation_user_not_found_prompt_message" = "Nie je možné nájsť používateľské profily pre toto Matrix ID. Chcete aj tak poslať priamu správu?"; +"room_participants_invite_unknown_participant_prompt_to_msg" = "Nie je možné nájsť profily pre toto Matrix ID. Ste si istí, že chcete pozvať %@ do %@?"; diff --git a/Riot/Assets/sv.lproj/Vector.strings b/Riot/Assets/sv.lproj/Vector.strings index bbb4b12c5..48d13aea8 100644 --- a/Riot/Assets/sv.lproj/Vector.strings +++ b/Riot/Assets/sv.lproj/Vector.strings @@ -2676,3 +2676,31 @@ "room_command_change_room_topic_description" = "Sätter rummets ämne"; "room_command_discard_session_description" = "Tvingar den aktuella utgående gruppsessionen i ett krypterat rum att kasseras"; "room_command_error_unknown_command" = "Ogiltigt eller obehandlat kommando"; + +// Legacy to Rust security upgrade + +"key_verification_self_verify_security_upgrade_alert_title" = "App uppdaterad"; +"key_verification_self_verify_security_upgrade_alert_message" = "Säker meddelandehantering har förbättrats med den senaste uppdateringen. Vänligen verifiera din enhet igen."; +"device_verification_self_verify_open_on_other_device_title" = "Öppna %@ på din andra enhet"; +"device_verification_self_verify_wait_recover_secrets_additional_help" = "Kan du inte komma åt en befintlig %@-session?"; +"key_verification_scan_qr_code_title" = "Skanna QR-kod"; +"device_verification_self_verify_open_on_other_device_information" = "Du behöver verifiera den här sessionen för att kunna läsa din säkra meddelandehistorik.\n\nÖppna Element på en av dina andra enheter och följ instruktionerna."; +"key_verification_scan_qr_code_information_other_user" = "Rikta kameran mot QR-koden som visas på deras enhet för att verifiera deras session"; +"room_waiting_other_participants_title" = "Väntar på att användare ska gå med i %@"; +"pill_message_from" = "Meddelande från %@"; +"pill_message_in" = "Meddelande i %@"; +"notice_display_name_changed_to" = "%@ bytte sitt visningsnamn till %@"; + +// MARK: - Launch loading + +"launch_loading_generic" = "Synkar dina konversationer"; +"launch_loading_delay_warning" = "Detta kan ta lite längre tid.\nTack för ditt tålamod."; +"key_verification_scan_qr_code_information_other_device" = "Rikta kameran mot QR-koden som visas på din andra enhet för att verifiera den här sessionen"; +"room_waiting_other_participants_message" = "När inbjudna användare har gått med i %@ kommer du att kunna chatta och rummet kommer att totalsträckskrypteras"; +"poll_timeline_loading" = "Laddar …"; + +// Pills +"pill_room_fallback_display_name" = "Utrymme/rum"; +"pill_message" = "Meddelande"; +"key_verification_scan_qr_code_information_new_session" = "Rikta kameran mot QR-koden som visas på din andra enhet för att verifiera din nya session"; +"key_verification_scan_qr_code_information_other_session" = "Rikta kameran mot QR-koden som visas på din andra enhet för att verifiera din session"; diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index bac3f4c30..d5113e3c7 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -184,7 +184,7 @@ "settings_remove_prompt_title" = "Підтвердження"; "settings_surname" = "Прізвище"; "settings_first_name" = "Ім’я"; -"settings_display_name" = "Показуване ім’я"; +"settings_display_name" = "Псевдонім"; "settings_profile_picture" = "Зображення профілю"; "settings_sign_out_e2e_warn" = "Ви втратите всі ваші ключі наскрізного шифрування. Це означає що ви більше не будете мати змогу читати старі повідомлення у зашифрованих кімнатах на цьому пристрої."; "settings_sign_out_confirmation" = "Ви впевнені?"; @@ -433,7 +433,7 @@ "room_widget_permission_theme_permission" = "Ваша тема"; "room_widget_permission_user_id_permission" = "Ваш ID користувача"; "room_widget_permission_avatar_url_permission" = "URL-адреса вашого аватара"; -"room_widget_permission_display_name_permission" = "Ваше показуване імʼя"; +"room_widget_permission_display_name_permission" = "Ваш псевдонім"; "room_widget_permission_creator_info_title" = "Цей віджет додано:"; // Room widget permissions @@ -1129,7 +1129,7 @@ "settings_show_url_previews" = "Попередній перегляд вебсайтів"; "settings_ui_theme_picker_message_match_system_theme" = "«Авто» застосовує тему вашого пристрою"; "settings_ui_theme_picker_message_invert_colours" = "«Авто» застосовує налаштування вашого пристрою «Інвертувати кольори»"; -"settings_messages_containing_display_name" = "Моє показуване імʼя"; +"settings_messages_containing_display_name" = "Мій псевдонім"; "settings_discovery_three_pid_details_title_phone_number" = "Керувати номером телефону"; "settings_discovery_three_pid_details_title_email" = "Керувати е-поштою"; "settings_discovery_error_message" = "Сталася помилка. Повторіть спробу."; @@ -1866,7 +1866,7 @@ "login_prompt_email_token" = "Введіть ключ підтвердження електронної пошти:"; "login_email_placeholder" = "Адреса е-пошти"; "login_email_info" = "Вкажіть адресу електронної пошти, щоб інші користувачі могли легше знаходити вас на Matrix і надання вам можливості відновити пароль у майбутньому."; -"login_display_name_placeholder" = "Видиме ім'я (наприклад Bob Obson)"; +"login_display_name_placeholder" = "Псевдонім (наприклад Bob Obson)"; "login_optional_field" = "необов'язково"; "login_password_placeholder" = "Пароль"; "login_user_id_placeholder" = "Ідентифікатор Matrix (наприклад @bob:matrix.org або bob)"; @@ -2113,10 +2113,10 @@ "notice_room_history_visible_to_members_from_joined_point_for_dm" = "%@ робить майбутню історію повідомлень видимою всім від часу їхнього приєднання."; "notice_room_history_visible_to_members_from_joined_point" = "%@ робить майбутню історію кімнати видимою всім учасникам кімнати від часу їхнього приєднання."; "notice_room_history_visible_to_members_from_invited_point_for_dm" = "%@ робить майбутню історію повідомлень видимою всім від часу їхнього запрошення."; -"notice_room_history_visible_to_members_from_invited_point" = "%@ робить майбутню історію кімнати видимою усім учасникам кімнати від часу їхнього запрошення."; -"notice_room_history_visible_to_members_for_dm" = "%@ робить майбутню історію повідомлень видимою усім учасникам кімнати."; -"notice_room_history_visible_to_members" = "%@ робить майбутню історію кімнати видимою усім учасникам кімнати."; -"notice_room_history_visible_to_anyone" = "%@ робить майбутню історію кімнати видимою усім."; +"notice_room_history_visible_to_members_from_invited_point" = "%@ робить майбутню історію кімнати видимою всім учасникам кімнати від часу їхнього запрошення."; +"notice_room_history_visible_to_members_for_dm" = "%@ робить майбутню історію повідомлень видимою всім учасникам кімнати."; +"notice_room_history_visible_to_members" = "%@ робить майбутню історію кімнати видимою всім учасникам кімнати."; +"notice_room_history_visible_to_anyone" = "%@ робить майбутню історію кімнати видимою всім."; "notice_redaction" = "%@ редагує подію (id: %@)"; "notice_feedback" = "Подія відгуку (id: %@): %@"; "notice_room_related_groups" = "Групи пов'язані з цією кімнатою: %@"; @@ -2136,9 +2136,9 @@ "notice_room_name_changed_for_dm" = "%@ змінює назву на %@."; "notice_room_name_changed" = "%@ змінює назву кімнати на %@."; "notice_topic_changed" = "%@ змінює тему на «%@»."; -"notice_display_name_removed" = "%@ вилучає своє показуване ім'я"; -"notice_display_name_changed_from" = "%@ змінює своє показуване ім'я з %@ на %@"; -"notice_display_name_set" = "%@ встановлює своїм показуваним іменем %@"; +"notice_display_name_removed" = "%@ вилучає свій псевдонім"; +"notice_display_name_changed_from" = "%@ змінює свій псевдонім з %@ на %@"; +"notice_display_name_set" = "%@ встановлює своїм псевдонімом %@"; "notice_room_withdraw" = "%@ анульовує запрошення для %@"; "notice_room_kick" = "%@ вилучає %@"; "notice_room_reject" = "%@ відхиляє запрошення"; @@ -2194,7 +2194,7 @@ "notification_settings_per_room_notifications" = "Сповіщення від кожної кімнати"; "notification_settings_per_sender_notifications" = "Сповіщення про кожного відправника"; "notification_settings_contain_my_user_name" = "Сповіщати звуком про повідомлення, що містять моє ім'я користиувача"; -"notification_settings_contain_my_display_name" = "Сповіщати звуком про повідомлення, що містять моє показуване ім'я"; +"notification_settings_contain_my_display_name" = "Сповіщати звуком про повідомлення з моїм псевдонімом"; "notification_settings_just_sent_to_me" = "Сповіщати звуком про надіслані лише мені повідомлення"; "notification_settings_invite_to_a_new_room" = "Сповіщати про запрошення до нових кімнат"; "notification_settings_people_join_leave_rooms" = "Сповіщати, коли люди приєднуються чи виходять з кімнат"; @@ -2209,7 +2209,7 @@ "account_error_email_wrong_title" = "Неправильна адреса е-пошти"; "account_error_matrix_session_is_not_opened" = "Сеанс Matrix не відкрито"; "account_error_picture_change_failed" = "Не вдалося змінити зображення"; -"account_error_display_name_change_failed" = "Не вдалося змінити показуване ім'я"; +"account_error_display_name_change_failed" = "Не вдалося змінити псевдонім"; "account_msisdn_validation_error" = "Не вдалося перевірити номер телефону."; "account_email_validation_title" = "Очікування перевірки"; "account_msisdn_validation_title" = "Очікування перевірки"; @@ -2241,12 +2241,12 @@ "e2e_passphrase_enter" = "Введіть парольну фразу"; "e2e_passphrase_empty" = "Парольна фраза не повинна бути порожньою"; "e2e_passphrase_confirm" = "Підтвердити парольну фразу"; -"notice_room_history_visible_to_members_from_joined_point_by_you_for_dm" = "Ви зробили майбутні повідомлення кімнати видимими будь-кому від часу їхнього приєднання."; -"notice_room_history_visible_to_members_from_joined_point_by_you" = "Ви зробили майбутню історію кімнати видимою усім учасникам кімнати від часу їхнього приєднання."; +"notice_room_history_visible_to_members_from_joined_point_by_you_for_dm" = "Ви зробили майбутні повідомлення кімнати видимими всім від часу їхнього приєднання."; +"notice_room_history_visible_to_members_from_joined_point_by_you" = "Ви зробили майбутню історію кімнати видимою всім учасникам кімнати від часу їхнього приєднання."; "notice_room_history_visible_to_members_from_invited_point_by_you_for_dm" = "Ви зробили майбутні повідомлення кімнати видимими будь-кому від часу запрошення їх."; -"notice_room_history_visible_to_members_from_invited_point_by_you" = "Ви зробили майбутню історію кімнати видимою усім учасникам кімнати від часу запрошення їх."; -"notice_room_history_visible_to_members_by_you_for_dm" = "Ви зробили майбутні повідомлення кімнати видимими усім учасникам кімнати."; -"notice_room_history_visible_to_members_by_you" = "Ви зробили майбутню історію кімнати видимою усім учасникам кімнати."; +"notice_room_history_visible_to_members_from_invited_point_by_you" = "Ви зробили майбутню історію кімнати видимою всім учасникам кімнати від часу запрошення їх."; +"notice_room_history_visible_to_members_by_you_for_dm" = "Ви зробили майбутні повідомлення кімнати видимими всім учасникам кімнати."; +"notice_room_history_visible_to_members_by_you" = "Ви зробили майбутню історію кімнати видимою всім учасникам кімнати."; "notice_room_history_visible_to_anyone_by_you" = "Ви зробили майбутню історію кімнати видимою будь-кому."; "notice_room_created_by_you" = "Ви створили й сконфігурували кімнату."; "notice_profile_change_redacted_by_you" = "Ви оновили свій профіль %@"; @@ -2261,9 +2261,9 @@ "notice_room_name_changed_by_you_for_dm" = "Ви змінили назву на %@."; "notice_room_name_changed_by_you" = "Ви змінили назву кімнати на %@."; "notice_topic_changed_by_you" = "Ви змінили тему на «%@»."; -"notice_display_name_removed_by_you" = "Ви вилучили показуване ім'я"; -"notice_display_name_changed_from_by_you" = "Ви змінили показуване ім'я з %@ на %@"; -"notice_display_name_set_by_you" = "Ви вказали показуваним іменем %@"; +"notice_display_name_removed_by_you" = "Ви вилучили псевдонім"; +"notice_display_name_changed_from_by_you" = "Ви змінили псевдонім з %@ на %@"; +"notice_display_name_set_by_you" = "Ви налаштували псевдонімом %@"; "notice_conference_call_finished" = "Голосовий груповий виклик завершено"; "notice_conference_call_started" = "Груповий голосовий виклик розпочато"; "notice_conference_call_request" = "%@ запрошує до групового голосового виклику"; @@ -2467,11 +2467,11 @@ "onboarding_avatar_accessibility_label" = "Зображення профілю"; "onboarding_avatar_message" = "Час додати обличчя до імені"; "onboarding_avatar_title" = "Додати зображення профілю"; -"onboarding_display_name_max_length" = "Ваше показуване ім'я повинно складатися з менш ніж 256 символів"; +"onboarding_display_name_max_length" = "Ваш псевдонім повинен складатися з менш ніж 256 символів"; "onboarding_display_name_hint" = "Ви можете змінити його пізніше"; -"onboarding_display_name_placeholder" = "Показуване ім'я"; +"onboarding_display_name_placeholder" = "Псевдонім"; "onboarding_display_name_message" = "Його буде показано у надісланих повідомленнях."; -"onboarding_display_name_title" = "Виберіть показуване ім'я"; +"onboarding_display_name_title" = "Оберіть псевдонім"; "onboarding_personalization_skip" = "Пропустити цей крок"; "onboarding_personalization_save" = "Зберегти й продовжити"; "onboarding_congratulations_home_button" = "На головну"; @@ -2960,5 +2960,10 @@ // Room commands descriptions "room_command_change_display_name_description" = "Змінює ваш нік"; -"notice_display_name_changed_to" = "%@ змінили своє показуване ім'я на %@"; +"notice_display_name_changed_to" = "%@ змінили свій псевдонім на %@"; "poll_timeline_loading" = "Завантаження..."; +"room_creation_user_not_found_prompt_invite_action" = "Усе одно розпочати приватну бесіду"; +"room_participants_invite_anyway" = "Усе одно запросити"; +"room_creation_user_not_found_prompt_title" = "Підтвердження"; +"room_creation_user_not_found_prompt_message" = "Не вдалося знайти профілі для цього Matrix ID. Усе одно хочете розпочати приватну бесіду?"; +"room_participants_invite_unknown_participant_prompt_to_msg" = "Не вдалося знайти профілі для цього Matrix ID. Ви впевнені, що хочете запросити %@ до %@?"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index c59cb5e4d..ce6d62fd6 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -3667,6 +3667,14 @@ public class VectorL10n: NSObject { public static var manageSessionNotTrusted: String { return VectorL10n.tr("Vector", "manage_session_not_trusted") } + /// You will be redirected to your server's authentication provider to complete sign out. + public static var manageSessionRedirect: String { + return VectorL10n.tr("Vector", "manage_session_redirect") + } + /// Functionality currently unavailable. Please contact your homeserver admin + public static var manageSessionRedirectError: String { + return VectorL10n.tr("Vector", "manage_session_redirect_error") + } /// Rename session public static var manageSessionRename: String { return VectorL10n.tr("Vector", "manage_session_rename") @@ -7791,6 +7799,18 @@ public class VectorL10n: NSObject { public static var settingsLinks: String { return VectorL10n.tr("Vector", "settings_links") } + /// Manage account + public static var settingsManageAccountAction: String { + return VectorL10n.tr("Vector", "settings_manage_account_action") + } + /// Manage your account at %@ + public static func settingsManageAccountDescription(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_manage_account_description", p1) + } + /// Account + public static var settingsManageAccountTitle: String { + return VectorL10n.tr("Vector", "settings_manage_account_title") + } /// Mark all messages as read public static var settingsMarkAllAsRead: String { return VectorL10n.tr("Vector", "settings_mark_all_as_read") diff --git a/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift b/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift index f1f409f65..24dfe5c78 100644 --- a/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift +++ b/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift @@ -52,11 +52,14 @@ final class HomeserverConfigurationBuilder: NSObject { } else { secureBackupSetupMethods = [.passphrase] } + + let deviceDehydrationEnabled = wellKnown?.jsonDictionary()["org.matrix.msc3814"] as? Bool == true let encryptionConfiguration = HomeserverEncryptionConfiguration(isE2EEByDefaultEnabled: isE2EEByDefaultEnabled, isSecureBackupRequired: isSecureBackupRequired, secureBackupSetupMethods: secureBackupSetupMethods, - outboundKeysPreSharingMode: outboundKeysPreSharingMode) + outboundKeysPreSharingMode: outboundKeysPreSharingMode, + deviceDehydrationEnabled: deviceDehydrationEnabled) // Jitsi configuration let jitsiPreferredDomain: String? diff --git a/Riot/Model/HomeserverConfiguration/HomeserverEncryptionConfiguration.swift b/Riot/Model/HomeserverConfiguration/HomeserverEncryptionConfiguration.swift index 6672bff45..671756f63 100644 --- a/Riot/Model/HomeserverConfiguration/HomeserverEncryptionConfiguration.swift +++ b/Riot/Model/HomeserverConfiguration/HomeserverEncryptionConfiguration.swift @@ -23,15 +23,18 @@ final class HomeserverEncryptionConfiguration: NSObject { let isSecureBackupRequired: Bool let secureBackupSetupMethods: [VectorWellKnownBackupSetupMethod] let outboundKeysPreSharingMode: MXKKeyPreSharingStrategy + let deviceDehydrationEnabled: Bool init(isE2EEByDefaultEnabled: Bool, isSecureBackupRequired: Bool, secureBackupSetupMethods: [VectorWellKnownBackupSetupMethod], - outboundKeysPreSharingMode: MXKKeyPreSharingStrategy) { + outboundKeysPreSharingMode: MXKKeyPreSharingStrategy, + deviceDehydrationEnabled: Bool) { self.isE2EEByDefaultEnabled = isE2EEByDefaultEnabled self.isSecureBackupRequired = isSecureBackupRequired self.outboundKeysPreSharingMode = outboundKeysPreSharingMode self.secureBackupSetupMethods = secureBackupSetupMethods + self.deviceDehydrationEnabled = deviceDehydrationEnabled super.init() } diff --git a/Riot/Modules/Authentication/SSO/SSOAuthenticationService.swift b/Riot/Modules/Authentication/SSO/SSOAuthenticationService.swift index a0282c742..706c3782d 100644 --- a/Riot/Modules/Authentication/SSO/SSOAuthenticationService.swift +++ b/Riot/Modules/Authentication/SSO/SSOAuthenticationService.swift @@ -50,7 +50,7 @@ final class SSOAuthenticationService: NSObject { var ssoRedirectPath = SSOURLConstants.Paths.redirect - if let identityProvider = identityProvider { + if let identityProvider = identityProvider, !identityProvider.isEmpty { ssoRedirectPath.append("/\(identityProvider)") } diff --git a/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.h b/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.h index 4b218086f..bbfbfed06 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.h +++ b/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.h @@ -305,15 +305,5 @@ */ - (void)showAuthenticationFallBackView; -#pragma mark - Device rehydration - -/** - Call this method at an appropriate time to attempt rehydrating from an existing dehydrated device - @param keyData Secret key data - @param credentials Account credentials - */ - -- (void)attemptDeviceRehydrationWithKeyData:(NSData *)keyData credentials:(MXCredentials *)credentials; - @end diff --git a/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m b/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m index f9874a96a..35dfd8271 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m +++ b/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m @@ -1513,68 +1513,6 @@ [self _createAccountWithCredentials:credentials]; } -- (void)attemptDeviceRehydrationWithKeyData:(NSData *)keyData - credentials:(MXCredentials *)credentials -{ - [self attemptDeviceRehydrationWithKeyData:keyData - credentials:credentials - retry:YES]; -} - -- (void)attemptDeviceRehydrationWithKeyData:(NSData *)keyData - credentials:(MXCredentials *)credentials - retry:(BOOL)retry -{ - MXLogDebug(@"[MXKAuthenticationViewController] attemptDeviceRehydration: starting device rehydration"); - - if (keyData == nil) - { - MXLogError(@"[MXKAuthenticationViewController] attemptDeviceRehydration: no key provided for device rehydration"); - [self _createAccountWithCredentials:credentials]; - return; - } - - MXRestClient *mxRestClient = [[MXRestClient alloc] initWithCredentials:credentials andOnUnrecognizedCertificateBlock:^BOOL(NSData *certificate) { - return NO; - } andPersistentTokenDataHandler:^(void (^handler)(NSArray *credentials, void (^completion)(BOOL didUpdateCredentials))) { - [[MXKAccountManager sharedManager] readAndWriteCredentials:handler]; - } andUnauthenticatedHandler: nil]; - - MXWeakify(self); - [[MXKAccountManager sharedManager].dehydrationService rehydrateDeviceWithMatrixRestClient:mxRestClient dehydrationKey:keyData success:^(NSString * deviceId) { - MXStrongifyAndReturnIfNil(self); - - if (deviceId) - { - MXLogDebug(@"[MXKAuthenticationViewController] attemptDeviceRehydration: device %@ rehydrated successfully.", deviceId); - credentials.deviceId = deviceId; - } - else - { - MXLogDebug(@"[MXKAuthenticationViewController] attemptDeviceRehydration: device rehydration has been canceled."); - } - - [self _createAccountWithCredentials:credentials]; - } failure:^(NSError *error) { - MXStrongifyAndReturnIfNil(self); - - if (retry) - { - MXLogErrorDetails(@"[MXKAuthenticationViewController] attemptDeviceRehydration: device rehydration failed due to error: Retrying", @{ - @"error": error ?: @"unknown" - }); - [self attemptDeviceRehydrationWithKeyData:keyData credentials:credentials retry:NO]; - return; - } - - MXLogErrorDetails(@"[MXKAuthenticationViewController] attemptDeviceRehydration: device rehydration failed due to error", @{ - @"error": error ?: @"unknown" - }); - - [self _createAccountWithCredentials:credentials]; - }]; -} - - (void)_createAccountWithCredentials:(MXCredentials *)credentials { MXKAccount *account = [[MXKAccount alloc] initWithCredentials:credentials]; diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.h b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.h index ce6d58b7f..19a210110 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.h +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.h @@ -375,13 +375,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer #pragma mark - Sync filter -/** - Call this method at an appropriate time to attempt dehydrating to a new backup device - */ -- (void)attemptDeviceDehydrationWithKeyData:(NSData *)keyData - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure; - /** Handle unauthenticated errors from the server triggering hard/soft logouts as appropriate. */ diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m index 1cd659943..d3669af6e 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m @@ -1727,70 +1727,6 @@ static NSArray *initialSyncSilentErrorsHTTPStatusCodes; }]; } -- (void)attemptDeviceDehydrationWithKeyData:(NSData *)keyData - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - [self attemptDeviceDehydrationWithKeyData:keyData retry:YES success:success failure:failure]; -} - -- (void)attemptDeviceDehydrationWithKeyData:(NSData *)keyData - retry:(BOOL)retry - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - if (keyData == nil) - { - MXLogWarning(@"[MXKAccount] attemptDeviceDehydrationWithRetry: no key provided for device dehydration"); - - if (failure) - { - failure(nil); - } - - return; - } - - if (![mxSession.crypto.crossSigning isKindOfClass:[MXLegacyCrossSigning class]]) { - MXLogFailure(@"Device dehydratation is currently only supported by legacy cross signing, add support to all implementations"); - if (failure) - { - failure(nil); - } - return; - } - MXLegacyCrossSigning *crossSigning = (MXLegacyCrossSigning *)mxSession.crypto.crossSigning;; - - MXLogDebug(@"[MXKAccount] attemptDeviceDehydrationWithRetry: starting device dehydration"); - [[MXKAccountManager sharedManager].dehydrationService dehydrateDeviceWithMatrixRestClient:mxRestClient crossSigning:crossSigning dehydrationKey:keyData success:^(NSString *deviceId) { - MXLogDebug(@"[MXKAccount] attemptDeviceDehydrationWithRetry: device successfully dehydrated"); - - if (success) - { - success(); - } - } failure:^(NSError *error) { - if (retry) - { - [self attemptDeviceDehydrationWithKeyData:keyData retry:NO success:success failure:failure]; - MXLogErrorDetails(@"[MXKAccount] attemptDeviceDehydrationWithRetry: device dehydration failed due to error: Retrying.", @{ - @"error": error ?: @"unknown" - }); - } - else - { - MXLogErrorDetails(@"[MXKAccount] attemptDeviceDehydrationWithRetry: device dehydration failed due to error", @{ - @"error": error ?: @"unknown" - }); - - if (failure) - { - failure(error); - } - } - }]; -} - - (void)onMatrixSessionStateChange { // Check if pause has been requested diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.h b/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.h index 7881832e0..2e21bd9a4 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.h +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.h @@ -104,8 +104,6 @@ extern NSString *const MXKAccountManagerDataType; */ @property (nonatomic) BOOL isPushAvailable; -@property (nonatomic, readonly) MXDehydrationService *dehydrationService; - /** Retrieve the MXKAccounts manager. diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m b/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m index 6a69ee6a7..c779e5c47 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m @@ -71,7 +71,6 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa if (self) { _storeClass = [MXFileStore class]; - _dehydrationService = [MXDehydrationService new]; _savingAccountsEnabled = YES; // Migrate old account file to new format diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m index f4f109ff5..f55db8ff7 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m @@ -830,38 +830,37 @@ static NSString *const kRepliedTextPattern = @".*
.*
(.* } case MXEventTypeRoomCreate: { - NSString *creatorId; - MXJSONModelSetString(creatorId, event.content[@"creator"]); + // Room version 11 removes `creator` in favour of `sender`. + // https://github.com/matrix-org/matrix-spec-proposals/pull/2175 + // Just use the sender as it is possible to create a v11 room and spoof the `creator`. + NSString *creatorId = event.sender; - if (creatorId) + if ([creatorId isEqualToString:mxSession.myUserId]) { - if ([creatorId isEqualToString:mxSession.myUserId]) + if (isRoomDirect) { - if (isRoomDirect) - { - displayText = [VectorL10n noticeRoomCreatedByYouForDm]; - } - else - { - displayText = [VectorL10n noticeRoomCreatedByYou]; - } + displayText = [VectorL10n noticeRoomCreatedByYouForDm]; } else { - if (isRoomDirect) - { - displayText = [VectorL10n noticeRoomCreatedForDm:(roomState ? [roomState.members memberName:creatorId] : creatorId)]; - } - else - { - displayText = [VectorL10n noticeRoomCreated:(roomState ? [roomState.members memberName:creatorId] : creatorId)]; - } + displayText = [VectorL10n noticeRoomCreatedByYou]; } - // Append redacted info if any - if (redactedInfo) + } + else + { + if (isRoomDirect) { - displayText = [NSString stringWithFormat:@"%@ %@", displayText, redactedInfo]; + displayText = [VectorL10n noticeRoomCreatedForDm:(roomState ? [roomState.members memberName:creatorId] : creatorId)]; } + else + { + displayText = [VectorL10n noticeRoomCreated:(roomState ? [roomState.members memberName:creatorId] : creatorId)]; + } + } + // Append redacted info if any + if (redactedInfo) + { + displayText = [NSString stringWithFormat:@"%@ %@", displayText, redactedInfo]; } break; } diff --git a/Riot/Modules/Pills/PillsFormatter.swift b/Riot/Modules/Pills/PillsFormatter.swift index 1b6256835..ecdfac5fe 100644 --- a/Riot/Modules/Pills/PillsFormatter.swift +++ b/Riot/Modules/Pills/PillsFormatter.swift @@ -197,12 +197,14 @@ class PillsFormatter: NSObject { guard let roomMember = roomState.members.member(withUserId: userId) else { return } + + let displayName = roomMember.displayname ?? userId pill.data?.items = [ .avatar(url: roomMember.avatarUrl, - string: roomMember.displayname, - matrixId: roomMember.userId), - .text(roomMember.displayname) + string: displayName, + matrixId: userId), + .text(displayName) ] default: break diff --git a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift index edd951fd6..60b6edf94 100644 --- a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift +++ b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift @@ -207,8 +207,14 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp } func mention(_ member: MXRoomMember) { - self.wysiwygViewModel.setMention(url: MXTools.permalinkToUser(withUserId: member.userId), - name: member.displayname, + guard let userId = member.userId else { + return + } + + let displayName = member.displayname ?? userId + + self.wysiwygViewModel.setMention(url: MXTools.permalinkToUser(withUserId: userId), + name: displayName, mentionType: .user) } diff --git a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift index 5d883def2..4f6f48e99 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift @@ -35,9 +35,9 @@ final class SecretsRecoveryWithKeyCoordinator: SecretsRecoveryWithKeyCoordinator // MARK: - Setup - init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal, cancellable: Bool) { + init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal, cancellable: Bool, dehydrationService: DehydrationService?) { - let secretsRecoveryWithKeyViewModel = SecretsRecoveryWithKeyViewModel(recoveryService: recoveryService, recoveryGoal: recoveryGoal) + let secretsRecoveryWithKeyViewModel = SecretsRecoveryWithKeyViewModel(recoveryService: recoveryService, recoveryGoal: recoveryGoal, dehydrationService: dehydrationService) let secretsRecoveryWithKeyViewController = SecretsRecoveryWithKeyViewController.instantiate(with: secretsRecoveryWithKeyViewModel, cancellable: cancellable) self.secretsRecoveryWithKeyViewController = secretsRecoveryWithKeyViewController self.secretsRecoveryWithKeyViewModel = secretsRecoveryWithKeyViewModel diff --git a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModel.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModel.swift index f56562f7d..c18222bd0 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModel.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModel.swift @@ -24,6 +24,8 @@ final class SecretsRecoveryWithKeyViewModel: SecretsRecoveryWithKeyViewModelType private let recoveryService: MXRecoveryService + private let dehydrationService: DehydrationService? + // MARK: Public let recoveryGoal: SecretsRecoveryGoal @@ -39,8 +41,9 @@ final class SecretsRecoveryWithKeyViewModel: SecretsRecoveryWithKeyViewModelType // MARK: - Setup - init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal) { + init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal, dehydrationService: DehydrationService?) { self.recoveryService = recoveryService + self.dehydrationService = dehydrationService self.recoveryGoal = recoveryGoal } @@ -83,6 +86,10 @@ final class SecretsRecoveryWithKeyViewModel: SecretsRecoveryWithKeyViewModelType } self.update(viewState: .loaded) self.coordinatorDelegate?.secretsRecoveryWithKeyViewModelDidRecover(self) + + Task { + await self.dehydrationService?.runDeviceDehydrationFlow(privateKeyData: privateKey) + } }, failure: { [weak self] error in guard let self = self else { return diff --git a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift index 3c0ad1dc3..a53929d12 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift @@ -35,8 +35,8 @@ final class SecretsRecoveryWithPassphraseCoordinator: SecretsRecoveryWithPassphr // MARK: - Setup - init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal, cancellable: Bool) { - let secretsRecoveryWithPassphraseViewModel = SecretsRecoveryWithPassphraseViewModel(recoveryService: recoveryService, recoveryGoal: recoveryGoal) + init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal, cancellable: Bool, dehydrationService: DehydrationService?) { + let secretsRecoveryWithPassphraseViewModel = SecretsRecoveryWithPassphraseViewModel(recoveryService: recoveryService, recoveryGoal: recoveryGoal, dehydrationService: dehydrationService) let secretsRecoveryWithPassphraseViewController = SecretsRecoveryWithPassphraseViewController.instantiate(with: secretsRecoveryWithPassphraseViewModel, cancellable: cancellable) self.secretsRecoveryWithPassphraseViewController = secretsRecoveryWithPassphraseViewController self.secretsRecoveryWithPassphraseViewModel = secretsRecoveryWithPassphraseViewModel diff --git a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift index 71e51e270..dd05f8425 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift @@ -24,6 +24,8 @@ final class SecretsRecoveryWithPassphraseViewModel: SecretsRecoveryWithPassphras private let recoveryService: MXRecoveryService + private let dehydrationService: DehydrationService? + // MARK: Public let recoveryGoal: SecretsRecoveryGoal @@ -39,8 +41,9 @@ final class SecretsRecoveryWithPassphraseViewModel: SecretsRecoveryWithPassphras // MARK: - Setup - init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal) { + init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal, dehydrationService: DehydrationService?) { self.recoveryService = recoveryService + self.dehydrationService = dehydrationService self.recoveryGoal = recoveryGoal } @@ -103,6 +106,10 @@ final class SecretsRecoveryWithPassphraseViewModel: SecretsRecoveryWithPassphras } self.update(viewState: .loaded) self.coordinatorDelegate?.secretsRecoveryWithPassphraseViewModelDidRecover(self) + + Task { + await self.dehydrationService?.runDeviceDehydrationFlow(privateKeyData: privateKey) + } }, failure: { [weak self] error in guard let self = self else { return diff --git a/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift b/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift index 593f10139..315e19721 100644 --- a/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift +++ b/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift @@ -86,14 +86,28 @@ final class SecretsRecoveryCoordinator: SecretsRecoveryCoordinatorType { // MARK: - Private + private var dehydrationService: DehydrationService? { + if self.session.vc_homeserverConfiguration().encryption.deviceDehydrationEnabled { + return self.session.crypto.dehydrationService + } + + return nil + } + private func createRecoverFromKeyCoordinator() -> SecretsRecoveryWithKeyCoordinator { - let coordinator = SecretsRecoveryWithKeyCoordinator(recoveryService: self.session.crypto.recoveryService, recoveryGoal: self.recoveryGoal, cancellable: self.cancellable) + let coordinator = SecretsRecoveryWithKeyCoordinator(recoveryService: self.session.crypto.recoveryService, + recoveryGoal: self.recoveryGoal, + cancellable: self.cancellable, + dehydrationService: dehydrationService) coordinator.delegate = self return coordinator } private func createRecoverFromPassphraseCoordinator() -> SecretsRecoveryWithPassphraseCoordinator { - let coordinator = SecretsRecoveryWithPassphraseCoordinator(recoveryService: self.session.crypto.recoveryService, recoveryGoal: self.recoveryGoal, cancellable: self.cancellable) + let coordinator = SecretsRecoveryWithPassphraseCoordinator(recoveryService: self.session.crypto.recoveryService, + recoveryGoal: self.recoveryGoal, + cancellable: self.cancellable, + dehydrationService: dehydrationService) coordinator.delegate = self return coordinator } diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift index 58ebf79ba..9dc055e49 100644 --- a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift @@ -42,8 +42,13 @@ final class SecretsSetupRecoveryKeyCoordinator: SecretsSetupRecoveryKeyCoordinat passphrase: String?, passphraseOnly: Bool, allowOverwrite: Bool = false, - cancellable: Bool) { - let secretsSetupRecoveryKeyViewModel = SecretsSetupRecoveryKeyViewModel(recoveryService: recoveryService, passphrase: passphrase, passphraseOnly: passphraseOnly, allowOverwrite: allowOverwrite) + cancellable: Bool, + dehydrationService: DehydrationService?) { + let secretsSetupRecoveryKeyViewModel = SecretsSetupRecoveryKeyViewModel(recoveryService: recoveryService, + passphrase: passphrase, + passphraseOnly: passphraseOnly, + allowOverwrite: allowOverwrite, + dehydrationService: dehydrationService) let secretsSetupRecoveryKeyViewController = SecretsSetupRecoveryKeyViewController.instantiate(with: secretsSetupRecoveryKeyViewModel, cancellable: cancellable) self.secretsSetupRecoveryKeyViewModel = secretsSetupRecoveryKeyViewModel self.secretsSetupRecoveryKeyViewController = secretsSetupRecoveryKeyViewController diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift index af85b7eb7..c91cb6787 100644 --- a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift @@ -28,6 +28,7 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy private let passphrase: String? private let passphraseOnly: Bool private let allowOverwrite: Bool + private let dehydrationService: DehydrationService? // MARK: Public @@ -36,11 +37,12 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy // MARK: - Setup - init(recoveryService: MXRecoveryService, passphrase: String?, passphraseOnly: Bool, allowOverwrite: Bool = false) { + init(recoveryService: MXRecoveryService, passphrase: String?, passphraseOnly: Bool, allowOverwrite: Bool = false, dehydrationService: DehydrationService?) { self.recoveryService = recoveryService self.passphrase = passphrase self.passphraseOnly = passphraseOnly self.allowOverwrite = allowOverwrite + self.dehydrationService = dehydrationService } // MARK: - Public @@ -76,6 +78,10 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy self.recoveryService.createRecovery(forSecrets: nil, withPassphrase: self.passphrase, createServicesBackups: true, success: { secretStorageKeyCreationInfo in self.update(viewState: .recoveryCreated(secretStorageKeyCreationInfo.recoveryKey)) + + Task { + await self.dehydrationService?.runDeviceDehydrationFlow(privateKeyData: secretStorageKeyCreationInfo.privateKey) + } }, failure: { error in self.update(viewState: .error(error)) }) diff --git a/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinator.swift b/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinator.swift index d58444206..b3a5b06d5 100644 --- a/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinator.swift +++ b/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinator.swift @@ -104,9 +104,22 @@ final class SecureBackupSetupCoordinator: SecureBackupSetupCoordinatorType { introViewController.delegate = self return introViewController } - + + private var dehydrationService: DehydrationService? { + if self.session.vc_homeserverConfiguration().encryption.deviceDehydrationEnabled { + return self.session.crypto.dehydrationService + } + + return nil + } + private func showSetupKey(passphraseOnly: Bool, passphrase: String? = nil) { - let coordinator = SecretsSetupRecoveryKeyCoordinator(recoveryService: self.recoveryService, passphrase: passphrase, passphraseOnly: passphraseOnly, allowOverwrite: allowOverwrite, cancellable: self.cancellable) + let coordinator = SecretsSetupRecoveryKeyCoordinator(recoveryService: self.recoveryService, + passphrase: passphrase, + passphraseOnly: passphraseOnly, + allowOverwrite: allowOverwrite, + cancellable: self.cancellable, + dehydrationService: dehydrationService) coordinator.delegate = self coordinator.start() diff --git a/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m b/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m index 74128bff0..a9abac92c 100644 --- a/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m +++ b/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m @@ -676,6 +676,52 @@ enum { } - (void)removeDevice +{ + MXWellKnownAuthentication *authentication = self.mainSession.homeserverWellknown.authentication; + if (authentication) + { + NSURL *logoutURL = [authentication getLogoutDeviceURLFromID:device.deviceId]; + if (logoutURL) + { + [self removeDeviceRedirectWithURL:logoutURL]; + } + else + { + [self showRemoveDeviceRedirectError]; + } + } + else + { + [self removeDeviceThroughAPI]; + } +} + +-(void) removeDeviceRedirectWithURL: (NSURL * _Nonnull) url +{ + UIAlertController *alert = [UIAlertController alertControllerWithTitle: [VectorL10n manageSessionRedirect] message: nil preferredStyle:UIAlertControllerStyleAlert]; + + __weak typeof(self) weakSelf = self; + UIAlertAction *action = [UIAlertAction actionWithTitle:[VectorL10n ok] + style:UIAlertActionStyleDefault + handler: ^(UIAlertAction * action) { + [UIApplication.sharedApplication openURL:url options:@{} completionHandler:^(BOOL success) { + if (success && weakSelf) + { + [weakSelf withdrawViewControllerAnimated:YES completion:nil]; + } + }]; + }]; + [alert addAction: action]; + [self presentViewController:alert animated:YES completion:nil]; +} + +-(void) showRemoveDeviceRedirectError +{ + UIAlertController *alert = [UIAlertController alertControllerWithTitle: [VectorL10n manageSessionRedirectError] message: nil preferredStyle:UIAlertControllerStyleAlert]; + [self presentViewController:alert animated:YES completion:nil]; +} + +-(void) removeDeviceThroughAPI { [self startActivityIndicator]; self.view.userInteractionEnabled = NO; diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index 3d6002e74..05d70d765 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -55,6 +55,7 @@ typedef NS_ENUM(NSUInteger, SECTION_TAG) SECTION_TAG_USER_SETTINGS, SECTION_TAG_NEW_FEATURES, SECTION_TAG_LOCATION_SHARING, + SECTION_TAG_ACCOUNT, SECTION_TAG_SENDING_MEDIA, SECTION_TAG_LINKS, SECTION_TAG_SECURITY, @@ -252,6 +253,11 @@ enum OUTDATED_WARNING = 0 }; +typedef NS_ENUM(NSUInteger, ACCOUNT) +{ + ACCOUNT_MANAGE_INDEX = 0, +}; + typedef void (^blockSettingsViewController_onReadyToDestroy)(void); #pragma mark - SettingsViewController @@ -466,7 +472,11 @@ ChangePasswordCoordinatorBridgePresenterDelegate> [sectionUserSettings addRowWithTag: USER_SETTINGS_PHONENUMBERS_OFFSET + index]; } } - if (BWIBuildSettings.shared.settingsScreenAllowAddingEmailThreepids) + if (BWIBuildSettings.shared.settingsScreenAllowAddingEmailThreepids && + BuildSettings.settingsScreenAllowAddingEmailThreepids && + // If the threePidChanges is nil we assume the capability to be true + (!self.mainSession.homeserverCapabilities.threePidChanges || + self.mainSession.homeserverCapabilities.threePidChanges.enabled)) { [sectionUserSettings addRowWithTag:USER_SETTINGS_ADD_EMAIL_INDEX]; } @@ -493,7 +503,18 @@ ChangePasswordCoordinatorBridgePresenterDelegate> [tmpSections addObject:sectionNewFeatures]; } - if (BWIBuildSettings.shared.settingsScreenShowConfirmMediaSize) + NSString *manageAccountURL = self.mainSession.homeserverWellknown.authentication.account; + if (manageAccountURL) + { + Section *account = [Section sectionWithTag: SECTION_TAG_ACCOUNT]; + [account addRowWithTag:ACCOUNT_MANAGE_INDEX]; + account.headerTitle = [VectorL10n settingsManageAccountTitle]; + account.footerTitle = [VectorL10n settingsManageAccountDescription:manageAccountURL]; + [tmpSections addObject:account]; + } + + if (BWIBuildSettings.shared.settingsScreenShowConfirmMediaSize && + BuildSettings.settingsScreenShowConfirmMediaSize) { Section *sectionMedia = [Section sectionWithTag:SECTION_TAG_SENDING_MEDIA]; [sectionMedia addRowWithTag:SENDING_MEDIA_CONFIRM_SIZE]; @@ -798,14 +819,15 @@ ChangePasswordCoordinatorBridgePresenterDelegate> } } - if (BWIBuildSettings.shared.settingsScreenAllowDeactivatingAccount) + if (BWIBuildSettings.shared.settingsScreenAllowDeactivatingAccount && + BuildSettings.settingsScreenAllowDeactivatingAccount && !self.mainSession.homeserverWellknown.authentication) { Section *sectionDeactivate = [Section sectionWithTag:SECTION_TAG_DEACTIVATE_ACCOUNT]; [sectionDeactivate addRowWithTag:0]; sectionDeactivate.headerTitle = [VectorL10n settingsDeactivateMyAccount]; [tmpSections addObject:sectionDeactivate]; } - + Section *sectionSignOut = [Section sectionWithTag:SECTION_TAG_SIGN_OUT]; [sectionSignOut addRowWithTag:0]; [tmpSections addObject:sectionSignOut]; @@ -3113,6 +3135,17 @@ ChangePasswordCoordinatorBridgePresenterDelegate> cell = showSettingsBtnCell; } } + else if (section == SECTION_TAG_ACCOUNT) + { + switch (row) + { + case ACCOUNT_MANAGE_INDEX: + cell = [self getDefaultTableViewCell:tableView]; + cell.textLabel.text = [VectorL10n settingsManageAccountAction]; + [cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme]; + break; + } + } return cell; } @@ -3497,6 +3530,14 @@ ChangePasswordCoordinatorBridgePresenterDelegate> [self pushViewController:webViewViewController]; } } + else if (section == SECTION_TAG_ACCOUNT) + { + switch(row) { + case ACCOUNT_MANAGE_INDEX: + [self onManageAccountTap]; + break; + } + } [tableView deselectRowAtIndexPath:indexPath animated:YES]; } @@ -4435,6 +4476,14 @@ ChangePasswordCoordinatorBridgePresenterDelegate> } } +- (void)onManageAccountTap +{ + NSURL *url = [NSURL URLWithString: self.mainSession.homeserverWellknown.authentication.account]; + if (url) { + [UIApplication.sharedApplication openURL:url options:@{} completionHandler:nil]; + } +} + - (void)showThemePicker { __weak typeof(self) weakSelf = self; diff --git a/Riot/SupportingFiles/Riot-Bridging-Header.h b/Riot/SupportingFiles/Riot-Bridging-Header.h index d144ac1db..66bb9bdfd 100644 --- a/Riot/SupportingFiles/Riot-Bridging-Header.h +++ b/Riot/SupportingFiles/Riot-Bridging-Header.h @@ -44,7 +44,7 @@ #import "ContactDetailsViewController.h" #import "RoomInputToolbarView.h" #import "NSArray+Element.h" -#import "ShareItemSender.h" +#import "ShareItemSenderProtocol.h" #import "Contact.h" #import "RoomTimelineCellProvider.h" #import "PlainRoomTimelineCellProvider.h" diff --git a/Riot/target.yml b/Riot/target.yml index 791c2cb47..7ecc5fd7e 100644 --- a/Riot/target.yml +++ b/Riot/target.yml @@ -34,8 +34,10 @@ targets: platform: iOS dependencies: - - target: RiotShareExtension -# - target: SiriIntents + # Disabled due to crypto corruption issues. + # https://github.com/vector-im/element-ios/issues/7618 + # - target: RiotShareExtension + # - target: SiriIntents // BWI: disabled - target: RiotNSE - target: BroadcastUploadExtension - target: DesignKit diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index eb606666e..fb92033ce 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -994,7 +994,9 @@ class NotificationService: UNNotificationServiceExtension { return Constants.toBeRemovedNotificationCategoryIdentifier } - return "QUICK_REPLY" + // Don't return QUICK_REPLY here as there is an issue + // with crypto corruption when sending from extensions. + return nil } /// Attempts to send trigger a VoIP push for the given event diff --git a/RiotSwiftUI/Modules/Common/Util/ListBackground.swift b/RiotSwiftUI/Modules/Common/Util/ListBackground.swift index d4e087da8..ddfc362c2 100644 --- a/RiotSwiftUI/Modules/Common/Util/ListBackground.swift +++ b/RiotSwiftUI/Modules/Common/Util/ListBackground.swift @@ -49,10 +49,4 @@ extension View { func listBackgroundColor(_ color: Color) -> some View { modifier(ListBackgroundModifier(color: color)) } - - /// Finds a `UICollectionView` from a `SwiftUI.List`, or `SwiftUI.List` child. - /// Stop gap until https://github.com/siteline/SwiftUI-Introspect/pull/169 - func introspectCollectionView(customize: @escaping (UICollectionView) -> Void) -> some View { - introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: customize) - } } diff --git a/RiotSwiftUI/Modules/UserSessions/Coordinator/UserSessionsFlowCoordinator.swift b/RiotSwiftUI/Modules/UserSessions/Coordinator/UserSessionsFlowCoordinator.swift index 17361759b..6ba28ad64 100644 --- a/RiotSwiftUI/Modules/UserSessions/Coordinator/UserSessionsFlowCoordinator.swift +++ b/RiotSwiftUI/Modules/UserSessions/Coordinator/UserSessionsFlowCoordinator.swift @@ -120,17 +120,29 @@ final class UserSessionsFlowCoordinator: NSObject, Coordinator, Presentable { case let .renameSession(sessionInfo): self.showRenameSessionScreen(for: sessionInfo) case let .logoutOfSession(sessionInfo): - if sessionInfo.isCurrent { - self.showLogoutConfirmationForCurrentSession() - } else { - self.showLogoutConfirmation(for: [sessionInfo]) - } + self.handleLogoutOfSession(sessionInfo: sessionInfo) case let .showSessionStateInfo(sessionInfo): self.showInfoSheet(parameters: .init(userSessionInfo: sessionInfo, parentSize: self.toPresentable().view.bounds.size)) } } pushScreen(with: coordinator) } + + private func handleLogoutOfSession(sessionInfo: UserSessionInfo) { + if sessionInfo.isCurrent { + self.showLogoutConfirmationForCurrentSession() + } else { + if let authentication = self.parameters.session.homeserverWellknown.authentication { + if let logoutURL = authentication.getLogoutDeviceURL(fromID: sessionInfo.id) { + self.openDeviceLogoutRedirectURL(logoutURL) + } else { + self.showDeviceLogoutRedirectError() + } + } else { + self.showLogoutConfirmation(for: [sessionInfo]) + } + } + } /// Shows the QR login screen. private func openQRLoginScreen() { @@ -182,6 +194,26 @@ final class UserSessionsFlowCoordinator: NSObject, Coordinator, Presentable { return UserOtherSessionsCoordinator(parameters: parameters) } + private func openDeviceLogoutRedirectURL(_ url: URL) { + let alert = UIAlertController(title: VectorL10n.manageSessionRedirect, message: nil, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: VectorL10n.ok, style: .default) { [weak self] _ in + UIApplication.shared.open(url) { [weak self] success in + guard success else { + return + } + self?.popToSessionsOverview() + } + }) + alert.popoverPresentationController?.sourceView = toPresentable().view + navigationRouter.present(alert, animated: true) + } + + private func showDeviceLogoutRedirectError() { + let alert = UIAlertController(title: VectorL10n.manageSessionRedirectError, message: nil, preferredStyle: .alert) + alert.popoverPresentationController?.sourceView = toPresentable().view + navigationRouter.present(alert, animated: true) + } + /// Shows a confirmation dialog to the user to sign out of a session. private func showLogoutConfirmation(for sessionInfos: [UserSessionInfo]) { // Use a UIAlertController as we don't have confirmationDialog in SwiftUI on iOS 14. diff --git a/project.yml b/project.yml index 4fa385c7c..05309f4e2 100644 --- a/project.yml +++ b/project.yml @@ -32,8 +32,6 @@ include: - path: Riot/target-bum-beta.yml - path: Riot/target-bum-open.yml - path: RiotTests/target.yml - - path: RiotShareExtension/target.yml - - path: SiriIntents/target.yml - path: RiotNSE/target.yml - path: BroadcastUploadExtension/target.yml - path: DesignKit/target.yml @@ -42,6 +40,10 @@ include: - path: RiotSwiftUI/targetUITests.yml - path: CommonKit/target.yml - path: CommonKit/targetUnitTests.yml + # Disabled due to crypto corruption issues. + # https://github.com/vector-im/element-ios/issues/7618 + # - path: RiotShareExtension/target.yml + # - path: SiriIntents/target.yml packages: AnalyticsEvents: