chore: Update to FOSS 1.11.31 (MESSENGER-7610)

Merge commit '822cbc5076da248fa7b997a5e3e906b03c4a09f7' into feature/7610_FOSS_Merge_1_11_31

# Conflicts:
#	Config/AppVersion.xcconfig
#	Podfile
#	Podfile.lock
#	README.md
#	Riot/Modules/Common/Recents/RecentsViewController.m
#	Riot/Modules/ContextMenu/Services/RoomContextActionService.swift
#	Riot/Modules/Room/Members/RoomParticipantsViewController.m
#	Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift
#	fastlane/Fastfile
This commit is contained in:
Frank Rotermund
2025-08-27 07:39:26 +02:00
61 changed files with 1278 additions and 363 deletions

View File

@@ -56,6 +56,9 @@ jobs:
run: |
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
- name: Xcodegen
run: mint run yonaskolb/XcodeGen@2.39.0
# Main step
- name: Unit tests

View File

@@ -51,6 +51,9 @@ jobs:
run: |
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
- name: Xcodegen
run: mint run yonaskolb/XcodeGen@2.39.0
# Main step
- name: UI tests

View File

@@ -1,3 +1,2 @@
brew "xcodegen"
brew "mint"
brew "getsentry/tools/sentry-cli"

View File

