diff --git a/CHANGES.md b/CHANGES.md index 4758b42f62..135aeacc0b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,24 @@ +## Changes in 1.11.5 (2023-11-28) + +🙌 Improvements + +- Upgrade MatrixSDK version ([v0.27.4](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.27.4)). +- Upgrade Rich Text Editor version. ([v2.18.0](https://github.com/matrix-org/matrix-rich-text-editor/releases/tag/2.18.0)) + +🐛 Bugfixes + +- Call destroy in dealloc to remove all observers of SettingsViewController ([#7697](https://github.com/vector-im/element-ios/pull/7697)) +- Editing a message that ends with an emoji now works as expected. ([#7681](https://github.com/vector-im/element-ios/issues/7681)) +- The "Quote" action has been removed from the menu of the selected message. ([#7691](https://github.com/vector-im/element-ios/issues/7691)) +- The slide to end call for everyone button for the Jitsi widget now also ends the call for the current user. ([#7704](https://github.com/vector-im/element-ios/issues/7704)) +- If a Jitsi call in a room is ongoing when the Jitsi widget is removed from such room the call ends. ([#7706](https://github.com/vector-im/element-ios/issues/7706)) +- If a moderator ends a Jitsi call for everyone the call is now dismissed. ([#7709](https://github.com/vector-im/element-ios/issues/7709)) + +⚠️ API Changes + +- Drop support for iOS 14, raising the deployment target to iOS 15 to support the latest Rich Text Editor version. ([#7711](https://github.com/vector-im/element-ios/pull/7711)) + + ## Changes in 1.11.4 (2023-10-04) 🙌 Improvements diff --git a/Config/AppVersion.xcconfig b/Config/AppVersion.xcconfig index de47f2bd6a..2d558a1be4 100644 --- a/Config/AppVersion.xcconfig +++ b/Config/AppVersion.xcconfig @@ -15,5 +15,5 @@ // // Version -MARKETING_VERSION = 1.11.4 -CURRENT_PROJECT_VERSION = 1.11.4 +MARKETING_VERSION = 1.11.5 +CURRENT_PROJECT_VERSION = 1.11.5 diff --git a/Config/Project.xcconfig b/Config/Project.xcconfig index bbca50a1bf..2413f765f4 100644 --- a/Config/Project.xcconfig +++ b/Config/Project.xcconfig @@ -26,7 +26,7 @@ KEYCHAIN_ACCESS_GROUP = $(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER).keychain BROADCAST_UPLOAD_EXTENSION_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER).broadcastUploadExtension // Build settings -IPHONEOS_DEPLOYMENT_TARGET = 14.0 +IPHONEOS_DEPLOYMENT_TARGET = 15.0 SDKROOT = iphoneos TARGETED_DEVICE_FAMILY = 1,2 SWIFT_VERSION = 5.6 diff --git a/Podfile b/Podfile index d408e4f72f..1fc0d1783e 100644 --- a/Podfile +++ b/Podfile @@ -1,7 +1,7 @@ source 'https://cdn.cocoapods.org/' # Uncomment this line to define a global platform for your project -platform :ios, '14.0' +platform :ios, '15.0' # By default, ignore all warnings from any pod inhibit_all_warnings! @@ -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.27.3' +$matrixSDKVersion = '= 0.27.4' # $matrixSDKVersion = :local # $matrixSDKVersion = { :branch => 'develop'} # $matrixSDKVersion = { :specHash => { git: 'https://git.io/fork123', branch: 'fix' } } diff --git a/Podfile.lock b/Podfile.lock index 90b67132b9..2ecd7f65ed 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -39,9 +39,9 @@ PODS: - LoggerAPI (1.9.200): - Logging (~> 1.1) - Logging (1.4.0) - - MatrixSDK (0.27.3): - - MatrixSDK/Core (= 0.27.3) - - MatrixSDK/Core (0.27.3): + - MatrixSDK (0.27.4): + - MatrixSDK/Core (= 0.27.4) + - MatrixSDK/Core (0.27.4): - AFNetworking (~> 4.0.0) - GZIP (~> 1.3.0) - libbase58 (~> 0.1.4) @@ -49,7 +49,7 @@ PODS: - OLMKit (~> 3.2.5) - Realm (= 10.27.0) - SwiftyBeaver (= 1.9.5) - - MatrixSDK/JingleCallStack (0.27.3): + - MatrixSDK/JingleCallStack (0.27.4): - JitsiMeetSDKLite (= 8.1.2-lite) - MatrixSDK/Core - MatrixSDKCrypto (0.3.13) @@ -102,8 +102,8 @@ DEPENDENCIES: - KeychainAccess (~> 4.2.2) - KTCenterFlowLayout (~> 1.3.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixSDK (= 0.27.3) - - MatrixSDK/JingleCallStack (= 0.27.3) + - MatrixSDK (= 0.27.4) + - MatrixSDK/JingleCallStack (= 0.27.4) - OLMKit - PostHog (~> 2.0.0) - ReadMoreTextView (~> 3.0.1) @@ -187,7 +187,7 @@ SPEC CHECKSUMS: libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75 LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d Logging: beeb016c9c80cf77042d62e83495816847ef108b - MatrixSDK: 83fd36133b8e9147aa2f770953dc7921dda4e8b5 + MatrixSDK: da1df31afa59611ec7251ec419a2ceca6d41c7c4 MatrixSDKCrypto: bf08b72f2cd015d8749420a2b8b92fc0536bedf4 OLMKit: da115f16582e47626616874e20f7bb92222c7a51 PostHog: 660ec6c9d80cec17b685e148f17f6785a88b597d @@ -208,6 +208,6 @@ SPEC CHECKSUMS: zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: 20f5e721c3f48c117b9610409e79661637454aa1 +PODFILE CHECKSUM: 3b328f8bb8500a6dd81136ab8a065e7434959805 -COCOAPODS: 1.11.3 +COCOAPODS: 1.13.0 diff --git a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved index 4a7151c773..28fdda2b19 100644 --- a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/Cocoanetics/DTCoreText", "state" : { - "revision" : "9d2d4d2296e5d2d852a7d3c592b817d913a5d020", - "version" : "1.6.27" + "revision" : "b664664825da565b4c2b7a17dbe2369f68ae43d9", + "version" : "1.6.26" } }, { @@ -50,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-wysiwyg-composer-swift", "state" : { - "revision" : "1100b217c04d096dfe072afb4484660ff794d805", - "version" : "2.2.2" + "revision" : "dfb74c89bf54b41ea000d564d6435ac6444ba6b4", + "version" : "2.18.0" } }, { diff --git a/Riot/Assets/ar.lproj/Vector.strings b/Riot/Assets/ar.lproj/Vector.strings index 7810fc3ccf..390819e6ad 100644 --- a/Riot/Assets/ar.lproj/Vector.strings +++ b/Riot/Assets/ar.lproj/Vector.strings @@ -1083,3 +1083,118 @@ // Others "or" = "أو"; "accessibility_selected" = "تم تحديده"; +"media_type_accessibility_sticker" = "مُلصَق"; +"unknown_devices_call_anyway" = "اتصل على أي حال"; +"notice_voice_broadcast_ended_by_you" = "لقد أنهيت بث صوتي."; +"thread_copy_link_to_thread" = "نسخ الرابط إلى الموضوع"; +"pill_message_in" = "رسالة في %@"; +"room_accessibility_record_voice_message" = "قم بتسجيل رسالة صوتية"; +"media_type_accessibility_image" = "صُورَة"; +"pill_message" = "رِسَالَة"; +"pill_message_from" = "رسالة من %@"; + +// 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" = "يدعو المستخدم بالمعرف المعطى للانضمام إلى الغرفة الحالية"; +"room_command_set_user_power_level_description" = "تعريف مستوى الصلاحية للمستخدم"; +"room_title_multiple_active_members" = "%@/%@ أعضاء نشطين"; +"room_first_message_placeholder" = "أرسل أول رسالة لك…"; +"threads_empty_title" = "حافظ على تنظيم المناقشات باستخدام المواضيع"; +"message_from_a_thread" = "من موضوع"; +"authentication_qr_login_failure_device_not_supported" = "الربط مع هذا الجهاز غير مَدعُومَ."; +"threads_action_my_threads" = "مواضيعي"; +"threads_beta_title" = "مواضيع"; +"room_title_members" = "%@ أعضاء"; +"settings_sending_media" = "إرسال الصور ومقاطع الفيديو"; + +// MARK: Password policy errors +"password_policy_too_short_pwd_error" = "كلمة مرور قصيرة جدً"; +"password_policy_weak_pwd_error" = "هذه كلمة المرور ضعيفة جدًا. يجب أن تحتوي على ما لا يقل عن 8 أحرف، بما في ذلك حرف كبير، حرف صغير، رقم وحرف خاص واحد على الأقل."; +"threads_beta_information_link" = "تعلم أكثر"; +"room_title_one_member" = "عضو واحد"; + +// MARK: Threads +"room_thread_title" = "موضوع"; +"threads_beta_cancel" = "ليس الآن"; +"media_type_accessibility_audio" = "صَوت"; +"unknown_devices_answer_anyway" = "أجب على أي حال"; +"threads_notice_done" = "فهمت"; +"notice_voice_broadcast_ended" = "%@ أنهى بثًا صوتيًا."; +"authentication_login_with_qr" = "تسجيل الدخول بواسطة رمز الاستجابة السريعة"; +"authentication_qr_login_start_step2" = "الإعدَادَات -> الأمَان والخُصُوصِيَّة"; +"authentication_qr_login_start_need_alternative" = "هل تحتاج إلى طريقة بديلة؟"; +"authentication_qr_login_start_subtitle" = "استخدم الكاميرا على هذا الجهاز لمسح رمز الاستجابة السريعة المعروض على جهازك الآخر:"; +"authentication_qr_login_start_step1" = "افتج برنامج Element على جهازك الأخر"; +"authentication_qr_login_start_display_qr" = "عرض رمز الاستجابة السريعة على هذا الجهاز"; +"authentication_qr_login_display_step2" = "اختر 'تسجيل الدخول بواسطة رمز الاستجابة السريعة'"; +"authentication_qr_login_confirm_title" = "تم إنشاء اتصال آمن"; +"authentication_qr_login_loading_connecting_device" = "الاتصال بالجهاز"; +"authentication_qr_login_loading_waiting_signin" = "انتظر حتى يقوم الجهاز بتسجيل الدخول."; +"authentication_qr_login_loading_signed_in" = "تم تسجيل دخولك على جهازك الآخر."; +"authentication_qr_login_failure_title" = "فشل الربط"; +"authentication_qr_login_failure_invalid_qr" = "رمز الاستجابة السريعة غير صالح."; +"authentication_qr_login_failure_retry" = "حاول مرة أخرى"; +"room_accessibility_record_voice_message_hint" = "انقر مزدوجًا واستمر في الضغط للتسجيل."; +"media_type_accessibility_file" = "مَلَفّ"; +"unknown_devices_title" = "الجلسات المجهولة"; +"room_title_invite_members" = "ادعو الأعضاء"; +"settings_user_settings" = "إعدَادَات المُستَخدِم"; +"notice_voice_broadcast_live" = "بث مباشر"; +"room_no_privileges_to_create_group_call" = "يجب أن تكون مشرفًا أو مديرًا لبدء مكالمة."; +"external_link_confirmation_title" = "قم بالتحقق تكرارا من هذا الرابط"; +"unknown_devices_verify" = "تحقق…"; +"settings_links" = "روابط"; +"room_creation_only_one_email_invite" = "يمكنك دعوة بريد إلكتروني واحد فقط في كل مرة"; +"room_command_kick_user_description" = "يزيل المستخدم بالمعرف من هذه الغرفة"; +"room_command_ban_user_description" = "يمنع المستخدم بالمعرف المعطى"; +"room_command_unban_user_description" = "يلغي الحظر عن المستخدم بالمعرف المعطى"; +"room_command_reset_user_power_level_description" = "إلغاء صلاحيات المستخدم بالمعرف المعطى"; +"room_command_change_room_topic_description" = "تعيين موضوع الغرفة"; +"threads_title" = "مواضيع"; +"threads_action_all_threads" = "جميع المواضيع"; +"threads_notice_title" = "المواضيع لم تعد تجريبية 🎉"; +"threads_empty_show_all_threads" = "عرض جميع المواضيع"; +"threads_beta_enable" = "جربها"; +"media_type_accessibility_video" = "مَقطَع مَرئي"; +"media_type_accessibility_location" = "مَوقِع جُغرَافِيّ"; +"room_multiple_typing_notification" = "%@ وغيرهم"; + +// Unknown devices +"unknown_devices_alert_title" = "الغرفة تحتوي على جلسات مجهولة"; +"unknown_devices_send_anyway" = "أرسل على أي حال"; +"room_title_one_active_member" = "%@/%@ عضو نشط"; + +// Room Title +"room_title_new_room" = "غرفة جديدة"; + +// Settings +"settings_title" = "الإعدَادَات"; +"notice_error_unformattable_event" = "** غير قادر على عرض الرسالة. يُرجى الإبلاغ عن خلل"; +"authentication_qr_login_start_step3" = "حَّدِد 'اربط الجهاز'"; +"authentication_qr_login_start_title" = "امسح رمز الاستجابة السريعة"; +"authentication_qr_login_start_step4" = "حَّدِد 'إظهَر رمز الQR الموجود في الجهاز'"; +"authentication_qr_login_display_title" = "ربط جهاز"; +"authentication_qr_login_display_subtitle" = "امسح رمز الاستجابة السريعة أدناه باستخدام جهازك الذي تم تسجيل الخروج منه."; +"authentication_qr_login_display_step1" = "افتح تطبيق Element على جهازك الآخر"; +"authentication_qr_login_scan_title" = "امسح رمز الاستجابة السريعة"; +"authentication_qr_login_failure_request_denied" = "تم رفض الطلب على الجهاز الآخر."; +"authentication_qr_login_failure_request_timed_out" = "لم يتم الانتهاء من الربط في الوقت المطلوب."; +"authentication_qr_login_scan_subtitle" = "ضع رمز الاستجابة السريعة في المربع أدناه"; +"authentication_qr_login_confirm_subtitle" = "تأكد أن الرمز أدناه متطابق مع جهازك الآخر:"; +"authentication_qr_login_confirm_alert" = "يرجى التأكد من مصدر هذا الرمز. من خلال ربط الأجهزة، ستمنح الشخص الأخر الصلاحية الكاملة على حسابك."; +"room_creation_user_not_found_prompt_title" = "تَأكيد"; +"room_creation_user_not_found_prompt_message" = "تعذر العثور على الهوية على مُعَرِّف Matrix. هل ترغب في بدء رسالة مباشرة على أي حال؟"; +"room_creation_user_not_found_prompt_invite_action" = "ابدأ الرسالة المباشرة على أي حال"; +"room_participants_invite_unknown_participant_prompt_to_msg" = "تعذر العثور على هذه الهوية على مُعَرِّف Matrix. هل أنت متأكد أنك تريد دعوة %@ إلى %@؟"; +"room_participants_invite_anyway" = "ادعُ بأي حال"; +"threads_empty_tip" = "نصيحة: انقر فوق رسالة واستخدم 'موضوع' لبدء رسالة جديدة."; + +// Pills +"pill_room_fallback_display_name" = "المساحة/الغرفة"; +"room_command_discard_session_description" = "يجبر على تجاهل الجلسة الجماعية الصادرة حاليا في غرفة مشفرة"; +"room_command_error_unknown_command" = "أمر غير صالح أو غير معالَج"; +"threads_empty_info_all" = "المواضيع تساعد في الحفاظ على محادثاتك ذات موضوع معين وتسهل تتبعها."; +"threads_empty_info_my" = "الرد على موضوع قائم أو النقر فوق الرسالة واستخدم خاصية 'الموضوع' لبدء رِسَالَة جديد."; diff --git a/Riot/Assets/ca.lproj/InfoPlist.strings b/Riot/Assets/ca.lproj/InfoPlist.strings index 912a5bbab5..8ddc0866c0 100644 --- a/Riot/Assets/ca.lproj/InfoPlist.strings +++ b/Riot/Assets/ca.lproj/InfoPlist.strings @@ -1,8 +1,9 @@ // Permissions usage explanations -"NSCameraUsageDescription" = "La càmera s'utilitza per fer fotos i vídeos, fer vídeo conferència."; -"NSPhotoLibraryUsageDescription" = "La fototeca s'utilitza per enviar fotos i vídeos."; +"NSCameraUsageDescription" = "La càmera s'utilitza per a fer videotrucades, o per a fer i pujar fotos i vídeos."; +"NSPhotoLibraryUsageDescription" = "Permeteu l'accés a les fotos per a pujar fotos i vídeos de la galeria."; "NSMicrophoneUsageDescription" = "Element necessita accedir al vostre micròfon per a fer i rebre trucades, vídeos i gravar missatges de veu."; -"NSContactsUsageDescription" = "Element us mostrarà els vostres contactes per si els voleu convidar a xatejar."; -"NSLocationWhenInUseUsageDescription" = "Quan compartiu la vostra localització amb altres, Element en necessita accés per mostrar-lis un mapa."; -"NSFaceIDUsageDescription" = "Face ID es fa servir per accedir a la vostra app."; +"NSContactsUsageDescription" = "Es compartiran amb el vostre servidor d'identitat per ajudar-vos a trobar contactes al Matrix."; +"NSLocationWhenInUseUsageDescription" = "Quan compartiu la vostra ubicació, l'Element en necessita accés per mostrar-los un mapa."; +"NSFaceIDUsageDescription" = "Face ID es fa servir per a accedir a l'aplicació."; "NSCalendarsUsageDescription" = "Consulteu la vostra agenda de reunions a l'app."; +"NSLocationAlwaysAndWhenInUseUsageDescription" = "Quan compartiu la vostra ubicació, l'Element en necessita accés per a mostrar-los un mapa."; diff --git a/Riot/Assets/ca.lproj/Localizable.strings b/Riot/Assets/ca.lproj/Localizable.strings index 9cc1026d71..81b26ff426 100644 --- a/Riot/Assets/ca.lproj/Localizable.strings +++ b/Riot/Assets/ca.lproj/Localizable.strings @@ -1,7 +1,7 @@ /* New message from a specific person, not referencing a room */ "MSG_FROM_USER" = "%@ ha enviat un missatge"; /* New message from a specific person in a named room */ -"MSG_FROM_USER_IN_ROOM" = "%@ publicat a %@"; +"MSG_FROM_USER_IN_ROOM" = "%@ ha publicat a %@"; /* New message from a specific person, not referencing a room. Content included. */ "MSG_FROM_USER_WITH_CONTENT" = "%@: %@"; /* New message from a specific person in a named room. Content included. */ @@ -14,19 +14,19 @@ /* New action message from a specific person in a named room. */ "IMAGE_FROM_USER_IN_ROOM" = "%@ ha publicat una foto %@ a %@"; /* Multiple unread messages in a room */ -"UNREAD_IN_ROOM" = "%@ nous missatges a %@"; +"UNREAD_IN_ROOM" = "%@ missatges nous a %@"; /* Multiple unread messages from a specific person, not referencing a room */ -"MSGS_FROM_USER" = "%@ nou missatge a %@"; +"MSGS_FROM_USER" = "%@ missatge nou a %@"; /* Multiple unread messages from two people */ -"MSGS_FROM_TWO_USERS" = "%@ nous missatges de %@ i %@"; +"MSGS_FROM_TWO_USERS" = "%@ missatges nous de: %@ i %@"; /* Multiple unread messages from three people */ -"MSGS_FROM_THREE_USERS" = "%@ nous missatges de %@, %@ i %@"; +"MSGS_FROM_THREE_USERS" = "%@ missatges nous de: %@, %@ i %@"; /* Multiple unread messages from two plus people (ie. for 4+ people: 'others' replaces the third person) */ -"MSGS_FROM_TWO_PLUS_USERS" = "%@ nous missatges de %@, %@ i altres"; +"MSGS_FROM_TWO_PLUS_USERS" = "%@ missatges nous de: %@, %@ i altres"; /* Multiple messages in two rooms */ -"MSGS_IN_TWO_ROOMS" = "%@ nous missatges a %@ i %@"; +"MSGS_IN_TWO_ROOMS" = "%@ missatges nous a %@ i %@"; /* Look, stuff's happened, alright? Just open the app. */ -"MSGS_IN_TWO_PLUS_ROOMS" = "%@ nous missatges a %@, %@ i altres"; +"MSGS_IN_TWO_PLUS_ROOMS" = "%@ missatges nous a %@, %@ i altres"; /* A user has invited you to a chat */ "USER_INVITE_TO_CHAT" = "%@ t'ha convidat a xatejar"; /* A user has invited you to an (unamed) group chat */ @@ -34,17 +34,17 @@ /* A user has invited you to a named room */ "USER_INVITE_TO_NAMED_ROOM" = "%@ t'ha convidat a %@"; /* Incoming one-to-one voice call */ -"VOICE_CALL_FROM_USER" = "Trucada de %@"; +"VOICE_CALL_FROM_USER" = "Trucada de: %@"; /* Incoming one-to-one video call */ -"VIDEO_CALL_FROM_USER" = "Vídeo trucada de %@"; +"VIDEO_CALL_FROM_USER" = "Videotrucada de: %@"; /* Incoming unnamed voice conference invite from a specific person */ -"VOICE_CONF_FROM_USER" = "Trucada grupal de %@"; +"VOICE_CONF_FROM_USER" = "Trucada grupal de: %@"; /* Incoming unnamed video conference invite from a specific person */ -"VIDEO_CONF_FROM_USER" = "Vídeo trucada grupal de %@"; +"VIDEO_CONF_FROM_USER" = "Videotrucada grupal de: %@"; /* Incoming named voice conference invite from a specific person */ -"VOICE_CONF_NAMED_FROM_USER" = "Trucada grupal de %@: '%@'"; +"VOICE_CONF_NAMED_FROM_USER" = "Trucada grupal de: %@: '%@'"; /* Incoming named video conference invite from a specific person */ -"VIDEO_CONF_NAMED_FROM_USER" = "Vídeo trucada grupal de %@: '%@'"; +"VIDEO_CONF_NAMED_FROM_USER" = "Videotrucada grupal de: %@: '%@'"; /* A single unread message in a room */ "SINGLE_UNREAD_IN_ROOM" = "Has rebut un missatge a %@"; /* A single unread message */ @@ -56,7 +56,7 @@ /* New message indicator on unknown room */ "MESSAGE" = "Missatge"; /* New message indicator from a DM */ -"MESSAGE_FROM_X" = "Missatge de %@"; +"MESSAGE_FROM_X" = "Missatge de: %@"; /* New message indicator on a room */ "MESSAGE_IN_X" = "Missatge a %@"; "KEY_VERIFICATION_REQUEST_FROM_USER" = "%@ vol verificar"; @@ -90,10 +90,10 @@ "REACTION_FROM_USER" = "%@ ha reaccionat amb %@"; /* New message with hidden content due to PIN enabled */ -"MESSAGE_PROTECTED" = "Nou missatge"; +"MESSAGE_PROTECTED" = "Missatge nou"; /* New file message from a specific person, not referencing a room. */ -"LOCATION_FROM_USER" = "%@ ha compartit la localització"; +"LOCATION_FROM_USER" = "%@ ha compartit la ubicació"; /* New file message from a specific person, not referencing a room. */ "FILE_FROM_USER" = "%@ ha enviat un fitxer %@"; @@ -113,10 +113,13 @@ "PICTURE_FROM_USER" = "%@ ha enviat una foto"; /* New message reply from a specific person in a named room. */ -"REPLY_FROM_USER_IN_ROOM_TITLE" = "%@ respost a %@"; +"REPLY_FROM_USER_IN_ROOM_TITLE" = "%@ ha respost a %@"; /* New message reply from a specific person, not referencing a room. */ -"REPLY_FROM_USER_TITLE" = "%@ respost"; +"REPLY_FROM_USER_TITLE" = "%@ ha respost"; /** General **/ "Notification" = "Notificació"; + +/* New voice broadcast from a specific person, not referencing a room. */ +"VOICE_BROADCAST_FROM_USER" = "%@ ha iniciat una retransmissió de veu"; diff --git a/Riot/Assets/ca.lproj/Vector.strings b/Riot/Assets/ca.lproj/Vector.strings index ddceb0f203..592a5ad4db 100644 --- a/Riot/Assets/ca.lproj/Vector.strings +++ b/Riot/Assets/ca.lproj/Vector.strings @@ -5,23 +5,23 @@ "title_rooms" = "Sales"; "warning" = "Avís"; // Actions -"view" = "Veure"; +"view" = "Mostra"; "next" = "Següent"; "back" = "Torna"; "continue" = "Continua"; -"create" = "Crear"; -"start" = "Començar"; -"leave" = "Sortir"; -"remove" = "Esborrar"; +"create" = "Crea"; +"start" = "Comença"; +"leave" = "Surt"; +"remove" = "Suprimeix"; "invite" = "Convida"; "retry" = "Torna a provar"; -"on" = "Activa"; -"off" = "Apaga"; -"cancel" = "Cancel·lar"; -"save" = "Desar"; -"join" = "Entrar"; -"decline" = "Declinar"; -"accept" = "Acceptar"; +"on" = "Activat"; +"off" = "Desactivat"; +"cancel" = "Cancel·la"; +"save" = "Desa"; +"join" = "Entra"; +"decline" = "Rebutja"; +"accept" = "Accepta"; "preview" = "Vista prèvia"; "camera" = "Càmera"; "voice" = "Veu"; @@ -29,10 +29,10 @@ "active_call" = "Trucada en curs"; "active_call_details" = "Trucada en curs (%@)"; "later" = "Més tard"; -"rename" = "Reanomenar"; -"collapse" = "Contraure"; -"send_to" = "Enviar a %@"; -"sending" = "Enviant"; +"rename" = "Canvia el nom"; +"collapse" = "contrau"; +"send_to" = "Envia a %@"; +"sending" = "S'està enviant"; // Authentication "auth_login" = "Iniciar sessió"; "auth_register" = "Registre"; @@ -551,7 +551,7 @@ // String for App Store "store_short_description" = "Xat/VoIP segur i descentralitzat"; "close" = "Tanca"; -"skip" = "Salta"; +"skip" = "Omet"; "joined" = "Unit"; "switch" = "Canvia"; "more" = "Més"; @@ -562,7 +562,6 @@ // MARK: - MatrixKit - "matrix" = "Matrix"; "login_password_placeholder" = "Contrasenya"; "login_optional_field" = "opcional"; @@ -909,3 +908,43 @@ "ssl_unexpected_existing_expl" = "El certificat ha canviat respecte al que el teu telèfon hi havia confiat. Això es MOLT INUSUAL. Es recomana que NO ACCEPTIS aquest nou certificat."; "ssl_expected_existing_expl" = "El certificat ha canviat del prèviament confiat a un que no es confiable. El servidor pot haver renovat el certificat. Posa't en contacte amb l'administrador del servidor per obtenir l'empremta digital desitjada."; "ssl_only_accept" = "NOMÉS accepteu el certificat si l'administrador del servidor ha publicat una empremta digital que coincideixi amb l'anterior."; +"store_full_description" = "Element és un tipus nou d'aplicació de missatgeria i col·laboració que:\n\n1. Us dona el control per a preservar la vostra privadesa\n2. Us permet comunicar-vos amb qualsevol persona de la xarxa Matrix, i encara més enllà integrant aplicacions com ara Slack\n3. Us protegeix de la publicitat, mineria de dades, portes ocultes i jardins emmurallats\n4. Us salvaguarda mitjançant el xifratge extrem a extrem, amb signatura creuada per a la verificació dels altres\n\nElement és completament diferent d'altres aplicacions de missatgeria i col·laboració perquè és descentralitzada i de codi obert.\n\nElement us permet muntar-vos un servidor propi (o triar un amfitrió) perquè tingueu privadesa i el control sobre les dades i les converses. Us dona accés a una xarxa oberta, no parlareu només amb altres usuaris d'Element. I és molt segura.\n\nElement pot fer tot això perquè opera en Matrix, l'estàndard de les comunicacions obertes i descentralitzades.\n\nElement us dona el control, permetent-vos triar qui és l'amfitrió de les vostres converses. Des de l'aplicació d'Element podeu triar l'amfitrió de maneres diferents:\n\n1. Creeu un compte gratuït al servidor públic de matrix.org\n2. Munteu un servidor local per a acollir el vostre compte\n3. Creeu un compte en un servidor personalitzat subscrivint-vos a la plataforma d'allotjament d'Element Matrix Services \n\nPer què triar Element?\n\nCONTROLEU LES VOSTRES DADES: Vós decidiu on desar les vostres dades i missatges. En sou el propietari i les controleu, no cap MEGACORPORACIÓ que les mina les vostres dades i permet que terceres parts hi accedeixen.\n\nCOL·LABORACIÓ I MISSATGERIA OBERTA: Podeu xatejar amb qualsevol a la xarxa Matrix, tant si utilitzen Element com qualsevol altra aplicació Matrix, i fins i tot si utilitzen algun sistema de missatgeria diferent, com ara Slack, IRC o XMPP.\n\nSUPERSEGURA: Xifratge extrem a extrem real (només els participants de la conversa poden desxifrar els missatges), i signatura creuada per a verificar els dispositius dels participants de la conversa.\n\nCOMUNICACIÓ COMPLETA: Missatges, trucades d'àudio i vídeo, compartició de fitxers, de pantalla i moltes altres integracions, bots i ginys. Construïu sales, comunitats, estigueu en contacte i aconseguir que les coses es facin.\n\nESTIGUEU ON ESTIGUEU: Comuniqueu-vos estigueu on estigueu amb l'historial de missatges completament sincronitzat a tots els dispositius i al web, a https://app.element.io."; +"store_promotional_text" = "Aplicació de col·laboració i missatgeria que preserva la privadesa, en una xarxa oberta. Descentralitzada per a donar-vos el control. Sense mineria de dades, portes ocultes ni accés a tercers."; + +// MARK: Onboarding +"onboarding_splash_register_button_title" = "Crea un compte"; +"onboarding_splash_page_3_message" = "Xifrat extrem a extrem i no cal cap número de telèfon. Sense publicitat ni mineria de dades."; +"confirm" = "Confirma"; +"callbar_only_multiple_paused" = "%@ trucades en espera"; +"existing" = "Existent"; +"add" = "Afegeix"; +"joining" = "Unió"; +"stop" = "Atura"; +"new_word" = "Nou"; +"suggest" = "Suggereix"; +"edit" = "Edita"; + +// Activities +"loading" = "S'està carregant"; +"saving" = "S'està desant"; +"accessibility_button_label" = "botó"; +"onboarding_splash_login_button_title" = "Ja tinc un compte"; +"onboarding_splash_page_1_title" = "Controleu les vostres converses."; +"onboarding_splash_page_1_message" = "Comunicació independent i segura que us dona el mateix nivell de privadesa que una conversa cara a cara en casa."; +"onboarding_splash_page_2_title" = "Sou en control."; +"onboarding_splash_page_2_message" = "Trieu on es desen les vostres converses, el que us dona control i independència. Connectat via Matrix."; +"onboarding_splash_page_3_title" = "Missatgeria segura."; +"open" = "Obre"; +"callbar_return" = "Torna"; +"invite_to" = "Convida a %@"; +"callbar_only_single_active_group" = "Toqueu per a unir-vos a la trucada grupal (%@)"; +"callbar_active_and_multiple_paused" = "1 trucada activa (%@) · %@ trucades en espera"; +"callbar_only_single_paused" = "Trucada en espera"; +"enable" = "Activa"; +"done" = "Fet"; + +// Call Bar +"callbar_only_single_active" = "Toqueu per a tornar a la trucada (%@)"; +"callbar_active_and_single_paused" = "1 trucada activa (%@) · 1 trucada en espera"; +"accessibility_selected" = "seleccionat"; +"less" = "Menys"; diff --git a/Riot/Assets/vi.lproj/Vector.strings b/Riot/Assets/vi.lproj/Vector.strings index d7449ba3f7..dd731de4d2 100644 --- a/Riot/Assets/vi.lproj/Vector.strings +++ b/Riot/Assets/vi.lproj/Vector.strings @@ -135,8 +135,8 @@ "directory_cell_title" = "Duyệt danh mục"; "directory_cell_description" = "%tu phòng"; "directory_search_results_title" = "Kết quả duyệt danh mục"; -"directory_search_results" = "%tu kết quả được tìm thấy cho %@"; -"directory_search_results_more_than" = ">%tu kết quả được tìm thấy cho %@"; +"directory_search_results" = "%1$tu kết quả được tìm thấy cho %2$@"; +"directory_search_results_more_than" = ">%1$tu kết quả được tìm thấy cho %2$@"; "directory_searching_title" = "Đang tìm danh mục…"; "directory_search_fail" = "Không thể tìm nạp dữ liệu"; // Contacts diff --git a/Riot/Assets/zh_Hans.lproj/Vector.strings b/Riot/Assets/zh_Hans.lproj/Vector.strings index 1c6c33e5fa..79177b974e 100644 --- a/Riot/Assets/zh_Hans.lproj/Vector.strings +++ b/Riot/Assets/zh_Hans.lproj/Vector.strings @@ -311,7 +311,7 @@ "room_details_access_section_anyone" = "任何知道此房间链接的人,包括访客"; "room_details_access_section_no_address_warning" = "要链接一个房间必须设置地址"; "room_details_access_section_directory_toggle" = "将此房间列入房间目录"; -"room_details_history_section" = "谁可以阅读历史?"; +"room_details_history_section" = "谁可以读取历史?"; "room_details_history_section_anyone" = "任何人"; "room_details_history_section_members_only" = "只有成员(从选择这个选项的时间开始)"; "room_details_history_section_members_only_since_invited" = "只有成员(从他们被邀请开始)"; @@ -2275,7 +2275,7 @@ "user_session_learn_more" = "了解更多"; "manage_session_name_info_link" = "了解更多"; "threads_beta_information_link" = "了解更多"; -"authentication_qr_login_display_subtitle" = "用你登出的设备扫描下面的QR码。"; +"authentication_qr_login_display_subtitle" = "用你已登出的设备扫描下面的QR码。"; "room_invite_to_space_option_detail" = "他们可以探索 %@,但不会成为 %@ 的成员。"; "analytics_prompt_message_new_user" = "通过分享匿名的使用数据,帮助我们识别问题并改进 %@ 。为了了解人们如何使用多个设备,我们将生成一个随机的标识符,由你的设备共享。"; "threads_notice_done" = "知道了"; @@ -2367,3 +2367,41 @@ "settings_presence_offline_mode" = "离线模式"; "room_details_polls" = "投票历史"; "settings_labs_enable_new_app_layout" = "新版应用布局"; +"room_details_access_row_title" = "访问"; +"room_access_settings_screen_restricted_message" = "让空间中的任何人都能找到并加入。\n将要求你确认哪些空间。"; +"room_access_settings_screen_upgrade_required" = "需要升级"; +"room_participants_invite_unknown_participant_prompt_to_msg" = "未找到该 Matrix ID 的配置文件。您确定要邀请 %@ 到 %@?"; +"room_access_settings_screen_upgrade_alert_auto_invite_switch" = "自动邀请成员到新房间"; +"room_preview_decline_invitation_options" = "您想拒绝邀请还是忽略该用户?"; +"room_access_settings_screen_title" = "谁能进入此房间?"; +"room_access_settings_screen_message" = "决定谁能找到并加入 %@。"; +"room_access_settings_screen_edit_spaces" = "编辑空间"; +"room_access_settings_screen_public_message" = "任何人都可以找到并加入。"; +"settings_presence" = "存在"; +"room_details_promote_room_suggest_title" = "推荐给空间成员"; +"settings_push_rules_error" = "更新您的个性化通知时发生错误。请再试一次。"; +"room_access_settings_screen_private_message" = "只有受邀者才能找到并加入。"; +"room_access_settings_screen_upgrade_alert_message" = "%@ 中的任何人都能找到并加入此房间,无需手动邀请所有人。您可随时在房间设置中进行更改。"; +"room_access_settings_screen_upgrade_alert_note" = "请注意,升级后将创建一个新版本的房间。当前所有消息都将保留在此归档房间中。"; +"room_access_settings_screen_upgrade_alert_upgrade_button" = "升级"; +"settings_acceptable_use" = "可接受使用政策"; +"room_command_reset_user_power_level_description" = "删除指定 id 的用户"; +"settings_manage_account_title" = "账户"; +"settings_manage_account_action" = "管理账户"; +"settings_manage_account_description" = "在 %@ 管理您的账户"; +"manage_session_redirect" = "你将被重定向到服务器的验证提供者以完成登出。"; +"manage_session_redirect_error" = "功能目前不可用。请联系你的家服务器管理员"; +"room_details_promote_room_title" = "推广房间"; + +// Room Access Settings +"room_access_settings_screen_nav_title" = "进入房间"; +"room_access_settings_screen_upgrade_alert_title" = "升级房间"; +"room_access_settings_screen_upgrade_alert_message_no_param" = "上级房间中的任何人都能找到并加入此房间,无需手动邀请所有人。您可随时在房间设置中进行更改。"; +"room_access_settings_screen_upgrade_alert_upgrading" = "升级房间"; +"room_access_settings_screen_setting_room_access" = "设置房间权限"; + +// Room suggestion Settings +"room_suggestion_settings_screen_nav_title" = "建议房间"; +"room_access_space_chooser_known_spaces_section" = "您知道的包含 %@ 的空间"; +"room_access_space_chooser_other_spaces_section_info" = "这些很可能是 %@ 的管理员参与。"; +"room_access_space_chooser_other_spaces_section" = "其他空间或房间"; diff --git a/Riot/Managers/Widgets/WidgetManager.m b/Riot/Managers/Widgets/WidgetManager.m index 3595eb4df5..31f33e4074 100644 --- a/Riot/Managers/Widgets/WidgetManager.m +++ b/Riot/Managers/Widgets/WidgetManager.m @@ -412,6 +412,13 @@ - (void)addMatrixSession:(MXSession *)mxSession // Broadcast the generic notification [[NSNotificationCenter defaultCenter] postNotificationName:kWidgetManagerDidUpdateWidgetNotification object:widget]; + // End jitsi call if a active call exists and widget has been updated to not be active + if ([[AppDelegate theDelegate].callPresenter.jitsiVC.widget.widgetId isEqualToString: widget.widgetId] && + [[AppDelegate theDelegate].callPresenter.jitsiVC.widget.roomId isEqualToString: event.roomId] && + !widget.isActive) + { + [[AppDelegate theDelegate].callPresenter endActiveJitsiCall]; + } } else { diff --git a/Riot/Modules/Integrations/Widgets/Jitsi/JitsiViewController.m b/Riot/Modules/Integrations/Widgets/Jitsi/JitsiViewController.m index 71d5283318..baa8907d46 100644 --- a/Riot/Modules/Integrations/Widgets/Jitsi/JitsiViewController.m +++ b/Riot/Modules/Integrations/Widgets/Jitsi/JitsiViewController.m @@ -321,26 +321,25 @@ - (void)conferenceJoined:(NSDictionary *)data - (void)conferenceTerminated:(NSDictionary *)data { + // If the call is terminated by a moderator the error key contains the "conference.destroyed" value if (data[kJitsiDataErrorKey] != nil) { MXLogDebug(@"[JitsiViewController] conferenceTerminated - data: %@", data); } - else - { - dispatch_async(dispatch_get_main_queue(), ^{ - - // The conference is over. Let the delegate close this view controller. - if (self.delegate) - { - [self.delegate jitsiViewController:self dismissViewJitsiController:nil]; - } - else - { - // Do it ourself - [self dismissViewControllerAnimated:YES completion:nil]; - } - }); - } + + dispatch_async(dispatch_get_main_queue(), ^{ + + // The conference is over. Let the delegate close this view controller. + if (self.delegate) + { + [self.delegate jitsiViewController:self dismissViewJitsiController:nil]; + } + else + { + // Do it ourself + [self dismissViewControllerAnimated:YES completion:nil]; + } + }); } - (void)enterPictureInPicture:(NSDictionary *)data diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift b/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift index 819eb632fd..6c7e43a90a 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift @@ -47,7 +47,9 @@ class HTMLFormatter: NSObject { var options: [AnyHashable: Any] = [ DTUseiOS6Attributes: true, - DTDefaultFontDescriptor: font.fontDescriptor, + DTDefaultFontFamily: font.familyName, + DTDefaultFontName: font.fontName, + DTDefaultFontSize: font.pointSize, DTDefaultLinkDecoration: false, DTDefaultLinkColor: ThemeService.shared().theme.colors.links, DTWillFlushBlockCallBack: sanitizeCallback diff --git a/Riot/Modules/Room/EventMenu/EventMenuItemType.swift b/Riot/Modules/Room/EventMenu/EventMenuItemType.swift index c15d2fbdbe..c8893858ee 100644 --- a/Riot/Modules/Room/EventMenu/EventMenuItemType.swift +++ b/Riot/Modules/Room/EventMenu/EventMenuItemType.swift @@ -25,7 +25,6 @@ enum EventMenuItemType: Int { case cancelSending case cancelDownloading case saveMedia - case quote case forward case permalink case share diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index ee118fcaf4..37cc5a7660 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -4056,26 +4056,6 @@ - (void)showAdditionalActionsMenuForEvent:(MXEvent*)selectedEvent inCell:(id%@\n\n", prefix, selectedComponent.textMessage]; - - // And display the keyboard - [self.inputToolbarView becomeFirstResponder]; - }]]; - } if (selectedEvent.sentState == MXEventSentStateSent && !selectedEvent.isTimelinePollEvent && @@ -8053,7 +8033,7 @@ - (void)removeJitsiWidgetViewDidCompleteSliding:(RemoveJitsiWidgetView *)view self.removeJitsiWidgetView.delegate = nil; // end active call if exists - if ([self isRoomHavingAJitsiCallForWidgetId:jitsiWidget.widgetId]) + if ([self isRoomHavingAJitsiCall]) { [self endActiveJitsiCall]; } diff --git a/Riot/Modules/Room/RoomViewController.swift b/Riot/Modules/Room/RoomViewController.swift index 727ca8f806..3468ffe304 100644 --- a/Riot/Modules/Room/RoomViewController.swift +++ b/Riot/Modules/Room/RoomViewController.swift @@ -200,15 +200,7 @@ extension RoomViewController { optionalTextView?.becomeFirstResponder() originalRect = wysiwygInputToolbar.convert(wysiwygInputToolbar.frame, to: view) } - // This tirggers a SwiftUI update that is handled correctly on iOS 16, but needs to be dispatchted async on older versions - // Dispatching on iOS 16 instead causes some weird SwiftUI update behaviours - if #available(iOS 16, *) { - wysiwygInputToolbar.showKeyboard() - } else { - DispatchQueue.main.async { - wysiwygInputToolbar.showKeyboard() - } - } + roomInputToolbarContainer.removeFromSuperview() let dimmingView = UIView() dimmingView.translatesAutoresizingMaskIntoConstraints = false @@ -235,7 +227,18 @@ extension RoomViewController { } let panGesture = UIPanGestureRecognizer(target: self, action: #selector(didPanRoomToolbarContainer(_ :))) roomInputToolbarContainer.addGestureRecognizer(panGesture) - optionalTextView?.removeFromSuperview() + if let optionalTextView { + // This tirggers a SwiftUI update that is handled correctly on iOS 16, but needs to be dispatchted async on older versions + // Dispatching on iOS 16 instead causes some weird SwiftUI update behaviours + if #available(iOS 16, *) { + wysiwygInputToolbar.showKeyboard() + } else { + DispatchQueue.main.async { + wysiwygInputToolbar.showKeyboard() + } + } + optionalTextView.removeFromSuperview() + } } else { let originalRect = wysiwygInputToolbar.convert(wysiwygInputToolbar.frame, to: view) var optionalTextView: UITextView? @@ -244,7 +247,6 @@ extension RoomViewController { optionalTextView = textView self.view.window?.addSubview(textView) optionalTextView?.becomeFirstResponder() - wysiwygInputToolbar.showKeyboard() } self.roomInputToolbarContainer.removeFromSuperview() maximisedToolbarDimmingView?.removeFromSuperview() @@ -257,7 +259,10 @@ extension RoomViewController { self.view.layoutIfNeeded() } roomInputToolbarContainer.gestureRecognizers?.removeAll() - optionalTextView?.removeFromSuperview() + if let optionalTextView { + wysiwygInputToolbar.showKeyboard() + optionalTextView.removeFromSuperview() + } } } @@ -384,8 +389,8 @@ extension RoomViewController: ComposerLinkActionBridgePresenterDelegate { } // MARK: - PermalinkReplacer -extension RoomViewController: PermalinkReplacer { - public func replacementForLink(_ url: String, text: String) -> NSAttributedString? { +extension RoomViewController: MentionReplacer { + public func replacementForMention(_ url: String, text: String) -> NSAttributedString? { guard #available(iOS 15.0, *), let url = URL(string: url), let session = roomDataSource.mxSession, diff --git a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift index 60b6edf943..864482dccf 100644 --- a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift +++ b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift @@ -140,6 +140,20 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp wysiwygViewModel.maxCompressedHeight } + override func paste(_ sender: Any?) { + let pasteboard = MXKPasteboardManager.shared.pasteboard + let types = pasteboard.types.map { UTI(rawValue: $0) } + + // Minimise the composer and dismiss the keyboard if it's an image, a video or a file + if types.contains(where: { $0.conforms(to: .image) || $0.conforms(to: .movie) || $0.conforms(to: .video) || $0.conforms(to: .application) }) { + wysiwygViewModel.maximised = false + DispatchQueue.main.async { + self.viewModel.dismissKeyboard() + } + } + super.paste(sender) + } + // MARK: - Setup override class func instantiate() -> MXKRoomInputToolbarView! { @@ -150,8 +164,8 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp return (delegate as? RoomInputToolbarViewDelegate) ?? nil } - private var permalinkReplacer: PermalinkReplacer? { - return (delegate as? PermalinkReplacer) + private var permalinkReplacer: MentionReplacer? { + return (delegate as? MentionReplacer) } override func awakeFromNib() { @@ -192,6 +206,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp } func showKeyboard() { + self.wysiwygViewModel.textView.becomeFirstResponder() self.viewModel.showKeyboard() } @@ -238,7 +253,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp self?.handleViewModelResult(result) } wysiwygViewModel.plainTextMode = !RiotSettings.shared.enableWysiwygTextFormatting - wysiwygViewModel.permalinkReplacer = permalinkReplacer + wysiwygViewModel.mentionReplacer = permalinkReplacer inputAccessoryViewForKeyboard = UIView(frame: .zero) diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index 6f9c22465f..39698f765f 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -340,6 +340,11 @@ - (void)finalizeInit self.screenTracker = [[AnalyticsScreenTracker alloc] initWithScreen:AnalyticsScreenSettings]; } +- (void)dealloc { + // Fix for destroy not being called + [self destroy]; +} + - (void)updateSections { NSMutableArray *tmpSections = [NSMutableArray arrayWithCapacity:SECTION_TAG_DEACTIVATE_ACCOUNT + 1]; diff --git a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift index 33d73ef4a2..f0b3a37804 100644 --- a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift +++ b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift @@ -156,7 +156,7 @@ extension FormatItem { extension FormatType { /// Convenience method to map it to the external ViewModel action - var action: WysiwygAction { + var action: ComposerAction { switch self { case .bold: return .bold diff --git a/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift b/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift index a74b0bb4d6..f2a60ce066 100644 --- a/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift +++ b/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift @@ -32,6 +32,7 @@ struct Composer: View { @Environment(\.theme) private var theme: ThemeSwiftUI @State private var isActionButtonShowing = false + @FocusState private var focused: Bool private let horizontalPadding: CGFloat = 12 private let borderHeight: CGFloat = 40 @@ -58,17 +59,8 @@ struct Composer: View { viewModel.viewState.shouldDisplayContext ? contextBannerHeight + standardVerticalPadding + verticalComponentSpacing : 0 } - /// Computes the total height of the composer (excluding the RTE formatting bar). - /// This height includes the text view, as well as the context banner - /// and user suggestion list when displayed. - private var composerHeight: CGFloat { - wysiwygViewModel.idealHeight - + composerTopPadding - + composerVerticalPadding - // Extra padding added on top of the VStack containing the composer - + standardVerticalPadding - + additionalHeightForContextBanner - } + /// the total height of the composer (excluding the RTE formatting bar). + @State private var composerHeight: CGFloat = .zero private var cornerRadius: CGFloat { if shouldFixRoundCorner { @@ -139,20 +131,39 @@ struct Composer: View { .padding(.horizontal, horizontalPadding) } HStack(alignment: shouldFixRoundCorner ? .top : .center, spacing: 0) { - WysiwygComposerView( - focused: $viewModel.focused, - viewModel: wysiwygViewModel - ) - .tintColor(theme.colors.accent) - .placeholder(viewModel.viewState.placeholder, color: theme.colors.tertiaryContent) - .onAppear { - if wysiwygViewModel.isContentEmpty { - wysiwygViewModel.setup() + // Use a GeometryReader to force the composer to fill the HStack + GeometryReader { _ in + WysiwygComposerView( + placeholder: viewModel.viewState.placeholder ?? "", + viewModel: wysiwygViewModel, + itemProviderHelper: nil, + keyCommandHandler: handleKeyCommand, + pasteHandler: nil + ) + .clipped() + .tint(theme.colors.accent) + .focused($focused) + .onChange(of: focused) { newValue in + viewModel.focused = newValue + } + .onChange(of: viewModel.focused) { newValue in + guard focused != newValue else { return } + focused = newValue + } + .onAppear { + if wysiwygViewModel.isContentEmpty { + wysiwygViewModel.setup() + } } } + if !viewModel.viewState.isMinimiseForced { Button { - wysiwygViewModel.maximised.toggle() + viewModel.focused = true + // Use a dispatched block so the focus state will be up to date when the composer size changes. + DispatchQueue.main.async { + wysiwygViewModel.maximised.toggle() + } } label: { Image(toggleButtonImageName) .resizable() @@ -167,15 +178,14 @@ struct Composer: View { .padding(.horizontal, horizontalPadding) .padding(.top, composerTopPadding) .padding(.bottom, composerVerticalPadding) + .layoutPriority(1) } .clipShape(rect) .overlay(rect.stroke(borderColor, lineWidth: 1)) .animation(.easeInOut(duration: resizeAnimationDuration), value: wysiwygViewModel.idealHeight) .padding(.top, standardVerticalPadding) .onTapGesture { - if viewModel.focused { - viewModel.focused = true - } + viewModel.focused = true } } @@ -218,6 +228,29 @@ struct Composer: View { } } + func handleKeyCommand(_ keyCommand: WysiwygKeyCommand) -> Bool { + switch keyCommand { + case .enter: + sendMessageAction(wysiwygViewModel.content) + wysiwygViewModel.clearContent() + return true + case .shiftEnter: + return false + } + } + + /// Computes the total height of the composer (excluding the RTE formatting bar). + /// This height includes the text view, as well as the context banner + /// and user suggestion list when displayed. + private func updateComposerHeight(idealHeight: CGFloat) { + composerHeight = idealHeight + + composerTopPadding + + composerVerticalPadding + // Extra padding added on top of the VStack containing the composer + + standardVerticalPadding + + additionalHeightForContextBanner + } + // MARK: Public init( @@ -287,6 +320,15 @@ struct Composer: View { .onChange(of: wysiwygViewModel.suggestionPattern) { newValue in sendMentionPattern(pattern: newValue) } + .onChange(of: wysiwygViewModel.idealHeight) { newValue in + updateComposerHeight(idealHeight: newValue) + } + .onChange(of: viewModel.viewState.shouldDisplayContext) { _ in + updateComposerHeight(idealHeight: wysiwygViewModel.idealHeight) + } + .task { + updateComposerHeight(idealHeight: wysiwygViewModel.idealHeight) + } } private func storeCurrentSelection() { diff --git a/project.yml b/project.yml index 430b39cbc0..a2f34eaa1e 100644 --- a/project.yml +++ b/project.yml @@ -59,10 +59,10 @@ packages: branch: 0.0.1 WysiwygComposer: url: https://github.com/matrix-org/matrix-wysiwyg-composer-swift - version: 2.2.2 + version: 2.18.0 DeviceKit: url: https://github.com/devicekit/DeviceKit majorVersion: 4.7.0 DTCoreText: url: https://github.com/Cocoanetics/DTCoreText - version: 1.6.27 + version: 1.6.26