@@ -1,3 +1,36 @@
## Changes in 1.11.31 (2025-07-28)
✨ Features
- Support for MSC 4289. ([#7950](https://github.com/element-hq/element-ios/pull/7950))
🙌 Improvements
- PL 150 users will be displayed as Owners. ([#7951](https://github.com/element-hq/element-ios/issues/7951))
- Owners can't leave the room if they are the last owners while also not being the last member. ([#7952](https://github.com/element-hq/element-ios/issues/7952))
🐛 Bugfixes
- Room continuity fix for room version 12. ([#7953](https://github.com/element-hq/element-ios/pull/7953))
## Changes in 1.11.30 (2025-06-10)
No significant changes.
## Changes in 1.11.29 (2025-05-29)
No significant changes.
## Changes in 1.11.28 (2025-05-28)
🙌 Improvements
- Support for experimental MSC4286 during event rendering. ([#7927](https://github.com/element-hq/element-ios/pull/7927))
## Changes in 1.11.27 (2025-03-28)
No significant changes.

View File

@@ -16,6 +16,6 @@
//
// Version
MARKETING_VERSION = 2.25.0
CURRENT_PROJECT_VERSION = 20220714163152
MARKETING_VERSION = 2.25.1
CURRENT_PROJECT_VERSION = 20220714163152

View File

@@ -26,17 +26,18 @@ GEM
artifactory (3.0.17)
atomos (0.1.3)
aws-eventstream (1.3.2)
aws-partitions (1.1063.0)
aws-sdk-core (3.220.1)
aws-partitions (1.1107.0)
aws-sdk-core (3.224.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
base64
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.99.0)
logger
aws-sdk-kms (1.101.0)
aws-sdk-core (~> 3, >= 3.216.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.182.0)
aws-sdk-s3 (1.186.1)
aws-sdk-core (~> 3, >= 3.216.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
@@ -90,13 +91,13 @@ GEM
commander (4.6.0)
highline (~> 2.0.0)
concurrent-ruby (1.3.5)
connection_pool (2.5.0)
connection_pool (2.5.3)
declarative (0.0.20)
digest-crc (0.7.0)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.6.20240107)
dotenv (2.8.1)
drb (2.2.1)
drb (2.2.3)
emoji_regex (3.2.3)
escape (0.0.4)
ethon (0.16.0)
@@ -131,7 +132,7 @@ GEM
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.4.0)
fastlane (2.226.0)
fastlane (2.227.2)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@@ -171,17 +172,17 @@ GEM
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.4.0)
xcpretty (~> 0.4.1)
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-plugin-brew (0.1.1)
fastlane-plugin-sentry (1.28.0)
fastlane-plugin-sentry (1.29.0)
os (~> 1.1, >= 1.1.4)
fastlane-plugin-versioning (0.7.1)
fastlane-plugin-xcodegen (1.1.0)
fastlane-plugin-brew (~> 0.1.1)
fastlane-sirp (1.0.0)
sysrandom (~> 1.0)
ffi (1.17.1)
ffi (1.17.2)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
@@ -229,14 +230,14 @@ GEM
i18n (1.14.7)
concurrent-ruby (~> 1.0)
jmespath (1.6.2)
json (2.10.1)
json (2.12.2)
jwt (2.10.1)
base64
logger (1.6.6)
logger (1.7.0)
mini_magick (4.13.2)
mini_mime (1.1.5)
mini_portile2 (2.8.8)
minitest (5.25.4)
mini_portile2 (2.8.9)
minitest (5.25.5)
molinillo (0.8.0)
multi_json (1.15.0)
multipart-post (2.4.1)
@@ -307,7 +308,7 @@ GEM
colored2 (~> 3.1)
nanaimo (~> 0.4.0)
rexml (>= 3.3.6, < 4.0)
xcpretty (0.4.0)
xcpretty (0.4.1)
rouge (~> 3.28.0)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)

View File

@@ -69,7 +69,6 @@ abstract_target 'RiotPods' do
# Piwik for analytics
pod 'MatomoTracker', '~> 7.5.2'
pod 'zxcvbn-ios'
# Tools

View File

@@ -14,49 +14,43 @@ PODS:
- AFNetworking/Serialization (4.0.1)
- AFNetworking/UIKit (4.0.1):
- AFNetworking/NSURLSession
- BlueCryptor (1.0.32)
- BlueECC (1.2.5)
- BlueRSA (1.0.200)
- Down (0.11.0)
- DSWaveformImage (6.1.1)
- DTCoreText (1.6.26):
- DTCoreText/Core (= 1.6.26)
- DTFoundation/Core (~> 1.7.5)
- DTFoundation/DTAnimatedGIF (~> 1.7.5)
- DTFoundation/DTHTMLParser (~> 1.7.5)
- DTFoundation/UIKit (~> 1.7.5)
- DTCoreText/Core (1.6.26):
- DTFoundation/Core (~> 1.7.5)
- DTFoundation/DTAnimatedGIF (~> 1.7.5)
- DTFoundation/DTHTMLParser (~> 1.7.5)
- DTFoundation/UIKit (~> 1.7.5)
- DTFoundation/Core (1.7.19)
- DTFoundation/DTAnimatedGIF (1.7.19)
- DTFoundation/DTHTMLParser (1.7.19):
- DTFoundation/Core
- DTFoundation/UIKit (1.7.19):
- DTFoundation/Core
- DTTJailbreakDetection (0.4.0)
- FLEX (5.22.10)
- FlowCommoniOS (1.12.2)
- GBDeviceInfo (7.1.0):
- GBDeviceInfo/Core (= 7.1.0)
- GBDeviceInfo/Core (7.1.0)
- GZIP (1.3.2)
- Introspect (0.12.0)
- JitsiMeetSDKLite (8.1.2-lite):
- JitsiWebRTC (~> 111.0)
- JitsiWebRTC (111.0.2)
- KeychainAccess (4.2.2)
- KituraContracts (1.2.1):
- LoggerAPI (~> 1.7)
- KTCenterFlowLayout (1.3.1)
- libbase58 (0.1.4)
- libPhoneNumber-iOS (0.9.15)
- MatomoTracker (7.5.2):
- MatomoTracker/Core (= 7.5.2)
- MatomoTracker/Core (7.5.2)
- LoggerAPI (1.9.200):
- Logging (~> 1.1)
- Logging (1.4.0)
- MatrixSDK (0.27.17):
- MatrixSDK/Core (= 0.27.17)
- MatrixSDK/Core (0.27.17):
- AFNetworking (~> 4.0.0)
- GZIP (~> 1.3.0)
- libbase58 (~> 0.1.4)
- MatrixSDKCrypto (= 0.4.3)
- MatrixSDKCrypto (= 0.11.1)
- Realm (= 10.27.0)
- SwiftyBeaver (= 1.9.5)
- MatrixSDKCrypto (0.4.3)
- MatrixSDK/JingleCallStack (0.27.17):
- JitsiMeetSDKLite (= 8.1.2-lite)
- MatrixSDK/Core
- MatrixSDKCrypto (0.11.1)
- ReadMoreTextView (3.0.1)
- Realm (10.27.0):
- Realm/Headers (= 10.27.0)
@@ -66,9 +60,19 @@ PODS:
- Reusable/View (= 4.1.2)
- Reusable/Storyboard (4.1.2)
- Reusable/View (4.1.2)
- Sentry (8.46.0):
- Sentry/Core (= 8.46.0)
- Sentry/Core (8.46.0)
- SideMenu (6.5.0)
- SwiftBase32 (0.9.0)
- SwiftFormat/CLI (0.54.5)
- SwiftGen (6.6.3)
- SwiftJWT (3.6.200):
- BlueCryptor (~> 1.0)
- BlueECC (~> 1.1)
- BlueRSA (~> 1.0)
- KituraContracts (~> 1.2)
- LoggerAPI (~> 1.7)
- SwiftLint (0.57.0)
- SwiftyBeaver (1.9.5)
- UICollectionViewLeftAlignedLayout (1.0.2)
@@ -82,21 +86,23 @@ PODS:
DEPENDENCIES:
- Down (~> 0.11.0)
- DSWaveformImage (~> 6.1.1)
- DTCoreText (= 1.6.26)
- DTTJailbreakDetection (~> 0.4.0)
- FLEX (~> 5.22.10)
- FlowCommoniOS (~> 1.12.0)
- GBDeviceInfo (~> 7.1.0)
- Introspect (~> 0.1)
- KeychainAccess (~> 4.2.2)
- KTCenterFlowLayout (~> 1.3.1)
- libPhoneNumber-iOS (~> 0.9.13)
- MatomoTracker (~> 7.5.2)
- MatrixSDK (from `https://dl-gitlab.example.com/bwmessenger/bundesmessenger/bundesmessenger-ios-sdk`, tag `v2.25.0-RC01`)
- MatrixSDK (from `matrix-ios-sdk/MatrixSDK.podspec`)
- MatrixSDK/JingleCallStack (from `matrix-ios-sdk/MatrixSDK.podspec`)
- ReadMoreTextView (~> 3.0.1)
- Reusable (~> 4.1)
- Sentry (~> 8.46.0)
- SideMenu (~> 6.5)
- SwiftBase32 (~> 0.9.0)
- SwiftFormat/CLI
- SwiftGen
- SwiftJWT (~> 3.6.200)
- SwiftLint
- UICollectionViewLeftAlignedLayout (~> 1.0.2)
- UICollectionViewRightAlignedLayout (~> 0.0.3)
@@ -106,28 +112,37 @@ DEPENDENCIES:
SPEC REPOS:
https://github.com/CocoaPods/Specs.git:
- MatrixSDKCrypto
- Sentry
trunk:
- AFNetworking
- BlueCryptor
- BlueECC
- BlueRSA
- Down
- DSWaveformImage
- DTCoreText
- DTFoundation
- DTTJailbreakDetection
- FLEX
- FlowCommoniOS
- GBDeviceInfo
- GZIP
- Introspect
- JitsiMeetSDKLite
- JitsiWebRTC
- KeychainAccess
- KituraContracts
- KTCenterFlowLayout
- libbase58
- libPhoneNumber-iOS
- MatomoTracker
- MatrixSDKCrypto
- LoggerAPI
- Logging
- ReadMoreTextView
- Realm
- Reusable
- SideMenu
- SwiftBase32
- SwiftFormat
- SwiftGen
- SwiftJWT
- SwiftLint
- SwiftyBeaver
- UICollectionViewLeftAlignedLayout
@@ -138,38 +153,40 @@ SPEC REPOS:
EXTERNAL SOURCES:
MatrixSDK:
:git: https://dl-gitlab.example.com/bwmessenger/bundesmessenger/bundesmessenger-ios-sdk
:tag: v2.25.0-RC01
CHECKOUT OPTIONS:
MatrixSDK:
:git: https://dl-gitlab.example.com/bwmessenger/bundesmessenger/bundesmessenger-ios-sdk
:tag: v2.25.0-RC01
:path: matrix-ios-sdk/MatrixSDK.podspec
SPEC CHECKSUMS:
AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58
BlueCryptor: b0aee3d9b8f367b49b30de11cda90e1735571c24
BlueECC: 0d18e93347d3ec6d41416de21c1ffa4d4cd3c2cc
BlueRSA: dfeef51db96bcc4edec654956c1581adbda4e6a3
Down: b6ba1bc985c9d2f4e15e3b293d2207766fa12612
DSWaveformImage: 3c718a0cf99291887ee70d1d0c18d80101d3d9ce
DTCoreText: ec749e013f2e1f76de5e7c7634642e600a7467ce
DTFoundation: 76b624967cf5bcaae6bb057d622c536c36ef36d0
DTTJailbreakDetection: 5e356c5badc17995f65a83ed9483f787a0057b71
FLEX: f21ee4f498eed3f8a1eded66b21939fd3b7a22ce
FlowCommoniOS: ca92071ab526dc89905495a37844fd7e78d1a7f2
GBDeviceInfo: 5d62fa85bdcce3ed288d83c28789adf1173e4376
GZIP: 3c0abf794bfce8c7cb34ea05a1837752416c8868
Introspect: b66b675de8a85d9ef832f3a710d8e3c7db186884
JitsiMeetSDKLite: 895213158cf62342069a10634a41d2f1c00057f7
JitsiWebRTC: 80f62908fcf2a1160e0d14b584323fb6e6be630b
KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51
KituraContracts: e845e60dc8627ad0a76fa55ef20a45451d8f830b
KTCenterFlowLayout: 6e02b50ab2bd865025ae82fe266ed13b6d9eaf97
libbase58: 8abc2a53ac38cd37720c0acbc53ef3660e9016c2
libbase58: 7c040313537b8c44b6e2d15586af8e21f7354efd
libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75
MatomoTracker: 1d98ddc58322fd9d65e1a6886b8e41363047bd13
MatrixSDK: 33d348122df228efa234d7b353c33620fc420a59
MatrixSDKCrypto: 27bee960e0e8b3a3039f3f3e93dd2ec88299c77e
LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d
Logging: beeb016c9c80cf77042d62e83495816847ef108b
MatrixSDK: 45f9f97e7424e5d8731bf6b207c728a71caa8eb1
MatrixSDKCrypto: e44608012cae9befc52f13cd8e56c6f51ac83702
ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d
Realm: 9ca328bd7e700cc19703799785e37f77d1a130f2
Reusable: 6bae6a5e8aa793c9c441db0213c863a64bce9136
Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854
SideMenu: f583187d21c5b1dd04c72002be544b555a2627a2
SwiftBase32: 9399c25a80666dc66b51e10076bf591e3bbb8f17
SwiftFormat: 543a7b1ab4a6ce2d88bd5616a17903446ca3dc5c
SwiftGen: 4993cbf71cbc4886f775e26f8d5c3a1188ec9f99
SwiftJWT: 88c412708f58c169d431d344c87bc79a87c830ae
SwiftLint: eb47480d47c982481592c195c221d11013a679cc
SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82
UICollectionViewLeftAlignedLayout: 830bf6fa5bab9f9b464f62e3384f9d2e00b3c0f6
@@ -178,6 +195,6 @@ SPEC CHECKSUMS:
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5
PODFILE CHECKSUM: b5e41c832b239f0b12152d3c9e66baf098d3d57d
PODFILE CHECKSUM: 60f4fe3695f2304b9bba734acf38d59c79678ac1
COCOAPODS: 1.16.2

View File

@@ -167,3 +167,5 @@
/** General **/
"Notification" = "إشعار";
"VOICE_BROADCAST_FROM_USER" = "%@ بدأ بثًا صوتيًا";
"UNSUPPORTED_CALL" = "مكالمة غير مدعومة";

View File

@@ -2080,3 +2080,556 @@
"poll_timeline_not_closed_subtitle" = "الرجاء المحاولة مرة أخرى";
"poll_edit_form_post_failure_title" = "فشل في نشر الاستطلاع";
"location_sharing_title" = "موقع";
"room_details_fail_to_update_history_visibility" = "فشل في تحديث رؤية التاريخ";
"room_details_copy_room_address" = "عنوان غرفة النسخ";
"room_details_copy_room_url" = "نسخ عنوان URL للغرفة";
"widget_integration_missing_room_id" = "room_id مفقود في الطلب.";
"room_access_settings_screen_message" = "قرر من يمكنه العثور على %@ والانضمام إليه.";
"room_access_settings_screen_upgrade_alert_message_no_param" = "سيتمكن أي شخص في مساحة الوالدين من العثور على هذه الغرفة والانضمام إليها - دون الحاجة إلى دعوة الجميع يدويًا. يمكنك تغيير ذلك في إعدادات الغرفة في أي وقت.";
"room_access_settings_screen_upgrade_alert_note" = "يرجى ملاحظة أن الترقية ستؤدي إلى إنشاء نسخة جديدة من الغرفة. ستبقى جميع الرسائل الحالية في هذه الغرفة المؤرشفة.";
"room_access_settings_screen_upgrade_alert_auto_invite_switch" = "دعوة الأعضاء تلقائيًا إلى غرفة جديدة";
"room_notifs_settings_notify_me_for" = "أعلمني بذلك";
"e2e_room_key_request_message" = "تطلب جلستك غير الموثقة '%@' مفاتيح التشفير.";
"secure_key_backup_setup_intro_use_security_key_title" = "استخدم مفتاح الأمان";
"secure_key_backup_setup_existing_backup_error_info" = "قم بإلغاء قفله لإعادة استخدامه في النسخ الاحتياطي الآمن أو احذفه لإنشاء نسخة احتياطية جديدة للرسائل في النسخ الاحتياطي الآمن.";
"room_details_fail_to_add_room_aliases" = "فشل في إضافة عناوين الغرف الجديدة";
"room_details_fail_to_update_room_direct" = "فشل في تحديث العلم المباشر لهذه الغرفة";
"room_access_settings_screen_restricted_message" = "اسمح لأي شخص في مساحة بالبحث والانضمام.\nسيُطلب منك تأكيد المساحات.";
"room_suggestion_settings_screen_title" = "إنشاء غرفة مقترحة في مساحة";
"group_details_title" = "تفاصيل المجتمع";
"group_invitation_format" = "لقد دعاك %@ للانضمام إلى هذا المجتمع";
"directory_server_type_homeserver" = "اكتب خادمًا رئيسيًا لإدراج الغرف العامة منه";
"bug_report_send_logs" = "إرسال تسجيلات الدخول";
"e2e_room_key_request_start_verification" = "بدء التحقق…";
"key_backup_setup_intro_manual_export_info" = "(متقدم)";
"room_access_settings_screen_upgrade_alert_upgrading" = "ترقية الغرفة";
"room_notifs_settings_done_action" = "منتهي";
"e2e_room_key_request_share_without_verifying" = "شارك دون التحقق";
"gdpr_consent_not_given_alert_review_now_action" = "قم بالمراجعة الآن";
"service_terms_modal_description_integration_manager" = "سيسمح لك هذا باستخدام الروبوتات والجسور والأدوات وحزم الملصقات.";
"key_backup_setup_title" = "النسخ الاحتياطي للمفتاح";
"group_details_home" = "الصفحة الرئيسية";
"room_details_set_main_address" = "تعيين كعنوان رئيسي";
"room_notifs_settings_manage_notifications" = "يمكنك إدارة الإشعارات في %@";
"room_notifs_settings_encrypted_room_notice" = "يرجى ملاحظة أن الإشارات وإشعارات الكلمات الرئيسية غير متوفرة في الغرف المشفرة على الهاتف المحمول.";
"group_details_rooms" = "الغرف";
"widget_integration_room_not_visible" = "الغرفة %@ غير مرئية.";
"room_widget_permission_room_id_permission" = "معرف الغرفة";
"service_terms_modal_information_title_integration_manager" = "مدير التكامل";
"service_terms_modal_information_description_integration_manager" = "يتيح لك مدير التكامل إضافة ميزات من جهات خارجية.";
"secure_key_backup_setup_intro_use_security_key_info" = "‌إنشاء مفتاح أمان لتخزينه في مكان آمن مثل مدير كلمات المرور أو الخزنة.";
"secure_key_backup_setup_cancel_alert_message" = "إذا ألغيت الاشتراك الآن، فقد تفقد رسائلك وبياناتك المشفرة إذا فقدت الوصول إلى بيانات تسجيل الدخول الخاصة بك.\n\nيمكنك أيضًا إعداد النسخ الاحتياطي الآمن وإدارة مفاتيحك في الإعدادات.";
"key_backup_setup_skip_alert_message" = "قد تفقد الرسائل الآمنة إذا قمت بتسجيل الخروج أو فقدت جهازك.";
"widget_integration_missing_user_id" = "معرف المستخدم مفقود في الطلب.";
"widget_picker_manage_integrations" = "إدارة التكاملات…";
"room_widget_permission_creator_info_title" = "تمت إضافة هذه الودجت بواسطة:";
"share_extension_auth_prompt" = "قم بتسجيل الدخول إلى التطبيق الرئيسي لمشاركة المحتوى";
"e2e_room_key_request_message_new_device" = "لقد قمت بإضافة جلسة جديدة '%@'، والتي تطلب مفاتيح التشفير.";
"room_access_settings_screen_setting_room_access" = "الوصول إلى غرفة الإعداد";
"group_home_multi_rooms_format" = "%tu غرف";
"group_participants_add_participant" = "إضافة مشارك";
"group_participants_leave_prompt_msg" = "هل أنت متأكد أنك تريد مغادرة المجموعة؟";
"room_widget_permission_widget_id_permission" = "معرف الودجت";
"share_extension_low_quality_video_title" = "سيتم إرسال الفيديو بجودة منخفضة";
"share_extension_low_quality_video_message" = "أرسل %@ للحصول على جودة أفضل، أو أرسل بجودة منخفضة أدناه.";
"e2e_room_key_request_title" = "طلب مفتاح التشفير";
"service_terms_modal_decline_button" = "رفض";
"service_terms_modal_description_identity_server" = "سيسمح هذا لأي شخص بالعثور عليك إذا كان لديه رقم هاتفك أو بريدك الإلكتروني محفوظًا في جهات اتصال هاتفه.";
"room_access_space_chooser_other_spaces_section_info" = "من المحتمل أن تكون هذه أشياء يشارك فيها مسؤولون آخرون في %@.";
"room_access_settings_screen_nav_title" = "الوصول إلى الغرفة";
"service_terms_modal_table_header_integration_manager" = "شروط مدير التكامل";
"room_access_settings_screen_public_message" = "يمكن لأي شخص العثور والانضمام.";
"room_notifs_settings_none" = "لا أحد";
"room_details_fail_to_enable_encryption" = "فشل في تمكين التشفير في هذه الغرفة";
"room_details_copy_room_id" = "معرف غرفة النسخ";
"room_access_settings_screen_upgrade_alert_upgrade_button" = "ترقية";
"room_suggestion_settings_screen_message" = "يتم الترويج للغرف المقترحة لأعضاء الفضاء باعتبارها غرفًا جيدة للانضمام إليها.";
"room_notifs_settings_account_settings" = "إعدادات الحساب";
"group_participants_remove_prompt_title" = "تأكيد";
"service_terms_modal_footer" = "يمكن تعطيل هذه الميزة في أي وقت من خلال الإعدادات.";
"deactivate_account_forget_messages_information_part1" = "يرجى نسيان جميع الرسائل التي أرسلتها عندما تم إلغاء تنشيط حسابي (";
"secure_key_backup_setup_intro_info" = "قم بحماية نفسك من فقدان الوصول إلى الرسائل والبيانات المشفرة عن طريق عمل نسخة احتياطية لمفاتيح التشفير على الخادم الخاص بك.";
"key_backup_setup_intro_manual_export_action" = "تصدير المفاتيح يدويًا";
"room_widget_permission_avatar_url_permission" = "عنوان URL للصورة الرمزية الخاصة بك";
"share_extension_failed_to_encrypt" = "فشل الإرسال. تحقق من إعدادات التشفير لهذه الغرفة في التطبيق الرئيسي";
"secure_key_backup_setup_intro_use_security_passphrase_info" = "أدخل عبارة سرية تعرفها أنت فقط، ثم قم بإنشاء مفتاح للنسخ الاحتياطي.";
"room_details_fail_to_remove_room_aliases" = "فشل في إزالة عناوين الغرف";
"room_access_settings_screen_private_message" = "يمكن فقط للأشخاص المدعوين العثور على الصفحة والانضمام إليها.";
"room_access_settings_screen_edit_spaces" = "تحرير المساحات";
"group_details_people" = "الناس";
"widget_integration_must_be_in_room" = "أنت لست في هذه الغرفة.";
"widget_integration_no_permission_in_room" = "ليس لديك الإذن للقيام بذلك في هذه الغرفة.";
"deactivate_account_informations_part2_emphasize" = "هذا الإجراء لا رجعة فيه.";
"deactivate_account_validate_action" = "إلغاء تنشيط الحساب";
"deactivate_account_password_alert_message" = "للمتابعة، الرجاء إدخال كلمة مرور حساب Matrix الخاص بك";
"secure_key_backup_setup_existing_backup_error_unlock_it" = "افتحه";
"room_notifs_settings_cancel_action" = "إلغاء";
"share_extension_send_now" = "أرسل الآن";
"service_terms_modal_title_message" = "للمتابعة، قم بقبول الشروط والأحكام أدناه";
"room_details_promote_room_suggest_title" = "اقترح على أعضاء المساحة";
"room_details_fail_to_update_room_canonical_alias" = "فشل في تحديث العنوان الرئيسي";
"room_details_unset_main_address" = "غير مُعيَّن كعنوان رئيسي";
"e2e_room_key_request_ignore_request" = "تجاهل الطلب";
"deactivate_account_forget_messages_information_part3" = ": سيؤدي هذا إلى جعل المستخدمين في المستقبل يرون عرضًا غير كامل للمحادثات)";
"rerequest_keys_alert_title" = "تم إرسال الطلب";
"room_details_fail_to_update_room_communities" = "فشل في تحديث المجتمعات ذات الصلة";
"group_home_one_room_format" = "غرفة واحدة";
"widget_creation_failure" = "فشل إنشاء الودجت";
"service_terms_modal_information_title_identity_server" = "خادم الهوية";
"deactivate_account_forget_messages_information_part2_emphasize" = "تحذير";
"key_backup_setup_skip_alert_title" = "هل أنت متأكد؟";
"key_backup_setup_intro_title" = "لا تفقد الرسائل المشفرة أبدًا";
"widget_picker_title" = "التكاملات";
"service_terms_modal_accept_button" = "قبول";
"key_backup_setup_intro_info" = "الرسائل في الغرف المشفرة مؤمنة بتشفير شامل. أنت والمستلم فقط لديهما مفاتيح قراءة هذه الرسائل.\n\nاحتفظ بنسخة احتياطية آمنة من مفاتيحك لتجنب فقدانها.";
"room_access_settings_screen_upgrade_alert_title" = "ترقية الغرفة";
"room_notifs_settings_all_messages" = "كل الرسائل";
"deactivate_account_title" = "إلغاء تنشيط الحساب";
"deactivate_account_informations_part4_emphasize" = "لا يؤدي افتراضيًا إلى نسيان الرسائل التي أرسلتها. ";
"secure_key_backup_setup_intro_use_security_passphrase_title" = "استخدم عبارة أمنية";
"key_backup_setup_passphrase_title" = "تأمين النسخة الاحتياطية الخاصة بك باستخدام عبارة أمان";
"bug_report_progress_zipping" = "جمع تسجيلات الدخول";
"room_details_save_changes_prompt" = "هل تريد حفظ التغييرات؟";
"room_access_settings_screen_title" = "من يمكنه الوصول إلى هذه الغرفة؟";
"room_access_settings_screen_upgrade_required" = "الترقية مطلوبة";
"room_access_settings_screen_upgrade_alert_message" = "سيتمكن أي شخص في %@ من العثور على هذه الغرفة والانضمام إليها - دون الحاجة إلى دعوة الجميع يدويًا. يمكنك تغيير ذلك في أي وقت من إعدادات الغرفة.";
"room_access_space_chooser_known_spaces_section" = "المساحات التي تعرف أنها تحتوي على %@";
"room_access_space_chooser_other_spaces_section" = "مساحات أو غرف أخرى";
"room_suggestion_settings_screen_nav_title" = "اقتراح غرفة";
"room_notifs_settings_mentions_and_keywords" = "الإشارات والكلمات الرئيسية فقط";
"group_home_one_member_format" = "عضو واحد";
"group_home_multi_members_format" = "%tu أعضاء";
"group_participants_leave_prompt_title" = "مغادرة المجموعة";
"event_formatter_widget_removed" = "تمت إزالة الودجت %@ بواسطة %@";
"widget_integration_room_not_recognised" = "لم يتم التعرف على هذه الغرفة.";
"widget_integration_positive_power_level" = "يجب أن يكون مستوى الطاقة عددًا صحيحًا موجبًا.";
"widget_integration_manager_disabled" = "يجب عليك تمكين مدير التكامل في الإعدادات";
"room_widget_permission_webview_information_title" = "قد يؤدي استخدامه إلى تعيين ملفات تعريف الارتباط ومشاركة البيانات مع %@:\n";
"room_widget_permission_display_name_permission" = "اسم العرض الخاص بك";
"room_widget_permission_user_id_permission" = "معرف المستخدم الخاص بك";
"gdpr_consent_not_given_alert_message" = "لمواصلة استخدام %@ homeserver، يجب عليك مراجعة الشروط والأحكام والموافقة عليها.";
"service_terms_modal_table_header_identity_server" = "شروط خادم الهوية";
"deactivate_account_informations_part1" = "سيؤدي هذا إلى تعطيل حسابك نهائيًا. لن تتمكن من تسجيل الدخول، ولن يتمكن أي شخص من إعادة تسجيل اسم المستخدم نفسه. سيؤدي هذا إلى مغادرة حسابك جميع الغرف التي يشارك فيها، وإزالة تفاصيل حسابك من خادم الهوية الخاص بك. ";
"deactivate_account_informations_part5" = "إذا كنت ترغب في حذف رسائلك، يُرجى وضع علامة في المربع أدناه.\n\nتشبه ميزة رؤية الرسائل في Matrix ميزة البريد الإلكتروني. تعني ميزة \"حذف رسائلك\" أن الرسائل التي أرسلتها لن تُشارك مع أي مستخدمين جدد أو غير مسجلين، بينما سيظل بإمكان المستخدمين المسجلين الذين لديهم حق الوصول إلى هذه الرسائل الوصول إلى نسخهم.";
"deactivate_account_password_alert_title" = "إلغاء تنشيط الحساب";
"secure_key_backup_setup_intro_title" = "النسخ الاحتياطي الآمن";
"secure_key_backup_setup_existing_backup_error_title" = "توجد نسخة احتياطية للرسائل بالفعل";
"secure_key_backup_setup_existing_backup_error_delete_it" = "احذفه";
"secure_key_backup_setup_cancel_alert_title" = "هل انت متأكد؟";
"secure_backup_setup_banner_title" = "النسخ الاحتياطي الآمن";
"secure_backup_setup_banner_subtitle" = "الحماية من فقدان الوصول إلى الرسائل والبيانات المشفرة";
"key_backup_setup_skip_alert_skip_action" = "تخطي";
"key_backup_setup_intro_setup_action_without_existing_backup" = "ابدأ باستخدام النسخ الاحتياطي للمفتاح";
"key_backup_setup_intro_setup_connect_action_with_existing_backup" = "قم بتوصيل هذا الجهاز بـ Key Backup";
"room_widget_permission_title" = "ودجت تحميل";
"room_widget_permission_information_title" = "قد يؤدي استخدامه إلى مشاركة البيانات مع %@:\n";
"service_terms_modal_information_description_identity_server" = "يساعدك خادم الهوية في العثور على جهات الاتصال الخاصة بك، من خلال البحث عن رقم الهاتف أو عنوان البريد الإلكتروني، لمعرفة ما إذا كان لديهم حساب بالفعل.";
"rerequest_keys_alert_message" = "يرجى تشغيل %@ على جهاز آخر يمكنه فك تشفير الرسالة حتى يتمكن من إرسال المفاتيح إلى هذه الجلسة.";
"key_backup_setup_passphrase_info" = "سنخزن نسخة مشفرة من مفاتيحك على خادمنا. احمِ نسختك الاحتياطية بعبارة للحفاظ عليها آمنة.\n\nلأقصى درجات الأمان، يجب أن تكون كلمة المرور مختلفة عن كلمة مرور حساب Matrix الخاص بك.";
"device_verification_emoji_trophy" = "غنيمة";
"key_backup_recover_invalid_passphrase_title" = "عبارة أمنية غير صحيحة";
"key_backup_recover_invalid_recovery_key_title" = "عدم تطابق مفتاح الأمان";
"key_backup_setup_passphrase_setup_recovery_key_info" = "أو قم بتأمين النسخة الاحتياطية الخاصة بك باستخدام مفتاح أمان، وحفظها في مكان آمن.";
"key_backup_recover_title" = "رسائل آمنة";
"key_backup_recover_from_private_key_info" = "جاري استعادة النسخة الاحتياطية…";
"device_verification_emoji_cloud" = "سحاب";
"device_verification_emoji_clock" = "ساعة";
"device_verification_emoji_pencil" = "قلم رصاص";
"key_backup_setup_passphrase_passphrase_invalid" = "حاول إضافة كلمة";
"key_backup_setup_passphrase_confirm_passphrase_invalid" = "العبارة لا تتطابق";
"key_backup_setup_success_from_recovery_key_info" = "يتم الآن نسخ مفاتيحك احتياطيًا.\n\nانسخ مفتاح الأمان هذا واحتفظ به في مكان آمن.";
"device_verification_emoji_panda" = "باندا";
"device_verification_emoji_telephone" = "الهاتف";
"device_verification_emoji_pig" = "خنزير";
"device_verification_emoji_paperclip" = "مشبك";
"device_verification_emoji_ball" = "كرة";
"device_verification_emoji_guitar" = "غيتار";
"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "تأكيد العبارة";
"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = ".";
"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "أدخل مفتاح الأمان";
"device_verification_emoji_glasses" = "نظارات";
"device_verification_emoji_unicorn" = "وحيد القرن";
"device_verification_emoji_flower" = "ورد";
"device_verification_emoji_tree" = "شجرة";
"device_verification_emoji_cactus" = "صبار";
"device_verification_emoji_smiley" = "ابتسامة";
"device_verification_emoji_scissors" = "مقص";
"device_verification_emoji_hammer" = "مطرقة";
"device_verification_emoji_banana" = "موز";
"device_verification_emoji_turtle" = "سلحفاة";
"key_backup_setup_passphrase_passphrase_valid" = "عظيم!";
"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "حفظ مفتاح الأمان";
"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "لا تعرف عبارة الأمان الخاصة بك؟ يمكنك ";
"device_verification_emoji_octopus" = "الأخطبوط";
"device_verification_emoji_strawberry" = "الفراولة";
"device_verification_emoji_heart" = "قلب";
"device_verification_emoji_hourglass" = "الساعة الرملية";
"device_verification_emoji_key" = "مفتاح";
"device_verification_emoji_flag" = "علَم";
"device_verification_emoji_bicycle" = "دراجة";
"device_verification_emoji_rocket" = "صاروخ";
"device_verification_emoji_aeroplane" = "طائرة";
"device_verification_emoji_horse" = "حصان";
"device_verification_emoji_elephant" = "فيل";
"device_verification_emoji_rabbit" = "أرنب";
"key_backup_setup_success_from_recovery_key_recovery_key_title" = "مفتاح الأمان";
"device_verification_emoji_rooster" = "ديك";
"device_verification_emoji_light bulb" = "مصباح كهربائي";
"key_backup_setup_passphrase_confirm_passphrase_valid" = "عظيم!";
"key_backup_setup_success_title" = "نجاح!";
"key_backup_recover_from_passphrase_info" = "استخدم عبارة الأمان الخاصة بك لفتح سجل رسائلك الآمنة";
"device_verification_emoji_penguin" = "البطريق";
"device_verification_emoji_apple" = "تفاحة";
"device_verification_emoji_book" = "كتاب";
"key_backup_setup_success_from_recovery_key_make_copy_action" = "اصنع نسخة";
"key_backup_setup_passphrase_setup_recovery_key_action" = "(متقدم) الإعداد باستخدام مفتاح الأمان";
"device_verification_emoji_fish" = "سمكة";
"device_verification_emoji_corn" = "حبوب ذرة";
"device_verification_emoji_robot" = "روبوت";
"key_backup_setup_passphrase_passphrase_title" = "دخول";
"device_verification_emoji_umbrella" = "مظلة";
"device_verification_emoji_cake" = "كعكة";
"device_verification_emoji_lock" = "قفل";
"security_settings_crosssigning_bootstrap" = "إعداد";
"event_formatter_call_decline" = "رفض";
"key_backup_setup_passphrase_passphrase_placeholder" = "أدخل العبارة";
"key_backup_setup_passphrase_confirm_passphrase_title" = "تأكيد";
"key_backup_setup_success_from_passphrase_info" = "يتم نسخ مفاتيحك احتياطيًا.\n\nمفتاح الأمان الخاص بك هو بمثابة شبكة أمان، يمكنك استخدامه لاستعادة الوصول إلى رسائلك المشفرة في حال نسيت كلمة المرور.\n\nاحتفظ بمفتاح الأمان الخاص بك في مكان آمن للغاية، مثل مدير كلمات المرور (أو خزنة).";
"key_backup_setup_success_from_passphrase_done_action" = "منتهي";
"key_backup_setup_success_from_recovery_key_made_copy_action" = "لقد قمت بعمل نسخة";
"key_backup_setup_success_from_secure_backup_info" = "يتم إجراء نسخة احتياطية لمفاتيحك.";
"key_backup_recover_invalid_passphrase" = "لم يتم فك تشفير النسخ الاحتياطي باستخدام هذه العبارة: يرجى التأكد من إدخال عبارة الأمان الصحيحة.";
"key_backup_recover_invalid_recovery_key" = "لم يتمكن من فك تشفير النسخة الاحتياطية باستخدام هذا المفتاح: يرجى التأكد من إدخال مفتاح الأمان الصحيح.";
"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "استخدم مفتاح الأمان الخاص بك";
"key_backup_recover_from_recovery_key_info" = "استخدم مفتاح الأمان الخاص بك لفتح سجل رسائلك الآمنة";
"device_verification_emoji_lion" = "الأسد";
"device_verification_emoji_butterfly" = "فراشة";
"device_verification_emoji_globe" = "الكرة الأرضية";
"device_verification_emoji_fire" = "نار";
"device_verification_emoji_pizza" = "بيتزا";
"device_verification_emoji_spanner" = "مفتاح البراغي";
"device_verification_emoji_gift" = "هدية";
"key_backup_recover_from_passphrase_recover_action" = "فتح التاريخ";
"device_verification_emoji_mushroom" = "فطر";
"device_verification_emoji_moon" = "القمر";
"key_backup_recover_from_passphrase_passphrase_placeholder" = "أدخل العبارة";
"deactivate_account_informations_part3" = "\n\nإلغاء تنشيط حسابك ";
"user_verification_sessions_list_user_trust_level_unknown_title" = "غير معروف";
"spaces_coming_soon_detail" = "لم تُطبّق هذه الميزة هنا، ولكنها في طريقها. حاليًا، يمكنك القيام بذلك باستخدام %@ على جهاز الكمبيوتر.";
"space_feature_unavailable_subtitle" = "لم يتم إطلاق Spaces على نظام التشغيل iOS بعد، ولكن يمكنك استخدامها الآن على الويب وسطح المكتب";
"space_feature_unavailable_information" = "المساحات وسيلة جديدة لتجميع الغرف والأشخاص.\n\nستتوفر قريبًا. حاليًا، إذا انضممت إلى واحدة عبر منصة أخرى، ستتمكن من الوصول إلى أي غرف تنضم إليها هنا.";
"space_tag" = "مساحة";
"spaces_empty_space_detail" = "قد تكون بعض الغرف مخفية لأنها خاصة وتحتاج إلى دعوة.";
"spaces_no_member_found_detail" = "هل تبحث عن شخص ليس في %@؟ حاليًا، يمكنك دعوته عبر الويب أو سطح المكتب.";
"spaces_no_room_found_detail" = "قد تكون بعض النتائج مخفية لأنها خاصة وتحتاج إلى دعوة للانضمام إليها.";
"space_settings_update_failed_message" = "فشل تحديث إعدادات المساحة. هل تريد إعادة المحاولة؟";
"space_private_join_rule" = "مساحة خاصة";
"major_update_learn_more_action" = "معرفة المزيد";
"emoji_picker_title" = "ردود الفعل";
"emoji_picker_symbols_category" = "الرموز";
"user_verification_start_information_part1" = "لمزيد من الأمان، قم بالتحقق ";
"user_verification_sessions_list_user_trust_level_trusted_title" = "موثوق به";
"user_verification_session_details_information_trusted_current_user" = "تمت الوثوق بهذه الجلسة للمراسلة الآمنة لأنك قمت بالتحقق منها:";
"user_verification_session_details_information_trusted_other_user_part1" = "هذه الجلسة موثوقة للمراسلة الآمنة لأنها ";
"user_verification_session_details_information_trusted_other_user_part2" = " تم التحقق من ذلك:";
"home_context_menu_mute" = "كتم";
"room_intro_cell_information_dm_sentence1_part1" = "هذه هي بداية رسالتك المباشرة مع ";
"room_intro_cell_information_dm_sentence2" = "أنتما الاثنان فقط في هذه المحادثة، ولا يمكن لأي شخص آخر الانضمام.";
"room_invite_not_enough_permission" = "ليس لديك إذن لدعوة الأشخاص إلى هذه الغرفة";
"leave_space_message" = "هل أنت متأكد أنك تريد مغادرة %@؟ هل تريد أيضًا مغادرة جميع الغرف والمساحات في هذه المساحة؟";
"spaces_suggested_room" = "مقترح";
"spaces_empty_space_title" = "لا يوجد غرف في هذه المساحة (حتى الآن)";
"spaces_add_rooms_coming_soon_title" = "سيتم إضافة الغرف قريبًا";
"space_participants_action_remove" = "إزالة من هذه المساحة";
"space_participants_action_ban" = "حظر من هذه المساحة";
"space_home_show_all_rooms" = "عرض جميع الغرف";
"space_private_join_rule_detail" = "للدعوة فقط، الأفضل لك أو لفريقك";
"spaces_invite_people" = "دعوة الناس";
"spaces_add_space" = "إضافة مساحة";
"spaces_feature_not_available" = "هذه الميزة غير متوفرة هنا. حاليًا، يمكنك القيام بذلك باستخدام %@ على جهاز الكمبيوتر الخاص بك.";
"spaces_creation_footer" = "يمكنك تغيير هذا لاحقًا";
"device_verification_emoji_headphones" = "سماعات الرأس";
"user_verification_session_details_information_untrusted_other_user" = " تم تسجيل الدخول باستخدام جلسة جديدة:";
"user_verification_session_details_additional_information_untrusted_other_user" = "حتى يثق هذا المستخدم بهذه الجلسة، تُعلَّم الرسائل المرسلة إليها ومنها بتحذيرات. يمكنك أيضًا التحقق منها يدويًا.";
"secrets_recovery_with_passphrase_passphrase_placeholder" = "أدخل عبارة الأمان";
"create_room_section_footer_type_restricted" = "يمكن لأي شخص في اسم المساحة البحث والانضمام.";
"space_beta_announce_information" = "المساحات هي طريقة جديدة لتجميع الغرف والأشخاص. لم تتوفر بعد على نظام iOS، ولكن يمكنك استخدامها الآن على الويب وسطح المكتب.";
"leave_space_message_admin_warning" = "أنت مسؤول عن هذه المساحة، تأكد من أنك قمت بنقل حقوق المسؤول إلى عضو آخر قبل المغادرة.";
"spaces_creation_settings_message" = "أضف بعض التفاصيل لإبرازها. يمكنك تغييرها في أي وقت.";
"device_verification_emoji_anchor" = "مِرسَاة";
"error_not_supported_on_mobile" = "لا يمكنك القيام بذلك من %@الجوال.";
"user_verification_sessions_list_user_trust_level_warning_title" = "تحذير";
"user_verification_session_details_verify_action_current_user" = "التحقق بشكل تفاعلي";
"create_room_placeholder_topic" = "ما هي هذه الغرفة؟";
"room_intro_cell_information_room_without_topic_sentence2_part1" = "أضف موضوعًا";
"space_invite_not_enough_permission" = "ليس لديك إذن لدعوة الأشخاص إلى هذه المساحة";
"space_public_join_rule_detail" = "مفتوح للجميع، الأفضل للمجتمعات";
"spaces_creation_visibility_message" = "للانضمام إلى مساحة موجودة، تحتاج إلى دعوة.";
"space_topic" = "وصف";
"reaction_history_title" = "ردود الفعل";
"home_context_menu_leave" = "يغادر";
"spaces_add_space_title" = "إنشاء مساحة";
"spaces_explore_rooms" = "استكشاف الغرف";
"device_verification_emoji_bell" = "جرس";
"device_verification_emoji_pin" = "دبوس";
"space_feature_unavailable_title" = "المساحات ليست هنا بعد";
"key_verification_bootstrap_not_setup_message" = "يجب عليك أولاً تمهيد التوقيع المتبادل.";
"user_verification_session_details_verify_action_other_user" = "التحقق يدويًا";
"secrets_recovery_with_passphrase_information_verify_device" = "استخدم عبارة الأمان الخاصة بك للتحقق من هذا الجهاز.";
"key_verification_alert_title" = "لديك جلسات غير موثقة";
"device_verification_emoji_folder" = "مجلد";
"file_upload_error_unsupported_file_type_message" = "نوع الملف غير مدعوم.";
"emoji_picker_places_category" = "السفر والأماكن";
"error_invite_3pid_with_no_identity_server" = "أضف خادم هوية في إعداداتك لدعوتك عبر البريد الإلكتروني.";
"user_verification_start_waiting_partner" = "في انتظار %@…";
"user_verification_session_details_trusted_title" = "موثوق به";
"user_verification_session_details_untrusted_title" = "غير موثوق به";
"secrets_recovery_reset_action_part_2" = "إعادة تعيين كل شيء";
"secrets_recovery_with_passphrase_title" = "عبارة أمنية";
"room_invite_to_space_option_detail" = "بإمكانهم استكشاف %@، ولكنهم لن يكونوا أعضاءً في %@.";
"spaces_home_space_title" = "الصفحة الرئيسية";
"spaces_left_panel_title" = "المساحات";
"leave_space_and_all_rooms_action" = "اترك جميع الغرف والمساحات";
"spaces_no_result_found_title" = "لم يتم العثور على نتائج";
"spaces_invites_coming_soon_title" = "الدعوات قادمة قريبا";
"spaces_add_room" = "إضافة غرفة";
"spaces_subspace_creation_visibility_message" = "سيتم إضافة المساحة التي تم إنشاؤها إلى %@.";
"space_public_join_rule" = "مساحة عامة";
"secrets_recovery_with_passphrase_information_default" = "قم بالوصول إلى سجل رسائلك الآمنة وهوية التوقيع المتبادل الخاصة بك للتحقق من الجلسات الأخرى عن طريق إدخال عبارة الأمان الخاصة بك.";
"emoji_picker_foods_category" = "الطعام والشراب";
"emoji_picker_activity_category" = "أنشطة";
"user_verification_sessions_list_table_title" = "الجلسات";
"create_room_type_restricted" = "أعضاء المساحة";
"room_invite_to_room_option_detail" = "لن يكونوا جزءًا من %@.";
"spaces_add_subspace_title" = "إنشاء مساحة داخل %@";
"space_settings_current_address_message" = "مساحتك متاحة للعرض على\n%@";
"leave_space_only_action" = "لا تغادر أي غرف";
"spaces_explore_rooms_room_number" = "%@ غرف";
"user_verification_sessions_list_session_trusted" = "موثوق به";
"create_room_show_in_directory_footer" = "سيساعد هذا الأشخاص في العثور على أعضاء جدد والانضمام إليهم.";
"room_intro_cell_information_room_without_topic_sentence2_part2" = " لإعلام الناس بما تحتويه هذه الغرفة.";
"spaces_create_space_title" = "إنشاء مساحة";
"spaces_create_subspace_title" = "إنشاء مساحة فرعية";
"file_upload_error_title" = "تحميل الملف";
"emoji_picker_people_category" = "الوجوه الضاحكة والأشخاص";
"emoji_picker_nature_category" = "الحيوانات والطبيعة";
"emoji_picker_objects_category" = "أشياء";
"emoji_picker_flags_category" = "الأعلام";
"key_verification_bootstrap_not_setup_title" = "خطأ";
"key_verification_tile_request_incoming_title" = "طلب التحقق";
"key_verification_tile_request_outgoing_title" = "تم إرسال التحقق";
"user_verification_start_verify_action" = "بدء التحقق";
"user_verification_start_information_part2" = " عن طريق التحقق من رمز لمرة واحدة على كلا الجهازين.";
"user_verification_start_additional_information" = "لضمان الأمان، قم بذلك شخصيًا أو استخدم طريقة أخرى للتواصل.";
"user_verification_sessions_list_information" = "الرسائل التي يتبادلها هذا المستخدم في هذه الغرفة تكون مشفرة من البداية إلى النهاية ولا يمكن لأطراف ثالثة قراءتها.";
"user_verification_session_details_information_untrusted_current_user" = "قم بالتحقق من هذه الجلسة لوضع علامة عليها كموثوقة ومنحها حق الوصول إلى الرسائل المشفرة:";
"user_verification_session_details_additional_information_untrusted_current_user" = "إذا لم تقم بتسجيل الدخول إلى هذه الجلسة، فقد يكون حسابك معرضًا للخطر.";
"user_verification_session_details_verify_action_current_user_manually" = "التحقق يدويًا عن طريق النص";
"secrets_recovery_reset_action_part_1" = "هل نسيت أو فقدت كافة خيارات الاسترداد؟ ";
"secrets_recovery_with_passphrase_passphrase_title" = "دخول";
"room_intro_cell_information_dm_sentence1_part3" = ". ";
"room_intro_cell_information_multiple_dm_sentence2" = "أنت وحدك من يشارك في هذه المحادثة، ما لم يقم أي منكم بدعوة شخص ما للانضمام.";
"room_invite_to_room_option_title" = "إلى هذه الغرفة فقط";
"spaces_explore_rooms_one_room" = "غرفة واحدة";
"space_settings_access_section" = "من يمكنه الوصول إلى هذه المساحة؟";
"spaces_creation_hint" = "المساحات هي طريقة جديدة لتجميع الغرف والأشخاص.";
"spaces_creation_visibility_title" = "ما نوع المساحة التي تريد إنشاءها؟";
"spaces_subspace_creation_visibility_title" = "ما نوع المساحة الفرعية التي تريد إنشاءها؟";
"spaces_creation_address" = "عنوان";
"space_beta_announce_title" = "المساحات قادمة قريبا";
"user_verification_sessions_list_session_untrusted" = "غير موثوق به";
"secrets_recovery_with_passphrase_recover_action" = "استخدم العبارة";
"biometrics_cant_unlocked_alert_title" = "لا يمكن فتح التطبيق";
"create_room_section_footer_type_public" = "يمكن فقط للأشخاص المدعوين العثور على المساحة والانضمام إليها، وليس فقط الأشخاص الموجودين في اسم المساحة.";
"space_beta_announce_subtitle" = "النسخة الجديدة من المجتمعات";
"user_session_verification_unknown" = "حالة التحقق غير معروفة";
"user_session_overview_session_title" = "جلسة";
"wysiwyg_composer_start_action_location" = "موقع";
"user_session_details_last_activity" = "آخر نشاط";
"spaces_creation_sharing_type_me_and_teammates_detail" = "مساحة خاصة لك ولزملائك في الفريق";
"user_session_details_device_os" = "نظام التشغيل";
"spaces_creation_private_space_title" = "مساحتك الخاصة";
"wysiwyg_composer_start_action_polls" = "استطلاعات الرأي";
"wysiwyg_composer_format_action_unordered_list" = "تبديل القائمة النقطية";
"wysiwyg_composer_format_action_indent" = "زيادة المسافة البادئة";
"wysiwyg_composer_format_action_un_indent" = "تقليل المسافة البادئة";
"spaces_creation_address_default_message" = "ستكون مساحتك متاحة للعرض في\n%@";
"spaces_creation_new_rooms_general" = "عام";
"notice_display_name_changed_to" = "%@ غيّر اسم العرض الخاص به إلى %@";
"network_offline_prompt" = "يبدو أنه غير متصل بالانترنت.";
"side_menu_action_invite_friends" = "دعوة الأصدقاء";
"spaces_creation_post_process_creating_space" = "إنشاء مساحة";
"user_session_unverified_additional_info" = "قم بالتحقق من جلستك الحالية للحصول على مراسلة آمنة معززة.";
"user_session_unverified_session_description" = "الجلسات غير المُتحققة هي الجلسات التي تم تسجيل الدخول إليها باستخدام بيانات اعتمادك ولكن لم يتم التحقق منها.\n\nيجب عليك التأكد بشكل خاص من التعرف على هذه الجلسات، فقد تُمثل استخدامًا غير مُصرّح به لحسابك.";
"user_other_session_menu_select_sessions" = "اختيار الجلسات";
"poll_timeline_total_votes_not_voted" = "تم الإدلاء بـ %lu من الأصوات. صوّت لرؤية النتائج";
"user_session_permanently_unverified_session_description" = "هذه الجلسة لا تدعم التشفير، لذا لا يمكن التحقق منها.\n\nلن تتمكن من المشاركة في الغرف التي يُفعّل فيها التشفير عند استخدام هذه الجلسة.\n\nلأعلى مستوى من الأمان والخصوصية، يُنصح باستخدام عملاء Matrix الذين يدعمون التشفير.";
"user_session_verified_session_description" = "الجلسات المُتحققة موجودة في أي مكان تستخدم فيه Element بعد إدخال كلمة المرور أو تأكيد هويتك بجلسة مُتحققة أخرى.\n\nهذا يعني أن لديك جميع المفاتيح اللازمة لفتح رسائلك المُشفرة وتأكيد ثقتك بهذه الجلسة للمستخدمين الآخرين.";
"device_name_desktop" = "%@ سطح المكتب";
"user_sessions_overview_current_session_section_title" = "الجلسة الحالية";
"user_sessions_view_all_action" = "عرض الكل (%d)";
"user_session_verify_action" = "التحقق من الجلسة";
"user_session_view_details" = "عرض التفاصيل";
"user_session_details_title" = "تفاصيل الجلسة";
"user_session_inactive_session_title" = "الجلسات غير النشطة";
"device_type_name_web" = "الويب";
"wysiwyg_composer_start_action_camera" = "كاميرا";
"wysiwyg_composer_start_action_voice_broadcast" = "البث الصوتي";
"wysiwyg_composer_format_action_code_block" = "مكون كود التبديل";
"user_other_session_no_verified_sessions" = "لم يتم العثور على جلسات تم التحقق منها.";
"user_inactive_session_item" = "غير نشط لمدة 90 يومًا أو أكثر";
"wysiwyg_composer_format_action_quote" = "اقتباس التبديل";
"wysiwyg_composer_link_action_edit_title" = "تعديل الرابط";
"key_backup_setup_passphrase_set_passphrase_action" = "تعيين عبارة";
"user_session_unverified_session_title" = "جلسة غير مُتحققة";
"device_name_web" = "%@ ويب";
"user_session_verification_unknown_short" = "غير معروف";
"message_reply_to_sender_sent_their_live_location" = "الموقع المباشر";
"spaces_creation_add_rooms_message" = "بما أن هذه المساحة مخصصة لك فقط، فلن يتم إعلام أحد. يمكنك إضافة المزيد لاحقًا.";
"spaces_creation_post_process_creating_space_task" = "إنشاء %@";
"spaces_creation_post_process_uploading_avatar" = "تحميل الصورة الرمزية";
"spaces_creation_in_spacename_plus_many" = "في مساحات %@ + %@";
"voice_message_stop_locked_mode_recording" = "اضغط على التسجيل الخاص بك لإيقافه أو الاستماع إليه";
"poll_edit_form_option_number" = "الخيار %lu";
"poll_timeline_total_votes" = "%lu الأصوات المدلى بها";
"location_sharing_live_timer_incoming" = "مباشر حتى %@";
"user_sessions_overview_link_device" = "ربط جهاز";
"user_other_session_permanently_unverified_additional_info" = "هذه الجلسة لا تدعم التشفير وبالتالي لا يمكن التحقق منها.";
"user_other_session_verified_additional_info" = "هذه الجلسة جاهزة للمراسلة الآمنة.";
"user_session_push_notifications_message" = "عند تشغيلها، ستتلقى هذه الجلسة إشعارات فورية.";
"user_session_details_device_browser" = "المتصفح";
"user_session_details_application_url" = "عنوان URL";
"wysiwyg_composer_action_maximise_action" = "توسيع الملحن";
"wysiwyg_composer_format_action_bold" = "تطبيق تنسيق غامق";
"wysiwyg_composer_format_action_italic" = "تطبيق التنسيق المائل";
"wysiwyg_composer_format_action_link" = "تطبيق تنسيق الرابط";
"wysiwyg_composer_link_action_link" = "وصلة";
"device_type_name_unknown" = "غير معروف";
"user_other_session_verified_sessions_header_subtitle" = "للحصول على أفضل مستوى من الأمان، قم بتسجيل الخروج من أي جلسة لا تعرفها أو لا تستخدمها بعد الآن.";
"device_type_name_desktop" = "سطح المكتب";
"wysiwyg_composer_link_action_create_title" = "إنشاء رابط";
"deselect_all" = "إلغاء تحديد الكل";
"ignore_user" = "تجاهل المستخدم";
"device_verification_emoji_train" = "يدرب";
"spaces_creation_address_invalid_characters" = "%@\nيحتوي على أحرف غير صالحة";
"spaces_creation_new_rooms_message" = "سننشئ غرفة لكل واحد منهم.";
"spaces_creation_new_rooms_random" = "عشوائي";
"spaces_add_room_missing_permission_message" = "ليس لديك الأذونات اللازمة لإضافة غرف إلى هذه المساحة.";
"side_menu_reveal_action_accessibility_label" = "اللوحة اليسرى";
"voice_message_release_to_send" = "اضغط باستمرار للتسجيل، ثم حرر للإرسال";
"leave_space_and_more_rooms" = "اترك مساحة و %@غرف";
"user_session_learn_more" = "معرفة المزيد";
"wysiwyg_composer_action_minimise_action" = "ملحن الانكماش";
"user_session_rename_session_description" = "يستطيع المستخدمون الآخرون في الرسائل المباشرة والغرف التي تنضم إليها عرض قائمة كاملة بجلساتك.\n\nهذا يمنحهم الثقة بأنهم يتحدثون إليك حقًا، ويعني أيضًا أنهم يستطيعون رؤية اسم الجلسة التي تُدخلها هنا.";
"user_other_session_filter_menu_verified" = "تم التحقق";
"user_other_session_no_inactive_sessions" = "لم يتم العثور على جلسات غير نشطة.";
"device_verification_emoji_hat" = "قبعة";
"space_beta_announce_badge" = "بيتا (تجريبي)";
"spaces_creation_new_rooms_title" = "ما هي بعض المناقشات التي ستجريها؟";
"spaces_creation_invite_by_username_title" = "ادعُ فريقك";
"spaces_creation_post_process_inviting_users" = "دعوة %@ المستخدمين";
"leave_space_action" = "غادر المساحة";
"leave_space_selection_all_rooms" = "حدد جميع الغرف";
"space_avatar_view_accessibility_hint" = "تغيير الصورة الرمزية للمساحة";
"leave_space_selection_title" = "تحديد الغرف";
"side_menu_action_settings" = "إعدادات";
"side_menu_coach_message" = "مرر لليمين أو انقر لرؤية جميع الغرف";
"voice_message_broadcast_in_progress_title" = "لا يمكن بدء الرسالة الصوتية";
"voice_message_broadcast_in_progress_message" = "لا يمكنك بدء رسالة صوتية لأنك تُسجِّل بثًا مباشرًا. يُرجى إنهاء البث المباشر لبدء تسجيل رسالة صوتية";
"voice_broadcast_unauthorized_title" = "لا يمكن بدء بث صوتي جديد";
"user_session_inactive_session_description" = "الجلسات غير النشطة هي جلسات لم تستخدمها منذ فترة، ولكنها تستمر في تلقي مفاتيح التشفير.\n\nيؤدي حذف الجلسات غير النشطة إلى تحسين الأمان والأداء، ويسهّل عليك تحديد ما إذا كانت الجلسة الجديدة مشبوهة.";
"user_other_session_unverified_sessions_header_subtitle" = "قم بالتحقق من جلساتك للحصول على رسائل آمنة معززة أو قم بتسجيل الخروج من الجلسات التي لا تعرفها أو لا تستخدمها بعد الآن.";
"user_session_name" = "%@: %@";
"user_other_session_filter_menu_unverified" = "غير مُتحقق";
"user_session_details_session_section_footer" = "انسخ أي بيانات عن طريق النقر عليها مع الاستمرار في الضغط عليها.";
"user_session_details_device_ip_location" = "موقع IP";
"user_inactive_session_item_with_date" = "غير نشط لمدة 90 يومًا أو أكثر (%@)";
"user_session_overview_session_details_button_title" = "تفاصيل الجلسة";
"wysiwyg_composer_start_action_media_picker" = "مكتبة الصور";
"user_session_unverified_short" = "غير مُتحقق";
"wysiwyg_composer_format_action_underline" = "تطبيق تنسيق التسطير";
"spaces_creation_empty_room_name_error" = "الاسم مطلوب";
"room_event_encryption_info_key_authenticity_not_guaranteed" = "لا يمكن ضمان صحة هذه الرسالة المشفرة على هذا الجهاز.";
"spaces_creation_sharing_type_me_and_teammates_title" = "أنا وزملائي في الفريق";
"spaces_creation_invite_by_username" = "دعوة عن طريق اسم المستخدم";
"spaces_creation_in_spacename_plus_one" = "في %@ + 1 مساحة";
"side_menu_action_help" = "مساعدة";
"user_session_push_notifications" = "إشعارات الدفع";
"user_other_session_filter_menu_all" = "جميع الجلسات";
"user_other_session_filter_menu_inactive" = "غير نشط";
"user_session_details_application_section_header" = "تطبيق";
"user_session_details_session_id" = "معرف الجلسة";
"wysiwyg_composer_start_action_attachments" = "المرفقات";
"wysiwyg_composer_format_action_ordered_list" = "تبديل القائمة المرقمة";
"device_verification_emoji_trumpet" = "بوق";
"key_verification_tile_request_incoming_approval_decline" = "رفض";
"spaces_creation_address_already_exists" = "%@\nموجود بالفعل";
"spaces_creation_email_invites_email_title" = "بريد إلكتروني";
"spaces_creation_sharing_type_just_me_title" = "أنا فقط";
"spaces_creation_sharing_type_just_me_detail" = "مساحة خاصة لتنظيم غرفك";
"spaces_creation_in_many_spaces" = "في %@ مساحات";
"key_backup_recover_from_recovery_key_recovery_key_title" = "دخول";
"device_verification_emoji_thumbs up" = "ممتاز";
"spaces_creation_cancel_title" = "توقف عن إنشاء مساحة؟";
"spaces_creation_cancel_message" = "سيتم فقدان تقدمك.";
"spaces_creation_new_rooms_support" = "دعم";
"spaces_creation_email_invites_title" = "ادعُ فريقك";
"leave_space_selection_no_rooms" = "عدم تحديد الغرف";
"space_avatar_view_accessibility_label" = "الصورة الرمزية";
"user_session_verified" = "جلسة تم التحقق منها";
"wysiwyg_composer_start_action_text_formatting" = "تنسيق النص";
"wysiwyg_composer_format_action_inline_code" = "تطبيق تنسيق الكود المضمن";
"wysiwyg_composer_link_action_text" = "نص";
"service_terms_modal_policy_checkbox_accessibility_hint" = "حدد لقبول %@";
"user_avatar_view_accessibility_label" = "الصورة الرمزية";
"spaces_creation_public_space_title" = "مساحتك العامة";
"spaces_creation_email_invites_message" = "ويمكنك دعوتهم لاحقًا أيضًا.";
"poll_timeline_reply_ended_poll" = "انتهى الاستطلاع";
"user_session_verified_additional_info" = "جلستك الحالية جاهزة للمراسلة الآمنة.";
"user_session_verification_unknown_additional_info" = "قم بالتحقق من جلستك الحالية للكشف عن حالة التحقق الخاصة بهذه الجلسة.";
"user_session_rename_session_title" = "إعادة تسمية الجلسات";
"user_session_item_details" = "%1$@ · %2$@";
"user_session_details_session_name" = "اسم الجلسة";
"user_session_details_application_name" = "اسم";
"user_other_session_selected_count" = "%@ تم اختياره";
"wysiwyg_composer_format_action_strikethrough" = "تطبيق تنسيق الشطب";
"spaces_creation_invite_by_username_message" = "ويمكنك دعوتهم لاحقًا أيضًا.";
"voice_message_remaining_recording_time" = "%@s متبقية";
"spaces_creation_sharing_type_title" = "مع من تعمل؟";
"side_menu_action_feedback" = "تعليق";
"user_session_unverified" = "جلسة غير مُتحققة";
"user_other_session_security_recommendation_title" = "جلسات أخرى";
"user_session_details_application_version" = "إصدار";
"key_backup_recover_from_passphrase_passphrase_title" = "دخول";
"device_verification_emoji_santa" = "سانتا";
"major_update_title" = "الشغب الآن %@";
"home_context_menu_unmute" = "إزالة الكتم";
"spaces_creation_new_rooms_room_name_title" = "اسم الغرفة";
"spaces_creation_sharing_type_message" = "تأكد من أن الأشخاص المناسبين لديهم حق الوصول %@. يمكنك تغيير هذا لاحقًا.";
"spaces_creation_add_rooms_title" = "ماذا تريد أن تضيف؟";
"spaces_creation_post_process_creating_room" = "إنشاء %@";
"spaces_creation_post_process_adding_rooms" = "إضافة %@ غرف";
"spaces_creation_in_one_space" = "في مساحة واحدة";
"voice_message_lock_screen_placeholder" = "رسالة صوتية";
"user_session_verified_short" = "تم التحقق";
"user_other_session_unverified_additional_info" = "قم بالتحقق من هذه الجلسة أو تسجيل الخروج منها للحصول على أفضل مستوى من الأمان والموثوقية.";
"user_other_session_current_session_details" = "جلستك الحالية";
"user_other_session_filter" = "عامل التصفية";
"user_other_session_no_unverified_sessions" = "لم يتم العثور على جلسات غير موثقة.";
"user_other_session_clear_filter" = "مسح عامل التصفية";
"user_other_session_menu_sign_out_sessions" = "تسجيل الخروج من جلسات %@";
"user_session_item_details_last_activity" = "آخر نشاط %@";
"user_session_details_device_section_header" = "جهاز";
"user_session_details_device_ip_address" = "عنوان IP";
"spaces_coming_soon_title" = "قادم قريباً";
"leave_space_and_one_room" = "اترك مساحة وغرفة واحدة";
"room_widget_permission_theme_permission" = "موضوعك";
"user_avatar_view_accessibility_hint" = "تغيير صورة المستخدم الرمزية";
"user_session_details_session_section_header" = "جلسة";
"user_session_overview_current_session_title" = "الجلسة الحالية";
"wysiwyg_composer_start_action_stickers" = "ملصقات";
"user_session_got_it" = "فهمتها";
"user_session_verified_session_title" = "الجلسات التي تم التحقق منها";
"user_session_details_device_model" = "نموذج";
"device_name_unknown" = "عميل غير معروف";
"device_type_name_mobile" = "جوال";

View File

@@ -479,6 +479,7 @@ Tap the + to start adding people.";
"room_participants_invite_malformed_id" = "Malformed ID. Should be an email address or a Matrix ID like '@localpart:domain'";
"room_participants_invited_section" = "INVITED";
"room_participants_start_new_chat_error_using_user_email_without_identity_server" = "No identity server is configured so you cannot start a chat with a contact using an email.";
"room_participants_leave_not_allowed_for_last_owner_msg" = "You can't leave the room since you're the only owner of it.";
"room_participants_online" = "Online";
"room_participants_offline" = "Offline";
@@ -519,10 +520,12 @@ Tap the + to start adding people.";
"room_participants_security_information_room_encrypted" = "Messages in this room are end-to-end encrypted.\n\nYour messages are secured with locks and only you and the recipient have the unique keys to unlock them.";
"room_participants_security_information_room_encrypted_for_dm" = "Messages here are end-to-end encrypted.\n\nYour messages are secured with locks and only you and the recipient have the unique keys to unlock them.";
"room_member_power_level_owner_in" = "Owner in %@";
"room_member_power_level_admin_in" = "Admin in %@";
"room_member_power_level_moderator_in" = "Moderator in %@";
"room_member_power_level_custom_in" = "Custom (%@) in %@";
"room_member_power_level_short_owner" = "Owner";
"room_member_power_level_short_admin" = "Admin";
"room_member_power_level_short_moderator" = "Mod";
"room_member_power_level_short_custom" = "Custom";

View File

@@ -2735,3 +2735,5 @@
// MARK: Sunset
"sunset_delegated_oidc_registration_not_supported_title" = "Selle rakendusega enam ei saa luua kasutajakontot teenuses %1$@";
"room_member_power_level_owner_in" = "„%@“ jututoa omanik";
"room_member_power_level_short_owner" = "Omanik";

View File

@@ -2990,3 +2990,5 @@
// MARK: Sunset
"sunset_delegated_oidc_registration_not_supported_title" = "Anda sudah tidak bisa lagi membuat akun dengan %1$@ menggunakan aplikasi ini";
"sunset_delegated_oidc_registration_not_supported_generic_error" = "Anda sudah tidak bisa lagi membuat akun dengan homeserver yang ditentukan menggunakan aplikasi ini";
"room_member_power_level_owner_in" = "Pemilik dalam %@";
"room_member_power_level_short_owner" = "Pemilik";

View File

@@ -121,3 +121,4 @@
/* New voice broadcast from a specific person, not referencing a room. */
"VOICE_BROADCAST_FROM_USER" = "%@ ha iniziato una trasmissione vocale";
"UNSUPPORTED_CALL" = "Chiamata non supportata";

View File

@@ -2754,3 +2754,10 @@
"wysiwyg_composer_action_maximise_action" = "Espandi il compositore";
"room_action_report" = "Segnala stanza";
"room_action_report_prompt_reason" = "Motivo della segnalazione della stanza";
"call_unsupported_matrix_rtc_call" = "Chiamata non supportata. Per unirsi a questa chiamata è necessaria la nuova app Element X.";
"sunset_delegated_oidc_registration_not_supported_title" = "Non puoi più creare un account con %1$@ usando quest'app";
"sunset_delegated_oidc_registration_not_supported_message" = "Scarica %1$@ per utilizzare %2$@ per il tuo account o scegli un homeserver differente.";
"sunset_download_banner_title" = "Scarica %1$@";
"sunset_download_banner_message" = "Più veloce, più sicura, e piena di potenti strumenti di collaborazione.";
"sunset_download_banner_learn_more" = "Per saperne di più";
"sunset_delegated_oidc_registration_not_supported_generic_error" = "Non puoi più creare un account con l'homeserver indicato usando quest'app";

View File

@@ -3,3 +3,4 @@
"NSPhotoLibraryUsageDescription" = "Foto bibliotēka tiek izmantota, lai nosūtītu fotoattēlus un video.";
"NSMicrophoneUsageDescription" = "Mikrofons tiek izmantots, lai uzņemtu video un veiktu zvanus.";
"NSContactsUsageDescription" = "Kontaktu grāmata tiek izmantota, lai meklētu lietotājus pēc epasta adreses vai telefona numura Riot aplikācijā.";
"NSFaceIDUsageDescription" = "Face ID tiek izmantots, lai piekļūtu lietotnei.";

View File

@@ -118,3 +118,5 @@
/* New file message from a specific person, not referencing a room. */
"LOCATION_FROM_USER" = "%@ compartilhou a localização dela(e)";
"VOICE_BROADCAST_FROM_USER" = "%@ iniciou uma transmissão de voz";
"UNSUPPORTED_CALL" = "Chamada não suportada";

View File

@@ -2556,8 +2556,8 @@
"authentication_qr_login_start_subtitle" = "Use a câmera neste dispositivo para scannar o QR code mostrado em seu outro dispositivo:";
"authentication_qr_login_start_title" = "Scannar QR code";
"authentication_login_with_qr" = "Fazer signin com QR code";
"wysiwyg_composer_format_action_underline" = "Aplicar formato sublinhar";
"wysiwyg_composer_format_action_strikethrough" = "Aplicar formato tachar";
"wysiwyg_composer_format_action_underline" = "Aplicar formato de sublinhado";
"wysiwyg_composer_format_action_strikethrough" = "Aplicar formato tachado";
"wysiwyg_composer_format_action_italic" = "Aplicar formato itálico";
// Formatting Actions
@@ -2671,3 +2671,77 @@
"room_command_join_room_description" = "Entra na sala com o endereço fornecido";
"room_command_invite_user_description" = "Convida o usuário com o ID fornecido para a sala atual";
"authentication_qr_login_failure_device_not_supported" = "Vincular com este dispositivo não é suportado.";
"key_verification_scan_qr_code_information_other_device" = "Aponte sua câmera para o código QR exibido em seu outro dispositivo para verificar esta sessão";
"wysiwyg_composer_action_minimise_action" = "Compositor psiquiatra";
"pill_message" = "Mensagem";
"sunset_delegated_oidc_registration_not_supported_title" = "Você não pode mais criar uma conta com %1$@ usando este aplicativo";
"sunset_delegated_oidc_registration_not_supported_message" = "Baixe %1$@ para usar %2$@ na sua conta ou escolha um servidor doméstico diferente.";
"sunset_delegated_oidc_registration_not_supported_generic_error" = "Você não pode mais criar uma conta com o servidor doméstico inserido usando este aplicativo";
"sunset_download_banner_title" = "Baixar %1$@";
"sunset_download_banner_message" = "Mais rápido, mais seguro e repleto de poderosas ferramentas de colaboração.";
"sunset_download_banner_learn_more" = "Saber mais";
"call_unsupported_matrix_rtc_call" = "Chamada não suportada. O novo aplicativo Element X é necessário para participar desta chamada.";
"voice_broadcast_voip_cannot_start_description" = "Você não pode iniciar uma chamada porque está gravando uma transmissão ao vivo. Encerre a transmissão ao vivo para iniciar a chamada.";
"poll_timeline_decryption_error" = "Devido a erros de descriptografia, alguns votos podem não ser contados";
"wysiwyg_composer_action_maximise_action" = "Expandir compositor";
"room_command_change_room_topic_description" = "Define o tema da sala";
"room_action_report" = "Sala de relatórios";
"room_action_report_prompt_reason" = "Motivo da denúncia desta sala";
"room_command_discard_session_description" = "Força o descarte da sessão de grupo de saída atual em uma sala criptografada";
"room_command_error_unknown_command" = "Comando inválido ou não tratado";
"pill_room_fallback_display_name" = "Espaço/Sala";
"device_verification_self_verify_wait_recover_secrets_additional_help" = "Não consegue acessar uma sessão %@ existente?";
"room_waiting_other_participants_title" = "Aguardando a adesão dos usuários %@";
"voice_broadcast_playback_lock_screen_placeholder" = "Transmissão de voz";
"device_verification_self_verify_open_on_other_device_title" = "Abra %@ no seu outro dispositivo";
"key_verification_scan_qr_code_information_other_session" = "Aponte sua câmera para o código QR exibido em seu outro dispositivo para verificar sua sessão";
"voice_message_broadcast_in_progress_title" = "Não é possível iniciar a mensagem de voz";
"voice_message_broadcast_in_progress_message" = "Você não pode iniciar uma mensagem de voz, pois está gravando uma transmissão ao vivo. Encerre a transmissão ao vivo para iniciar a gravação da mensagem de voz.";
"notice_display_name_changed_to" = "%@ mudou seu nome de exibição para %@";
"pill_message_from" = "Mensagem de %@";
"poll_timeline_loading" = "Carregando...";
"wysiwyg_composer_format_action_un_indent" = "Diminuir recuo";
"wysiwyg_composer_format_action_indent" = "Aumentar o recuo";
"room_details_polls" = "Histórico de pesquisas";
"poll_history_no_active_poll_text" = "Não há enquetes ativas nesta sala";
"poll_history_fetching_error" = "Erro ao buscar enquetes.";
"wysiwyg_composer_format_action_code_block" = "Alternar bloco de código";
"poll_history_detail_view_in_timeline" = "Ver enquete na linha do tempo";
"settings_acceptable_use" = "Política de Uso Aceitável";
"launch_loading_generic" = "Sincronizando suas conversas";
"launch_loading_delay_warning" = "Isso pode demorar um pouco mais.\nObrigado pela sua paciência.";
"poll_history_no_past_poll_text" = "Não há pesquisas anteriores nesta sala";
"poll_timeline_ended_text" = "Terminou a enquete";
"home_context_menu_mark_as_unread" = "Marcar como não lido";
"poll_history_title" = "Histórico de pesquisas";
"poll_history_active_segment_title" = "Pesquisas ativas";
"wysiwyg_composer_format_action_unordered_list" = "Alternar lista com marcadores";
"voice_broadcast_recorder_connection_error" = "Erro de conexão - Gravação pausada";
"wysiwyg_composer_format_action_quote" = "Alternar citação";
"manage_session_redirect" = "Você será redirecionado ao provedor de autenticação do seu servidor para concluir o logout.";
"manage_session_redirect_error" = "Funcionalidade indisponível no momento. Entre em contato com o administrador do seu servidor doméstico.";
"room_command_reset_user_power_level_description" = "Usuário Deops com ID fornecido";
"settings_push_rules_error" = "Ocorreu um erro ao atualizar suas preferências de notificação. Tente alternar sua opção novamente.";
"key_verification_scan_qr_code_title" = "Escaneie o código QR";
"key_verification_scan_qr_code_information_other_user" = "Aponte sua câmera para o código QR exibido no dispositivo para verificar a sessão";
"key_verification_scan_qr_code_information_new_session" = "Aponte sua câmera para o código QR exibido em seu outro dispositivo para verificar sua nova sessão";
"voice_broadcast_connection_error_title" = "Erro de conexão";
"voice_broadcast_connection_error_message" = "Infelizmente, não podemos iniciar a gravação no momento. Tente novamente mais tarde.";
"voice_broadcast_playback_unable_to_decrypt" = "Não é possível decifrar esta transmissão de voz.";
"poll_history_past_segment_title" = "Pesquisas anteriores";
"poll_history_load_more" = "Carregar mais enquetes";
"poll_timeline_reply_ended_poll" = "Enquete encerrada";
"wysiwyg_composer_format_action_ordered_list" = "Alternar lista numerada";
"key_backup_recover_from_private_key_progress" = "%@%% Concluído";
"pill_message_in" = "Mensagem em %@";
"poll_history_loading_text" = "Exibindo enquetes";
"poll_history_no_active_poll_period_text" = "Não há enquetes ativas nos últimos %@ dias. Carregue mais enquetes para ver as enquetes dos meses anteriores.";
"poll_history_no_past_poll_period_text" = "Não há enquetes anteriores nos últimos %@ dias. Carregue mais enquetes para ver as enquetes dos meses anteriores.";
"settings_manage_account_title" = "Conta";
"settings_manage_account_action" = "Gerenciar conta";
"settings_manage_account_description" = "Gerencie sua conta em %@";
"key_verification_self_verify_security_upgrade_alert_title" = "Aplicativo atualizado";
"key_verification_self_verify_security_upgrade_alert_message" = "A segurança das mensagens foi aprimorada com a atualização mais recente. Verifique novamente o seu dispositivo.";
"device_verification_self_verify_open_on_other_device_information" = "Você precisa verificar esta sessão para ler seu histórico de mensagens seguras.\n\nAbra o Element em um dos seus outros dispositivos e siga as instruções.";
"voice_broadcast_voip_cannot_start_title" = "Não é possível iniciar uma chamada";
"room_waiting_other_participants_message" = "Depois que os usuários convidados se juntarem a %@, você poderá bater papo e a sala será criptografada de ponta a ponta";

View File

@@ -2124,7 +2124,7 @@
"onboarding_use_case_existing_server_message" = "Хотите присоединиться к существующему серверу?";
"onboarding_use_case_skip_button" = "Пропустить вопрос";
/* The placeholder string contains onboarding_use_case_skip_button as a tappable action */
"onboarding_use_case_not_sure_yet" = "Ещё не уверенны? %@";
"onboarding_use_case_not_sure_yet" = "Ещё не уверены? %@";
"onboarding_use_case_community_messaging" = "Сообщества";
"onboarding_use_case_work_messaging" = "Команды";
"onboarding_use_case_personal_messaging" = "Друзья и семья";

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1 @@
"store_short_description" = "Xavfsiz markazlashmagan chat/VoIP";

View File

@@ -1,5 +1,5 @@
// Permissions usage explanations
"NSCameraUsageDescription" = "Máy ảnh được sử dụng để chụp ảnh và quay phim, thực hiện các cuộc gọi video.";
"NSCameraUsageDescription" = "Máy ảnh được ng để gọi truyền hình hoặc quay phim, chụp ảnh.";
"NSPhotoLibraryUsageDescription" = "Thư viện ảnh được dùng để gửi hình ảnh và videos.";
"NSMicrophoneUsageDescription" = "Element cần quyền truy cập vào mi-crô của bạn để nhận và thực hiện cuộc gọi, quay video, và ghi âm các tin nhắn thoại.";
"NSContactsUsageDescription" = "Element sẽ hiển thị danh bạ của bạn để bạn có thể mời họ trò chuyện.";

View File

@@ -38,17 +38,17 @@
/* Incoming one-to-one video call */
"VIDEO_CALL_FROM_USER" = "Gọi video từ %@";
/* Incoming unnamed voice conference invite from a specific person */
"VOICE_CONF_FROM_USER" = "Gọi nhóm từ %@";
"VOICE_CONF_FROM_USER" = "Cuộc gọi nhóm từ %@";
/* Incoming unnamed video conference invite from a specific person */
"VIDEO_CONF_FROM_USER" = "Gọi video nhóm từ %@";
"VIDEO_CONF_FROM_USER" = "Cuộc gọi truyền hình nhóm từ %@";
/* Incoming named voice conference invite from a specific person */
"VOICE_CONF_NAMED_FROM_USER" = "Gọi nhóm từ %@: '%@'";
"VOICE_CONF_NAMED_FROM_USER" = "Cuộc gọi nhóm từ %@: '%@'";
/* Incoming named video conference invite from a specific person */
"VIDEO_CONF_NAMED_FROM_USER" = "Gọi video nhóm từ %@: '%@'";
"VIDEO_CONF_NAMED_FROM_USER" = "Cuộc gọi video nhóm từ %@: '%@'";
/** Key verification **/
"KEY_VERIFICATION_REQUEST_FROM_USER" = "%@ muốn xác minh";
"KEY_VERIFICATION_REQUEST_FROM_USER" = "%@ muốn xác thực";
/* Group call from user, CallKit caller name */
"GROUP_CALL_FROM_USER" = "%@ (cuộc gọi nhóm)";
@@ -60,7 +60,7 @@
"USER_MEMBERSHIP_UPDATED" = "%@ đã cập nhật hồ sơ";
/* A user has change their avatar */
"USER_UPDATED_AVATAR" = "%@ đã đổi avatar";
"USER_UPDATED_AVATAR" = "%@ đã đổi ảnh đại diện";
/* A user has change their name to a new name which we don't know */
"GENERIC_USER_UPDATED_DISPLAYNAME" = "%@ đã đổi tên";
@@ -71,7 +71,7 @@
"USER_UPDATED_DISPLAYNAME" = "%@ đã đổi tên sang %@";
/* A user has reacted to a message, but the reaction content is unknown */
"GENERIC_REACTION_FROM_USER" = "%@ đã gửi một tương tác";
"GENERIC_REACTION_FROM_USER" = "%@ đã bày tỏ cảm xúc";
/** Reactions **/
@@ -96,10 +96,10 @@
"STICKER_FROM_USER" = "%@ đã gửi một sticker";
/* A single unread message */
"SINGLE_UNREAD" = "Bạn đã nhận một tin nhắn";
"SINGLE_UNREAD" = "Bạn nhận được một tin nhắn";
/* A single unread message in a room */
"SINGLE_UNREAD_IN_ROOM" = "Bạn đã nhận một tin nhắn trong %@";
"SINGLE_UNREAD_IN_ROOM" = "Bạn nhận được một tin nhắn trong %@";
/* New file message from a specific person, not referencing a room. */
"FILE_FROM_USER" = "%@ đã gửi một tệp %@";
@@ -131,3 +131,6 @@
/** General **/
"Notification" = "Thông báo";
"VOICE_BROADCAST_FROM_USER" = "%@ bắt đầu phát thanh";
"UNSUPPORTED_CALL" = "Cuộc gọi không được hỗ trợ";
"LOCATION_FROM_USER" = "%@ đã chia sẻ vị trí của họ";

View File

@@ -37,14 +37,14 @@
"auth_register" = "Đăng kí";
"auth_submit" = "Gửi đi";
"auth_skip" = "Bỏ qua";
"auth_send_reset_email" = "Gửi email đặt lại";
"auth_send_reset_email" = "Gửi thư đặt lại";
"auth_return_to_login" = "Trở về trang đăng kí";
"auth_user_id_placeholder" = "Email hoặc tên đăng nhập";
"auth_user_id_placeholder" = "Địa chỉ thư điện tử hoặc tên đăng nhập";
"auth_password_placeholder" = "Mật khẩu";
"auth_new_password_placeholder" = "Mật khẩu mới";
"auth_user_name_placeholder" = "Tên đăng nhập";
"auth_optional_email_placeholder" = "Địa chỉ email (không bắt buộc)";
"auth_email_placeholder" = "Địa chỉ email";
"auth_optional_email_placeholder" = "Địa chỉ thư điện tử (không bắt buộc)";
"auth_email_placeholder" = "Địa chỉ thư điện tử";
"auth_optional_phone_placeholder" = "Số điện thoại (không bắt buộc)";
"auth_phone_placeholder" = "Số điện thoại";
"auth_repeat_password_placeholder" = "Nhật lại mật khẩu";
@@ -55,38 +55,38 @@
"warning" = "Cảnh báo";
"auth_invalid_user_name" = "Tên đăng nhập chỉ được chứa các chữ cái, dấu chấm, dấu gạch ngang và dấu gạch dưới";
"auth_invalid_password" = "Mật khẩu quá ngắn (tối thiểu 6 kí tự)";
"auth_invalid_email" = "Địa chỉ Email không hợp lệ";
"auth_invalid_email" = "Địa chỉ Địa thư điện tử không hợp lệ";
"auth_invalid_phone" = "Số điện thoại không hợp lệ";
"auth_missing_password" = "Thiếu mật khẩu";
"auth_add_email_message" = "Thêm địa chỉ email vào tài khoản để người dùng khác có thể tìm thấy bạn và giúp bạn thay đổi mật khẩu về sau.";
"auth_add_phone_message" = "Thêm số điện thoại vào tài khoản của bạn để giúp người dùng khác có thể tìm thấy bạn.";
"auth_add_email_phone_message" = "Thêm địa chỉ email hoặc số điện thoại vào tài khoản để người dùng khác có thể tìm thấy bạn. Địa chỉ email cũng sẽ giúp bạn thay đổi mật khẩu về sau.";
"auth_add_email_and_phone_message" = "Thêm địa chỉ email hoặc số điện thoại vào tài khoản để người dùng khác có thể tìm thấy bạn. Địa chỉ email cũng sẽ giúp bạn thay đổi mật khẩu về sau.";
"auth_missing_email" = "Thiếu địa chỉ email";
"auth_missing_email" = "Thiếu địa chỉ thư điện tử";
"auth_missing_phone" = "Thiếu số điện thoại";
"auth_missing_email_or_phone" = "Thiếu địa chỉ email hoặc số điện thoại";
"auth_email_in_use" = "Địa chỉ email này đã được sử dụng";
"auth_missing_email_or_phone" = "Thiếu địa chỉ thư điện tử hoặc số điện thoại";
"auth_email_in_use" = "Địa chỉ thư điện tử này đã được sử dụng";
"auth_phone_in_use" = "Số điện thoại này đã được sử dụng";
"auth_untrusted_id_server" = "Máy chủ xác thực không được tin cậy";
"auth_password_dont_match" = "Mật khẩu không trùng khớp";
"auth_username_in_use" = "Tên đăng nhập đang được sử dụng";
"auth_forgot_password" = "Quên mật khẩu danh khoản Matrix?";
"auth_email_not_found" = "Gửi email thất bại: Địa chỉ email này không thể tìm thấy";
"auth_email_not_found" = "Gửi thư thất bại: Địa chỉ thư điện tử này không thể tìm thấy";
"auth_use_server_options" = "Sử dụng tùy chọn máy chủ tuỳ chỉnh (nâng cao)";
"auth_email_validation_message" = "Vui lòng kiểm tra email của bẹn để tiếp tục đăng kí";
"auth_email_validation_message" = "Vui lòng kiểm tra hòm thư của bẹn để tiếp tục đăng ký";
"auth_msisdn_validation_title" = "Xác minh đang chờ xử lí";
"auth_msisdn_validation_message" = "Chúng tôi đã gửi mã kích hoạt qua SMS. Vui lòng nhập mã kích hoạt bên dưới.";
"auth_msisdn_validation_error" = "Không thể xác thực số điện thoại.";
"auth_recaptcha_message" = "Home Server này muốn đảm bảo rằng bạn không phải là Robot";
"auth_reset_password_message" = "Để thay đổi mật khẩu, nhập địa chỉ email được kết nối với tài khoản của bạn:";
"auth_reset_password_missing_email" = "Bạn phải nhập địa chỉ email đã được kết nối với tài khoản của bạn.";
"auth_reset_password_message" = "Để thay đổi mật khẩu, nhập địa chỉ thư điện tử được kết nối với tài khoản của bạn:";
"auth_reset_password_missing_email" = "Bạn phải nhập địa chỉ thư điện tử đã được kết nối với tài khoản của bạn.";
"auth_reset_password_missing_password" = "Bạn phải nhập mật khẩu mới.";
"auth_reset_password_email_validation_message" = "Email đã được gửi tới %@. Khi bạn đã theo liên kết trong đó, bấm vào dưới đây.";
"auth_reset_password_next_step_button" = "Tôi đã xác thực địa chỉ email của tôi";
"auth_reset_password_error_unauthorized" = "Xác thực địa chỉ email thất bại: hãy đảm bảo rằng bạn đã bấm vào địa chỉ đính kèm trong email";
"auth_reset_password_error_not_found" = "Địa chỉ email có vẻ chưa được liên kết với Matrix ID trên homeserver này.";
"auth_reset_password_email_validation_message" = "Thư đã được gửi tới %@. Khi bạn đã theo liên kết trong đó, bấm vào dưới đây.";
"auth_reset_password_next_step_button" = "Tôi đã xác thực địa chỉ thư điện tử của tôi";
"auth_reset_password_error_unauthorized" = "Xác thực địa chỉ thư điện tử thất bại: hãy đảm bảo rằng bạn đã bấm vào địa chỉ đính kèm trong thư";
"auth_reset_password_error_not_found" = "Địa chỉ thư điện tử của bạn không có tài khoản trên máy chủ nhà này.";
"auth_reset_password_success_message" = "Mật khẩu của bạn đã được thiết lập lại.\n\nBạn đã được đăng xuất khỏi tất cả các thiết bị và sẽ không còn nhận được thông báo. Để bật lại thông báo, đăng nhập lại trên mỗi thiết bị.";
"auth_add_email_and_phone_warning" = "Đăng kí với mật khẩu và số điện thoại cùng lúc chưa được hỗ trợ cho tới khi api được thiết lập. Duy nhất số điện thoại sẽ được liên kết với với tài khoản. Bạn sẽ phải thêm email vào hồ sơ trong mục cài đặt.";
"auth_add_email_and_phone_warning" = "Đăng ký bằng cả địa chỉ thư điện tử và số điện thoại chưa được hỗ trợ vì chưa có API. Chỉ có số điện thoại được thêm vào tài khoản của bạn. Bạn có thể thêm địa chỉ thư điện tử trong phần cài đặt hồ sơ.";
// Chat creation
"room_creation_title" = "Cuộc trò chuyện mới";
"room_creation_account" = "Tài khoản";
@@ -1594,10 +1594,10 @@
"auth_reset_password_error_is_required" = "Không có máy chủ xác thực nào được cấu hình: thêm một trong các tùy chọn máy chủ để đặt lại mật khẩu của bạn.";
"auth_forgot_password_error_no_configured_identity_server" = "Không có máy chủ xác thực nào được cấu hình: thêm một để đặt lại mật khẩu của bạn.";
"auth_phone_is_required" = "Không có máy chủ xác thực nào được cấu hình để bạn không thể thêm số điện thoại để đặt lại mật khẩu của mình trong tương lai.";
"auth_email_is_required" = "Không có máy chủ xác thực nào được cấu hình để bạn không thể thêm địa chỉ email để đặt lại mật khẩu của mình trong tương lai.";
"auth_add_email_phone_message_2" = "Đặt email để khôi phục tài khoản. Sử dụng email hoặc điện thoại sau này để được tùy chọn phát hiện bởi những người biết bạn.";
"auth_email_is_required" = "Không có máy chủ xác thực nào được cấu hình nên bạn không thể thêm địa chỉ thư điện tử để đặt lại mật khẩu của mình trong tương lai.";
"auth_add_email_phone_message_2" = "Đặt địa chỉ thư điện tử để khôi phục tài khoản. Sử dụng địa chỉ thư điện tử hoặc điện thoại sau này để được tùy chọn phát hiện bởi những người biết bạn.";
"auth_add_phone_message_2" = "Đặt điện thoại và sau đó có thể tùy chọn được tìm kiếm bởi những người biết bạn.";
"auth_add_email_message_2" = "Đặt email để khôi phục tài khoản và sau đó có thể tùy chọn được tìm kiếm bởi những người biết bạn.";
"auth_add_email_message_2" = "Đặt địa chỉ thư điện tử để khôi phục tài khoản và sau đó có thể tùy chọn được tìm kiếm bởi những người biết bạn.";
"auth_login_single_sign_on" = "Đăng nhập";
// Accessibility
@@ -1994,3 +1994,16 @@
"onboarding_splash_register_button_title" = "Tạo danh khoản";
"accessibility_button_label" = "nút";
"enable" = "Bật";
"authentication_choose_password_not_verified_title" = "Địa chỉ thư điện tử chưa được xác nhận";
"authentication_verify_email_text_field_placeholder" = "Địa chỉ thư điện tử";
"authentication_verify_email_waiting_title" = "Xác nhận địa chỉ thư điện tử của bạn.";
"authentication_forgot_password_input_title" = "Nhập địa chỉ thư điện tử";
"authentication_forgot_password_text_field_placeholder" = "Địa chỉ thư điện tử";
"authentication_forgot_password_waiting_button" = "Gửi lại";
"authentication_login_username" = "Tên đăng nhập / Địa chỉ thư điện tử / Số điện thoại";
"authentication_verify_email_input_title" = "Nhập địa chỉ thư điện tử";
"authentication_verify_email_waiting_hint" = "Không nhận được thư?";
"authentication_verify_email_waiting_button" = "Gửi lại";
"authentication_verify_email_input_message" = "%@ cần xác thực tài khoản của bạn";
"authentication_verify_email_waiting_message" = "Làm theo chỉ dẫn được gửi đến %@";
"authentication_forgot_password_waiting_title" = "Kiểm tra hòm thư của bạn.";

View File

@@ -1598,7 +1598,7 @@
"accessibility_button_label" = "按钮";
"enable" = "启用";
"onboarding_splash_page_1_message" = "独立安全的通信,为您提供与家中面谈相同的私密性。";
"onboarding_splash_page_1_title" = "掌控的对话。";
"onboarding_splash_page_1_title" = "掌控的对话。";
// Onboarding
"onboarding_splash_register_button_title" = "注册";
@@ -2086,7 +2086,7 @@
"room_many_users_are_typing" = "%@、%@和其他人正在输入……";
/* The placeholder %1$tu will be replaced with a number and %2$@ with the user's search terms. */
"directory_search_results" = "为%2$@找到%1$tu个结果";
"onboarding_splash_page_2_title" = "一切都在的掌控中。";
"onboarding_splash_page_2_title" = "一切都在的掌控中。";
"onboarding_congratulations_personalize_button" = "个性化用户资料";
/* The placeholder string contains the user's matrix ID */
"onboarding_congratulations_message" = "你的账户%@已创建";
@@ -2101,7 +2101,7 @@
"onboarding_use_case_title" = "你会和谁聊得最多?";
"onboarding_splash_page_4_message" = "Element也很适合工作场所。受世界最安全的组织的信任。";
"onboarding_splash_page_3_message" = "端到端加密且不要求电话号码。无广告或数据挖掘。";
"onboarding_splash_page_2_message" = "选择在哪里保存的对话,给控制和独立。通过Matrix连接。";
"onboarding_splash_page_2_message" = "选择在哪里保存的对话,给控制和独立。通过Matrix连接。";
// MARK: Reactions
@@ -2433,5 +2433,10 @@
"room_command_discard_session_description" = "强制当前已在加密房间中的外部群组会话失效";
"call_jitsi_unable_to_start" = "无法开始会议通话";
"room_suggestion_settings_screen_title" = "将房间设置为该空间中的建议房间";
"room_suggestion_settings_screen_message" = "向空间成员推荐建议房间";
"room_suggestion_settings_screen_message" = "向空间成员推荐建议房间";
"key_backup_recover_from_private_key_progress" = "完成 %@%%";
"sunset_download_banner_learn_more" = "了解更多";
"sunset_delegated_oidc_registration_not_supported_message" = "下载 %1$@ 后才能在您的账户中使用 %2$@,或选择另一个家服务器。";
"sunset_delegated_oidc_registration_not_supported_generic_error" = "您已无法用该家服务器在本应用中创建账户";
"sunset_download_banner_title" = "下载 %1$@";
"sunset_delegated_oidc_registration_not_supported_title" = "您已无法再用 %1$@ 在本应用中创建账户";

View File

@@ -0,0 +1,36 @@
//
// Copyright 2025 New Vector Ltd
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
//
@objc
extension MXRoom {
/// Returns true if the user is the last owner of the room, but not the last member.
func isLastOwner() async throws -> Bool {
let userID = mxSession.myUserId
let state = try await state()
let requiredPowerLevel: RoomPowerLevel = state.isMSC4289Supported() ? .owner : .admin
guard state.powerLevelOfUser(withUserID: userID) >= requiredPowerLevel.rawValue else {
return false
}
guard let joinedMembers = try await members()?.members(with: .join) else {
return false
}
var areOtherMembers = false
for member in joinedMembers where member.userId != userID {
// User is not the last member in the whole room.
areOtherMembers = true
// If there are other owners/admins the user can leave
if state.powerLevelOfUser(withUserID: member.userId) >= requiredPowerLevel.rawValue {
return false
}
}
return areOtherMembers
}
}

View File

@@ -6155,6 +6155,10 @@ public class VectorL10n: NSObject {
public static func roomMemberPowerLevelModeratorIn(_ p1: String) -> String {
return VectorL10n.tr("Vector", "room_member_power_level_moderator_in", p1)
}
/// Owner in %@
public static func roomMemberPowerLevelOwnerIn(_ p1: String) -> String {
return VectorL10n.tr("Vector", "room_member_power_level_owner_in", p1)
}
/// You will not be able to undo this change as you are promoting the user to have the same power level as yourself.\nAre you sure?
public static var roomMemberPowerLevelPrompt: String {
return VectorL10n.tr("Vector", "room_member_power_level_prompt")
@@ -6171,6 +6175,10 @@ public class VectorL10n: NSObject {
public static var roomMemberPowerLevelShortModerator: String {
return VectorL10n.tr("Vector", "room_member_power_level_short_moderator")
}
/// Owner
public static var roomMemberPowerLevelShortOwner: String {
return VectorL10n.tr("Vector", "room_member_power_level_short_owner")
}
/// Editing
public static var roomMessageEditing: String {
return VectorL10n.tr("Vector", "room_message_editing")
@@ -6439,6 +6447,10 @@ public class VectorL10n: NSObject {
public static var roomParticipantsInvitedSection: String {
return VectorL10n.tr("Vector", "room_participants_invited_section")
}
/// You can't leave the room since you're the only owner of it.
public static var roomParticipantsLeaveNotAllowedForLastOwnerMsg: String {
return VectorL10n.tr("Vector", "room_participants_leave_not_allowed_for_last_owner_msg")
}
/// Leaving
public static var roomParticipantsLeaveProcessing: String {
return VectorL10n.tr("Vector", "room_participants_leave_processing")

View File

@@ -348,7 +348,7 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
// Check user's power in the room
MXRoomPowerLevels *powerLevels = roomState.powerLevels;
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:room.mxSession.myUser.userId];
NSInteger oneSelfPowerLevel = [roomState powerLevelOfUserWithUserID:room.mxSession.myUser.userId];
// The user must be able to send state events to manage widgets
if (oneSelfPowerLevel < powerLevels.stateDefault)

View File

@@ -10,13 +10,16 @@ import Foundation
/// Riot Standard Room Member Power Level
@objc
public enum RoomPowerLevel: Int {
case owner = 150
case admin = 100
case moderator = 50
case user = 0
public init?(rawValue: Int) {
switch rawValue {
case 100...:
case 150...:
self = .owner
case 100...149:
self = .admin
case 50...99:
self = .moderator

View File

@@ -125,7 +125,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
[tableSearchBar setImage:AssetImages.filterOff.image
forSearchBarIcon:UISearchBarIconSearch
state:UIControlStateNormal];
tableSearchBar.delegate = self;
displayedSectionHeaders = [NSMutableArray array];
@@ -133,7 +133,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
_contextMenuProvider = [RecentCellContextMenuProvider new];
self.contextMenuProvider.serviceDelegate = self;
self.contextMenuProvider.menuProviderDelegate = self;
// Set itself as delegate by default.
self.delegate = self;
}
@@ -151,10 +151,10 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
// Register key backup banner cells
[self.recentsTableView registerNib:SecureBackupBannerCell.nib forCellReuseIdentifier:SecureBackupBannerCell.defaultReuseIdentifier];
// Register key verification banner cells
[self.recentsTableView registerNib:CrossSigningSetupBannerCell.nib forCellReuseIdentifier:CrossSigningSetupBannerCell.defaultReuseIdentifier];
[self.recentsTableView registerClass:SectionHeaderView.class
forHeaderFooterViewReuseIdentifier:SectionHeaderView.defaultReuseIdentifier];
@@ -181,7 +181,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
[self.recentsSearchBar setImage:AssetImages.filterOff.image
forSearchBarIcon:UISearchBarIconSearch
state:UIControlStateNormal];
// Observe user interface theme change.
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
@@ -196,7 +196,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
- (void)userInterfaceThemeDidChange
{
[ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar];
self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor;
// Use the primary bg color for the recents table view in plain style.
@@ -204,15 +204,15 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
self.recentsTableView.separatorColor = ThemeService.shared.theme.lineBreakColor;
topview.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
self.view.backgroundColor = ThemeService.shared.theme.backgroundColor;
[ThemeService.shared.theme applyStyleOnSearchBar:tableSearchBar];
[ThemeService.shared.theme applyStyleOnSearchBar:self.recentsSearchBar];
// Force table refresh
[self.recentsTableView reloadData];
[self.emptyView updateWithTheme:ThemeService.shared.theme];
[self setNeedsStatusBarAppearanceUpdate];
}
@@ -270,7 +270,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
[super viewWillAppear:animated];
isViewVisible = YES;
[self.screenTracker trackScreen];
// Reset back user interactions
self.userInteractionEnabled = YES;
@@ -341,7 +341,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
// the selected room (if any) is highlighted.
[self refreshCurrentSelectedCell:YES];
}
if (self.recentsDataSource)
{
[self refreshRecentsTable];
@@ -913,10 +913,10 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
{
Analytics.shared.joinedRoomTrigger = AnalyticsJoinedRoomTriggerInvite;
}
// Avoid multiple openings of rooms
self.userInteractionEnabled = NO;
// Do not stack views when showing room
ScreenPresentationParameters *presentationParameters = [[ScreenPresentationParameters alloc] initWithRestoreInitialDisplay:NO stackAboveVisibleViews:NO];
@@ -935,7 +935,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
- (void)showRoomPreviewWithData:(RoomPreviewData*)roomPreviewData
{
Analytics.shared.joinedRoomTrigger = AnalyticsJoinedRoomTriggerRoomDirectory;
// Do not stack views when showing room
ScreenPresentationParameters *presentationParameters = [[ScreenPresentationParameters alloc] initWithRestoreInitialDisplay:NO stackAboveVisibleViews:NO sender:nil sourceView:nil];
@@ -1008,7 +1008,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
{
// Retrieve the invited room
MXRoom *invitedRoom = userInfo[kInviteRecentTableViewCellRoomKey];
if (invitedRoom.summary.roomType == MXRoomTypeSpace)
{
// Indicates that spaces are not supported
@@ -1023,7 +1023,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
{
// Retrieve the invited room
MXRoom *invitedRoom = userInfo[kInviteRecentTableViewCellRoomKey];
if (invitedRoom.summary.roomType == MXRoomTypeSpace)
{
// Indicates that spaces are not supported
@@ -1062,7 +1062,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
[super dataSource:dataSource didCellChange:changes];
return;
}
if ([changes isKindOfClass:NSIndexPath.class])
{
NSIndexPath *indexPath = (NSIndexPath *)changes;
@@ -1073,7 +1073,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
TableViewCellWithCollectionView *collectionViewCell = (TableViewCellWithCollectionView *)cell;
[collectionViewCell.collectionView reloadData];
CGRect headerFrame = [self.recentsTableView rectForHeaderInSection:indexPath.section];
UIView *headerView = [self.recentsTableView headerViewForSection:indexPath.section];
UIView *updatedHeaderView = [self.dataSource viewForHeaderInSection:indexPath.section withFrame:headerFrame inTableView:self.recentsTableView];
@@ -1111,7 +1111,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
}
[self showEmptyViewIfNeeded];
if (dataSource.state == MXKDataSourceStateReady)
{
[[NSNotificationCenter defaultCenter] postNotificationName:RecentsViewControllerDataReadyNotification
@@ -1120,109 +1120,120 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
}
#pragma mark - Swipe actions
- (void)leaveEditedRoom
{
if (editedRoomId)
{
NSString *currentRoomId = editedRoomId;
__weak typeof(self) weakSelf = self;
NSString *title, *message;
if ([self.mainSession roomWithRoomId:currentRoomId].isDirect)
{
title = [VectorL10n roomParticipantsLeavePromptTitleForDm];
message = [VectorL10n roomParticipantsLeavePromptMsgForDm];
}
else
{
title = [VectorL10n roomParticipantsLeavePromptTitle];
message = [VectorL10n roomParticipantsLeavePromptMsg];
}
// confirm leave
UIAlertController *leavePrompt = [UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
[leavePrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
}
}]];
[leavePrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n leave]
style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
// Check whether the user didn't leave the room yet
// TODO: Handle multi-account
MXRoom *room = [self.mainSession roomWithRoomId:currentRoomId];
if (room)
{
[self startActivityIndicatorWithLabel:[VectorL10n roomParticipantsLeaveProcessing]];
// cancel pending uploads/downloads
// they are useless by now
[MXMediaManager cancelDownloadsInCacheFolder:room.roomId];
// TODO GFO cancel pending uploads related to this room
MXLogDebug(@"[RecentsViewController] Leave room (%@)", room.roomId);
[room leave:^{
if (weakSelf)
{
typeof(self) self = weakSelf;
[self stopActivityIndicator];
[self.userIndicatorStore presentSuccessWithLabel:[VectorL10n roomParticipantsLeaveSuccess]];
// Force table refresh
[self cancelEditionMode:YES];
}
} failure:^(NSError *error) {
MXLogDebug(@"[RecentsViewController] Failed to leave room");
if (weakSelf)
{
typeof(self) self = weakSelf;
// Notify the end user
NSString *userId = room.mxSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification
object:error
userInfo:userId ? @{kMXKErrorUserIdKey: userId} : nil];
[self stopActivityIndicator];
// Leave editing mode
[self cancelEditionMode:self->isRefreshPending];
}
}];
}
else
{
// Leave editing mode
[self cancelEditionMode:self->isRefreshPending];
}
}
}]];
[leavePrompt mxk_setAccessibilityIdentifier:@"LeaveEditedRoomAlert"];
[self presentViewController:leavePrompt animated:YES completion:nil];
currentAlert = leavePrompt;
MXWeakify(self);
MXRoom *room = [self.mainSession roomWithRoomId:currentRoomId];
__weak typeof(room) weakRoom = room;
[room isLastOwnerWithCompletionHandler:^(BOOL isLastOwner, NSError* error){
if (isLastOwner)
{
UIAlertController *isLastOwnerPrompt = [UIAlertController alertControllerWithTitle:[VectorL10n error]
message:[VectorL10n roomParticipantsLeaveNotAllowedForLastOwnerMsg]
preferredStyle:UIAlertControllerStyleAlert];
[isLastOwnerPrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n ok]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
}]];
MXStrongifyAndReturnIfNil(self);
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:isLastOwnerPrompt animated:YES completion:nil];
self->currentAlert = isLastOwnerPrompt;
});
}
else
{
NSString *title, *message;
MXStrongifyAndReturnIfNil(self);
if ([self.mainSession roomWithRoomId:currentRoomId].isDirect)
{
title = [VectorL10n roomParticipantsLeavePromptTitleForDm];
message = [VectorL10n roomParticipantsLeavePromptMsgForDm];
}
else
{
title = [VectorL10n roomParticipantsLeavePromptTitle];
message = [VectorL10n roomParticipantsLeavePromptMsg];
}
// confirm leave
UIAlertController *leavePrompt = [UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
MXWeakify(self);
[leavePrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
}]];
[leavePrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n leave]
style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
// Check whether the user didn't leave the room yet
// TODO: Handle multi-account
if (weakRoom)
{
[self startActivityIndicatorWithLabel:[VectorL10n roomParticipantsLeaveProcessing]];
// cancel pending uploads/downloads
// they are useless by now
[MXMediaManager cancelDownloadsInCacheFolder:weakRoom.roomId];
// TODO GFO cancel pending uploads related to this room
MXLogDebug(@"[RecentsViewController] Leave room (%@)", weakRoom.roomId);
MXWeakify(self);
[weakRoom leave:^{
MXStrongifyAndReturnIfNil(self);
[self stopActivityIndicator];
[self.userIndicatorStore presentSuccessWithLabel:[VectorL10n roomParticipantsLeaveSuccess]];
// Force table refresh
[self cancelEditionMode:YES];
} failure:^(NSError *error) {
MXLogDebug(@"[RecentsViewController] Failed to leave room");
MXStrongifyAndReturnIfNil(self);
// Notify the end user
NSString *userId = room.mxSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification
object:error
userInfo:userId ? @{kMXKErrorUserIdKey: userId} : nil];
[self stopActivityIndicator];
// Leave editing mode
[self cancelEditionMode:self->isRefreshPending];
}];
}
else
{
// Leave editing mode
[self cancelEditionMode:self->isRefreshPending];
}
}]];
[leavePrompt mxk_setAccessibilityIdentifier:@"LeaveEditedRoomAlert"];
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:leavePrompt animated:YES completion:nil];
self->currentAlert = leavePrompt;
});
}
}];
}
}

View File

@@ -134,10 +134,12 @@ class AllChatsEditActionProvider {
spaceRoom.state { [weak self] roomState in
guard let self = self else { return }
guard let powerLevels = roomState?.powerLevels, let userId = session.myUserId else {
guard let roomState,
let powerLevels = roomState.powerLevels,
let userId = session.myUserId else {
return
}
let userPowerLevel = powerLevels.powerLevelOfUser(withUserID: userId)
let userPowerLevel = roomState.powerLevelOfUser(withUserID: userId)
self.isInviteAvailable = userPowerLevel >= powerLevels.invite
self.isAddRoomAvailable = userPowerLevel >= parentSpace.minimumPowerLevelForAddingRoom(with: powerLevels)

View File

@@ -77,10 +77,12 @@ class AllChatsSpaceActionProvider {
spaceRoom.state { [weak self] roomState in
guard let self = self else { return }
guard let powerLevels = roomState?.powerLevels, let userId = session.myUserId else {
guard let roomState,
let powerLevels = roomState.powerLevels,
let userId = session.myUserId else {
return
}
let userPowerLevel = powerLevels.powerLevelOfUser(withUserID: userId)
let userPowerLevel = roomState.powerLevelOfUser(withUserID: userId)
self.isInviteAvailable = userPowerLevel >= powerLevels.invite

View File

@@ -132,6 +132,7 @@ class RoomContextActionService: NSObject, RoomContextActionServiceProtocol {
func leaveRoom(promptUser: Bool) {
guard promptUser else {
// Only used for declining an invite
self.leaveRoom()
return
}
@@ -141,6 +142,26 @@ class RoomContextActionService: NSObject, RoomContextActionServiceProtocol {
self.delegate?.roomContextActionService(self, presentAlert: self.getLeaveAlertController())
} else {
self.delegate?.roomContextActionService(self, presentAlert: self.leaveAlertController)
Task {
if try await room.isLastOwner() {
await MainActor.run {
let alertController = UIAlertController(title: VectorL10n.error, message: VectorL10n.roomParticipantsLeaveNotAllowedForLastOwnerMsg, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: VectorL10n.ok, style: .cancel, handler: nil))
self.delegate?.roomContextActionService(self, presentAlert: alertController)
}
} else {
let title = room.isDirect ? VectorL10n.roomParticipantsLeavePromptTitleForDm : VectorL10n.roomParticipantsLeavePromptTitle
let message = room.isDirect ? VectorL10n.roomParticipantsLeavePromptMsgForDm : VectorL10n.roomParticipantsLeavePromptMsg
await MainActor.run {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: VectorL10n.cancel, style: .cancel, handler: nil))
alertController.addAction(UIAlertAction(title: VectorL10n.leave, style: .default, handler: { [weak self] action in
self?.leaveRoom()
}))
self.delegate?.roomContextActionService(self, presentAlert: alertController)
}
}
}
}

View File

@@ -489,7 +489,7 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
MXJSONModelSetBoolean(isState, requestData[@"is_state"]);
MXRoomPowerLevels *powerLevels = roomState.powerLevels;
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:self->mxSession.myUser.userId];
NSInteger userPowerLevel = [roomState powerLevelOfUserWithUserID:self->mxSession.myUser.userId];
BOOL canSend = NO;

View File

@@ -94,7 +94,7 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse
{
// Check user's power in the room
MXRoomPowerLevels *powerLevels = roomState.powerLevels;
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:session.myUser.userId];
NSInteger oneSelfPowerLevel = [roomState powerLevelOfUserWithUserID:session.myUser.userId];
// The user must be able to send state events to manage widgets
if (oneSelfPowerLevel >= powerLevels.stateDefault)

View File

@@ -23,6 +23,16 @@ public extension DTHTMLElement {
// Remove any attachments to fix rendering.
textAttachment = nil
// Handle special case for span with data-mx-external-payment-details
// This could be based on Storefront.current.countryCode to show the link
// content in unrestricted countries. e.g. currently USA
if name == "span",
let attributes = attributes as? [String: String],
attributes["data-msc4286-external-payment-details"] != nil {
parent.removeChildNode(self)
return
}
// If the element has plain text content show that,
// otherwise prevent the tag from displaying.
if let stringContent = attributedString()?.string,

View File

@@ -220,20 +220,47 @@ Please see LICENSE in the repository root for full details.
}
case MXKRoomMemberDetailsActionLeave:
{
[self addPendingActionMask];
[self.mxRoom leave:^{
[self removePendingActionMask];
[self withdrawViewControllerAnimated:YES completion:nil];
} failure:^(NSError *error) {
[self removePendingActionMask];
MXLogDebug(@"[MXKRoomMemberDetailsVC] Leave room %@ failed", self->mxRoom.roomId);
// Notify MatrixKit user
NSString *myUserId = self.mainSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
MXWeakify(self);
[self.mxRoom isLastOwnerWithCompletionHandler:^(BOOL isLastOwner, NSError* error){
if (isLastOwner)
{
UIAlertController *isLastOwnerPrompt = [UIAlertController alertControllerWithTitle:[VectorL10n error]
message:[VectorL10n roomParticipantsLeaveNotAllowedForLastOwnerMsg]
preferredStyle:UIAlertControllerStyleAlert];
[isLastOwnerPrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n ok]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
}]];
MXStrongifyAndReturnIfNil(self);
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:isLastOwnerPrompt animated:YES completion:nil];
self->currentAlert = isLastOwnerPrompt;
});
}
else
{
MXStrongifyAndReturnIfNil(self);
[self addPendingActionMask];
MXWeakify(self);
[self.mxRoom leave:^{
MXStrongifyAndReturnIfNil(self);
[self removePendingActionMask];
[self withdrawViewControllerAnimated:YES completion:nil];
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
[self removePendingActionMask];
MXLogDebug(@"[MXKRoomMemberDetailsVC] Leave room %@ failed", self->mxRoom.roomId);
// Notify MatrixKit user
NSString *myUserId = self.mainSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
}
}];
break;
}
@@ -639,9 +666,10 @@ Please see LICENSE in the repository root for full details.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Check user's power level before allowing an action (kick, ban, ...)
MXRoomState *roomState = self.mxRoomLiveTimeline.state;
MXRoomPowerLevels *powerLevels = [self.mxRoomLiveTimeline.state powerLevels];
NSInteger memberPowerLevel = [powerLevels powerLevelOfUserWithUserID:_mxRoomMember.userId];
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
NSInteger memberPowerLevel = [roomState powerLevelOfUserWithUserID:_mxRoomMember.userId];
NSInteger oneSelfPowerLevel = [roomState powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
[actionsArray removeAllObjects];
@@ -894,14 +922,14 @@ Please see LICENSE in the repository root for full details.
- (void)setPowerLevel:(NSInteger)value promptUser:(BOOL)promptUser
{
NSInteger currentPowerLevel = [self.mxRoomLiveTimeline.state.powerLevels powerLevelOfUserWithUserID:_mxRoomMember.userId];
NSInteger currentPowerLevel = [self.mxRoomLiveTimeline.state powerLevelOfUserWithUserID:_mxRoomMember.userId];
// check if the power level has not yet been set to 0
if (value != currentPowerLevel)
{
__weak typeof(self) weakSelf = self;
if (promptUser && value == [self.mxRoomLiveTimeline.state.powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId])
if (promptUser && value == [self.mxRoomLiveTimeline.state powerLevelOfUserWithUserID:self.mainSession.myUser.userId])
{
// If the user is setting the same power level as his to another user, ask him for a confirmation
if (currentAlert)
@@ -999,7 +1027,7 @@ Please see LICENSE in the repository root for full details.
typeof(self) self = weakSelf;
textField.secureTextEntry = NO;
textField.text = [NSString stringWithFormat:@"%ld", (long)[self.mxRoomLiveTimeline.state.powerLevels powerLevelOfUserWithUserID:self.mxRoomMember.userId]];
textField.text = [NSString stringWithFormat:@"%ld", (long)[self.mxRoomLiveTimeline.state powerLevelOfUserWithUserID:self.mxRoomMember.userId]];
textField.placeholder = nil;
textField.keyboardType = UIKeyboardTypeDecimalPad;
}];

View File

@@ -307,7 +307,7 @@ Please see LICENSE in the repository root for full details.
if (showInvitationOption && self->dataSource)
{
// Check conditions to be able to invite someone
NSInteger oneSelfPowerLevel = [roomState.powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
NSInteger oneSelfPowerLevel = [roomState powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
if (oneSelfPowerLevel < [roomState.powerLevels invite])
{
showInvitationOption = NO;

View File

@@ -690,7 +690,7 @@ Please see LICENSE in the repository root for full details.
- (BOOL)canInvitePeople
{
NSInteger requiredLevel = roomDataSource.roomState.powerLevels.invite;
NSInteger myLevel = [roomDataSource.roomState.powerLevels powerLevelOfUserWithUserID:roomDataSource.mxSession.myUserId];
NSInteger myLevel = [roomDataSource.roomState powerLevelOfUserWithUserID:roomDataSource.mxSession.myUserId];
return myLevel >= requiredLevel;
}

View File

@@ -123,7 +123,7 @@ Please see LICENSE in the repository root for full details.
MXStrongifyAndReturnIfNil(self);
MXRoomPowerLevels *powerLevels = [roomState powerLevels];
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:self->mxSession.myUser.userId];
NSInteger userPowerLevel = [roomState powerLevelOfUserWithUserID:self->mxSession.myUser.userId];
if (powerLevels.redact)
{
if (userPowerLevel >= powerLevels.redact)

View File

@@ -176,7 +176,7 @@ Please see LICENSE in the repository root for full details.
// Check whether the user has enough power to rename the room
MXRoomPowerLevels *powerLevels = _mxRoom.dangerousSyncState.powerLevels;
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:_mxRoom.mxSession.myUser.userId];
NSInteger userPowerLevel = [_mxRoom.dangerousSyncState powerLevelOfUserWithUserID:_mxRoom.mxSession.myUser.userId];
if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomName])
{
// Only the room name is edited here, update the text field with the room name

View File

@@ -354,7 +354,7 @@ Please see LICENSE in the repository root for full details.
{
// Check whether the user has enough power to rename the room
MXRoomPowerLevels *powerLevels = self.mxRoom.dangerousSyncState.powerLevels;
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mxRoom.mxSession.myUser.userId];
NSInteger userPowerLevel = [self.mxRoom.dangerousSyncState powerLevelOfUserWithUserID:self.mxRoom.mxSession.myUser.userId];
if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomName])
{
// Only the room name is edited here, update the text field with the room name
@@ -384,7 +384,7 @@ Please see LICENSE in the repository root for full details.
{
// Check whether the user has enough power to edit room topic
MXRoomPowerLevels *powerLevels = self.mxRoom.dangerousSyncState.powerLevels;
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mxRoom.mxSession.myUser.userId];
NSInteger userPowerLevel = [self.mxRoom.dangerousSyncState powerLevelOfUserWithUserID:self.mxRoom.mxSession.myUser.userId];
if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomTopic])
{
textField.backgroundColor = [UIColor whiteColor];

View File

@@ -420,11 +420,15 @@ Please see LICENSE in the repository root for full details.
MXStrongifyAndReturnIfNil(self);
MXRoomPowerLevels *powerLevels = [roomState powerLevels];
NSInteger powerLevel = [powerLevels powerLevelOfUserWithUserID:self.mxRoomMember.userId];
NSInteger powerLevel = [roomState powerLevelOfUserWithUserID:self.mxRoomMember.userId];
RoomPowerLevel roomPowerLevel = [RoomPowerLevelHelper roomPowerLevelFrom:powerLevel];
switch (roomPowerLevel) {
case RoomPowerLevelOwner:
self.roomMemberPowerLevelLabel.text = [VectorL10n roomMemberPowerLevelOwnerIn:self.mxRoom.summary.displayName];
self.roomMemberPowerLevelContainerView.hidden = NO;
break;
case RoomPowerLevelAdmin:
self.roomMemberPowerLevelLabel.text = [BWIL10n roomMemberPowerLevelAdminIn:self.mxRoom.summary.displayName];
self.roomMemberPowerLevelContainerView.hidden = NO;
@@ -594,8 +598,8 @@ Please see LICENSE in the repository root for full details.
// Check user's power level before allowing an action (kick, ban, ...)
MXRoomPowerLevels *powerLevels = [self.mxRoom.dangerousSyncState powerLevels];
NSInteger memberPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mxRoomMember.userId];
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
NSInteger memberPowerLevel = [self.mxRoom.dangerousSyncState powerLevelOfUserWithUserID:self.mxRoomMember.userId];
NSInteger oneSelfPowerLevel = [self.mxRoom.dangerousSyncState powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
[adminActionsArray removeAllObjects];
[otherActionsArray removeAllObjects];

View File

@@ -796,8 +796,8 @@ Please see LICENSE in the repository root for full details.
{
// Order first by power levels (admins then moderators then others)
MXRoomPowerLevels *powerLevels = [roomState powerLevels];
NSInteger powerLevelA = [powerLevels powerLevelOfUserWithUserID:contactA.mxMember.userId];
NSInteger powerLevelB = [powerLevels powerLevelOfUserWithUserID:contactB.mxMember.userId];
NSInteger powerLevelA = [roomState powerLevelOfUserWithUserID:contactA.mxMember.userId];
NSInteger powerLevelB = [roomState powerLevelOfUserWithUserID:contactB.mxMember.userId];
if (powerLevelA == powerLevelB)
{
@@ -1099,6 +1099,9 @@ Please see LICENSE in the repository root for full details.
NSString *powerLevelText;
switch (roomPowerLevel) {
case RoomPowerLevelOwner:
powerLevelText = [VectorL10n roomMemberPowerLevelShortOwner];
break;
case RoomPowerLevelAdmin:
powerLevelText = [VectorL10n roomMemberPowerLevelShortAdmin];
break;
@@ -1309,62 +1312,7 @@ Please see LICENSE in the repository root for full details.
if (section == participantsSection && userParticipant && (0 == row) && !currentSearchText.length)
{
// Leave ?
MXWeakify(self);
NSString *title, *message;
if (self.mxRoom.isDirect)
{
title = [VectorL10n roomParticipantsLeavePromptTitleForDm];
message = [VectorL10n roomParticipantsLeavePromptMsgForDm];
}
else
{
title = [VectorL10n roomParticipantsLeavePromptTitle];
message = [VectorL10n roomParticipantsLeavePromptMsg];
}
currentAlert = [UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
}]];
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n leave]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
[self addPendingActionMask];
MXWeakify(self);
[self.mxRoom leave:^{
MXStrongifyAndReturnIfNil(self);
[self withdrawViewControllerAnimated:YES completion:nil];
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
[self removePendingActionMask];
MXLogDebug(@"[RoomParticipantsVC] Leave room %@ failed", self.mxRoom.roomId);
// Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}]];
[currentAlert mxk_setAccessibilityIdentifier:@"RoomParticipantsVCLeaveAlert"];
[self presentViewController:currentAlert animated:YES completion:nil];
[self leaveRoom];
}
else
{
@@ -1511,6 +1459,90 @@ Please see LICENSE in the repository root for full details.
}
}
- (void)leaveRoom {
MXWeakify(self);
[self.mxRoom isLastOwnerWithCompletionHandler:^(BOOL isLastOwner, NSError* error) {
if (isLastOwner)
{
MXStrongifyAndReturnIfNil(self);
self->currentAlert = [UIAlertController alertControllerWithTitle:[VectorL10n error]
message:[VectorL10n roomParticipantsLeaveNotAllowedForLastOwnerMsg]
preferredStyle:UIAlertControllerStyleAlert];
[self->currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
}]];
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:self->currentAlert animated:YES completion:nil];
});
}
else
{
// Leave ?
MXStrongifyAndReturnIfNil(self);
NSString *title, *message;
if (self.mxRoom.isDirect)
{
title = [VectorL10n roomParticipantsLeavePromptTitleForDm];
message = [VectorL10n roomParticipantsLeavePromptMsgForDm];
}
else
{
title = [VectorL10n roomParticipantsLeavePromptTitle];
message = [VectorL10n roomParticipantsLeavePromptMsg];
}
self->currentAlert = [UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
MXWeakify(self);
[self->currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
}]];
[self->currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n leave]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
[self addPendingActionMask];
MXWeakify(self);
[self.mxRoom leave:^{
MXStrongifyAndReturnIfNil(self);
[self withdrawViewControllerAnimated:YES completion:nil];
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
[self removePendingActionMask];
MXLogDebug(@"[RoomParticipantsVC] Leave room %@ failed", self.mxRoom.roomId);
// Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}]];
[self->currentAlert mxk_setAccessibilityIdentifier:@"RoomParticipantsVCLeaveAlert"];
[self presentViewController:self->currentAlert animated:YES completion:nil];
}
}];
}
- (void)onCancel:(id)sender
{
[self withdrawViewControllerAnimated:YES completion:nil];

View File

@@ -163,12 +163,13 @@ final class RoomParticipantsInviteCoordinatorBridgePresenter: NSObject {
}
room.state { roomState in
guard let powerLevels = roomState?.powerLevels else {
guard let roomState,
let powerLevels = roomState.powerLevels else {
MXLog.error("[RoomParticipantsInviteCoordinatorBridgePresenter] canInvite: room powerLevels not found")
completion(false)
return
}
let userPowerLevel = powerLevels.powerLevelOfUser(withUserID: userId)
let userPowerLevel = roomState.powerLevelOfUser(withUserID: userId)
completion(userPowerLevel >= powerLevels.invite)
}

View File

@@ -102,6 +102,18 @@ final class RoomInfoListViewController: UIViewController {
return self.doNotLeaveUIAlertController()
}
}
private lazy var isLastOwnerAlertController: UIAlertController = {
let title = VectorL10n.error
let message = VectorL10n.roomParticipantsLeaveNotAllowedForLastOwnerMsg
let controller = UIAlertController(title: title, message: message, preferredStyle: .alert)
controller.addAction(UIAlertAction(title: VectorL10n.ok, style: .default, handler: nil))
controller.mxk_setAccessibilityIdentifier("RoomSettingsVCLastOwnerAlert")
return controller
}()
private enum RowType {
case `default`
@@ -267,8 +279,11 @@ final class RoomInfoListViewController: UIViewController {
VectorL10n.roomParticipantsLeavePromptTitleForDm :
VectorL10n.roomParticipantsLeavePromptTitle
let rowLeave = Row(type: .destructive, icon: Asset.Images.roomActionLeave.image, text: leaveTitle, accessoryType: .none) {
if BWIBuildSettings.shared.lastAdminIsNotAllowedToLeaveRoom {
self.present(self.getLeaveAlertController(), animated: true, completion: nil)
} else if viewData.isLastOwner {
self.present(self.isLastOwnerAlertController, animated: true, completion: nil)
} else {
self.present(self.leaveAlertController, animated: true, completion: nil)
}

View File

@@ -24,4 +24,5 @@ struct RoomInfoListViewData {
let isEncrypted: Bool
let isDirect: Bool
let basicInfoViewData: RoomInfoBasicViewData
let isLastOwner: Bool
}

View File

@@ -27,6 +27,7 @@ final class RoomInfoListViewModel: NSObject, RoomInfoListViewModelType {
private let session: MXSession
private let room: MXRoom
private var isLastOwner = false
// MARK: Public
@@ -70,7 +71,8 @@ final class RoomInfoListViewModel: NSObject, RoomInfoListViewModelType {
return RoomInfoListViewData(numberOfMembers: Int(room.summary.membersCount.joined),
isEncrypted: room.summary.isEncrypted,
isDirect: room.isDirect,
basicInfoViewData: basicInfoViewData)
basicInfoViewData: basicInfoViewData,
isLastOwner: isLastOwner)
}
// bwi: 5216 - federation
@@ -163,6 +165,9 @@ final class RoomInfoListViewModel: NSObject, RoomInfoListViewModelType {
@objc private func roomSummaryUpdated(_ notification: Notification) {
// force update view
self.update(viewState: .loaded(viewData: viewData))
Task {
isLastOwner = (try? await room.isLastOwner()) == true
}
}
private func loadData() {

View File

@@ -1235,7 +1235,7 @@ static CGSize kThreadListBarButtonItemImageSize;
if (self.roomDataSource.roomState)
{
MXRoomPowerLevels *powerLevels = self.roomDataSource.roomState.powerLevels;
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
NSInteger userPowerLevel = [self.roomDataSource.roomState powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
BOOL canSend = (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsMessage:kMXEventTypeStringRoomMessage]);
BOOL isRoomObsolete = self.roomDataSource.roomState.isObsolete;
@@ -1906,7 +1906,7 @@ static CGSize kThreadListBarButtonItemImageSize;
{
MXRoomPowerLevels *powerLevels = [self.roomDataSource.roomState powerLevels];
NSInteger requiredPower = [powerLevels minimumPowerLevelForSendingEventAsStateEvent:eventTypeString];
NSInteger myPower = [powerLevels powerLevelOfUserWithUserID:self.roomDataSource.mxSession.myUserId];
NSInteger myPower = [self.roomDataSource.roomState powerLevelOfUserWithUserID:self.roomDataSource.mxSession.myUserId];
return myPower >= requiredPower;
}
@@ -2724,23 +2724,6 @@ static CGSize kThreadListBarButtonItemImageSize;
}
}
- (void)leaveRoom
{
[self startActivityIndicator];
[self.roomDataSource.room leave:^{
[self stopActivityIndicator];
[self notifyDelegateOnLeaveRoomIfNecessary];
} failure:^(NSError *error) {
[self stopActivityIndicator];
MXLogDebug(@"[RoomVC] Failed to reject an invited room (%@) failed", self.roomDataSource.room.roomId);
}];
}
- (void)notifyDelegateOnLeaveRoomIfNecessary {
if (isRoomLeft) {
return;

View File

@@ -678,7 +678,7 @@ BOOL reloadToggleCell = false;
{
// Check user's power level to know whether the user is allowed to turn on the encryption mode
MXRoomPowerLevels *powerLevels = [mxRoomState powerLevels];
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
NSInteger oneSelfPowerLevel = [mxRoomState powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
if (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomEncryption])
{
@@ -721,7 +721,7 @@ BOOL reloadToggleCell = false;
return;
MXRoomPowerLevels *powerLevels = [mxRoomState powerLevels];
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
NSInteger oneSelfPowerLevel = [mxRoomState powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
if (oneSelfPowerLevel < [powerLevels minimumPowerLevelForSendingEventAsStateEvent:eventTypeForSelectedField])
return;
@@ -2249,7 +2249,7 @@ BOOL reloadToggleCell = false;
// Check user's power level to know which settings are editable.
MXRoomPowerLevels *powerLevels = [mxRoomState powerLevels];
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
NSInteger oneSelfPowerLevel = [mxRoomState powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
// general settings
if (section == SECTION_TAG_MAIN)
@@ -3398,7 +3398,7 @@ BOOL reloadToggleCell = false;
{
// Check user's power level to know whether the user is allowed to set the main address
MXRoomPowerLevels *powerLevels = [mxRoomState powerLevels];
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
NSInteger oneSelfPowerLevel = [mxRoomState powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
if (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomAliases])
{

View File

@@ -329,11 +329,12 @@ final class SideMenuCoordinator: NSObject, SideMenuCoordinatorType {
spaceRoom.state { [weak self] roomState in
guard let self = self else { return }
guard let powerLevels = roomState?.powerLevels, let userId = session.myUserId else {
guard let roomState,
let powerLevels = roomState.powerLevels, let userId = session.myUserId else {
MXLog.error("[SpaceMembersCoordinator] spaceMemberListCoordinatorShowInvite: failed to find powerLevels for room")
return
}
let userPowerLevel = powerLevels.powerLevelOfUser(withUserID: userId)
let userPowerLevel = roomState.powerLevelOfUser(withUserID: userId)
guard userPowerLevel >= powerLevels.invite else {
let alert = UIAlertController(title: VectorL10n.spacesInvitePeople, message: VectorL10n.spaceInviteNotEnoughPermission, preferredStyle: .alert)

View File

@@ -165,11 +165,12 @@ extension SpaceMembersCoordinator: SpaceMemberListCoordinatorDelegate {
spaceRoom.state { [weak self] roomState in
guard let self = self else { return }
guard let powerLevels = roomState?.powerLevels, let userId = self.parameters.session.myUserId else {
guard let roomState,
let powerLevels = roomState.powerLevels, let userId = self.parameters.session.myUserId else {
MXLog.error("[SpaceMembersCoordinator] spaceMemberListCoordinatorShowInvite: failed to find powerLevels for room")
return
}
let userPowerLevel = powerLevels.powerLevelOfUser(withUserID: userId)
let userPowerLevel = roomState.powerLevelOfUser(withUserID: userId)
guard userPowerLevel >= powerLevels.invite else {
let alert = UIAlertController(title: VectorL10n.spacesInvitePeople, message: VectorL10n.spaceInviteNotEnoughPermission, preferredStyle: .alert)

View File

@@ -166,7 +166,7 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType {
if let spaceRoom = self.spaceRoom {
spaceRoom.state { roomState in
self.powerLevels = roomState?.powerLevels
self.powerLevelOfCurrentUser = self.powerLevels?.powerLevelOfUser(withUserID: self.session.myUserId)
self.powerLevelOfCurrentUser = roomState?.powerLevelOfUser(withUserID: self.session.myUserId)
}
}

View File

@@ -159,8 +159,9 @@ final class LocationSharingCoordinator: Coordinator, Presentable {
// Check if user can send beacon info state event
private func canShareLiveLocation() -> Bool {
guard let myUserId = parameters.roomDataSource.mxSession.myUserId,
let roomPowerLevels = parameters.roomDataSource.roomState.powerLevels,
let userPowerLevel = RoomPowerLevel(rawValue: roomPowerLevels.powerLevelOfUser(withUserID: myUserId)) else {
let roomState = parameters.roomDataSource.roomState,
let roomPowerLevels = roomState.powerLevels,
let userPowerLevel = RoomPowerLevel(rawValue: roomState.powerLevelOfUser(withUserID: myUserId)) else {
return false
}

View File

@@ -162,8 +162,10 @@ private class CompletionSuggestionCoordinatorRoomMemberProvider: RoomMembersProv
/// Gets the power levels for the room to update suggestions accordingly.
func updateWithPowerLevels() {
room.state { [weak self] state in
guard let self, let powerLevels = state?.powerLevels else { return }
let userPowerLevel = powerLevels.powerLevelOfUser(withUserID: self.userID)
guard let self,
let state,
let powerLevels = state.powerLevels else { return }
let userPowerLevel = state.powerLevelOfUser(withUserID: self.userID)
let mentionRoomPowerLevel = powerLevels.minimumPowerLevel(forNotifications: kMXRoomPowerLevelNotificationsRoomKey,
defaultPower: kMXRoomPowerLevelNotificationsRoomDefault)
self.canMentionRoom = userPowerLevel >= mentionRoomPowerLevel
@@ -208,9 +210,11 @@ private class CompletionSuggestionCoordinatorCommandProvider: CommandsProviderPr
func updateWithPowerLevels() {
room.state { [weak self] state in
guard let self, let powerLevels = state?.powerLevels else { return }
guard let self,
let state,
let powerLevels = state.powerLevels else { return }
let userPowerLevel = powerLevels.powerLevelOfUser(withUserID: self.userID)
let userPowerLevel = state.powerLevelOfUser(withUserID: self.userID)
self.isRoomAdmin = RoomPowerLevel(rawValue: userPowerLevel) == .admin
}
}

View File

@@ -150,12 +150,12 @@ class SpaceSettingsService: SpaceSettingsServiceProtocol {
return allowedParentIds
}
private func isField(ofType notification: String, editableWith powerLevels: MXRoomPowerLevels?) -> Bool {
guard let powerLevels = powerLevels else {
private func isField(ofType notification: String, editableWith roomState: MXRoomState) -> Bool {
guard let powerLevels = roomState.powerLevels else {
return false
}
let userPowerLevel = powerLevels.powerLevelOfUser(withUserID: session.myUserId)
let userPowerLevel = roomState.powerLevelOfUser(withUserID: session.myUserId)
return userPowerLevel >= powerLevels.minimumPowerLevel(forNotifications: notification, defaultPower: powerLevels.stateDefault)
}
@@ -226,11 +226,11 @@ class SpaceSettingsService: SpaceSettingsServiceProtocol {
avatarUrl: roomState.avatar,
visibility: visibility(with: roomState),
allowedParentIds: allowedParentIds(with: roomState),
isAvatarEditable: isField(ofType: kMXEventTypeStringRoomAvatar, editableWith: roomState.powerLevels),
isNameEditable: isField(ofType: kMXEventTypeStringRoomName, editableWith: roomState.powerLevels),
isTopicEditable: isField(ofType: kMXEventTypeStringRoomTopic, editableWith: roomState.powerLevels),
isAddressEditable: isField(ofType: kMXEventTypeStringRoomAliases, editableWith: roomState.powerLevels),
isAccessEditable: isField(ofType: kMXEventTypeStringRoomJoinRules, editableWith: roomState.powerLevels)
isAvatarEditable: isField(ofType: kMXEventTypeStringRoomAvatar, editableWith: roomState),
isNameEditable: isField(ofType: kMXEventTypeStringRoomName, editableWith: roomState),
isTopicEditable: isField(ofType: kMXEventTypeStringRoomTopic, editableWith: roomState),
isAddressEditable: isField(ofType: kMXEventTypeStringRoomAliases, editableWith: roomState),
isAccessEditable: isField(ofType: kMXEventTypeStringRoomJoinRules, editableWith: roomState)
)
}

View File

@@ -49,7 +49,6 @@ targets:
PRODUCT_BUNDLE_IDENTIFIER: org.matrix.$(PRODUCT_NAME:rfc1034identifier)
PRODUCT_NAME: RiotSwiftUnitTests
configs:
Debug:
Release:
PROVISIONING_PROFILE: $(RIOT_PROVISIONING_PROFILE)
PROVISIONING_PROFILE_SPECIFIER: $(RIOT_PROVISIONING_PROFILE_SPECIFIER)

View File

@@ -239,6 +239,24 @@ Please see LICENSE in the repository root for full details.
XCTAssertFalse(hasAttachment, @"iFrame attachments should be removed as they're not included in the allowedHTMLTags array.");
}
- (void)testMxExternalPaymentDetailsRemoved
{
// Given an HTML string containing a <span> with data-mx-external-payment-details.
NSString *html = @"This is visible<span data-msc4286-external-payment-details>. But text is hidden <a href=\"https://matrix.org\">and this link too</a></span>";
// When rendering this string as an attributed string.
NSAttributedString *attributedString = [eventFormatter renderHTMLString:html
forEvent:anEvent
withRoomState:nil
andLatestRoomState:nil];
// Then the attributed string should have the <span> stripped and not include any attachments.
XCTAssertEqualObjects(attributedString.string, @"This is visible", @"The <span data-msc4286-external-payment-details> tag content should be removed.");
BOOL hasAttachment = [attributedString containsAttachmentsInRange:NSMakeRange(0, attributedString.length)];
XCTAssertFalse(hasAttachment, @"span attachments should be removed as they're not included in the allowedHTMLTags array.");
}
- (void)testRenderHTMLStringWithMXReply
{
// Given an HTML string representing a matrix reply.

View File

@@ -25,7 +25,8 @@ class EncryptionTrustLevelTests: XCTestCase {
identity: .other(
userId: "Bob",
masterKey: "MSK",
selfSigningKey: "SSK"
selfSigningKey: "SSK",
hasVerificationViolation: false
),
isVerified: isVerified
)

View File

@@ -52,7 +52,6 @@ targets:
SWIFT_OBJC_BRIDGING_HEADER: RiotTests/RiotTests-Bridging-Header.h
TEST_HOST: $(BUILT_PRODUCTS_DIR)/Element.app/Element
configs:
Debug:
Release:
PROVISIONING_PROFILE: $(RIOT_PROVISIONING_PROFILE)
PROVISIONING_PROFILE_SPECIFIER: $(RIOT_PROVISIONING_PROFILE_SPECIFIER)

Submodule matrix-ios-sdk updated: 6a49ee70a5...b0f967c9c8