Merge branch 'develop' into feature/fga/pinned_message_banner_ui

This commit is contained in:
ganfra 2024-07-31 13:11:51 +02:00
commit a8115e0a21
128 changed files with 2576 additions and 962 deletions

View file

@ -21,6 +21,7 @@
<locale android:name="sk"/> <locale android:name="sk"/>
<locale android:name="sv"/> <locale android:name="sv"/>
<locale android:name="uk"/> <locale android:name="uk"/>
<locale android:name="uz"/>
<locale android:name="zh-CN"/> <locale android:name="zh-CN"/>
<locale android:name="zh-TW"/> <locale android:name="zh-TW"/>
</locale-config> </locale-config>

View file

@ -61,7 +61,7 @@ allprojects {
config.from(files("$rootDir/tools/detekt/detekt.yml")) config.from(files("$rootDir/tools/detekt/detekt.yml"))
} }
dependencies { dependencies {
detektPlugins("io.nlopez.compose.rules:detekt:0.4.5") detektPlugins("io.nlopez.compose.rules:detekt:0.4.8")
} }
// KtLint // KtLint

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_settings_help_us_improve">"Udostępniaj anonimowe dane dotyczące użytkowania, aby pomóc nam identyfikować problemy."</string> <string name="screen_analytics_settings_help_us_improve">"Udostępniaj anonimowe dane dotyczące użytkowania, aby pomóc nam identyfikować problemy."</string>
<string name="screen_analytics_settings_read_terms">"Możesz przeczytać wszystkie nasze warunki %1$s."</string> <string name="screen_analytics_settings_read_terms">"Przeczytaj nasze warunki użytkowania %1$s."</string>
<string name="screen_analytics_settings_read_terms_content_link">"tutaj"</string> <string name="screen_analytics_settings_read_terms_content_link">"tutaj"</string>
<string name="screen_analytics_settings_share_data">"Udostępniaj dane analityczne"</string> <string name="screen_analytics_settings_share_data">"Udostępniaj dane analityczne"</string>
</resources> </resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_settings_help_us_improve">"Muammolarni aniqlashda yordam berish uchun anonim foydalanish maʼlumotlarini baham koʻring."</string>
<string name="screen_analytics_settings_read_terms">"Siz bizning barcha shartlarimizni o\'qishingiz mumkin%1$s."</string>
<string name="screen_analytics_settings_read_terms_content_link">"Bu yerga"</string>
<string name="screen_analytics_settings_share_data">"Analitik ma\'lumotlarni ulashish"</string>
</resources>

View file

@ -2,7 +2,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_prompt_data_usage">"Nie będziemy rejestrować ani profilować żadnych danych osobistych"</string> <string name="screen_analytics_prompt_data_usage">"Nie będziemy rejestrować ani profilować żadnych danych osobistych"</string>
<string name="screen_analytics_prompt_help_us_improve">"Udostępniaj anonimowe dane dotyczące użytkowania, aby pomóc nam identyfikować problemy."</string> <string name="screen_analytics_prompt_help_us_improve">"Udostępniaj anonimowe dane dotyczące użytkowania, aby pomóc nam identyfikować problemy."</string>
<string name="screen_analytics_prompt_read_terms">"Możesz przeczytać wszystkie nasze warunki %1$s."</string> <string name="screen_analytics_prompt_read_terms">"Przeczytaj nasze warunki użytkowania %1$s."</string>
<string name="screen_analytics_prompt_read_terms_content_link">"tutaj"</string> <string name="screen_analytics_prompt_read_terms_content_link">"tutaj"</string>
<string name="screen_analytics_prompt_settings">"Możesz to wyłączyć w dowolnym momencie"</string> <string name="screen_analytics_prompt_settings">"Możesz to wyłączyć w dowolnym momencie"</string>
<string name="screen_analytics_prompt_third_party_sharing">"Nie będziemy udostępniać Twoich danych podmiotom trzecim"</string> <string name="screen_analytics_prompt_third_party_sharing">"Nie będziemy udostępniać Twoich danych podmiotom trzecim"</string>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_prompt_data_usage">"Biz hech qanday shaxsiy ma\'lumotlarni yozmaymiz yoki profilga kiritmaymiz"</string>
<string name="screen_analytics_prompt_help_us_improve">"Muammolarni aniqlashda yordam berish uchun anonim foydalanish maʼlumotlarini baham koʻring."</string>
<string name="screen_analytics_prompt_read_terms">"Siz bizning barcha shartlarimizni o\'qishingiz mumkin%1$s."</string>
<string name="screen_analytics_prompt_read_terms_content_link">"Bu yerga"</string>
<string name="screen_analytics_prompt_settings">"Buni istalgan vaqtda oʻchirib qoʻyishingiz mumkin"</string>
<string name="screen_analytics_prompt_third_party_sharing">"Biz sizning ma\'lumotlaringizni uchinchi tomonlar bilan baham ko\'rmaymiz"</string>
<string name="screen_analytics_prompt_title">"Yaxshilashga yordam bering%1$s"</string>
</resources>

View file

@ -3,4 +3,5 @@
<string name="call_foreground_service_channel_title_android">"Połączenie w trakcie"</string> <string name="call_foreground_service_channel_title_android">"Połączenie w trakcie"</string>
<string name="call_foreground_service_message_android">"Stuknij, aby wrócić do rozmowy"</string> <string name="call_foreground_service_message_android">"Stuknij, aby wrócić do rozmowy"</string>
<string name="call_foreground_service_title_android">"☎️ Rozmowa w toku"</string> <string name="call_foreground_service_title_android">"☎️ Rozmowa w toku"</string>
<string name="screen_incoming_call_subtitle_android">"Przychodzące połączenie Element"</string>
</resources> </resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="call_foreground_service_channel_title_android">"Davom etayotgan qo\'ng\'iroq"</string>
<string name="call_foreground_service_message_android">"Qo\'ng\'iroqqa qaytish uchun bosing"</string>
<string name="call_foreground_service_title_android">"☎️ Qongiroq davom etmoqda"</string>
</resources>

View file

@ -6,7 +6,7 @@
<string name="screen_create_room_private_option_description">"Wiadomości w tym pokoju są szyfrowane. Szyfrowania nie można później wyłączyć."</string> <string name="screen_create_room_private_option_description">"Wiadomości w tym pokoju są szyfrowane. Szyfrowania nie można później wyłączyć."</string>
<string name="screen_create_room_private_option_title">"Pokój prywatny (tylko zaproszenie)"</string> <string name="screen_create_room_private_option_title">"Pokój prywatny (tylko zaproszenie)"</string>
<string name="screen_create_room_public_option_description">"Wiadomości nie są szyfrowane i każdy może je odczytać. Możesz aktywować szyfrowanie później."</string> <string name="screen_create_room_public_option_description">"Wiadomości nie są szyfrowane i każdy może je odczytać. Możesz aktywować szyfrowanie później."</string>
<string name="screen_create_room_public_option_title">"Pokój publiczny (każdy)"</string> <string name="screen_create_room_public_option_title">"Pokój publiczny (wszyscy)"</string>
<string name="screen_create_room_room_name_label">"Nazwa pokoju"</string> <string name="screen_create_room_room_name_label">"Nazwa pokoju"</string>
<string name="screen_create_room_title">"Utwórz pokój"</string> <string name="screen_create_room_title">"Utwórz pokój"</string>
<string name="screen_create_room_topic_label">"Temat (opcjonalnie)"</string> <string name="screen_create_room_topic_label">"Temat (opcjonalnie)"</string>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_create_room_action_create_room">"Yangi xona"</string>
<string name="screen_create_room_add_people_title">"Odamlarni taklif qiling"</string>
<string name="screen_create_room_error_creating_room">"Xonani yaratishda xatolik yuz berdi"</string>
<string name="screen_create_room_private_option_description">"Bu xonadagi xabarlar shifrlangan. Keyinchalik shifrlashni ochirib bolmaydi."</string>
<string name="screen_create_room_private_option_title">"Shaxsiy xona (faqat taklif)"</string>
<string name="screen_create_room_public_option_description">"Xabarlar shifrlanmagan va har kim ularni o\'qiy oladi. Keyinchalik shifrlashni yoqishingiz mumkin."</string>
<string name="screen_create_room_public_option_title">"Jamoat xonasi (har kim)"</string>
<string name="screen_create_room_room_name_label">"Xona nomi"</string>
<string name="screen_create_room_title">"Xonani yaratish"</string>
<string name="screen_create_room_topic_label">"Mavzu (ixtiyoriy)"</string>
<string name="screen_start_chat_error_starting_chat">"Suhbatni boshlashda xatolik yuz berdi"</string>
</resources>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Sozlamalaringizni keyinroq o\'zgartirishingiz mumkin."</string>
<string name="screen_notification_optin_title">"Bildirishnomalarga ruxsat bering va hech qachon xabarni o\'tkazib yubormang"</string>
<string name="screen_welcome_bullet_1">"Qo\'ng\'iroqlar, so\'ro\'vlar, qidiruv va boshqalar shu yil oxirida qo\'shiladi."</string>
<string name="screen_welcome_bullet_2">"Shifrlangan xonalar uchun xabarlar tarixi hali mavjud emas."</string>
<string name="screen_welcome_bullet_3">"Biz sizdan eshitishni istardik, sozlamalar sahifasi orqali fikringizni bildiring."</string>
<string name="screen_welcome_button">"Qani ketdik!"</string>
<string name="screen_welcome_subtitle">"Buni bilishingiz kerak:"</string>
<string name="screen_welcome_title">"%1$sga Xush kelibsiz!"</string>
</resources>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_invites_decline_chat_message">"Czy na pewno chcesz odrzucić zaproszenie do dołączenia do %1$s?"</string> <string name="screen_invites_decline_chat_message">"Czy na pewno chcesz odrzucić zaproszenie dołączenia do %1$s?"</string>
<string name="screen_invites_decline_chat_title">"Odrzuć zaproszenie"</string> <string name="screen_invites_decline_chat_title">"Odrzuć zaproszenie"</string>
<string name="screen_invites_decline_direct_chat_message">"Czy na pewno chcesz odrzucić rozmowę prywatną z %1$s?"</string> <string name="screen_invites_decline_direct_chat_message">"Czy na pewno chcesz odrzucić rozmowę prywatną z %1$s?"</string>
<string name="screen_invites_decline_direct_chat_title">"Odrzuć czat"</string> <string name="screen_invites_decline_direct_chat_title">"Odrzuć czat"</string>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_invites_decline_chat_message">"Haqiqatan ham qo\'shilish taklifini rad qilmoqchimisiz%1$s ?"</string>
<string name="screen_invites_decline_chat_title">"Taklifni rad etish"</string>
<string name="screen_invites_decline_direct_chat_message">"Haqiqatan ham bu shaxsiy chatni rad qilmoqchimisiz%1$s ?"</string>
<string name="screen_invites_decline_direct_chat_title">"Chatni rad etish"</string>
<string name="screen_invites_empty_list">"Takliflar yo\'q"</string>
<string name="screen_invites_invited_you">"%1$s(%2$s ) sizni taklif qildi"</string>
</resources>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_join_room_join_action">"Dołącz do pokoju"</string>
<string name="screen_join_room_knock_action">"Zapukaj, by dołączyć"</string>
<string name="screen_join_room_space_not_supported_description">"%1$s jeszcze nie obsługuje przestrzeni. Uzyskaj dostęp do przestrzeni w wersji web."</string>
<string name="screen_join_room_space_not_supported_title">"Przestrzenie nie są jeszcze obsługiwane"</string>
<string name="screen_join_room_subtitle_knock">"Kliknij przycisk poniżej, aby powiadomić administratora pokoju. Po zatwierdzeniu będziesz mógł dołączyć do rozmowy."</string>
<string name="screen_join_room_subtitle_no_preview">"Musisz być członkiem tego pokoju, aby wyświetlić historię wiadomości."</string>
<string name="screen_join_room_title_knock">"Chcesz dołączyć do tego pokoju?"</string>
<string name="screen_join_room_title_no_preview">"Podgląd nie jest dostępny"</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="leave_room_alert_empty_subtitle">"Bu xonani tark etmoqchi ekanligingizga ishonchingiz komilmi? Siz bu yerda yagona odamsiz. Agar siz tark etsangiz, kelajakda hech kim qo\'shila olmaydi, jumladan siz ham."</string>
<string name="leave_room_alert_private_subtitle">"Bu xonani tark etmoqchi ekanligingizga ishonchingiz komilmi? Bu xona ochiq emas va siz taklifsiz qayta qoshila olmaysiz."</string>
<string name="leave_room_alert_subtitle">"Xonani tark etmoqchi ekanligingizga ishonchingiz komilmi?"</string>
</resources>

View file

@ -16,11 +16,11 @@
<string name="screen_app_lock_setup_confirm_pin">"Potwierdź PIN"</string> <string name="screen_app_lock_setup_confirm_pin">"Potwierdź PIN"</string>
<string name="screen_app_lock_setup_pin_context">"Zablokuj %1$s, aby zwiększyć bezpieczeństwo swoich czatów. <string name="screen_app_lock_setup_pin_context">"Zablokuj %1$s, aby zwiększyć bezpieczeństwo swoich czatów.
Wybierz coś łatwego do zapamiętania. Jeśli zapomnisz tego PINU, zostaniesz wylogowany z aplikacji."</string> Wybierz coś łatwego do zapamiętania. Jeśli zapomnisz tego PIN\'u, zostaniesz wylogowany z aplikacji."</string>
<string name="screen_app_lock_setup_pin_forbidden_dialog_content">"Nie możesz wybrać tego PINU ze względów bezpieczeństwa"</string> <string name="screen_app_lock_setup_pin_forbidden_dialog_content">"Nie możesz wybrać tego PIN\'u ze względów bezpieczeństwa"</string>
<string name="screen_app_lock_setup_pin_forbidden_dialog_title">"Wybierz inny kod PIN"</string> <string name="screen_app_lock_setup_pin_forbidden_dialog_title">"Wybierz inny kod PIN"</string>
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Wprowadź ten sam kod PIN dwa razy"</string> <string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Wprowadź ten sam kod PIN dwa razy"</string>
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"PINY nie pasują do siebie"</string> <string name="screen_app_lock_setup_pin_mismatch_dialog_title">"PIN\'y nie pasują do siebie"</string>
<string name="screen_app_lock_signout_alert_message">"Aby kontynuować, zaloguj się ponownie i utwórz nowy kod PIN"</string> <string name="screen_app_lock_signout_alert_message">"Aby kontynuować, zaloguj się ponownie i utwórz nowy kod PIN"</string>
<string name="screen_app_lock_signout_alert_title">"Trwa wylogowywanie"</string> <string name="screen_app_lock_signout_alert_title">"Trwa wylogowywanie"</string>
<plurals name="screen_app_lock_subtitle"> <plurals name="screen_app_lock_subtitle">

View file

@ -1,14 +1,27 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_app_lock_biometric_authentication">"autenticação por biometria"</string>
<string name="screen_app_lock_biometric_unlock">"desbloqueio por biometria"</string>
<string name="screen_app_lock_biometric_unlock_title_android">"Desbloquear com biometria"</string>
<string name="screen_app_lock_forgot_pin">"Esqueceu o PIN?"</string> <string name="screen_app_lock_forgot_pin">"Esqueceu o PIN?"</string>
<string name="screen_app_lock_settings_change_pin">"Mudar código de PIN"</string> <string name="screen_app_lock_settings_change_pin">"Alterar código de PIN"</string>
<string name="screen_app_lock_settings_enable_biometric_unlock">"Permitir desbloqueio biométrico"</string> <string name="screen_app_lock_settings_enable_biometric_unlock">"Permitir desbloqueio biométrico"</string>
<string name="screen_app_lock_settings_remove_pin">"Remover PIN"</string> <string name="screen_app_lock_settings_remove_pin">"Remover PIN"</string>
<string name="screen_app_lock_settings_remove_pin_alert_message">"Tem certeza de que quer remover o PIN?"</string> <string name="screen_app_lock_settings_remove_pin_alert_message">"Tem certeza de que quer remover o PIN?"</string>
<string name="screen_app_lock_settings_remove_pin_alert_title">"Remover PIN?"</string> <string name="screen_app_lock_settings_remove_pin_alert_title">"Remover PIN?"</string>
<string name="screen_app_lock_setup_biometric_unlock_allow_title">"Permitir %1$s"</string>
<string name="screen_app_lock_setup_biometric_unlock_skip">"Prefiro usar o PIN"</string>
<string name="screen_app_lock_setup_biometric_unlock_subtitle">"Poupe tempo e use %1$s para desbloquear o aplicativo todas as vezes"</string>
<string name="screen_app_lock_setup_choose_pin">"Escolher PIN"</string> <string name="screen_app_lock_setup_choose_pin">"Escolher PIN"</string>
<string name="screen_app_lock_setup_confirm_pin">"Confirmar PIN"</string> <string name="screen_app_lock_setup_confirm_pin">"Confirmar PIN"</string>
<string name="screen_app_lock_setup_pin_context">"Bloqueie o %1$s para adicionar uma segurança extra às suas conversas.
Escolha algo memorável. Se você esquecer este PIN, você será desconectado do app."</string>
<string name="screen_app_lock_setup_pin_forbidden_dialog_content">"Você não pode escolher este PIN por razões de segurança"</string>
<string name="screen_app_lock_setup_pin_forbidden_dialog_title">"Escolha um PIN diferente"</string>
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Por favor, insira o mesmo PIN duas vezes"</string>
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"Os PINs não correspondem"</string> <string name="screen_app_lock_setup_pin_mismatch_dialog_title">"Os PINs não correspondem"</string>
<string name="screen_app_lock_signout_alert_message">"Você terá que fazer login novamente e criar um novo PIN para prosseguir"</string>
<string name="screen_app_lock_signout_alert_title">"Você está sendo desconectado"</string> <string name="screen_app_lock_signout_alert_title">"Você está sendo desconectado"</string>
<plurals name="screen_app_lock_subtitle"> <plurals name="screen_app_lock_subtitle">
<item quantity="one">"Você tem %1$d tentativa de debloqueio"</item> <item quantity="one">"Você tem %1$d tentativa de debloqueio"</item>
@ -18,5 +31,7 @@
<item quantity="one">"PIN incorreto. Você tem mais %1$d chance"</item> <item quantity="one">"PIN incorreto. Você tem mais %1$d chance"</item>
<item quantity="other">"PIN incorreto. Você tem mais %1$d chances"</item> <item quantity="other">"PIN incorreto. Você tem mais %1$d chances"</item>
</plurals> </plurals>
<string name="screen_app_lock_use_biometric_android">"Usar biometria"</string>
<string name="screen_app_lock_use_pin_android">"Usar PIN"</string>
<string name="screen_signout_in_progress_dialog_content">"Saindo…"</string> <string name="screen_signout_in_progress_dialog_content">"Saindo…"</string>
</resources> </resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_signout_in_progress_dialog_content">"Chiqish…"</string>
</resources>

View file

@ -6,7 +6,7 @@
<string name="screen_account_provider_form_subtitle">"Szukaj serwera firmowego, społeczności lub prywatnego."</string> <string name="screen_account_provider_form_subtitle">"Szukaj serwera firmowego, społeczności lub prywatnego."</string>
<string name="screen_account_provider_form_title">"Znajdź dostawcę konta"</string> <string name="screen_account_provider_form_title">"Znajdź dostawcę konta"</string>
<string name="screen_account_provider_signin_subtitle">"Tutaj będą przechowywane Twoje konwersacje - w podobnej formie jak wiadomości widnieją na skrzynce e-mail."</string> <string name="screen_account_provider_signin_subtitle">"Tutaj będą przechowywane Twoje konwersacje - w podobnej formie jak wiadomości widnieją na skrzynce e-mail."</string>
<string name="screen_account_provider_signin_title">"Zamierzasz się zalogować %s"</string> <string name="screen_account_provider_signin_title">"Zamierzasz zalogować się do %s"</string>
<string name="screen_account_provider_signup_subtitle">"Tutaj będą przechowywane Twoje konwersacje - w podobnej formie jak wiadomości widnieją na skrzynce e-mail."</string> <string name="screen_account_provider_signup_subtitle">"Tutaj będą przechowywane Twoje konwersacje - w podobnej formie jak wiadomości widnieją na skrzynce e-mail."</string>
<string name="screen_account_provider_signup_title">"Zamierzasz założyć konto na %s"</string> <string name="screen_account_provider_signup_title">"Zamierzasz założyć konto na %s"</string>
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org jest ogromnym i darmowym serwerem na publicznej sieci Matrix zapewniający bezpieczną i zdecentralizowaną komunikację zarządzaną przez Fundację Matrix.org."</string> <string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org jest ogromnym i darmowym serwerem na publicznej sieci Matrix zapewniający bezpieczną i zdecentralizowaną komunikację zarządzaną przez Fundację Matrix.org."</string>
@ -14,20 +14,64 @@
<string name="screen_change_account_provider_subtitle">"Użyj innego dostawcy konta, takiego jak własny serwer lub konta służbowego."</string> <string name="screen_change_account_provider_subtitle">"Użyj innego dostawcy konta, takiego jak własny serwer lub konta służbowego."</string>
<string name="screen_change_account_provider_title">"Zmień dostawcę konta"</string> <string name="screen_change_account_provider_title">"Zmień dostawcę konta"</string>
<string name="screen_change_server_error_invalid_homeserver">"Nie mogliśmy połączyć się z tym serwerem domowym. Sprawdź, czy adres URL serwera został wprowadzony poprawnie. Jeśli adres URL jest poprawny, skontaktuj się z administratorem serwera w celu uzyskania dalszej pomocy."</string> <string name="screen_change_server_error_invalid_homeserver">"Nie mogliśmy połączyć się z tym serwerem domowym. Sprawdź, czy adres URL serwera został wprowadzony poprawnie. Jeśli adres URL jest poprawny, skontaktuj się z administratorem serwera w celu uzyskania dalszej pomocy."</string>
<string name="screen_change_server_error_invalid_well_known">"Sliding sync nie jest dostępny z powodu problemu w znanym pliku:
%1$s"</string>
<string name="screen_change_server_error_no_sliding_sync_message">"Ten serwer obecnie nie obsługuje technologii Sliding Sync."</string> <string name="screen_change_server_error_no_sliding_sync_message">"Ten serwer obecnie nie obsługuje technologii Sliding Sync."</string>
<string name="screen_change_server_form_header">"Adres URL serwera domowego"</string> <string name="screen_change_server_form_header">"URL serwera domowego"</string>
<string name="screen_change_server_form_notice">"Możesz połączyć się tylko z serwerem, który obsługuje technologię Sliding Sync. Administrator serwera domowego będzie musiał ją skonfigurować. %1$s"</string> <string name="screen_change_server_form_notice">"Możesz połączyć się tylko z serwerem, który obsługuje technologię Sliding Sync. Administrator serwera domowego będzie musiał ją skonfigurować. %1$s"</string>
<string name="screen_change_server_subtitle">"Jaki jest adres Twojego serwera?"</string> <string name="screen_change_server_subtitle">"Jaki jest adres Twojego serwera?"</string>
<string name="screen_change_server_title">"Wybierz swój serwer"</string> <string name="screen_change_server_title">"Wybierz swój serwer"</string>
<string name="screen_login_error_deactivated_account">"To konto zostało dezaktywowane."</string> <string name="screen_login_error_deactivated_account">"To konto zostało dezaktywowane."</string>
<string name="screen_login_error_invalid_credentials">"Nieprawidłowa nazwa użytkownika i/lub hasło"</string> <string name="screen_login_error_invalid_credentials">"Nieprawidłowa nazwa użytkownika i/lub hasło"</string>
<string name="screen_login_error_invalid_user_id">"To nie jest prawidłowy identyfikator użytkownika. Oczekiwany format: \'@user:homeserver.org\'"</string> <string name="screen_login_error_invalid_user_id">"To nie jest prawidłowy identyfikator użytkownika. Oczekiwany format: \'@user:homeserver.org\'"</string>
<string name="screen_login_error_refresh_tokens">"Ten serwer został skonfigurowany do korzystania z tokenów odświeżania. Nie są one obsługiwane, gdy korzystasz z hasła."</string>
<string name="screen_login_error_unsupported_authentication">"Wybrany serwer domowy nie obsługuje uwierzytelniania hasłem, ani OIDC. Skontaktuj się z jego administratorem lub wybierz inny serwer domowy."</string> <string name="screen_login_error_unsupported_authentication">"Wybrany serwer domowy nie obsługuje uwierzytelniania hasłem, ani OIDC. Skontaktuj się z jego administratorem lub wybierz inny serwer domowy."</string>
<string name="screen_login_form_header">"Wprowadź swoje dane"</string> <string name="screen_login_form_header">"Wprowadź swoje dane"</string>
<string name="screen_login_subtitle">"Matrix to otwarta sieć do bezpiecznej i zdecentralizowanej komunikacji."</string> <string name="screen_login_subtitle">"Matrix to otwarta sieć do bezpiecznej i zdecentralizowanej komunikacji."</string>
<string name="screen_login_title">"Witaj ponownie!"</string> <string name="screen_login_title">"Witaj ponownie!"</string>
<string name="screen_login_title_with_homeserver">"Zaloguj się do %1$s"</string> <string name="screen_login_title_with_homeserver">"Zaloguj się do %1$s"</string>
<string name="screen_qr_code_login_connecting_subtitle">"Nawiązanie bezpiecznego połączenia"</string>
<string name="screen_qr_code_login_connection_note_secure_state_description">"Nie udało się nawiązać bezpiecznego połączenia z nowym urządzeniem. Twoje istniejące urządzenia są nadal bezpieczne i nie musisz się o nie martwić."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"Co teraz?"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Spróbuj zalogować się ponownie za pomocą kodu QR, jeśli byłby to problem z siecią"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Jeśli napotkasz ten sam problem, użyj innej sieci Wi-FI lub danych mobilnych"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"Jeśli to nie zadziała, zaloguj się ręcznie"</string>
<string name="screen_qr_code_login_connection_note_secure_state_title">"Połączenie nie jest bezpieczne"</string>
<string name="screen_qr_code_login_device_code_subtitle">"Zostaniesz poproszony o wprowadzenie dwóch cyfr widocznych na tym urządzeniu."</string>
<string name="screen_qr_code_login_device_code_title">"Wprowadź numer poniżej na innym urządzeniu"</string>
<string name="screen_qr_code_login_device_not_signed_in_scan_state_description">"Zaloguj się na drugie urządzenie lub użyj tego, które jest już zalogowane, a następnie spróbuj ponownie."</string>
<string name="screen_qr_code_login_device_not_signed_in_scan_state_subtitle">"Drugie urządzenie nie jest zalogowane"</string>
<string name="screen_qr_code_login_error_cancelled_subtitle">"Logowanie zostało anulowane na drugim urządzeniu."</string>
<string name="screen_qr_code_login_error_cancelled_title">"Prośba o logowanie została anulowana"</string>
<string name="screen_qr_code_login_error_declined_subtitle">"Logowanie zostało odrzucone na drugim urządzeniu."</string>
<string name="screen_qr_code_login_error_declined_title">"Logowanie odrzucone"</string>
<string name="screen_qr_code_login_error_expired_subtitle">"Logowanie wygasło. Spróbuj ponownie."</string>
<string name="screen_qr_code_login_error_expired_title">"Logowanie nie zostało ukończone na czas"</string>
<string name="screen_qr_code_login_error_linking_not_suported_subtitle">"Twoje drugie urządzenie nie wspiera logowania się do %s za pomocą kodu QR.
Spróbuj zalogować się ręcznie lub zeskanuj kod QR na innym urządzeniu."</string>
<string name="screen_qr_code_login_error_linking_not_suported_title">"Kod QR nie jest wspierany"</string>
<string name="screen_qr_code_login_error_sliding_sync_not_supported_subtitle">"Twój dostawca konta nie obsługuje %1$s."</string>
<string name="screen_qr_code_login_error_sliding_sync_not_supported_title">"%1$s nie jest wspierany"</string>
<string name="screen_qr_code_login_initial_state_button_title">"Gotowy do skanowania"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Otwórz %1$s na urządzeniu stacjonarnym"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Kliknij na swój awatar"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Wybierz %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"“Powiąż nowe urządzenie”"</string>
<string name="screen_qr_code_login_initial_state_item_4">"Zeskanuj kod QR za pomocą tego urządzenia"</string>
<string name="screen_qr_code_login_initial_state_title">"Otwórz %1$s na innym urządzeniu, aby uzyskać kod QR"</string>
<string name="screen_qr_code_login_invalid_scan_state_description">"Użyj kodu QR widocznego na drugim urządzeniu."</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Spróbuj ponownie"</string> <string name="screen_qr_code_login_invalid_scan_state_retry_button">"Spróbuj ponownie"</string>
<string name="screen_qr_code_login_invalid_scan_state_subtitle">"Błędny kod QR"</string>
<string name="screen_qr_code_login_no_camera_permission_button">"Przejdź do ustawień aparatu"</string>
<string name="screen_qr_code_login_no_camera_permission_state_description">"Musisz przyznać uprawnienia %1$s do korzystania z kamery, aby kontynuować."</string>
<string name="screen_qr_code_login_no_camera_permission_state_title">"Zezwól na dostęp do kamery, aby zeskanować kod QR"</string>
<string name="screen_qr_code_login_scanning_state_title">"Skanuj kod QR"</string>
<string name="screen_qr_code_login_start_over_button">"Zacznij od nowa"</string>
<string name="screen_qr_code_login_unknown_error_description">"Wystąpił nieoczekiwany błąd. Spróbuj ponownie."</string>
<string name="screen_qr_code_login_verify_code_loading">"Oczekiwanie na drugie urządzenie"</string>
<string name="screen_qr_code_login_verify_code_subtitle">"Twój dostawca konta może poprosić o podany kod, aby zweryfikować logowanie."</string>
<string name="screen_qr_code_login_verify_code_title">"Twój kod weryfikacyjny"</string>
<string name="screen_server_confirmation_change_server">"Zmień dostawcę konta"</string> <string name="screen_server_confirmation_change_server">"Zmień dostawcę konta"</string>
<string name="screen_server_confirmation_message_login_element_dot_io">"Serwer prywatny dla pracowników Element."</string> <string name="screen_server_confirmation_message_login_element_dot_io">"Serwer prywatny dla pracowników Element."</string>
<string name="screen_server_confirmation_message_login_matrix_dot_org">"Matrix to otwarta sieć do bezpiecznej i zdecentralizowanej komunikacji."</string> <string name="screen_server_confirmation_message_login_matrix_dot_org">"Matrix to otwarta sieć do bezpiecznej i zdecentralizowanej komunikacji."</string>

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_account_provider_change">"Hisob provayderini o\'zgartiring"</string>
<string name="screen_account_provider_form_hint">"Uy server manzili"</string>
<string name="screen_account_provider_form_notice">"Qidiruv so\'zini yoki domen manzilini kiriting."</string>
<string name="screen_account_provider_form_subtitle">"Kompaniya, jamoa yoki shaxsiy serverni qidiring."</string>
<string name="screen_account_provider_form_title">"Hisob provayderini toping"</string>
<string name="screen_account_provider_signin_subtitle">"Bu sizning suhbatlaringiz yashaydigan joy - xuddi siz elektron pochta xabarlaringizni saqlash uchun elektron pochta provayderidan foydalanganingiz kabi."</string>
<string name="screen_account_provider_signin_title">"Siz %sga kirmoqchisiz"</string>
<string name="screen_account_provider_signup_subtitle">"Bu sizning suhbatlaringiz yashaydigan joy - xuddi siz elektron pochta xabarlaringizni saqlash uchun elektron pochta provayderidan foydalanganingiz kabi."</string>
<string name="screen_account_provider_signup_title">"Siz %sda hisob yaratmoqchisiz"</string>
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org - bu Matrix.org Jamg\'armasi tomonidan boshqariladigan xavfsiz, markazlashtirilmagan aloqa uchun ommaviy Matrix tarmog\'idagi katta, bepul server."</string>
<string name="screen_change_account_provider_other">"Boshqa"</string>
<string name="screen_change_account_provider_subtitle">"Shaxsiy serveringiz yoki ishchi hisob qaydnomangiz kabi boshqa hisob provayderidan foydalaning."</string>
<string name="screen_change_account_provider_title">"Hisob provayderini o\'zgartiring"</string>
<string name="screen_change_server_error_invalid_homeserver">"Bu uy serveriga kira olmadik. Iltimos, uy serverining URL manzilini to\'ri kiritganingizni tekshiring. Agar URL toʻgʻri boʻlsa, qoʻshimcha yordam olish uchun uy serveri administratoriga murojaat qiling."</string>
<string name="screen_change_server_error_no_sliding_sync_message">"Hozirda bu server siljish sinxronlashni qollab-quvvatlamaydi."</string>
<string name="screen_change_server_form_header">"Uy serverining URL manzili"</string>
<string name="screen_change_server_form_notice">"Siz faqat siljish sinxronlashni qo\'llab-quvvatlaydigan mavjud serverga ulanishingiz mumkin. Uy serveringiz administratori uni sozlashi kerak.%1$s"</string>
<string name="screen_change_server_subtitle">"Serveringizning manzili nima?"</string>
<string name="screen_change_server_title">"Serveringizni tanlang"</string>
<string name="screen_login_error_deactivated_account">"Bu hisob ochirilgan."</string>
<string name="screen_login_error_invalid_credentials">"Notog\'ri foydalanuvchi nomi va/yoki parol"</string>
<string name="screen_login_error_invalid_user_id">"Bu haqiqiy foydalanuvchi identifikatori emas. Kutilayotgan format: \'@user:homeserver.org\'"</string>
<string name="screen_login_error_unsupported_authentication">"Tanlangan uy serveri parol yoki OIDC loginni qo\'lab-quvvatlamaydi. Iltimos, administratoringizga murojaat qiling yoki boshqa uy serverini tanlang."</string>
<string name="screen_login_form_header">"Tafsilotlaringizni kiriting"</string>
<string name="screen_login_subtitle">"Matrix xavfsiz, markazlashmagan aloqa uchun ochiq tarmoqdir."</string>
<string name="screen_login_title">"Qaytib kelganingizdan xursandmiz!"</string>
<string name="screen_login_title_with_homeserver">"Kirish%1$s"</string>
<string name="screen_server_confirmation_change_server">"Hisob provayderini o\'zgartiring"</string>
<string name="screen_server_confirmation_message_login_element_dot_io">"Element xodimlari uchun shaxsiy server."</string>
<string name="screen_server_confirmation_message_login_matrix_dot_org">"Matrix xavfsiz, markazlashmagan aloqa uchun ochiq tarmoqdir."</string>
<string name="screen_server_confirmation_message_register">"Bu sizning suhbatlaringiz yashaydigan joy - xuddi siz elektron pochta xabarlaringizni saqlash uchun elektron pochta provayderidan foydalanganingiz kabi."</string>
<string name="screen_server_confirmation_title_login">"Siz tizimga kirmoqchisiz%1$s"</string>
<string name="screen_server_confirmation_title_register">"Hisob yaratmoqchisiz%1$s"</string>
<string name="screen_waitlist_message">"Hozirgi paytda %2$sga %1$sda talab yuqori. Bir necha kundan keyin ilovaga qayting va qaytadan urining.
Sabr-toqatingiz uchun rahmat!"</string>
<string name="screen_waitlist_message_success">"%1$sga Xush kelibsiz!"</string>
<string name="screen_waitlist_title">"Siz deyarli keldingiz."</string>
<string name="screen_waitlist_title_success">"Siz kirdingiz."</string>
</resources>

View file

@ -4,5 +4,9 @@
<string name="screen_signout_confirmation_dialog_submit">"Sair"</string> <string name="screen_signout_confirmation_dialog_submit">"Sair"</string>
<string name="screen_signout_confirmation_dialog_title">"Sair"</string> <string name="screen_signout_confirmation_dialog_title">"Sair"</string>
<string name="screen_signout_in_progress_dialog_content">"Saindo…"</string> <string name="screen_signout_in_progress_dialog_content">"Saindo…"</string>
<string name="screen_signout_key_backup_disabled_title">"Você desativou o backup"</string>
<string name="screen_signout_key_backup_ongoing_title">"O backup das suas chaves ainda está em andamento"</string>
<string name="screen_signout_preference_item">"Sair"</string> <string name="screen_signout_preference_item">"Sair"</string>
<string name="screen_signout_recovery_disabled_title">"A recuperação não está configurada"</string>
<string name="screen_signout_save_recovery_key_title">"Você salvou sua chave de recuperação?"</string>
</resources> </resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_signout_confirmation_dialog_content">"Haqiqatan ham tizimdan chiqmoqchimisiz?"</string>
<string name="screen_signout_confirmation_dialog_submit">"Tizimdan chiqish"</string>
<string name="screen_signout_confirmation_dialog_title">"Tizimdan chiqish"</string>
<string name="screen_signout_in_progress_dialog_content">"Chiqish…"</string>
<string name="screen_signout_preference_item">"Tizimdan chiqish"</string>
</resources>

View file

@ -58,7 +58,11 @@ import kotlin.math.roundToInt
* @param modifier The modifier for the layout. * @param modifier The modifier for the layout.
* @param sheetContentKey The key for the sheet content. If the key changes, the sheet will be remeasured. * @param sheetContentKey The key for the sheet content. If the key changes, the sheet will be remeasured.
*/ */
@Suppress("ContentTrailingLambda") @Suppress(
"ContentTrailingLambda",
// False positive
"MultipleEmitters",
)
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
internal fun ExpandableBottomSheetScaffold( internal fun ExpandableBottomSheetScaffold(

View file

@ -20,10 +20,12 @@ import android.os.Build
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
@ -75,12 +77,13 @@ import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.MessageEventType
import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.isDm
import io.element.android.libraries.matrix.api.room.powerlevels.canPinUnpin
import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOther
import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOwn
import io.element.android.libraries.matrix.api.room.powerlevels.canSendMessage
import io.element.android.libraries.matrix.ui.messages.reply.map import io.element.android.libraries.matrix.ui.messages.reply.map
import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.libraries.matrix.ui.room.canCall import io.element.android.libraries.matrix.ui.room.canCall
import io.element.android.libraries.matrix.ui.room.canRedactOtherAsState
import io.element.android.libraries.matrix.ui.room.canRedactOwnAsState
import io.element.android.libraries.matrix.ui.room.canSendMessageAsState
import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.textcomposer.model.MessageComposerMode
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentList
@ -135,10 +138,9 @@ class MessagesPresenter @AssistedInject constructor(
val pinnedMessagesBannerState = pinnedMessagesBannerPresenter.present() val pinnedMessagesBannerState = pinnedMessagesBannerPresenter.present()
val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value)
val userHasPermissionToRedactOwn by room.canRedactOwnAsState(updateKey = syncUpdateFlow.value) val userEventPermissions by userEventPermissions(syncUpdateFlow.value)
val userHasPermissionToRedactOther by room.canRedactOtherAsState(updateKey = syncUpdateFlow.value)
val userHasPermissionToSendReaction by room.canSendMessageAsState(type = MessageEventType.REACTION, updateKey = syncUpdateFlow.value)
val roomName: AsyncData<String> by remember { val roomName: AsyncData<String> by remember {
derivedStateOf { roomInfo?.name?.let { AsyncData.Success(it) } ?: AsyncData.Uninitialized } derivedStateOf { roomInfo?.name?.let { AsyncData.Success(it) } ?: AsyncData.Uninitialized }
} }
@ -215,11 +217,8 @@ class MessagesPresenter @AssistedInject constructor(
roomName = roomName, roomName = roomName,
roomAvatar = roomAvatar, roomAvatar = roomAvatar,
heroes = heroes, heroes = heroes,
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
userHasPermissionToRedactOwn = userHasPermissionToRedactOwn,
userHasPermissionToRedactOther = userHasPermissionToRedactOther,
userHasPermissionToSendReaction = userHasPermissionToSendReaction,
composerState = composerState, composerState = composerState,
userEventPermissions = userEventPermissions,
voiceMessageComposerState = voiceMessageComposerState, voiceMessageComposerState = voiceMessageComposerState,
timelineState = timelineState, timelineState = timelineState,
typingNotificationState = typingNotificationState, typingNotificationState = typingNotificationState,
@ -240,6 +239,19 @@ class MessagesPresenter @AssistedInject constructor(
) )
} }
@Composable
private fun userEventPermissions(updateKey: Long): State<UserEventPermissions> {
return produceState(UserEventPermissions.DEFAULT, key1 = updateKey) {
value = UserEventPermissions(
canSendMessage = room.canSendMessage(type = MessageEventType.ROOM_MESSAGE).getOrElse { true },
canSendReaction = room.canSendMessage(type = MessageEventType.REACTION).getOrElse { true },
canRedactOwn = room.canRedactOwn().getOrElse { false },
canRedactOther = room.canRedactOther().getOrElse { false },
canPinUnpin = room.canPinUnpin().getOrElse { false },
)
}
}
private fun MatrixRoomInfo.avatarData(): AvatarData { private fun MatrixRoomInfo.avatarData(): AvatarData {
return AvatarData( return AvatarData(
id = id.value, id = id.value,
@ -273,6 +285,30 @@ class MessagesPresenter @AssistedInject constructor(
TimelineItemAction.Forward -> handleForwardAction(targetEvent) TimelineItemAction.Forward -> handleForwardAction(targetEvent)
TimelineItemAction.ReportContent -> handleReportAction(targetEvent) TimelineItemAction.ReportContent -> handleReportAction(targetEvent)
TimelineItemAction.EndPoll -> handleEndPollAction(targetEvent, timelineState) TimelineItemAction.EndPoll -> handleEndPollAction(targetEvent, timelineState)
TimelineItemAction.Pin -> handlePinAction(targetEvent)
TimelineItemAction.Unpin -> handleUnpinAction(targetEvent)
}
}
private suspend fun handlePinAction(targetEvent: TimelineItem.Event) {
if (targetEvent.eventId == null) return
timelineController.invokeOnCurrentTimeline {
pinEvent(targetEvent.eventId)
.onFailure {
Timber.e(it, "Failed to pin event ${targetEvent.eventId}")
snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_error))
}
}
}
private suspend fun handleUnpinAction(targetEvent: TimelineItem.Event) {
if (targetEvent.eventId == null) return
timelineController.invokeOnCurrentTimeline {
unpinEvent(targetEvent.eventId)
.onFailure {
Timber.e(it, "Failed to unpin event ${targetEvent.eventId}")
snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_error))
}
} }
} }

View file

@ -38,10 +38,7 @@ data class MessagesState(
val roomName: AsyncData<String>, val roomName: AsyncData<String>,
val roomAvatar: AsyncData<AvatarData>, val roomAvatar: AsyncData<AvatarData>,
val heroes: ImmutableList<AvatarData>, val heroes: ImmutableList<AvatarData>,
val userHasPermissionToSendMessage: Boolean, val userEventPermissions: UserEventPermissions,
val userHasPermissionToRedactOwn: Boolean,
val userHasPermissionToRedactOther: Boolean,
val userHasPermissionToSendReaction: Boolean,
val composerState: MessageComposerState, val composerState: MessageComposerState,
val voiceMessageComposerState: VoiceMessageComposerState, val voiceMessageComposerState: VoiceMessageComposerState,
val timelineState: TimelineState, val timelineState: TimelineState,

View file

@ -55,7 +55,7 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
aMessagesState(), aMessagesState(),
aMessagesState(hasNetworkConnection = false), aMessagesState(hasNetworkConnection = false),
aMessagesState(composerState = aMessageComposerState(showAttachmentSourcePicker = true)), aMessagesState(composerState = aMessageComposerState(showAttachmentSourcePicker = true)),
aMessagesState(userHasPermissionToSendMessage = false), aMessagesState(userEventPermissions = aUserEventPermissions(canSendMessage = false)),
aMessagesState(showReinvitePrompt = true), aMessagesState(showReinvitePrompt = true),
aMessagesState( aMessagesState(
roomName = AsyncData.Uninitialized, roomName = AsyncData.Uninitialized,
@ -101,10 +101,7 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
fun aMessagesState( fun aMessagesState(
roomName: AsyncData<String> = AsyncData.Success("Room name"), roomName: AsyncData<String> = AsyncData.Success("Room name"),
roomAvatar: AsyncData<AvatarData> = AsyncData.Success(AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom)), roomAvatar: AsyncData<AvatarData> = AsyncData.Success(AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom)),
userHasPermissionToSendMessage: Boolean = true, userEventPermissions: UserEventPermissions = aUserEventPermissions(),
userHasPermissionToRedactOwn: Boolean = false,
userHasPermissionToRedactOther: Boolean = false,
userHasPermissionToSendReaction: Boolean = true,
composerState: MessageComposerState = aMessageComposerState( composerState: MessageComposerState = aMessageComposerState(
textEditorState = TextEditorState.Rich(aRichTextEditorState(initialText = "Hello", initialFocus = true)), textEditorState = TextEditorState.Rich(aRichTextEditorState(initialText = "Hello", initialFocus = true)),
isFullScreen = false, isFullScreen = false,
@ -131,10 +128,7 @@ fun aMessagesState(
roomName = roomName, roomName = roomName,
roomAvatar = roomAvatar, roomAvatar = roomAvatar,
heroes = persistentListOf(), heroes = persistentListOf(),
userHasPermissionToSendMessage = userHasPermissionToSendMessage, userEventPermissions = userEventPermissions,
userHasPermissionToRedactOwn = userHasPermissionToRedactOwn,
userHasPermissionToRedactOther = userHasPermissionToRedactOther,
userHasPermissionToSendReaction = userHasPermissionToSendReaction,
composerState = composerState, composerState = composerState,
voiceMessageComposerState = voiceMessageComposerState, voiceMessageComposerState = voiceMessageComposerState,
typingNotificationState = aTypingNotificationState(), typingNotificationState = aTypingNotificationState(),
@ -155,6 +149,20 @@ fun aMessagesState(
eventSink = eventSink, eventSink = eventSink,
) )
fun aUserEventPermissions(
canRedactOwn: Boolean = false,
canRedactOther: Boolean = false,
canSendMessage: Boolean = true,
canSendReaction: Boolean = true,
canPinUnpin: Boolean = false,
) = UserEventPermissions(
canRedactOwn = canRedactOwn,
canRedactOther = canRedactOther,
canSendMessage = canSendMessage,
canSendReaction = canSendReaction,
canPinUnpin = canPinUnpin,
)
fun aReactionSummaryState( fun aReactionSummaryState(
target: ReactionSummaryState.Summary? = null, target: ReactionSummaryState.Summary? = null,
eventSink: (ReactionSummaryEvents) -> Unit = {} eventSink: (ReactionSummaryEvents) -> Unit = {}

View file

@ -160,10 +160,7 @@ fun MessagesView(
state.actionListState.eventSink( state.actionListState.eventSink(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = event, event = event,
canRedactOwn = state.userHasPermissionToRedactOwn, userEventPermissions = state.userEventPermissions,
canRedactOther = state.userHasPermissionToRedactOther,
canSendMessage = state.userHasPermissionToSendMessage,
canSendReaction = state.userHasPermissionToSendReaction,
) )
) )
} }
@ -426,7 +423,7 @@ private fun MessagesViewComposerBottomSheetContents(
subcomposing: Boolean, subcomposing: Boolean,
state: MessagesState, state: MessagesState,
) { ) {
if (state.userHasPermissionToSendMessage) { if (state.userEventPermissions.canSendMessage) {
Column(modifier = Modifier.fillMaxWidth()) { Column(modifier = Modifier.fillMaxWidth()) {
MentionSuggestionsPickerView( MentionSuggestionsPickerView(
modifier = Modifier modifier = Modifier

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.messages.impl
/**
* Represents the permissions a user has in a room.
* It's dependent of the user's power level in the room.
*/
data class UserEventPermissions(
val canRedactOwn: Boolean,
val canRedactOther: Boolean,
val canSendMessage: Boolean,
val canSendReaction: Boolean,
val canPinUnpin: Boolean,
) {
companion object {
val DEFAULT = UserEventPermissions(
canRedactOwn = false,
canRedactOther = false,
canSendMessage = true,
canSendReaction = true,
canPinUnpin = false
)
}
}

View file

@ -16,15 +16,13 @@
package io.element.android.features.messages.impl.actionlist package io.element.android.features.messages.impl.actionlist
import io.element.android.features.messages.impl.UserEventPermissions
import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItem
sealed interface ActionListEvents { sealed interface ActionListEvents {
data object Clear : ActionListEvents data object Clear : ActionListEvents
data class ComputeForMessage( data class ComputeForMessage(
val event: TimelineItem.Event, val event: TimelineItem.Event,
val canRedactOwn: Boolean, val userEventPermissions: UserEventPermissions,
val canRedactOther: Boolean,
val canSendMessage: Boolean,
val canSendReaction: Boolean,
) : ActionListEvents ) : ActionListEvents
} }

View file

@ -23,25 +23,36 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import io.element.android.features.messages.impl.UserEventPermissions
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemCallNotifyContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemCallNotifyContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLegacyCallInviteContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLegacyCallInviteContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
import io.element.android.features.messages.impl.timeline.model.event.canBeCopied import io.element.android.features.messages.impl.timeline.model.event.canBeCopied
import io.element.android.features.messages.impl.timeline.model.event.canBeForwarded
import io.element.android.features.messages.impl.timeline.model.event.canReact import io.element.android.features.messages.impl.timeline.model.event.canReact
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.preferences.api.store.AppPreferencesStore
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
class ActionListPresenter @Inject constructor( class ActionListPresenter @Inject constructor(
private val appPreferencesStore: AppPreferencesStore, private val appPreferencesStore: AppPreferencesStore,
private val featureFlagsService: FeatureFlagService,
private val room: MatrixRoom,
) : Presenter<ActionListState> { ) : Presenter<ActionListState> {
@Composable @Composable
override fun present(): ActionListState { override fun present(): ActionListState {
@ -52,17 +63,20 @@ class ActionListPresenter @Inject constructor(
} }
val isDeveloperModeEnabled by appPreferencesStore.isDeveloperModeEnabledFlow().collectAsState(initial = false) val isDeveloperModeEnabled by appPreferencesStore.isDeveloperModeEnabledFlow().collectAsState(initial = false)
val isPinnedEventsEnabled by featureFlagsService.isFeatureEnabledFlow(FeatureFlags.PinnedEvents).collectAsState(initial = false)
val pinnedEventIds by remember {
room.roomInfoFlow.map { it.pinnedEventIds }
}.collectAsState(initial = persistentListOf())
fun handleEvents(event: ActionListEvents) { fun handleEvents(event: ActionListEvents) {
when (event) { when (event) {
ActionListEvents.Clear -> target.value = ActionListState.Target.None ActionListEvents.Clear -> target.value = ActionListState.Target.None
is ActionListEvents.ComputeForMessage -> localCoroutineScope.computeForMessage( is ActionListEvents.ComputeForMessage -> localCoroutineScope.computeForMessage(
timelineItem = event.event, timelineItem = event.event,
userCanRedactOwn = event.canRedactOwn, usersEventPermissions = event.userEventPermissions,
userCanRedactOther = event.canRedactOther,
userCanSendMessage = event.canSendMessage,
userCanSendReaction = event.canSendReaction,
isDeveloperModeEnabled = isDeveloperModeEnabled, isDeveloperModeEnabled = isDeveloperModeEnabled,
isPinnedEventsEnabled = isPinnedEventsEnabled,
pinnedEventIds = pinnedEventIds,
target = target, target = target,
) )
} }
@ -76,136 +90,22 @@ class ActionListPresenter @Inject constructor(
private fun CoroutineScope.computeForMessage( private fun CoroutineScope.computeForMessage(
timelineItem: TimelineItem.Event, timelineItem: TimelineItem.Event,
userCanRedactOwn: Boolean, usersEventPermissions: UserEventPermissions,
userCanRedactOther: Boolean,
userCanSendMessage: Boolean,
userCanSendReaction: Boolean,
isDeveloperModeEnabled: Boolean, isDeveloperModeEnabled: Boolean,
isPinnedEventsEnabled: Boolean,
pinnedEventIds: ImmutableList<EventId>,
target: MutableState<ActionListState.Target> target: MutableState<ActionListState.Target>
) = launch { ) = launch {
target.value = ActionListState.Target.Loading(timelineItem) target.value = ActionListState.Target.Loading(timelineItem)
val canRedact = timelineItem.isMine && userCanRedactOwn || !timelineItem.isMine && userCanRedactOther
val actions = val actions = buildActions(
when (timelineItem.content) { timelineItem = timelineItem,
is TimelineItemCallNotifyContent -> { usersEventPermissions = usersEventPermissions,
if (isDeveloperModeEnabled) { isDeveloperModeEnabled = isDeveloperModeEnabled,
listOf(TimelineItemAction.ViewSource) isPinnedEventsEnabled = isPinnedEventsEnabled,
} else { isEventPinned = pinnedEventIds.contains(timelineItem.eventId),
emptyList() )
} val displayEmojiReactions = usersEventPermissions.canSendReaction &&
}
is TimelineItemRedactedContent -> {
if (isDeveloperModeEnabled) {
listOf(TimelineItemAction.ViewSource)
} else {
emptyList()
}
}
is TimelineItemStateContent -> {
buildList {
add(TimelineItemAction.Copy)
if (timelineItem.isRemote) {
add(TimelineItemAction.CopyLink)
}
if (isDeveloperModeEnabled) {
add(TimelineItemAction.ViewSource)
}
}
}
is TimelineItemPollContent -> {
val canEndPoll = timelineItem.isRemote &&
!timelineItem.content.isEnded &&
(timelineItem.isMine || canRedact)
buildList {
if (timelineItem.isRemote) {
// Can only reply or forward messages already uploaded to the server
add(TimelineItemAction.Reply)
}
if (timelineItem.isRemote && timelineItem.isEditable) {
add(TimelineItemAction.Edit)
}
if (canEndPoll) {
add(TimelineItemAction.EndPoll)
}
if (timelineItem.content.canBeCopied()) {
add(TimelineItemAction.Copy)
}
if (timelineItem.isRemote) {
add(TimelineItemAction.CopyLink)
}
if (isDeveloperModeEnabled) {
add(TimelineItemAction.ViewSource)
}
if (!timelineItem.isMine) {
add(TimelineItemAction.ReportContent)
}
if (canRedact) {
add(TimelineItemAction.Redact)
}
}
}
is TimelineItemVoiceContent -> {
buildList {
if (timelineItem.isRemote) {
add(TimelineItemAction.Reply)
add(TimelineItemAction.Forward)
add(TimelineItemAction.CopyLink)
}
if (isDeveloperModeEnabled) {
add(TimelineItemAction.ViewSource)
}
if (!timelineItem.isMine) {
add(TimelineItemAction.ReportContent)
}
if (canRedact) {
add(TimelineItemAction.Redact)
}
}
}
is TimelineItemLegacyCallInviteContent -> {
buildList {
if (isDeveloperModeEnabled) {
add(TimelineItemAction.ViewSource)
}
}
}
else -> buildList<TimelineItemAction> {
if (timelineItem.isRemote) {
// Can only reply or forward messages already uploaded to the server
if (userCanSendMessage) {
if (timelineItem.isThreaded) {
add(TimelineItemAction.ReplyInThread)
} else {
add(TimelineItemAction.Reply)
}
}
// Stickers can't be forwarded (yet) so we don't show the option
// See https://github.com/element-hq/element-x-android/issues/2161
if (!timelineItem.isSticker) {
add(TimelineItemAction.Forward)
}
}
if (timelineItem.isEditable) {
add(TimelineItemAction.Edit)
}
if (timelineItem.content.canBeCopied()) {
add(TimelineItemAction.Copy)
}
if (timelineItem.isRemote) {
add(TimelineItemAction.CopyLink)
}
if (isDeveloperModeEnabled) {
add(TimelineItemAction.ViewSource)
}
if (!timelineItem.isMine) {
add(TimelineItemAction.ReportContent)
}
if (canRedact) {
add(TimelineItemAction.Redact)
}
}
}
val displayEmojiReactions = userCanSendReaction &&
timelineItem.isRemote && timelineItem.isRemote &&
timelineItem.content.canReact() timelineItem.content.canReact()
if (actions.isNotEmpty() || displayEmojiReactions) { if (actions.isNotEmpty() || displayEmojiReactions) {
@ -219,3 +119,71 @@ class ActionListPresenter @Inject constructor(
} }
} }
} }
private fun buildActions(
timelineItem: TimelineItem.Event,
usersEventPermissions: UserEventPermissions,
isDeveloperModeEnabled: Boolean,
isPinnedEventsEnabled: Boolean,
isEventPinned: Boolean,
): List<TimelineItemAction> {
val canRedact = timelineItem.isMine && usersEventPermissions.canRedactOwn || !timelineItem.isMine && usersEventPermissions.canRedactOther
return buildList {
if (timelineItem.canBeRepliedTo && usersEventPermissions.canSendMessage) {
if (timelineItem.isThreaded) {
add(TimelineItemAction.ReplyInThread)
} else {
add(TimelineItemAction.Reply)
}
}
if (timelineItem.isRemote && timelineItem.content.canBeForwarded()) {
add(TimelineItemAction.Forward)
}
if (timelineItem.isEditable) {
add(TimelineItemAction.Edit)
}
if (canRedact && timelineItem.content is TimelineItemPollContent && !timelineItem.content.isEnded) {
add(TimelineItemAction.EndPoll)
}
val canPinUnpin = isPinnedEventsEnabled && usersEventPermissions.canPinUnpin && timelineItem.isRemote
if (canPinUnpin) {
if (isEventPinned) {
add(TimelineItemAction.Unpin)
} else {
add(TimelineItemAction.Pin)
}
}
if (timelineItem.content.canBeCopied()) {
add(TimelineItemAction.Copy)
}
if (timelineItem.isRemote) {
add(TimelineItemAction.CopyLink)
}
if (isDeveloperModeEnabled) {
add(TimelineItemAction.ViewSource)
}
if (!timelineItem.isMine) {
add(TimelineItemAction.ReportContent)
}
if (canRedact) {
add(TimelineItemAction.Redact)
}
}.postFilter(timelineItem.content)
}
/**
* Post filter the actions based on the content of the event.
*/
private fun List<TimelineItemAction>.postFilter(content: TimelineItemEventContent): List<TimelineItemAction> {
return filter { action ->
when (content) {
is TimelineItemCallNotifyContent,
is TimelineItemLegacyCallInviteContent,
is TimelineItemStateContent,
is TimelineItemRedactedContent -> {
action == TimelineItemAction.ViewSource
}
else -> true
}
}
}

View file

@ -218,6 +218,7 @@ private fun SheetContent(
} }
} }
@Suppress("MultipleEmitters") // False positive
@Composable @Composable
private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modifier) { private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modifier) {
val content: @Composable () -> Unit val content: @Composable () -> Unit

View file

@ -39,4 +39,8 @@ sealed class TimelineItemAction(
data object ViewSource : TimelineItemAction(CommonStrings.action_view_source, CommonDrawables.ic_developer_options) data object ViewSource : TimelineItemAction(CommonStrings.action_view_source, CommonDrawables.ic_developer_options)
data object ReportContent : TimelineItemAction(CommonStrings.action_report_content, CompoundDrawables.ic_compound_chat_problem, destructive = true) data object ReportContent : TimelineItemAction(CommonStrings.action_report_content, CompoundDrawables.ic_compound_chat_problem, destructive = true)
data object EndPoll : TimelineItemAction(CommonStrings.action_end_poll, CompoundDrawables.ic_compound_polls_end) data object EndPoll : TimelineItemAction(CommonStrings.action_end_poll, CompoundDrawables.ic_compound_polls_end)
data object Pin : TimelineItemAction(CommonStrings.action_pin, CompoundDrawables.ic_compound_pin)
// TODO use the Unpin compound icon when available.
data object Unpin : TimelineItemAction(CommonStrings.action_unpin, CompoundDrawables.ic_compound_pin)
} }

View file

@ -127,6 +127,7 @@ internal fun aTimelineItemEvent(
transactionId: TransactionId? = null, transactionId: TransactionId? = null,
isMine: Boolean = false, isMine: Boolean = false,
isEditable: Boolean = false, isEditable: Boolean = false,
canBeRepliedTo: Boolean = false,
senderDisplayName: String = "Sender", senderDisplayName: String = "Sender",
displayNameAmbiguous: Boolean = false, displayNameAmbiguous: Boolean = false,
content: TimelineItemEventContent = aTimelineItemTextContent(), content: TimelineItemEventContent = aTimelineItemTextContent(),
@ -150,6 +151,7 @@ internal fun aTimelineItemEvent(
sentTime = "12:34", sentTime = "12:34",
isMine = isMine, isMine = isMine,
isEditable = isEditable, isEditable = isEditable,
canBeRepliedTo = canBeRepliedTo,
senderProfile = aProfileTimelineDetailsReady( senderProfile = aProfileTimelineDetailsReady(
displayName = senderDisplayName, displayName = senderDisplayName,
displayNameAmbiguous = displayNameAmbiguous, displayNameAmbiguous = displayNameAmbiguous,

View file

@ -77,7 +77,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
import io.element.android.features.messages.impl.timeline.model.event.canBeRepliedTo
import io.element.android.libraries.designsystem.colors.AvatarColorsProvider import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
import io.element.android.libraries.designsystem.components.EqualWidthColumn import io.element.android.libraries.designsystem.components.EqualWidthColumn
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
@ -148,7 +147,7 @@ fun TimelineItemEventRow(
} else { } else {
Spacer(modifier = Modifier.height(2.dp)) Spacer(modifier = Modifier.height(2.dp))
} }
val canReply = timelineRoomInfo.userHasPermissionToSendMessage && event.content.canBeRepliedTo() val canReply = timelineRoomInfo.userHasPermissionToSendMessage && event.canBeRepliedTo
if (canReply) { if (canReply) {
val state: SwipeableActionsState = rememberSwipeableActionsState() val state: SwipeableActionsState = rememberSwipeableActionsState()
val offset = state.offset.floatValue val offset = state.offset.floatValue
@ -377,6 +376,7 @@ private fun MessageSenderInformation(
} }
} }
@Suppress("MultipleEmitters") // False positive
@Composable @Composable
private fun MessageEventBubbleContent( private fun MessageEventBubbleContent(
event: TimelineItem.Event, event: TimelineItem.Event,

View file

@ -110,7 +110,7 @@ fun TimelineItemReactionsLayout(
} }
val rows = rowsIn.toMutableList() val rows = rowsIn.toMutableList()
val secondLastRow = rows[rows.size - 2].toMutableList() val secondLastRow = rows[rows.size - 2].toMutableList()
val expandButtonPlaceable = secondLastRow.removeLast() val expandButtonPlaceable = secondLastRow.removeAt(secondLastRow.lastIndex)
lastRow.add(0, expandButtonPlaceable) lastRow.add(0, expandButtonPlaceable)
rows[rows.size - 2] = secondLastRow rows[rows.size - 2] = secondLastRow
rows[rows.size - 1] = lastRow rows[rows.size - 1] = lastRow

View file

@ -76,6 +76,7 @@ class TimelineItemEventFactory @Inject constructor(
content = contentFactory.create(currentTimelineItem.event), content = contentFactory.create(currentTimelineItem.event),
isMine = currentTimelineItem.event.isOwn, isMine = currentTimelineItem.event.isOwn,
isEditable = currentTimelineItem.event.isEditable, isEditable = currentTimelineItem.event.isEditable,
canBeRepliedTo = currentTimelineItem.event.canBeRepliedTo,
sentTime = sentTime, sentTime = sentTime,
groupPosition = groupPosition, groupPosition = groupPosition,
reactionsState = currentTimelineItem.computeReactionsState(), reactionsState = currentTimelineItem.computeReactionsState(),

View file

@ -74,6 +74,7 @@ sealed interface TimelineItem {
val sentTime: String = "", val sentTime: String = "",
val isMine: Boolean = false, val isMine: Boolean = false,
val isEditable: Boolean, val isEditable: Boolean,
val canBeRepliedTo: Boolean,
val groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.None, val groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.None,
val reactionsState: TimelineItemReactions, val reactionsState: TimelineItemReactions,
val readReceiptState: TimelineItemReadReceipts, val readReceiptState: TimelineItemReadReceipts,

View file

@ -24,27 +24,27 @@ sealed interface TimelineItemEventContent {
} }
/** /**
* Only text based content and states can be copied. * Only text based content can be copied.
*/ */
fun TimelineItemEventContent.canBeCopied(): Boolean = fun TimelineItemEventContent.canBeCopied(): Boolean =
when (this) { this is TimelineItemTextBasedContent
is TimelineItemTextBasedContent,
is TimelineItemStateContent,
is TimelineItemRedactedContent -> true
else -> false
}
/** /**
* Determine if the event content can be replied to. * Returns true if the event content can be forwarded.
* Note: it should match the logic in [io.element.android.features.messages.impl.actionlist.ActionListPresenter].
*/ */
fun TimelineItemEventContent.canBeRepliedTo(): Boolean = fun TimelineItemEventContent.canBeForwarded(): Boolean =
when (this) { when (this) {
is TimelineItemRedactedContent, is TimelineItemTextBasedContent,
is TimelineItemLegacyCallInviteContent, is TimelineItemImageContent,
is TimelineItemCallNotifyContent, is TimelineItemFileContent,
is TimelineItemStateContent -> false is TimelineItemAudioContent,
else -> true is TimelineItemVideoContent,
is TimelineItemLocationContent,
is TimelineItemVoiceContent -> true
// Stickers can't be forwarded (yet) so we don't show the option
// See https://github.com/element-hq/element-x-android/issues/2161
is TimelineItemStickerContent -> false
else -> false
} }
/** /**

View file

@ -53,6 +53,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMember
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@Suppress("MultipleEmitters") // False positive
@Composable @Composable
fun TypingNotificationView( fun TypingNotificationView(
state: TypingNotificationState, state: TypingNotificationState,

View file

@ -39,12 +39,18 @@
<string name="screen_room_timeline_read_marker_title">"Nowe"</string> <string name="screen_room_timeline_read_marker_title">"Nowe"</string>
<plurals name="screen_room_timeline_state_changes"> <plurals name="screen_room_timeline_state_changes">
<item quantity="one">"%1$d zmiana pokoju"</item> <item quantity="one">"%1$d zmiana pokoju"</item>
<item quantity="few">"%1$d zmian pokoju"</item> <item quantity="few">"%1$d zmiany pokoju"</item>
<item quantity="many">"%1$d zmiany pokoju"</item> <item quantity="many">"%1$d zmian pokoju"</item>
</plurals>
<plurals name="screen_room_typing_many_members">
<item quantity="one">"%1$s, %2$s i %3$d inny"</item>
<item quantity="few">"%1$s, %2$s i %3$d innych"</item>
<item quantity="many">"%1$s, %2$s i %3$d innych"</item>
</plurals> </plurals>
<plurals name="screen_room_typing_notification"> <plurals name="screen_room_typing_notification">
<item quantity="one">"%1$s piszę"</item> <item quantity="one">"%1$s piszę"</item>
<item quantity="few">"%1$s piszą"</item> <item quantity="few">"%1$s piszą"</item>
<item quantity="many">"%1$s piszą"</item> <item quantity="many">"%1$s pisze"</item>
</plurals> </plurals>
<string name="screen_room_typing_two_members">"%1$s i %2$s"</string>
</resources> </resources>

View file

@ -23,6 +23,7 @@
<string name="screen_room_encrypted_history_banner">"O histórico de mensagens não está disponível no momento."</string> <string name="screen_room_encrypted_history_banner">"O histórico de mensagens não está disponível no momento."</string>
<string name="screen_room_invite_again_alert_message">"Gostaria de convidá-los de volta?"</string> <string name="screen_room_invite_again_alert_message">"Gostaria de convidá-los de volta?"</string>
<string name="screen_room_invite_again_alert_title">"Você está sozinho neste chat"</string> <string name="screen_room_invite_again_alert_title">"Você está sozinho neste chat"</string>
<string name="screen_room_mentions_at_room_subtitle">"Notificar a sala inteira"</string>
<string name="screen_room_mentions_at_room_title">"Todos"</string> <string name="screen_room_mentions_at_room_title">"Todos"</string>
<string name="screen_room_retry_send_menu_send_again_action">"Enviar novamente"</string> <string name="screen_room_retry_send_menu_send_again_action">"Enviar novamente"</string>
<string name="screen_room_retry_send_menu_title">"Sua mensagem não foi enviada"</string> <string name="screen_room_retry_send_menu_title">"Sua mensagem não foi enviada"</string>
@ -36,7 +37,16 @@
<string name="screen_room_timeline_reactions_show_more">"Mostrar mais"</string> <string name="screen_room_timeline_reactions_show_more">"Mostrar mais"</string>
<string name="screen_room_timeline_read_marker_title">"Novo"</string> <string name="screen_room_timeline_read_marker_title">"Novo"</string>
<plurals name="screen_room_timeline_state_changes"> <plurals name="screen_room_timeline_state_changes">
<item quantity="one">"%1$d mudança de sala"</item> <item quantity="one">"%1$d alteração na sala"</item>
<item quantity="other">"%1$d mudanças de salas"</item> <item quantity="other">"%1$d alterações na sala"</item>
</plurals> </plurals>
<plurals name="screen_room_typing_many_members">
<item quantity="one">"%1$s, %2$s e %3$d outro"</item>
<item quantity="other">"%1$s, %2$s e %3$d outros"</item>
</plurals>
<plurals name="screen_room_typing_notification">
<item quantity="one">"%1$s está digitando"</item>
<item quantity="other">"%1$s estão digitando"</item>
</plurals>
<string name="screen_room_typing_two_members">"%1$s e %2$s"</string>
</resources> </resources>

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="emoji_picker_category_activity">"Faoliyatlar"</string>
<string name="emoji_picker_category_flags">"Bayroqlar"</string>
<string name="emoji_picker_category_foods">"Oziq-ovqat va ichimliklar"</string>
<string name="emoji_picker_category_nature">"Hayvonlar va tabiat"</string>
<string name="emoji_picker_category_objects">"Ob\'ektlar"</string>
<string name="emoji_picker_category_people">"Smayllar va odamlar"</string>
<string name="emoji_picker_category_places">"Sayohat va Joylar"</string>
<string name="emoji_picker_category_symbols">"Belgilar"</string>
<string name="screen_report_content_block_user">"Foydalanuvchini bloklash"</string>
<string name="screen_report_content_block_user_hint">"Ushbu foydalanuvchidan barcha joriy va kelajakdagi xabarlarni yashirishni xohlayotganingizni tekshiring"</string>
<string name="screen_report_content_explanation">"Bu xabar uy serveringiz administratoriga xabar qilinadi. Ular hech qanday shifrlangan xabarlarni o\'qiy olmaydi."</string>
<string name="screen_report_content_hint">"Ushbu kontent haqida xabar berish sababi"</string>
<string name="screen_room_attachment_source_camera">"Kamera"</string>
<string name="screen_room_attachment_source_camera_photo">"Rasmga olmoq"</string>
<string name="screen_room_attachment_source_camera_video">"Video yozib olish"</string>
<string name="screen_room_attachment_source_files">"Biriktirma"</string>
<string name="screen_room_attachment_source_gallery">"Fotosurat va video kutubxonasi"</string>
<string name="screen_room_attachment_source_location">"Joylashuv"</string>
<string name="screen_room_attachment_source_poll">"So\'ro\'vnoma"</string>
<string name="screen_room_attachment_text_formatting">"Matnni formatlash"</string>
<string name="screen_room_encrypted_history_banner">"Xabarlar tarixi hozirda mavjud emas."</string>
<string name="screen_room_invite_again_alert_message">"Ularni yana taklif qilmoqchimisiz?"</string>
<string name="screen_room_invite_again_alert_title">"Siz bu chatda yolg\'izsiz"</string>
<string name="screen_room_mentions_at_room_title">"Har kim"</string>
<string name="screen_room_retry_send_menu_send_again_action">"Yana yuboring"</string>
<string name="screen_room_retry_send_menu_title">"Xabaringiz yuborilmadi"</string>
<string name="screen_room_timeline_add_reaction">"Emoji qo\'shmoq"</string>
<string name="screen_room_timeline_beginning_of_room">"Bu %1$sni boshlanishi"</string>
<string name="screen_room_timeline_beginning_of_room_no_name">"Bu suhbatning boshlanishi."</string>
<string name="screen_room_timeline_less_reactions">"Kamroq ko\'rsatish"</string>
<string name="screen_room_timeline_message_copied">"Xabar nusxalandi"</string>
<string name="screen_room_timeline_no_permission_to_post">"Sizda bu xonaga post yozishga ruxsat yoq"</string>
<string name="screen_room_timeline_reactions_show_less">"Kamroq ko\'rsatish"</string>
<string name="screen_room_timeline_reactions_show_more">"Ko\'proq ko\'rsatish"</string>
<string name="screen_room_timeline_read_marker_title">"Yangi"</string>
<plurals name="screen_room_timeline_state_changes">
<item quantity="one">"%1$dxonani almashtirish"</item>
<item quantity="other">"%1$dxona o\'zgarishi"</item>
</plurals>
</resources>

View file

@ -44,6 +44,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
import io.element.android.features.messages.impl.typing.TypingNotificationPresenter import io.element.android.features.messages.impl.typing.TypingNotificationPresenter
import io.element.android.features.messages.impl.utils.FakeTextPillificationHelper import io.element.android.features.messages.impl.utils.FakeTextPillificationHelper
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPlayer import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPlayer
@ -79,6 +80,7 @@ import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID_2 import io.element.android.libraries.matrix.test.A_SESSION_ID_2
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.libraries.matrix.test.permalink.FakePermalinkBuilder import io.element.android.libraries.matrix.test.permalink.FakePermalinkBuilder
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
@ -139,8 +141,8 @@ class MessagesPresenterTest {
assertThat(initialState.roomName).isEqualTo(AsyncData.Success("")) assertThat(initialState.roomName).isEqualTo(AsyncData.Success(""))
assertThat(initialState.roomAvatar) assertThat(initialState.roomAvatar)
.isEqualTo(AsyncData.Success(AvatarData(id = A_ROOM_ID.value, name = "", url = AN_AVATAR_URL, size = AvatarSize.TimelineRoom))) .isEqualTo(AsyncData.Success(AvatarData(id = A_ROOM_ID.value, name = "", url = AN_AVATAR_URL, size = AvatarSize.TimelineRoom)))
assertThat(initialState.userHasPermissionToSendMessage).isTrue() assertThat(initialState.userEventPermissions.canSendMessage).isTrue()
assertThat(initialState.userHasPermissionToRedactOwn).isTrue() assertThat(initialState.userEventPermissions.canRedactOwn).isTrue()
assertThat(initialState.hasNetworkConnection).isTrue() assertThat(initialState.hasNetworkConnection).isTrue()
assertThat(initialState.snackbarMessage).isNull() assertThat(initialState.snackbarMessage).isNull()
assertThat(initialState.inviteProgress).isEqualTo(AsyncData.Uninitialized) assertThat(initialState.inviteProgress).isEqualTo(AsyncData.Uninitialized)
@ -157,7 +159,8 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
assertThat(room.markAsReadCalls).isEmpty() assertThat(room.markAsReadCalls).isEmpty()
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -177,7 +180,8 @@ class MessagesPresenterTest {
canRedactOwnResult = { Result.success(true) }, canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
).apply { canUserPinUnpinResult = { Result.success(true) },
).apply {
givenRoomInfo(aRoomInfo(hasRoomCall = true)) givenRoomInfo(aRoomInfo(hasRoomCall = true))
} }
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
@ -205,7 +209,8 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers) val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -242,7 +247,8 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers) val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -300,7 +306,8 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
val presenter = createMessagesPresenter( val presenter = createMessagesPresenter(
clipboardHelper = clipboardHelper, clipboardHelper = clipboardHelper,
matrixRoom = matrixRoom, matrixRoom = matrixRoom,
@ -489,7 +496,8 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(true) } val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(true) }
liveTimeline.redactEventLambda = redactEventLambda liveTimeline.redactEventLambda = redactEventLambda
@ -563,7 +571,8 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -598,7 +607,8 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -622,7 +632,8 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -646,7 +657,8 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
room.givenRoomMembersState( room.givenRoomMembersState(
MatrixRoomMembersState.Ready( MatrixRoomMembersState.Ready(
persistentListOf( persistentListOf(
@ -681,7 +693,8 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
room.givenRoomMembersState( room.givenRoomMembersState(
MatrixRoomMembersState.Error( MatrixRoomMembersState.Error(
failure = Throwable(), failure = Throwable(),
@ -717,7 +730,8 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
room.givenRoomMembersState(MatrixRoomMembersState.Unknown) room.givenRoomMembersState(MatrixRoomMembersState.Unknown)
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -743,7 +757,8 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
room.givenRoomMembersState( room.givenRoomMembersState(
MatrixRoomMembersState.Ready( MatrixRoomMembersState.Ready(
persistentListOf( persistentListOf(
@ -783,13 +798,14 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
val presenter = createMessagesPresenter(matrixRoom = matrixRoom) val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
val state = awaitFirstItem() val state = awaitFirstItem()
assertThat(state.userHasPermissionToSendMessage).isTrue() assertThat(state.userEventPermissions.canSendMessage).isTrue()
} }
} }
@ -807,15 +823,16 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
val presenter = createMessagesPresenter(matrixRoom = matrixRoom) val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
// Default value // Default value
assertThat(awaitItem().userHasPermissionToSendMessage).isTrue() assertThat(awaitItem().userEventPermissions.canSendMessage).isTrue()
skipItems(1) skipItems(1)
assertThat(awaitItem().userHasPermissionToSendMessage).isFalse() assertThat(awaitItem().userEventPermissions.canSendMessage).isFalse()
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
} }
} }
@ -828,14 +845,15 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(false) }, canRedactOtherResult = { Result.success(false) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
val presenter = createMessagesPresenter(matrixRoom = matrixRoom) val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
val initialState = consumeItemsUntilPredicate { it.userHasPermissionToRedactOwn }.last() val initialState = consumeItemsUntilPredicate { it.userEventPermissions.canRedactOwn }.last()
assertThat(initialState.userHasPermissionToRedactOwn).isTrue() assertThat(initialState.userEventPermissions.canRedactOwn).isTrue()
assertThat(initialState.userHasPermissionToRedactOther).isFalse() assertThat(initialState.userEventPermissions.canRedactOther).isFalse()
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
} }
} }
@ -848,14 +866,15 @@ class MessagesPresenterTest {
canRedactOwnResult = { Result.success(false) }, canRedactOwnResult = { Result.success(false) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
) canUserPinUnpinResult = { Result.success(true) },
)
val presenter = createMessagesPresenter(matrixRoom = matrixRoom) val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
val initialState = consumeItemsUntilPredicate { it.userHasPermissionToRedactOther }.last() val initialState = consumeItemsUntilPredicate { it.userEventPermissions.canRedactOther }.last()
assertThat(initialState.userHasPermissionToRedactOwn).isFalse() assertThat(initialState.userEventPermissions.canRedactOwn).isFalse()
assertThat(initialState.userHasPermissionToRedactOther).isTrue() assertThat(initialState.userEventPermissions.canRedactOther).isTrue()
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
} }
} }
@ -880,6 +899,74 @@ class MessagesPresenterTest {
} }
} }
@Test
fun `present - handle action pin`() = runTest {
val successPinEventLambda = lambdaRecorder { _: EventId -> Result.success(true) }
val failurePinEventLambda = lambdaRecorder { _: EventId -> Result.failure<Boolean>(A_THROWABLE) }
val timeline = FakeTimeline()
val room = FakeMatrixRoom(
liveTimeline = timeline,
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) },
)
val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val messageEvent = aMessageEvent(
content = aTimelineItemTextContent()
)
val initialState = awaitFirstItem()
timeline.pinEventLambda = successPinEventLambda
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Pin, messageEvent))
assert(successPinEventLambda).isCalledOnce().with(value(messageEvent.eventId))
timeline.pinEventLambda = failurePinEventLambda
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Pin, messageEvent))
assert(failurePinEventLambda).isCalledOnce().with(value(messageEvent.eventId))
assertThat(awaitItem().snackbarMessage).isNotNull()
}
}
@Test
fun `present - handle action unpin`() = runTest {
val successUnpinEventLambda = lambdaRecorder { _: EventId -> Result.success(true) }
val failureUnpinEventLambda = lambdaRecorder { _: EventId -> Result.failure<Boolean>(A_THROWABLE) }
val timeline = FakeTimeline()
val room = FakeMatrixRoom(
liveTimeline = timeline,
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) },
)
val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val messageEvent = aMessageEvent(
content = aTimelineItemTextContent()
)
val initialState = awaitFirstItem()
timeline.unpinEventLambda = successUnpinEventLambda
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Unpin, messageEvent))
assert(successUnpinEventLambda).isCalledOnce().with(value(messageEvent.eventId))
timeline.unpinEventLambda = failureUnpinEventLambda
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Unpin, messageEvent))
assert(failureUnpinEventLambda).isCalledOnce().with(value(messageEvent.eventId))
assertThat(awaitItem().snackbarMessage).isNotNull()
}
}
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T { private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
// Skip 2 item if Mentions feature is enabled, else 1 // Skip 2 item if Mentions feature is enabled, else 1
skipItems(if (FeatureFlags.Mentions.defaultValue(aBuildMeta())) 2 else 1) skipItems(if (FeatureFlags.Mentions.defaultValue(aBuildMeta())) 2 else 1)
@ -894,6 +981,7 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) },
).apply { ).apply {
givenRoomInfo(aRoomInfo(id = roomId, name = "")) givenRoomInfo(aRoomInfo(id = roomId, name = ""))
}, },
@ -960,11 +1048,17 @@ class MessagesPresenterTest {
return timelinePresenter return timelinePresenter
} }
} }
val actionListPresenter = ActionListPresenter(appPreferencesStore = appPreferencesStore) val featureFlagService = FakeFeatureFlagService()
val actionListPresenter = ActionListPresenter(
appPreferencesStore = appPreferencesStore,
featureFlagsService = featureFlagService,
room = matrixRoom,
)
val typingNotificationPresenter = TypingNotificationPresenter( val typingNotificationPresenter = TypingNotificationPresenter(
room = matrixRoom, room = matrixRoom,
sessionPreferencesStore = sessionPreferencesStore, sessionPreferencesStore = sessionPreferencesStore,
) )
val readReceiptBottomSheetPresenter = ReadReceiptBottomSheetPresenter() val readReceiptBottomSheetPresenter = ReadReceiptBottomSheetPresenter()
val customReactionPresenter = CustomReactionPresenter(emojibaseProvider = FakeEmojibaseProvider()) val customReactionPresenter = CustomReactionPresenter(emojibaseProvider = FakeEmojibaseProvider())
val reactionSummaryPresenter = ReactionSummaryPresenter(room = matrixRoom) val reactionSummaryPresenter = ReactionSummaryPresenter(room = matrixRoom)
@ -984,7 +1078,7 @@ class MessagesPresenterTest {
snackbarDispatcher = SnackbarDispatcher(), snackbarDispatcher = SnackbarDispatcher(),
navigator = navigator, navigator = navigator,
clipboardHelper = clipboardHelper, clipboardHelper = clipboardHelper,
featureFlagsService = FakeFeatureFlagService(), featureFlagsService = featureFlagService,
buildMeta = aBuildMeta(), buildMeta = aBuildMeta(),
dispatchers = coroutineDispatchers, dispatchers = coroutineDispatchers,
htmlConverterProvider = FakeHtmlConverterProvider(), htmlConverterProvider = FakeHtmlConverterProvider(),

View file

@ -26,6 +26,7 @@ import androidx.compose.ui.test.onAllNodesWithContentDescription
import androidx.compose.ui.test.onAllNodesWithTag import androidx.compose.ui.test.onAllNodesWithTag
import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onFirst import androidx.compose.ui.test.onFirst
import androidx.compose.ui.test.onLast
import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.onNodeWithText
@ -43,7 +44,6 @@ import io.element.android.features.messages.impl.actionlist.model.TimelineItemAc
import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.Attachment
import io.element.android.features.messages.impl.messagecomposer.aMessageComposerState import io.element.android.features.messages.impl.messagecomposer.aMessageComposerState
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
import io.element.android.features.messages.impl.timeline.aTimelineItemList
import io.element.android.features.messages.impl.timeline.aTimelineItemReadReceipts import io.element.android.features.messages.impl.timeline.aTimelineItemReadReceipts
import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo
import io.element.android.features.messages.impl.timeline.aTimelineState import io.element.android.features.messages.impl.timeline.aTimelineState
@ -53,7 +53,6 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum
import io.element.android.features.messages.impl.timeline.components.receipt.aReadReceiptData import io.element.android.features.messages.impl.timeline.components.receipt.aReadReceiptData
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents
import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ -169,16 +168,20 @@ class MessagesViewTest {
userHasPermissionToRedactOwn: Boolean = false, userHasPermissionToRedactOwn: Boolean = false,
userHasPermissionToRedactOther: Boolean = false, userHasPermissionToRedactOther: Boolean = false,
userHasPermissionToSendReaction: Boolean = false, userHasPermissionToSendReaction: Boolean = false,
userCanPinEvent: Boolean = false,
) { ) {
val eventsRecorder = EventsRecorder<ActionListEvents>() val eventsRecorder = EventsRecorder<ActionListEvents>()
val state = aMessagesState( val state = aMessagesState(
actionListState = anActionListState( actionListState = anActionListState(
eventSink = eventsRecorder eventSink = eventsRecorder
), ),
userHasPermissionToSendMessage = userHasPermissionToSendMessage, userEventPermissions = UserEventPermissions(
userHasPermissionToRedactOwn = userHasPermissionToRedactOwn, canSendMessage = userHasPermissionToSendMessage,
userHasPermissionToRedactOther = userHasPermissionToRedactOther, canRedactOwn = userHasPermissionToRedactOwn,
userHasPermissionToSendReaction = userHasPermissionToSendReaction, canRedactOther = userHasPermissionToRedactOther,
canSendReaction = userHasPermissionToSendReaction,
canPinUnpin = userCanPinEvent,
),
) )
val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event
rule.setMessagesView( rule.setMessagesView(
@ -189,10 +192,7 @@ class MessagesViewTest {
eventsRecorder.assertSingle( eventsRecorder.assertSingle(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = timelineItem, event = timelineItem,
canRedactOwn = state.userHasPermissionToRedactOwn, userEventPermissions = state.userEventPermissions,
canRedactOther = state.userHasPermissionToRedactOther,
canSendMessage = state.userHasPermissionToSendMessage,
canSendReaction = state.userHasPermissionToSendReaction,
) )
) )
} }
@ -237,9 +237,11 @@ class MessagesViewTest {
private fun swipeTest(userHasPermissionToSendMessage: Boolean) { private fun swipeTest(userHasPermissionToSendMessage: Boolean) {
val eventsRecorder = EventsRecorder<MessagesEvents>() val eventsRecorder = EventsRecorder<MessagesEvents>()
val canBeRepliedEvent = aTimelineItemEvent(canBeRepliedTo = true)
val cannotBeRepliedEvent = aTimelineItemEvent(canBeRepliedTo = false)
val state = aMessagesState( val state = aMessagesState(
timelineState = aTimelineState( timelineState = aTimelineState(
timelineItems = aTimelineItemList(aTimelineItemTextContent()), timelineItems = persistentListOf(canBeRepliedEvent, cannotBeRepliedEvent),
timelineRoomInfo = aTimelineRoomInfo( timelineRoomInfo = aTimelineRoomInfo(
userHasPermissionToSendMessage = userHasPermissionToSendMessage userHasPermissionToSendMessage = userHasPermissionToSendMessage
), ),
@ -249,10 +251,12 @@ class MessagesViewTest {
rule.setMessagesView( rule.setMessagesView(
state = state, state = state,
) )
rule.onAllNodesWithTag(TestTags.messageBubble.value).onFirst().performTouchInput { swipeRight(endX = 200f) } rule.onAllNodesWithTag(TestTags.messageBubble.value).apply {
onFirst().performTouchInput { swipeRight(endX = 200f) }
onLast().performTouchInput { swipeRight(endX = 200f) }
}
if (userHasPermissionToSendMessage) { if (userHasPermissionToSendMessage) {
val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event eventsRecorder.assertSingle(MessagesEvents.HandleAction(TimelineItemAction.Reply, canBeRepliedEvent))
eventsRecorder.assertSingle(MessagesEvents.HandleAction(TimelineItemAction.Reply, timelineItem))
} else { } else {
eventsRecorder.assertEmpty() eventsRecorder.assertEmpty()
} }

View file

@ -20,6 +20,7 @@ import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow import app.cash.molecule.moleculeFlow
import app.cash.turbine.test import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.features.messages.impl.aUserEventPermissions
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
import io.element.android.features.messages.impl.fixtures.aMessageEvent import io.element.android.features.messages.impl.fixtures.aMessageEvent
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
@ -31,7 +32,13 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemVoiceContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemVoiceContent
import io.element.android.features.poll.api.pollcontent.aPollAnswerItemList import io.element.android.features.poll.api.pollcontent.aPollAnswerItemList
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_MESSAGE
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.test.room.aRoomInfo
import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
@ -46,7 +53,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - initial state`() = runTest { fun `present - initial state`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = true) val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -57,7 +64,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for message from me redacted`() = runTest { fun `present - compute for message from me redacted`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = true) val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -66,10 +73,13 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = false, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = false,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true,
)
) )
) )
// val loadingState = awaitItem() // val loadingState = awaitItem()
@ -91,7 +101,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for message from others redacted`() = runTest { fun `present - compute for message from others redacted`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = true) val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -104,10 +114,13 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = false, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = false,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true,
)
) )
) )
// val loadingState = awaitItem() // val loadingState = awaitItem()
@ -129,7 +142,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for others message`() = runTest { fun `present - compute for others message`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = true) val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -142,10 +155,13 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = false, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = false,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true,
)
) )
) )
// val loadingState = awaitItem() // val loadingState = awaitItem()
@ -158,6 +174,7 @@ class ActionListPresenterTest {
actions = persistentListOf( actions = persistentListOf(
TimelineItemAction.Reply, TimelineItemAction.Reply,
TimelineItemAction.Forward, TimelineItemAction.Forward,
TimelineItemAction.Pin,
TimelineItemAction.Copy, TimelineItemAction.Copy,
TimelineItemAction.CopyLink, TimelineItemAction.CopyLink,
TimelineItemAction.ViewSource, TimelineItemAction.ViewSource,
@ -172,7 +189,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for others message cannot sent message`() = runTest { fun `present - compute for others message cannot sent message`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = true) val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -185,10 +202,13 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = true, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = true,
canSendMessage = false, canRedactOther = false,
canSendReaction = true canSendMessage = false,
canSendReaction = true,
canPinUnpin = true,
)
) )
) )
// val loadingState = awaitItem() // val loadingState = awaitItem()
@ -200,6 +220,7 @@ class ActionListPresenterTest {
displayEmojiReactions = true, displayEmojiReactions = true,
actions = persistentListOf( actions = persistentListOf(
TimelineItemAction.Forward, TimelineItemAction.Forward,
TimelineItemAction.Pin,
TimelineItemAction.Copy, TimelineItemAction.Copy,
TimelineItemAction.CopyLink, TimelineItemAction.CopyLink,
TimelineItemAction.ViewSource, TimelineItemAction.ViewSource,
@ -214,7 +235,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for others message and can redact`() = runTest { fun `present - compute for others message and can redact`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = true) val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -227,10 +248,13 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = false, userEventPermissions = aUserEventPermissions(
canRedactOther = true, canRedactOwn = false,
canSendMessage = true, canRedactOther = true,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true,
)
) )
) )
val successState = awaitItem() val successState = awaitItem()
@ -241,6 +265,7 @@ class ActionListPresenterTest {
actions = persistentListOf( actions = persistentListOf(
TimelineItemAction.Reply, TimelineItemAction.Reply,
TimelineItemAction.Forward, TimelineItemAction.Forward,
TimelineItemAction.Pin,
TimelineItemAction.Copy, TimelineItemAction.Copy,
TimelineItemAction.CopyLink, TimelineItemAction.CopyLink,
TimelineItemAction.ViewSource, TimelineItemAction.ViewSource,
@ -256,7 +281,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for others message and cannot send reaction`() = runTest { fun `present - compute for others message and cannot send reaction`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = true) val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -269,10 +294,13 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = false, userEventPermissions = aUserEventPermissions(
canRedactOther = true, canRedactOwn = false,
canSendMessage = true, canRedactOther = true,
canSendReaction = false canSendMessage = true,
canSendReaction = false,
canPinUnpin = true,
)
) )
) )
val successState = awaitItem() val successState = awaitItem()
@ -283,6 +311,7 @@ class ActionListPresenterTest {
actions = persistentListOf( actions = persistentListOf(
TimelineItemAction.Reply, TimelineItemAction.Reply,
TimelineItemAction.Forward, TimelineItemAction.Forward,
TimelineItemAction.Pin,
TimelineItemAction.Copy, TimelineItemAction.Copy,
TimelineItemAction.CopyLink, TimelineItemAction.CopyLink,
TimelineItemAction.ViewSource, TimelineItemAction.ViewSource,
@ -298,7 +327,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for my message`() = runTest { fun `present - compute for my message`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = true) val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -310,10 +339,13 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = true, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = true,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true,
)
) )
) )
// val loadingState = awaitItem() // val loadingState = awaitItem()
@ -327,6 +359,7 @@ class ActionListPresenterTest {
TimelineItemAction.Reply, TimelineItemAction.Reply,
TimelineItemAction.Forward, TimelineItemAction.Forward,
TimelineItemAction.Edit, TimelineItemAction.Edit,
TimelineItemAction.Pin,
TimelineItemAction.Copy, TimelineItemAction.Copy,
TimelineItemAction.CopyLink, TimelineItemAction.CopyLink,
TimelineItemAction.ViewSource, TimelineItemAction.ViewSource,
@ -341,7 +374,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for my message cannot redact`() = runTest { fun `present - compute for my message cannot redact`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = true) val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -353,10 +386,13 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = false, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = false,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true,
)
) )
) )
// val loadingState = awaitItem() // val loadingState = awaitItem()
@ -370,6 +406,7 @@ class ActionListPresenterTest {
TimelineItemAction.Reply, TimelineItemAction.Reply,
TimelineItemAction.Forward, TimelineItemAction.Forward,
TimelineItemAction.Edit, TimelineItemAction.Edit,
TimelineItemAction.Pin,
TimelineItemAction.Copy, TimelineItemAction.Copy,
TimelineItemAction.CopyLink, TimelineItemAction.CopyLink,
TimelineItemAction.ViewSource, TimelineItemAction.ViewSource,
@ -383,7 +420,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for a media item`() = runTest { fun `present - compute for a media item`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = true) val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -396,10 +433,13 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = true, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = true,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true,
),
) )
) )
// val loadingState = awaitItem() // val loadingState = awaitItem()
@ -412,6 +452,7 @@ class ActionListPresenterTest {
actions = persistentListOf( actions = persistentListOf(
TimelineItemAction.Reply, TimelineItemAction.Reply,
TimelineItemAction.Forward, TimelineItemAction.Forward,
TimelineItemAction.Pin,
TimelineItemAction.CopyLink, TimelineItemAction.CopyLink,
TimelineItemAction.ViewSource, TimelineItemAction.ViewSource,
TimelineItemAction.Redact, TimelineItemAction.Redact,
@ -425,7 +466,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for a state item in debug build`() = runTest { fun `present - compute for a state item in debug build`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = true) val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -437,10 +478,13 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = stateEvent, event = stateEvent,
canRedactOwn = false, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = false,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true,
)
) )
) )
// val loadingState = awaitItem() // val loadingState = awaitItem()
@ -451,8 +495,6 @@ class ActionListPresenterTest {
event = stateEvent, event = stateEvent,
displayEmojiReactions = false, displayEmojiReactions = false,
actions = persistentListOf( actions = persistentListOf(
TimelineItemAction.Copy,
TimelineItemAction.CopyLink,
TimelineItemAction.ViewSource, TimelineItemAction.ViewSource,
) )
) )
@ -464,7 +506,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for a state item in non-debuggable build`() = runTest { fun `present - compute for a state item in non-debuggable build`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = false) val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -476,33 +518,24 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = stateEvent, event = stateEvent,
canRedactOwn = false, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = false,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true,
)
) )
) )
// val loadingState = awaitItem() // val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
assertThat(successState.target).isEqualTo(
ActionListState.Target.Success(
event = stateEvent,
displayEmojiReactions = false,
actions = persistentListOf(
TimelineItemAction.Copy,
TimelineItemAction.CopyLink,
)
)
)
initialState.eventSink.invoke(ActionListEvents.Clear)
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None) assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
} }
} }
@Test @Test
fun `present - compute message in non-debuggable build`() = runTest { fun `present - compute message in non-debuggable build`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = false) val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -514,10 +547,59 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = true, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = true,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true,
)
)
)
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
assertThat(successState.target).isEqualTo(
ActionListState.Target.Success(
event = messageEvent,
displayEmojiReactions = true,
actions = persistentListOf(
TimelineItemAction.Reply,
TimelineItemAction.Forward,
TimelineItemAction.Edit,
TimelineItemAction.Pin,
TimelineItemAction.Copy,
TimelineItemAction.CopyLink,
TimelineItemAction.Redact,
)
)
)
initialState.eventSink.invoke(ActionListEvents.Clear)
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
}
}
@Test
fun `present - compute message when user can't pin`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
val messageEvent = aMessageEvent(
isMine = true,
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null)
)
initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage(
event = messageEvent,
userEventPermissions = aUserEventPermissions(
canRedactOwn = true,
canRedactOther = false,
canSendMessage = true,
canSendReaction = true,
canPinUnpin = false,
)
) )
) )
// val loadingState = awaitItem() // val loadingState = awaitItem()
@ -533,6 +615,61 @@ class ActionListPresenterTest {
TimelineItemAction.Edit, TimelineItemAction.Edit,
TimelineItemAction.Copy, TimelineItemAction.Copy,
TimelineItemAction.CopyLink, TimelineItemAction.CopyLink,
TimelineItemAction.ViewSource,
TimelineItemAction.Redact,
)
)
)
initialState.eventSink.invoke(ActionListEvents.Clear)
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
}
}
@Test
fun `present - compute message when event is already pinned`() = runTest {
val room = FakeMatrixRoom().apply {
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
}
val presenter = createActionListPresenter(
isDeveloperModeEnabled = true,
isPinFeatureEnabled = true,
room = room
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
val messageEvent = aMessageEvent(
isMine = true,
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null)
)
initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage(
event = messageEvent,
userEventPermissions = aUserEventPermissions(
canRedactOwn = true,
canRedactOther = false,
canSendMessage = true,
canSendReaction = true,
canPinUnpin = true,
)
)
)
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
assertThat(successState.target).isEqualTo(
ActionListState.Target.Success(
event = messageEvent,
displayEmojiReactions = true,
actions = persistentListOf(
TimelineItemAction.Reply,
TimelineItemAction.Forward,
TimelineItemAction.Edit,
TimelineItemAction.Unpin,
TimelineItemAction.Copy,
TimelineItemAction.CopyLink,
TimelineItemAction.ViewSource,
TimelineItemAction.Redact, TimelineItemAction.Redact,
) )
) )
@ -544,7 +681,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute message with no actions`() = runTest { fun `present - compute message with no actions`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = false) val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -561,10 +698,13 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = false, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = false,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true,
)
) )
) )
assertThat(awaitItem().target).isInstanceOf(ActionListState.Target.Success::class.java) assertThat(awaitItem().target).isInstanceOf(ActionListState.Target.Success::class.java)
@ -572,10 +712,12 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = redactedEvent, event = redactedEvent,
canRedactOwn = false, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = false,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
)
) )
) )
awaitItem().run { awaitItem().run {
@ -586,7 +728,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute not sent message`() = runTest { fun `present - compute not sent message`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = false) val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -595,16 +737,20 @@ class ActionListPresenterTest {
// No event id, so it's not sent yet // No event id, so it's not sent yet
eventId = null, eventId = null,
isMine = true, isMine = true,
canBeRepliedTo = false,
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null), content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null),
) )
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = true, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = true,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true,
)
) )
) )
val successState = awaitItem() val successState = awaitItem()
@ -624,7 +770,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for editable poll message`() = runTest { fun `present - compute for editable poll message`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = false) val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -637,10 +783,13 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = true, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = true,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true,
)
) )
) )
val successState = awaitItem() val successState = awaitItem()
@ -652,6 +801,7 @@ class ActionListPresenterTest {
TimelineItemAction.Reply, TimelineItemAction.Reply,
TimelineItemAction.Edit, TimelineItemAction.Edit,
TimelineItemAction.EndPoll, TimelineItemAction.EndPoll,
TimelineItemAction.Pin,
TimelineItemAction.CopyLink, TimelineItemAction.CopyLink,
TimelineItemAction.Redact, TimelineItemAction.Redact,
) )
@ -662,7 +812,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for non-editable poll message`() = runTest { fun `present - compute for non-editable poll message`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = false) val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -675,10 +825,13 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = true, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = true,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true
)
) )
) )
val successState = awaitItem() val successState = awaitItem()
@ -689,6 +842,7 @@ class ActionListPresenterTest {
actions = persistentListOf( actions = persistentListOf(
TimelineItemAction.Reply, TimelineItemAction.Reply,
TimelineItemAction.EndPoll, TimelineItemAction.EndPoll,
TimelineItemAction.Pin,
TimelineItemAction.CopyLink, TimelineItemAction.CopyLink,
TimelineItemAction.Redact, TimelineItemAction.Redact,
) )
@ -699,7 +853,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for ended poll message`() = runTest { fun `present - compute for ended poll message`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = false) val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -712,10 +866,13 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = true, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = true,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true,
)
) )
) )
val successState = awaitItem() val successState = awaitItem()
@ -725,6 +882,7 @@ class ActionListPresenterTest {
displayEmojiReactions = true, displayEmojiReactions = true,
actions = persistentListOf( actions = persistentListOf(
TimelineItemAction.Reply, TimelineItemAction.Reply,
TimelineItemAction.Pin,
TimelineItemAction.CopyLink, TimelineItemAction.CopyLink,
TimelineItemAction.Redact, TimelineItemAction.Redact,
) )
@ -735,22 +893,26 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for voice message`() = runTest { fun `present - compute for voice message`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = false) val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
val initialState = awaitItem() val initialState = awaitItem()
val messageEvent = aMessageEvent( val messageEvent = aMessageEvent(
isMine = true, isMine = true,
isEditable = false,
content = aTimelineItemVoiceContent(), content = aTimelineItemVoiceContent(),
) )
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = true, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = true,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
canPinUnpin = true
)
) )
) )
val successState = awaitItem() val successState = awaitItem()
@ -761,6 +923,7 @@ class ActionListPresenterTest {
actions = persistentListOf( actions = persistentListOf(
TimelineItemAction.Reply, TimelineItemAction.Reply,
TimelineItemAction.Forward, TimelineItemAction.Forward,
TimelineItemAction.Pin,
TimelineItemAction.CopyLink, TimelineItemAction.CopyLink,
TimelineItemAction.Redact, TimelineItemAction.Redact,
) )
@ -771,7 +934,7 @@ class ActionListPresenterTest {
@Test @Test
fun `present - compute for call notify`() = runTest { fun `present - compute for call notify`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = true) val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -783,10 +946,12 @@ class ActionListPresenterTest {
initialState.eventSink.invoke( initialState.eventSink.invoke(
ActionListEvents.ComputeForMessage( ActionListEvents.ComputeForMessage(
event = messageEvent, event = messageEvent,
canRedactOwn = true, userEventPermissions = aUserEventPermissions(
canRedactOther = false, canRedactOwn = true,
canSendMessage = true, canRedactOther = false,
canSendReaction = true, canSendMessage = true,
canSendReaction = true,
)
) )
) )
val successState = awaitItem() val successState = awaitItem()
@ -803,7 +968,20 @@ class ActionListPresenterTest {
} }
} }
private fun createActionListPresenter(isDeveloperModeEnabled: Boolean): ActionListPresenter { private fun createActionListPresenter(
isDeveloperModeEnabled: Boolean,
isPinFeatureEnabled: Boolean,
room: MatrixRoom = FakeMatrixRoom(),
): ActionListPresenter {
val preferencesStore = InMemoryAppPreferencesStore(isDeveloperModeEnabled = isDeveloperModeEnabled) val preferencesStore = InMemoryAppPreferencesStore(isDeveloperModeEnabled = isDeveloperModeEnabled)
return ActionListPresenter(appPreferencesStore = preferencesStore) val featureFlagsService = FakeFeatureFlagService(
initialState = mapOf(
FeatureFlags.PinnedEvents.key to isPinFeatureEnabled,
)
)
return ActionListPresenter(
appPreferencesStore = preferencesStore,
featureFlagsService = featureFlagsService,
room = room
)
} }

View file

@ -42,6 +42,7 @@ internal fun aMessageEvent(
transactionId: TransactionId? = null, transactionId: TransactionId? = null,
isMine: Boolean = true, isMine: Boolean = true,
isEditable: Boolean = true, isEditable: Boolean = true,
canBeRepliedTo: Boolean = true,
content: TimelineItemEventContent = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, formattedBody = null, isEdited = false), content: TimelineItemEventContent = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, formattedBody = null, isEdited = false),
inReplyTo: InReplyToDetails? = null, inReplyTo: InReplyToDetails? = null,
isThreaded: Boolean = false, isThreaded: Boolean = false,
@ -58,6 +59,7 @@ internal fun aMessageEvent(
sentTime = "", sentTime = "",
isMine = isMine, isMine = isMine,
isEditable = isEditable, isEditable = isEditable,
canBeRepliedTo = canBeRepliedTo,
reactionsState = aTimelineItemReactions(count = 0), reactionsState = aTimelineItemReactions(count = 0),
readReceiptState = TimelineItemReadReceipts(emptyList<ReadReceiptData>().toImmutableList()), readReceiptState = TimelineItemReadReceipts(emptyList<ReadReceiptData>().toImmutableList()),
localSendState = sendState, localSendState = sendState,

View file

@ -46,6 +46,7 @@ class TimelineItemGrouperTest {
readReceiptState = TimelineItemReadReceipts(emptyList<ReadReceiptData>().toImmutableList()), readReceiptState = TimelineItemReadReceipts(emptyList<ReadReceiptData>().toImmutableList()),
localSendState = LocalEventSendState.Sent(AN_EVENT_ID), localSendState = LocalEventSendState.Sent(AN_EVENT_ID),
isEditable = false, isEditable = false,
canBeRepliedTo = false,
inReplyTo = null, inReplyTo = null,
isThreaded = false, isThreaded = false,
debugInfo = aTimelineItemDebugInfo(), debugInfo = aTimelineItemDebugInfo(),

View file

@ -85,6 +85,7 @@ fun aRedactedMatrixTimeline(eventId: EventId) = listOf<MatrixTimelineItem>(
eventId = eventId, eventId = eventId,
transactionId = null, transactionId = null,
isEditable = false, isEditable = false,
canBeRepliedTo = false,
isLocal = false, isLocal = false,
isOwn = false, isOwn = false,
isRemote = false, isRemote = false,

View file

@ -5,4 +5,5 @@
<string name="screen_onboarding_sign_up">"Criar conta"</string> <string name="screen_onboarding_sign_up">"Criar conta"</string>
<string name="screen_onboarding_welcome_message">"Bem-vindo ao mais rápido %1$s de todos os tempos. Turbinado para velocidade e simplicidade."</string> <string name="screen_onboarding_welcome_message">"Bem-vindo ao mais rápido %1$s de todos os tempos. Turbinado para velocidade e simplicidade."</string>
<string name="screen_onboarding_welcome_subtitle">"Bem-vindo ao %1$s. Turbinado, para velocidade e simplicidade"</string> <string name="screen_onboarding_welcome_subtitle">"Bem-vindo ao %1$s. Turbinado, para velocidade e simplicidade"</string>
<string name="screen_onboarding_welcome_title">"Esteja no seu elemento"</string>
</resources> </resources>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_onboarding_sign_in_manually">"Qo\'lda tizimga kiring"</string>
<string name="screen_onboarding_sign_in_with_qr_code">"QR kod bilan tizimga kiring"</string>
<string name="screen_onboarding_sign_up">"Hisob yaratish"</string>
<string name="screen_onboarding_welcome_message">"Eng tezkor %1$sga xush kelibsiz. Tezlik va oddylik uchun super zaryadlangan."</string>
<string name="screen_onboarding_welcome_subtitle">"%1$sga Xush kelibsiz. Tezlik va oddylik uchun o\'ta zaryadlangan."</string>
<string name="screen_onboarding_welcome_title">"Elementingizda bo\'ling"</string>
</resources>

View file

@ -7,6 +7,12 @@
<string name="screen_create_poll_question_desc">"Pergunta ou tópico"</string> <string name="screen_create_poll_question_desc">"Pergunta ou tópico"</string>
<string name="screen_create_poll_question_hint">"Sobre o que é a enquete?"</string> <string name="screen_create_poll_question_hint">"Sobre o que é a enquete?"</string>
<string name="screen_create_poll_title">"Criar enquete"</string> <string name="screen_create_poll_title">"Criar enquete"</string>
<string name="screen_edit_poll_delete_confirmation">"Tem certeza de que quer deletar esta enquete?"</string>
<string name="screen_edit_poll_delete_confirmation_title">"Excluir Enquete"</string> <string name="screen_edit_poll_delete_confirmation_title">"Excluir Enquete"</string>
<string name="screen_edit_poll_title">"Editar enquete"</string> <string name="screen_edit_poll_title">"Editar enquete"</string>
<string name="screen_polls_history_empty_ongoing">"Não foi possível encontrar nenhuma enquete em andamento."</string>
<string name="screen_polls_history_empty_past">"Não foi possível encontrar nenhuma enquete anterior."</string>
<string name="screen_polls_history_filter_ongoing">"Em andamento"</string>
<string name="screen_polls_history_filter_past">"Anteriores"</string>
<string name="screen_polls_history_title">"Enquetes"</string>
</resources> </resources>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_create_poll_add_option_btn">"Variant qo\'shish"</string>
<string name="screen_create_poll_anonymous_desc">"Natijalarni faqat soʻrov tugagandan keyin koʻrsatish"</string>
<string name="screen_create_poll_anonymous_headline">"Ovozlarni yashirish"</string>
<string name="screen_create_poll_answer_hint">"Variant%1$d"</string>
<string name="screen_create_poll_question_desc">"Savol yoki mavzu"</string>
<string name="screen_create_poll_question_hint">"So\'rovnoma nima haqida?"</string>
<string name="screen_create_poll_title">"Sorovnoma yaratish"</string>
<string name="screen_edit_poll_title">"Sorovnomani tahrirlash"</string>
</resources>

View file

@ -3,22 +3,29 @@
<string name="full_screen_intent_banner_message">"Upewnij się, że nie pominiesz żadnego połączenia. Zmień swoje ustawienia i zezwól na powiadomienia na blokadzie ekranu."</string> <string name="full_screen_intent_banner_message">"Upewnij się, że nie pominiesz żadnego połączenia. Zmień swoje ustawienia i zezwól na powiadomienia na blokadzie ekranu."</string>
<string name="full_screen_intent_banner_title">"Popraw jakość swoich rozmów"</string> <string name="full_screen_intent_banner_title">"Popraw jakość swoich rozmów"</string>
<string name="screen_advanced_settings_choose_distributor_dialog_title_android">"Wybierz sposób otrzymywania powiadomień"</string> <string name="screen_advanced_settings_choose_distributor_dialog_title_android">"Wybierz sposób otrzymywania powiadomień"</string>
<string name="screen_advanced_settings_developer_mode">"Tryb dewelopera"</string> <string name="screen_advanced_settings_developer_mode">"Tryb programisty"</string>
<string name="screen_advanced_settings_developer_mode_description">"Włącz, aby uzyskać dostęp do funkcji dla deweloperów."</string> <string name="screen_advanced_settings_developer_mode_description">"Włącz, aby uzyskać dostęp do funkcji dla deweloperów."</string>
<string name="screen_advanced_settings_element_call_base_url">"Własny bazowy URL dla połączeń Element"</string> <string name="screen_advanced_settings_element_call_base_url">"Własny bazowy URL dla połączeń Element"</string>
<string name="screen_advanced_settings_element_call_base_url_description">"Ustaw własny bazowy URL dla połączeń Element"</string> <string name="screen_advanced_settings_element_call_base_url_description">"Ustaw własny bazowy URL dla połączeń Element"</string>
<string name="screen_advanced_settings_element_call_base_url_validation_error">"Nieprawidłowy adres URL, upewnij się, że zawiera protokół (http/https) i poprawny adres."</string> <string name="screen_advanced_settings_element_call_base_url_validation_error">"Nieprawidłowy adres URL, upewnij się, że zawiera protokół (http/https) i poprawny adres."</string>
<string name="screen_advanced_settings_push_provider_android">"Dostawca powiadomień push"</string>
<string name="screen_advanced_settings_rich_text_editor_description">"Wyłącz edytor tekstu bogatego, aby pisać tekst Markdown ręcznie."</string> <string name="screen_advanced_settings_rich_text_editor_description">"Wyłącz edytor tekstu bogatego, aby pisać tekst Markdown ręcznie."</string>
<string name="screen_advanced_settings_send_read_receipts">"Potwierdzenia odczytania"</string>
<string name="screen_advanced_settings_send_read_receipts_description">"Gdy wyłączona, Twoje potwierdzenia odczytania nie zostaną wysłane. Potwierdzenia od innych wciąż będą odbierane."</string>
<string name="screen_advanced_settings_share_presence">"Udostępnij obecność"</string>
<string name="screen_advanced_settings_share_presence_description">"Gdy wyłączona, nie będziesz mógł wysyłać lub odbierać potwierdzeń odczytu ani powiadomień pisania."</string>
<string name="screen_advanced_settings_view_source_description">"Włącz opcję, aby wyświetlić źródło wiadomości na osi czasu."</string> <string name="screen_advanced_settings_view_source_description">"Włącz opcję, aby wyświetlić źródło wiadomości na osi czasu."</string>
<string name="screen_blocked_users_empty">"Nie blokujesz żadnych użytkowników"</string>
<string name="screen_blocked_users_unblock_alert_action">"Odblokuj"</string> <string name="screen_blocked_users_unblock_alert_action">"Odblokuj"</string>
<string name="screen_blocked_users_unblock_alert_description">"Będziesz mógł ponownie zobaczyć wszystkie wiadomości od tego użytkownika."</string> <string name="screen_blocked_users_unblock_alert_description">"Będziesz mógł ponownie zobaczyć wszystkie wiadomości od tego użytkownika."</string>
<string name="screen_blocked_users_unblock_alert_title">"Odblokuj użytkownika"</string> <string name="screen_blocked_users_unblock_alert_title">"Odblokuj użytkownika"</string>
<string name="screen_blocked_users_unblocking">"Odblokowuję…"</string>
<string name="screen_edit_profile_display_name">"Wyświetlana nazwa"</string> <string name="screen_edit_profile_display_name">"Wyświetlana nazwa"</string>
<string name="screen_edit_profile_display_name_placeholder">"Twoja wyświetlana nazwa"</string> <string name="screen_edit_profile_display_name_placeholder">"Twoja wyświetlana nazwa"</string>
<string name="screen_edit_profile_error">"Wystąpił nieznany błąd przez co nie można było zmienić informacji."</string> <string name="screen_edit_profile_error">"Wystąpił nieznany błąd przez co nie można było zmienić informacji."</string>
<string name="screen_edit_profile_error_title">"Nie można zaktualizować profilu"</string> <string name="screen_edit_profile_error_title">"Nie można zaktualizować profilu"</string>
<string name="screen_edit_profile_title">"Edytuj profil"</string> <string name="screen_edit_profile_title">"Edytuj profil"</string>
<string name="screen_edit_profile_updating_details">"Aktualizowanie profilu…"</string> <string name="screen_edit_profile_updating_details">"Aktualizuję profil…"</string>
<string name="screen_notification_settings_additional_settings_section_title">"Dodatkowe ustawienia"</string> <string name="screen_notification_settings_additional_settings_section_title">"Dodatkowe ustawienia"</string>
<string name="screen_notification_settings_calls_label">"Połączenia audio i wideo"</string> <string name="screen_notification_settings_calls_label">"Połączenia audio i wideo"</string>
<string name="screen_notification_settings_configuration_mismatch">"Niezgodność konfiguracji"</string> <string name="screen_notification_settings_configuration_mismatch">"Niezgodność konfiguracji"</string>
@ -46,4 +53,6 @@ Niektóre ustawienia mogą ulec zmianie, jeśli kontynuujesz."</string>
<string name="screen_notification_settings_system_notifications_action_required_content_link">"ustawienia systemowe"</string> <string name="screen_notification_settings_system_notifications_action_required_content_link">"ustawienia systemowe"</string>
<string name="screen_notification_settings_system_notifications_turned_off">"Powiadomienia systemowe wyłączone"</string> <string name="screen_notification_settings_system_notifications_turned_off">"Powiadomienia systemowe wyłączone"</string>
<string name="screen_notification_settings_title">"Powiadomienia"</string> <string name="screen_notification_settings_title">"Powiadomienia"</string>
<string name="troubleshoot_notifications_entry_point_section">"Rozwiązywanie problemów"</string>
<string name="troubleshoot_notifications_entry_point_title">"Powiadomienia rozwiązywania problemów"</string>
</resources> </resources>

View file

@ -3,10 +3,18 @@
<string name="screen_advanced_settings_choose_distributor_dialog_title_android">"Escolha como receber notificações"</string> <string name="screen_advanced_settings_choose_distributor_dialog_title_android">"Escolha como receber notificações"</string>
<string name="screen_advanced_settings_developer_mode">"Modo de desenvolvedor"</string> <string name="screen_advanced_settings_developer_mode">"Modo de desenvolvedor"</string>
<string name="screen_advanced_settings_developer_mode_description">"Habilite para ter acesso a recursos e funcionalidades para desenvolvedores."</string> <string name="screen_advanced_settings_developer_mode_description">"Habilite para ter acesso a recursos e funcionalidades para desenvolvedores."</string>
<string name="screen_advanced_settings_element_call_base_url_validation_error">"URL inválida, por favor verifique se o protocolo (http/https) e o endereço correto estão presentes."</string>
<string name="screen_advanced_settings_rich_text_editor_description">"Desative o editor de rich text para digitar Markdown manualmente."</string> <string name="screen_advanced_settings_rich_text_editor_description">"Desative o editor de rich text para digitar Markdown manualmente."</string>
<string name="screen_advanced_settings_send_read_receipts">"Confirmações de leitura"</string>
<string name="screen_advanced_settings_send_read_receipts_description">"Se desligado, suas confirmações de leitura não serão enviadas para ninguém. Você ainda receberá confirmações de leitura de outros usuários."</string>
<string name="screen_advanced_settings_share_presence">"Compartilhar presença"</string>
<string name="screen_advanced_settings_share_presence_description">"Se desligado, você não poderá enviar ou receber confirmações de leitura ou notificações de digitação."</string>
<string name="screen_advanced_settings_view_source_description">"Ativar a opção de visualizar o fonte da mensagem na linha do tempo."</string>
<string name="screen_blocked_users_empty">"Você não tem usuários bloqueados"</string>
<string name="screen_blocked_users_unblock_alert_action">"Desbloquear"</string> <string name="screen_blocked_users_unblock_alert_action">"Desbloquear"</string>
<string name="screen_blocked_users_unblock_alert_description">"Você poderá ver todas as mensagens deles novamente."</string> <string name="screen_blocked_users_unblock_alert_description">"Você poderá ver todas as mensagens deles novamente."</string>
<string name="screen_blocked_users_unblock_alert_title">"Desbloquear usuário"</string> <string name="screen_blocked_users_unblock_alert_title">"Desbloquear usuário"</string>
<string name="screen_blocked_users_unblocking">"Desbloqueando…"</string>
<string name="screen_edit_profile_display_name">"Nome de exibição"</string> <string name="screen_edit_profile_display_name">"Nome de exibição"</string>
<string name="screen_edit_profile_display_name_placeholder">"Seu nome de exibição"</string> <string name="screen_edit_profile_display_name_placeholder">"Seu nome de exibição"</string>
<string name="screen_edit_profile_error">"Um erro desconhecido foi encontrado e as informações não puderam ser alteradas."</string> <string name="screen_edit_profile_error">"Um erro desconhecido foi encontrado e as informações não puderam ser alteradas."</string>
@ -29,6 +37,7 @@ Se você continuar, algumas de suas configurações poderão mudar."</string>
<string name="screen_notification_settings_enable_notifications">"Ativar notificações neste dispositivo"</string> <string name="screen_notification_settings_enable_notifications">"Ativar notificações neste dispositivo"</string>
<string name="screen_notification_settings_failed_fixing_configuration">"A configuração não foi corrigida, tente novamente."</string> <string name="screen_notification_settings_failed_fixing_configuration">"A configuração não foi corrigida, tente novamente."</string>
<string name="screen_notification_settings_group_chats">"Bate-papos em grupo"</string> <string name="screen_notification_settings_group_chats">"Bate-papos em grupo"</string>
<string name="screen_notification_settings_invite_for_me_label">"Convites"</string>
<string name="screen_notification_settings_mentions_section_title">"Menções"</string> <string name="screen_notification_settings_mentions_section_title">"Menções"</string>
<string name="screen_notification_settings_mode_all">"Todos"</string> <string name="screen_notification_settings_mode_all">"Todos"</string>
<string name="screen_notification_settings_mode_mentions">"Menções"</string> <string name="screen_notification_settings_mode_mentions">"Menções"</string>

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_advanced_settings_choose_distributor_dialog_title_android">"Bildirishnomalarni qanday qabul qilishni tanlang"</string>
<string name="screen_advanced_settings_developer_mode">"Dasturchi rejimi"</string>
<string name="screen_advanced_settings_developer_mode_description">"Ishlab chiquvchilar uchun xususiyatlar va funksiyalarga kirishni yoqing."</string>
<string name="screen_advanced_settings_element_call_base_url">"Maxsus element qongiroqlar bazasi URL manzili"</string>
<string name="screen_advanced_settings_rich_text_editor_description">"Boy matn muharriri o\'chiring Markdown bilan qo\'lda yozish uchun"</string>
<string name="screen_blocked_users_unblock_alert_action">"Blokdan chiqarish"</string>
<string name="screen_blocked_users_unblock_alert_description">"Ulardan kelgan barcha xabarlarni yana koʻrishingiz mumkin boʻladi."</string>
<string name="screen_blocked_users_unblock_alert_title">"Foydalanuvchini blokdan chiqarish"</string>
<string name="screen_edit_profile_display_name">"Ko\'rsatiladigan ism"</string>
<string name="screen_edit_profile_display_name_placeholder">"Ismingizni ko\'rsating"</string>
<string name="screen_edit_profile_error">"Noma\'lum xatolik yuz berdi va ma\'lumotni o\'zgartirib bo\'lmadi."</string>
<string name="screen_edit_profile_error_title">"Profilni yangilab bolmadi"</string>
<string name="screen_edit_profile_title">"Profilni tahrirlash"</string>
<string name="screen_edit_profile_updating_details">"Profil yangilanmoqda…"</string>
<string name="screen_notification_settings_additional_settings_section_title">"Qo\'shimcha sozlamalar"</string>
<string name="screen_notification_settings_calls_label">"Audio va video qo\'ng\'iroqlar"</string>
<string name="screen_notification_settings_configuration_mismatch">"Konfiguratsiya mos kelmasligi"</string>
<string name="screen_notification_settings_configuration_mismatch_description">"Variantlarni topishni osonlashtirish uchun bildirishnomalar sozlamalarini soddalashtirdik. Ilgari siz tanlagan baʼzi shaxsiy sozlamalar bu yerda koʻrsatilmaydi, lekin ular hali ham faol.
Davom ettirsangiz, baʼzi sozlamalaringiz oʻzgarishi mumkin."</string>
<string name="screen_notification_settings_direct_chats">"To\'g\'ridan-to\'g\'ri suhbatlar"</string>
<string name="screen_notification_settings_edit_custom_settings_section_title">"Har bir suhbat uchun moslashtirilgan sozlama"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Bildirishnoma sozlamalarini yangilashda xatolik yuz berdi."</string>
<string name="screen_notification_settings_edit_mode_all_messages">"Barcha xabarlar"</string>
<string name="screen_notification_settings_edit_mode_mentions_and_keywords">"Faqat eslatmalar va kalit so\'zlar"</string>
<string name="screen_notification_settings_edit_screen_direct_section_header">"To\'g\'ridan-to\'g\'ri suhbats, menga xabar bering"</string>
<string name="screen_notification_settings_edit_screen_group_section_header">"Guruh suhbatlarida menga xabar bering"</string>
<string name="screen_notification_settings_enable_notifications">"Ushbu qurilmada bildirishnomalarni yoqing"</string>
<string name="screen_notification_settings_failed_fixing_configuration">"Konfiguratsiya tuzatilmadi, qayta urinib ko\'ring."</string>
<string name="screen_notification_settings_group_chats">"Guruh suhbatlari"</string>
<string name="screen_notification_settings_mentions_section_title">"Eslatmalar"</string>
<string name="screen_notification_settings_mode_all">"Hammasi"</string>
<string name="screen_notification_settings_mode_mentions">"Eslatmalar"</string>
<string name="screen_notification_settings_notification_section_title">"Menga xabar bering"</string>
<string name="screen_notification_settings_room_mention_label">"Menga @room orqali xabar bering"</string>
<string name="screen_notification_settings_system_notifications_action_required">"Bildirishnomalarni olish uchun, iltimos, o\'zingizni %1$singizni o\'zgartiring."</string>
<string name="screen_notification_settings_system_notifications_action_required_content_link">"tizim sozlamalari"</string>
<string name="screen_notification_settings_system_notifications_turned_off">"Tizim bildirishnomalari o\'chirilgan"</string>
<string name="screen_notification_settings_title">"Bildirishnomalar"</string>
</resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="crash_detection_dialog_content">"%1$soxirgi marta ishlatilganda qulab tushdi. Biz bilan nosozlik hisobotini baham ko\'rmoqchimisiz?"</string>
<string name="rageshake_detection_dialog_content">"Siz hafsalasi pir bo\'lib telefonni silkitayotganga o\'xshaysiz. Xatolar haqida hisobot ekranini ochmoqchimisiz?"</string>
<string name="settings_rageshake">"G\'azablanish"</string>
<string name="settings_rageshake_detection_threshold">"Aniqlash chegarasi"</string>
</resources>

View file

@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bug_report_attach_screenshot">"Dołącz zrzut ekranu"</string> <string name="screen_bug_report_attach_screenshot">"Załącz zrzut ekranu"</string>
<string name="screen_bug_report_contact_me">"Możecie skontaktować się ze mną, jeśli macie jakiekolwiek dodatkowe pytania."</string> <string name="screen_bug_report_contact_me">"Możecie skontaktować się ze mną, jeśli macie jakiekolwiek dodatkowe pytania."</string>
<string name="screen_bug_report_contact_me_title">"Napisz do mnie"</string> <string name="screen_bug_report_contact_me_title">"Napisz do mnie"</string>
<string name="screen_bug_report_edit_screenshot">"Edytuj zrzut ekranu"</string> <string name="screen_bug_report_edit_screenshot">"Edytuj zrzut ekranu"</string>
<string name="screen_bug_report_editor_description">"Opisz problem. Co zrobiłeś? Czego oczekiwałeś? Co się stało zamiast tego. Podaj jak najwięcej szczegółów."</string> <string name="screen_bug_report_editor_description">"Opisz problem. Co zrobiłeś? Czego oczekiwałeś? Co się stało zamiast tego. Podaj jak najwięcej szczegółów."</string>
<string name="screen_bug_report_editor_placeholder">"Opisz problem…"</string> <string name="screen_bug_report_editor_placeholder">"Opisz problem…"</string>
<string name="screen_bug_report_editor_supporting">"Jeśli to możliwe, napisz zgłoszenje w języku angielskim."</string> <string name="screen_bug_report_editor_supporting">"Jeśli to możliwe, napisz zgłoszenje w języku angielskim."</string>
<string name="screen_bug_report_error_description_too_short">"Opis jest zbyt krótki, podaj więcej szczegółów na temat tego co się stało. Dzięki!"</string>
<string name="screen_bug_report_include_crash_logs">"Wyślij logi awarii"</string> <string name="screen_bug_report_include_crash_logs">"Wyślij logi awarii"</string>
<string name="screen_bug_report_include_logs">"Zezwól na logi"</string> <string name="screen_bug_report_include_logs">"Zezwól na logi"</string>
<string name="screen_bug_report_include_screenshot">"Wyślij zrzut ekranu"</string> <string name="screen_bug_report_include_screenshot">"Wyślij zrzut ekranu"</string>

View file

@ -12,4 +12,5 @@
<string name="screen_bug_report_include_screenshot">"Enviar captura de tela"</string> <string name="screen_bug_report_include_screenshot">"Enviar captura de tela"</string>
<string name="screen_bug_report_logs_description">"Os registros serão incluídos com sua mensagem para garantir que tudo esteja funcionando corretamente. Para enviar sua mensagem sem registros, desative essa configuração."</string> <string name="screen_bug_report_logs_description">"Os registros serão incluídos com sua mensagem para garantir que tudo esteja funcionando corretamente. Para enviar sua mensagem sem registros, desative essa configuração."</string>
<string name="screen_bug_report_rash_logs_alert_title">"%1$s fechou inesperadamente na última vez que foi usado. Gostaria de compartilhar um relatório de falhas conosco?"</string> <string name="screen_bug_report_rash_logs_alert_title">"%1$s fechou inesperadamente na última vez que foi usado. Gostaria de compartilhar um relatório de falhas conosco?"</string>
<string name="screen_bug_report_view_logs">"Ver registros"</string>
</resources> </resources>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bug_report_attach_screenshot">"Ekran tasvirini biriktirish"</string>
<string name="screen_bug_report_contact_me">"Agar sizda keyingi savollar bo\'lsa, men bilan bog\'lanishingiz mumkin."</string>
<string name="screen_bug_report_contact_me_title">"Men bilan bog\'laning"</string>
<string name="screen_bug_report_edit_screenshot">"Ekran tasvirini tahrirlash"</string>
<string name="screen_bug_report_editor_description">"Iltimos, muammoni tasvirlab bering. Nima qildingiz? Nima bo\'lishini kutgan edingiz? Aslida nima bo\'ldi. Iltimos, iloji boricha batafsilroq ma\'lumot bering."</string>
<string name="screen_bug_report_editor_placeholder">"Muammoni tasvirlab bering…"</string>
<string name="screen_bug_report_editor_supporting">"Iloji bo\'lsa, tavsifni ingliz tilida yozing."</string>
<string name="screen_bug_report_include_crash_logs">"Buzilish jurnallarini yuboring"</string>
<string name="screen_bug_report_include_logs">"Jurnallarga ruxsat bering"</string>
<string name="screen_bug_report_include_screenshot">"Ekran tasvirini yuboring "</string>
<string name="screen_bug_report_logs_description">"Har bir narsa to\'ri ishlayotganiga ishonch hosil qilish uchun xabaringizga jurnallar kiritiladi. Xabarni jurnallarsiz yuborish uchun ushbu sozlamani oʻchiring."</string>
<string name="screen_bug_report_rash_logs_alert_title">"%1$soxirgi marta ishlatilganda qulab tushdi. Biz bilan nosozlik hisobotini baham ko\'rmoqchimisiz?"</string>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_alias_resolver_resolve_alias_failure">"Nie udało się uzyskać aliasu pokoju."</string>
</resources>

View file

@ -3,10 +3,41 @@
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Wystąpił błąd podczas aktualizacji ustawienia powiadomień."</string> <string name="screen_notification_settings_edit_failed_updating_default_mode">"Wystąpił błąd podczas aktualizacji ustawienia powiadomień."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Twój serwer domowy nie wspiera tej opcji w pokojach szyfrowanych, możesz nie otrzymać powiadomień z niektórych pokoi."</string> <string name="screen_notification_settings_mentions_only_disclaimer">"Twój serwer domowy nie wspiera tej opcji w pokojach szyfrowanych, możesz nie otrzymać powiadomień z niektórych pokoi."</string>
<string name="screen_polls_history_title">"Ankiety"</string> <string name="screen_polls_history_title">"Ankiety"</string>
<string name="screen_room_change_permissions_administrators">"Tylko administratorzy"</string>
<string name="screen_room_change_permissions_ban_people">"Banowanie osób"</string>
<string name="screen_room_change_permissions_delete_messages">"Usuwanie wiadomości"</string>
<string name="screen_room_change_permissions_everyone">"Wszyscy"</string> <string name="screen_room_change_permissions_everyone">"Wszyscy"</string>
<string name="screen_room_change_permissions_invite_people">"Zapraszanie osób"</string>
<string name="screen_room_change_permissions_member_moderation">"Moderacja członków"</string>
<string name="screen_room_change_permissions_messages_and_content">"Wiadomości i zawartość"</string>
<string name="screen_room_change_permissions_moderators">"Administratorzy i moderatorzy"</string>
<string name="screen_room_change_permissions_remove_people">"Usuwanie osób"</string>
<string name="screen_room_change_permissions_room_avatar">"Zmień awatar pokoju"</string>
<string name="screen_room_change_permissions_room_details">"Szczegóły pokoju"</string>
<string name="screen_room_change_permissions_room_name">"Zmień nazwę pokoju"</string>
<string name="screen_room_change_permissions_room_topic">"Zmień temat pokoju"</string>
<string name="screen_room_change_permissions_send_messages">"Wysyłanie wiadomości"</string>
<string name="screen_room_change_role_administrators_title">"Edytuj administratorów"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Tej akcji nie będzie można cofnąć. Promujesz użytkownika, który będzie posiadał takie same uprawnienia jak Ty."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Dodać administratora?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Zdegraduj"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Nie będzie można cofnąć tej zmiany, jeśli się zdegradujesz. Jeśli jesteś ostatnim uprzywilejowanym użytkownikiem w pokoju, nie będziesz w stanie odzyskać uprawnień."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Zdegradować siebie?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Oczekujące)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Oczekujący)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Administratorzy automatycznie mają uprawnienia moderatora"</string>
<string name="screen_room_change_role_moderators_title">"Edytuj moderatorów"</string>
<string name="screen_room_change_role_section_administrators">"Administratorzy"</string>
<string name="screen_room_change_role_section_moderators">"Moderatorzy"</string>
<string name="screen_room_change_role_section_users">"Członków"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Masz niezapisane zmiany."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Zapisać zmiany?"</string>
<string name="screen_room_details_add_topic_title">"Dodaj temat"</string> <string name="screen_room_details_add_topic_title">"Dodaj temat"</string>
<string name="screen_room_details_already_a_member">"Jest już członkiem"</string> <string name="screen_room_details_already_a_member">"Jest już członkiem"</string>
<string name="screen_room_details_already_invited">"Już zaproszony"</string> <string name="screen_room_details_already_invited">"Już zaproszony"</string>
<string name="screen_room_details_badge_encrypted">"Szyfrowany"</string>
<string name="screen_room_details_badge_not_encrypted">"Nieszyfrowany"</string>
<string name="screen_room_details_badge_public">"Pokój publiczny"</string>
<string name="screen_room_details_edit_room_title">"Edytuj pokój"</string> <string name="screen_room_details_edit_room_title">"Edytuj pokój"</string>
<string name="screen_room_details_edition_error">"Wystąpił nieznany błąd i nie można było zmienić informacji."</string> <string name="screen_room_details_edition_error">"Wystąpił nieznany błąd i nie można było zmienić informacji."</string>
<string name="screen_room_details_edition_error_title">"Nie można zaktualizować pokoju"</string> <string name="screen_room_details_edition_error_title">"Nie można zaktualizować pokoju"</string>
@ -21,18 +52,40 @@
<string name="screen_room_details_notification_mode_custom">"Niestandardowy"</string> <string name="screen_room_details_notification_mode_custom">"Niestandardowy"</string>
<string name="screen_room_details_notification_mode_default">"Domyślny"</string> <string name="screen_room_details_notification_mode_default">"Domyślny"</string>
<string name="screen_room_details_notification_title">"Powiadomienia"</string> <string name="screen_room_details_notification_title">"Powiadomienia"</string>
<string name="screen_room_details_roles_and_permissions">"Role i uprawnienia"</string>
<string name="screen_room_details_room_name_label">"Nazwa pokoju"</string> <string name="screen_room_details_room_name_label">"Nazwa pokoju"</string>
<string name="screen_room_details_security_title">"Bezpieczeństwo"</string> <string name="screen_room_details_security_title">"Bezpieczeństwo"</string>
<string name="screen_room_details_share_room_title">"Udostępnij pokój"</string> <string name="screen_room_details_share_room_title">"Udostępnij pokój"</string>
<string name="screen_room_details_title">"Informacje pokoju"</string>
<string name="screen_room_details_topic_title">"Temat"</string> <string name="screen_room_details_topic_title">"Temat"</string>
<string name="screen_room_details_updating_room">"Aktualizuję pokój…"</string> <string name="screen_room_details_updating_room">"Aktualizuję pokój…"</string>
<string name="screen_room_member_list_ban_member_confirmation_action">"Zbanuj"</string>
<string name="screen_room_member_list_ban_member_confirmation_description">"Nie będą mogli ponownie dołączyć do tego pokoju, jeśli zostaną zaproszeni."</string>
<string name="screen_room_member_list_ban_member_confirmation_title">"Czy na pewno chcesz zbanować tego członka?"</string>
<string name="screen_room_member_list_banned_empty">"W tym pokoju nie ma zbanowanych użytkowników."</string>
<string name="screen_room_member_list_banning_user">"Banowanie %1$s"</string>
<plurals name="screen_room_member_list_header_title"> <plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d osoba"</item> <item quantity="one">"%1$d osoba"</item>
<item quantity="few">"%1$d osoby"</item> <item quantity="few">"%1$d osoby"</item>
<item quantity="many">"%1$d osób"</item> <item quantity="many">"%1$d osób"</item>
</plurals> </plurals>
<string name="screen_room_member_list_manage_member_ban">"Usuń i zbanuj członka"</string>
<string name="screen_room_member_list_manage_member_remove">"Usuń z pokoju"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Usuń i zbanuj członka"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Tylko usuń członka"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_title">"Usunąć członka i zablokować możliwość dołączenia w przyszłości?"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Odbanuj"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Będą mogli ponownie dołączyć do tego pokoju, jeśli zostaną zaproszeni."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Odbanuj użytkownika"</string>
<string name="screen_room_member_list_manage_member_user_info">"Wyświetl profil"</string>
<string name="screen_room_member_list_mode_banned">"Zbanowanych"</string>
<string name="screen_room_member_list_mode_members">"Członków"</string>
<string name="screen_room_member_list_pending_header_title">"Oczekiwanie"</string> <string name="screen_room_member_list_pending_header_title">"Oczekiwanie"</string>
<string name="screen_room_member_list_removing_user">"Usuwanie %1$s…"</string>
<string name="screen_room_member_list_role_administrator">"Administrator"</string>
<string name="screen_room_member_list_role_moderator">"Moderator"</string>
<string name="screen_room_member_list_room_members_header_title">"Członkowie pokoju"</string> <string name="screen_room_member_list_room_members_header_title">"Członkowie pokoju"</string>
<string name="screen_room_member_list_unbanning_user">"Odbanowanie %1$s"</string>
<string name="screen_room_notification_settings_allow_custom">"Zezwalaj na ustawienia niestandardowe"</string> <string name="screen_room_notification_settings_allow_custom">"Zezwalaj na ustawienia niestandardowe"</string>
<string name="screen_room_notification_settings_allow_custom_footnote">"Włączenie tej opcji nadpisze ustawienie domyślne"</string> <string name="screen_room_notification_settings_allow_custom_footnote">"Włączenie tej opcji nadpisze ustawienie domyślne"</string>
<string name="screen_room_notification_settings_custom_settings_title">"Powiadamiaj mnie o tym czacie przez"</string> <string name="screen_room_notification_settings_custom_settings_title">"Powiadamiaj mnie o tym czacie przez"</string>
@ -47,4 +100,18 @@
<string name="screen_room_notification_settings_mode_all_messages">"Wszystkie wiadomości"</string> <string name="screen_room_notification_settings_mode_all_messages">"Wszystkie wiadomości"</string>
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Tylko wzmianki i słowa kluczowe"</string> <string name="screen_room_notification_settings_mode_mentions_and_keywords">"Tylko wzmianki i słowa kluczowe"</string>
<string name="screen_room_notification_settings_room_custom_settings_title">"W tym pokoju, powiadamiaj mnie przez"</string> <string name="screen_room_notification_settings_room_custom_settings_title">"W tym pokoju, powiadamiaj mnie przez"</string>
<string name="screen_room_roles_and_permissions_admins">"Administratorzy"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Zmień moją rolę"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Zdegraduj do członka"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Zdegraduj do moderatora"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Moderacja członków"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Wiadomości i zawartość"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderatorzy"</string>
<string name="screen_room_roles_and_permissions_permissions_header">"Uprawnienia"</string>
<string name="screen_room_roles_and_permissions_reset">"Resetuj uprawnienia"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Po zresetowaniu uprawnień utracisz bieżące ustawienia."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Zresetować uprawnienia?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Role"</string>
<string name="screen_room_roles_and_permissions_room_details">"Szczegóły pokoju"</string>
<string name="screen_room_roles_and_permissions_title">"Role i uprawnienia"</string>
</resources> </resources>

View file

@ -1,7 +1,29 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Ocorreu um erro ao atualizar a configuração de notificação."</string> <string name="screen_notification_settings_edit_failed_updating_default_mode">"Ocorreu um erro ao atualizar a configuração de notificação."</string>
<string name="screen_polls_history_title">"Enquetes"</string>
<string name="screen_room_change_permissions_administrators">"Somente administradores"</string>
<string name="screen_room_change_permissions_ban_people">"Banir pessoas"</string>
<string name="screen_room_change_permissions_delete_messages">"Remover mensagens"</string>
<string name="screen_room_change_permissions_everyone">"Todos"</string> <string name="screen_room_change_permissions_everyone">"Todos"</string>
<string name="screen_room_change_permissions_invite_people">"Convidar pessoas"</string>
<string name="screen_room_change_permissions_member_moderation">"Moderação de membros"</string>
<string name="screen_room_change_permissions_messages_and_content">"Mensagens e conteúdo"</string>
<string name="screen_room_change_permissions_moderators">"Administradores e moderadores"</string>
<string name="screen_room_change_permissions_remove_people">"Remover pessoas"</string>
<string name="screen_room_change_permissions_room_avatar">"Alterar avatar da sala"</string>
<string name="screen_room_change_permissions_room_details">"Detalhes da sala"</string>
<string name="screen_room_change_permissions_room_name">"Alterar nome da sala"</string>
<string name="screen_room_change_permissions_room_topic">"Alterar tópico da sala"</string>
<string name="screen_room_change_permissions_send_messages">"Enviar mensagens"</string>
<string name="screen_room_change_role_administrators_title">"Editar administradores"</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Adicionar administrador?"</string>
<string name="screen_room_change_role_moderators_title">"Editar moderadores"</string>
<string name="screen_room_change_role_section_administrators">"Administradores"</string>
<string name="screen_room_change_role_section_moderators">"Moderadores"</string>
<string name="screen_room_change_role_section_users">"Membros"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Você tem alterações não salvas."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Salvar alterações?"</string>
<string name="screen_room_details_add_topic_title">"Adicionar tópico"</string> <string name="screen_room_details_add_topic_title">"Adicionar tópico"</string>
<string name="screen_room_details_already_a_member">"Já é membro"</string> <string name="screen_room_details_already_a_member">"Já é membro"</string>
<string name="screen_room_details_already_invited">"Já foi convidado"</string> <string name="screen_room_details_already_invited">"Já foi convidado"</string>
@ -19,17 +41,35 @@
<string name="screen_room_details_notification_mode_custom">"Personalizado"</string> <string name="screen_room_details_notification_mode_custom">"Personalizado"</string>
<string name="screen_room_details_notification_mode_default">"Padrão"</string> <string name="screen_room_details_notification_mode_default">"Padrão"</string>
<string name="screen_room_details_notification_title">"Notificações"</string> <string name="screen_room_details_notification_title">"Notificações"</string>
<string name="screen_room_details_roles_and_permissions">"Cargos e permissões"</string>
<string name="screen_room_details_room_name_label">"Nome da sala"</string> <string name="screen_room_details_room_name_label">"Nome da sala"</string>
<string name="screen_room_details_security_title">"Segurança"</string> <string name="screen_room_details_security_title">"Segurança"</string>
<string name="screen_room_details_share_room_title">"Compartilhar sala"</string> <string name="screen_room_details_share_room_title">"Compartilhar sala"</string>
<string name="screen_room_details_topic_title">"Tópico"</string> <string name="screen_room_details_topic_title">"Tópico"</string>
<string name="screen_room_details_updating_room">"Atualizando a sala…"</string> <string name="screen_room_details_updating_room">"Atualizando a sala…"</string>
<string name="screen_room_member_list_ban_member_confirmation_action">"Banir"</string>
<string name="screen_room_member_list_ban_member_confirmation_title">"Tem certeza de que quer banir este membro?"</string>
<string name="screen_room_member_list_banning_user">"Banindo %1$s"</string>
<plurals name="screen_room_member_list_header_title"> <plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d pessoa"</item> <item quantity="one">"%1$d pessoa"</item>
<item quantity="other">"%1$d pessoas"</item> <item quantity="other">"%1$d pessoas"</item>
</plurals> </plurals>
<string name="screen_room_member_list_manage_member_ban">"Remover e banir membro"</string>
<string name="screen_room_member_list_manage_member_remove">"Remover da sala"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Remover e banir membro"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Somente remover membro"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_title">"Remover membro e banir de entrar novamente no futuro?"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Desbanir"</string>
<string name="screen_room_member_list_manage_member_unban_title">"Desbanir usuário"</string>
<string name="screen_room_member_list_manage_member_user_info">"Ver perfil"</string>
<string name="screen_room_member_list_mode_banned">"Banidos"</string>
<string name="screen_room_member_list_mode_members">"Membros"</string>
<string name="screen_room_member_list_pending_header_title">"Pendente"</string> <string name="screen_room_member_list_pending_header_title">"Pendente"</string>
<string name="screen_room_member_list_removing_user">"Removendo %1$s…"</string>
<string name="screen_room_member_list_role_administrator">"Administrador"</string>
<string name="screen_room_member_list_role_moderator">"Moderador"</string>
<string name="screen_room_member_list_room_members_header_title">"Membros da sala"</string> <string name="screen_room_member_list_room_members_header_title">"Membros da sala"</string>
<string name="screen_room_member_list_unbanning_user">"Desbanindo %1$s"</string>
<string name="screen_room_notification_settings_allow_custom">"Permitir configuração personalizada"</string> <string name="screen_room_notification_settings_allow_custom">"Permitir configuração personalizada"</string>
<string name="screen_room_notification_settings_allow_custom_footnote">"Ativar isso substituirá sua configuração padrão"</string> <string name="screen_room_notification_settings_allow_custom_footnote">"Ativar isso substituirá sua configuração padrão"</string>
<string name="screen_room_notification_settings_custom_settings_title">"Me notifique nesta conversa para"</string> <string name="screen_room_notification_settings_custom_settings_title">"Me notifique nesta conversa para"</string>
@ -43,4 +83,15 @@
<string name="screen_room_notification_settings_mode_all_messages">"Todas as mensagens"</string> <string name="screen_room_notification_settings_mode_all_messages">"Todas as mensagens"</string>
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Somente menções e palavras-chave"</string> <string name="screen_room_notification_settings_mode_mentions_and_keywords">"Somente menções e palavras-chave"</string>
<string name="screen_room_notification_settings_room_custom_settings_title">"Nesta sala, notifique-me para"</string> <string name="screen_room_notification_settings_room_custom_settings_title">"Nesta sala, notifique-me para"</string>
<string name="screen_room_roles_and_permissions_admins">"Administradores"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Alterar meu cargo"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Moderação de membros"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Mensagens e conteúdo"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderadores"</string>
<string name="screen_room_roles_and_permissions_permissions_header">"Permissões"</string>
<string name="screen_room_roles_and_permissions_reset">"Redefinir permissões"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Redefinir permissões?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Cargos"</string>
<string name="screen_room_roles_and_permissions_room_details">"Detalhes da sala"</string>
<string name="screen_room_roles_and_permissions_title">"Cargos e permissões"</string>
</resources> </resources>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Bildirishnoma sozlamalarini yangilashda xatolik yuz berdi."</string>
<string name="screen_room_change_permissions_everyone">"Har kim"</string>
<string name="screen_room_details_add_topic_title">"Mavzu qo\'shish"</string>
<string name="screen_room_details_already_a_member">"Allaqachon a\'zo"</string>
<string name="screen_room_details_already_invited">"Allaqachon taklif qilingan"</string>
<string name="screen_room_details_edit_room_title">"Xonani tahrirlash"</string>
<string name="screen_room_details_edition_error">"Nomaʼlum xatolik yuz berdi va maʼlumotni oʻzgartirib boʻlmadi."</string>
<string name="screen_room_details_edition_error_title">"Xonani yangilab bolmadi"</string>
<string name="screen_room_details_encryption_enabled_subtitle">"Xabarlar qulflar bilan himoyalangan. Faqat siz va qabul qiluvchilar ularni qulfdan chiqarish uchun noyob kalitlarga ega."</string>
<string name="screen_room_details_encryption_enabled_title">"Xabarni shifrlash yoqilgan"</string>
<string name="screen_room_details_error_loading_notification_settings">"Bildirishnoma sozlamalarini yuklashda xatolik yuz berdi."</string>
<string name="screen_room_details_error_muting">"Bu xona ovozini ochirib bolmadi, qayta urinib koring."</string>
<string name="screen_room_details_error_unmuting">"Bu xonaning ovozi yoqilmadi, qayta urinib koring."</string>
<string name="screen_room_details_invite_people_title">"Odamlarni taklif qiling"</string>
<string name="screen_room_details_leave_room_title">"Xonani tark etish "</string>
<string name="screen_room_details_notification_mode_custom">"Maxsus"</string>
<string name="screen_room_details_notification_mode_default">"Standart"</string>
<string name="screen_room_details_notification_title">"Bildirishnomalar"</string>
<string name="screen_room_details_room_name_label">"Xona nomi"</string>
<string name="screen_room_details_security_title">"Xavfsizlik"</string>
<string name="screen_room_details_share_room_title">"Xonani baham ko\'ring"</string>
<string name="screen_room_details_topic_title">"Mavzu"</string>
<string name="screen_room_details_updating_room">"Xona yangilanmoqda…"</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$dodam"</item>
<item quantity="other">"%1$dodamlar"</item>
</plurals>
<string name="screen_room_member_list_pending_header_title">"Kutilmoqda"</string>
<string name="screen_room_member_list_room_members_header_title">"Xona a\'zolari"</string>
<string name="screen_room_notification_settings_allow_custom">"Moslashtirilgan sozlamalarga ruxsat bering"</string>
<string name="screen_room_notification_settings_allow_custom_footnote">"Buni yoqsangiz, standart sozlamalaringiz bekor qilinadi"</string>
<string name="screen_room_notification_settings_custom_settings_title">"Bu chatda menga xabar bering"</string>
<string name="screen_room_notification_settings_default_setting_footnote">"Siz buni o\'zgartira olasiz o\'zingizning %1$sda."</string>
<string name="screen_room_notification_settings_default_setting_footnote_content_link">"global sozlamalar"</string>
<string name="screen_room_notification_settings_default_setting_title">"Standart sozlama"</string>
<string name="screen_room_notification_settings_edit_remove_setting">"Maxsus sozlamani olib tashlang"</string>
<string name="screen_room_notification_settings_error_loading_settings">"Bildirishnoma sozlamalarini yuklashda xatolik yuz berdi."</string>
<string name="screen_room_notification_settings_error_restoring_default">"Standart rejimni tiklab bolmadi, qaytadan urinib koring."</string>
<string name="screen_room_notification_settings_error_setting_mode">"Rejimni ornatib bolmadi, qayta urinib koring."</string>
<string name="screen_room_notification_settings_mode_all_messages">"Barcha xabarlar"</string>
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Faqat eslatmalar va kalit so\'zlar"</string>
<string name="screen_room_notification_settings_room_custom_settings_title">"Bu xonada menga xabar bering"</string>
</resources>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_directory_search_loading_error">"Błąd wczytywania"</string>
<string name="screen_room_directory_search_title">"Katalog pokoi"</string>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_directory_search_title">"Diretório de salas"</string>
</resources>

View file

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="confirm_recovery_key_banner_message">"Twoja kopia zapasowa czatu jest obecnie niezsynchronizowana. Aby zachować dostęp do kopii zapasowej czatu, musisz potwierdzić klucz odzyskiwania."</string> <string name="confirm_recovery_key_banner_message">"Twoja kopia zapasowa czatu jest obecnie niezsynchronizowana. Aby zachować dostęp do kopii zapasowej czatu, musisz potwierdzić klucz odzyskiwania."</string>
<string name="confirm_recovery_key_banner_title">"Potwierdź klucz odzyskiwania"</string> <string name="confirm_recovery_key_banner_title">"Wprowadź swój klucz przywracania"</string>
<string name="full_screen_intent_banner_message">"Upewnij się, że nie pominiesz żadnego połączenia. Zmień swoje ustawienia i zezwól na powiadomienia na blokadzie ekranu."</string> <string name="full_screen_intent_banner_message">"Upewnij się, że nie pominiesz żadnego połączenia. Zmień swoje ustawienia i zezwól na powiadomienia na blokadzie ekranu."</string>
<string name="full_screen_intent_banner_title">"Popraw jakość swoich rozmów"</string> <string name="full_screen_intent_banner_title">"Popraw jakość swoich rozmów"</string>
<string name="screen_invites_decline_chat_message">"Czy na pewno chcesz odrzucić zaproszenie do dołączenia do %1$s?"</string> <string name="screen_invites_decline_chat_message">"Czy na pewno chcesz odrzucić zaproszenie dołączenia do %1$s?"</string>
<string name="screen_invites_decline_chat_title">"Odrzuć zaproszenie"</string> <string name="screen_invites_decline_chat_title">"Odrzuć zaproszenie"</string>
<string name="screen_invites_decline_direct_chat_message">"Czy na pewno chcesz odrzucić rozmowę prywatną z %1$s?"</string> <string name="screen_invites_decline_direct_chat_message">"Czy na pewno chcesz odrzucić rozmowę prywatną z %1$s?"</string>
<string name="screen_invites_decline_direct_chat_title">"Odrzuć czat"</string> <string name="screen_invites_decline_direct_chat_title">"Odrzuć czat"</string>
@ -15,8 +15,26 @@
<string name="screen_roomlist_a11y_create_message">"Utwórz nową rozmowę lub pokój"</string> <string name="screen_roomlist_a11y_create_message">"Utwórz nową rozmowę lub pokój"</string>
<string name="screen_roomlist_empty_message">"Wyślij komuś wiadomość, aby rozpocząć."</string> <string name="screen_roomlist_empty_message">"Wyślij komuś wiadomość, aby rozpocząć."</string>
<string name="screen_roomlist_empty_title">"Brak czatów."</string> <string name="screen_roomlist_empty_title">"Brak czatów."</string>
<string name="screen_roomlist_filter_favourites">"Ulubione"</string>
<string name="screen_roomlist_filter_favourites_empty_state_subtitle">"Możesz dodać czat do ulubionych w ustawieniach czatu.
Na razie możesz wyczyścić filtry, aby zobaczyć pozostałe czaty"</string>
<string name="screen_roomlist_filter_favourites_empty_state_title">"Nie masz jeszcze ulubionych czatów"</string>
<string name="screen_roomlist_filter_invites">"Zaproszenia"</string>
<string name="screen_roomlist_filter_invites_empty_state_title">"Nie masz żadnych oczekujących zaproszeń."</string>
<string name="screen_roomlist_filter_low_priority">"Niski priorytet"</string>
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Wyczyść filtry, aby zobaczyć pozostałe czaty"</string>
<string name="screen_roomlist_filter_mixed_empty_state_title">"Brak czatów dla podanych kryteriów"</string>
<string name="screen_roomlist_filter_people">"Osoby"</string> <string name="screen_roomlist_filter_people">"Osoby"</string>
<string name="screen_roomlist_filter_people_empty_state_title">"Nie masz jeszcze żadnych PW"</string>
<string name="screen_roomlist_filter_rooms">"Pokoje"</string>
<string name="screen_roomlist_filter_rooms_empty_state_title">"Nie jesteś jeszcze w żadnym pokoju"</string>
<string name="screen_roomlist_filter_unreads">"Nieprzeczytane"</string>
<string name="screen_roomlist_filter_unreads_empty_state_title">"Gratulacje!
Nie masz żadnych nieprzeczytanych wiadomości!"</string>
<string name="screen_roomlist_main_space_title">"Wszystkie czaty"</string> <string name="screen_roomlist_main_space_title">"Wszystkie czaty"</string>
<string name="screen_roomlist_mark_as_read">"Oznacz jako przeczytane"</string>
<string name="screen_roomlist_mark_as_unread">"Oznacz jako nieprzeczytane"</string>
<string name="screen_roomlist_room_directory_button_title">"Przeglądaj wszystkie pokoje"</string>
<string name="session_verification_banner_message">"Wygląda na to, że używasz nowego urządzenia. Zweryfikuj się innym urządzeniem, aby uzyskać dostęp do zaszyfrowanych wiadomości."</string> <string name="session_verification_banner_message">"Wygląda na to, że używasz nowego urządzenia. Zweryfikuj się innym urządzeniem, aby uzyskać dostęp do zaszyfrowanych wiadomości."</string>
<string name="session_verification_banner_title">"Potwierdź, że to Ty"</string> <string name="session_verification_banner_title">"Potwierdź, że to Ty"</string>
</resources> </resources>

View file

@ -12,8 +12,21 @@
<string name="screen_roomlist_a11y_create_message">"Criar uma nova conversa ou sala"</string> <string name="screen_roomlist_a11y_create_message">"Criar uma nova conversa ou sala"</string>
<string name="screen_roomlist_empty_message">"Comece enviando uma mensagem para alguém."</string> <string name="screen_roomlist_empty_message">"Comece enviando uma mensagem para alguém."</string>
<string name="screen_roomlist_empty_title">"Ainda não há conversas."</string> <string name="screen_roomlist_empty_title">"Ainda não há conversas."</string>
<string name="screen_roomlist_filter_favourites">"Favoritos"</string>
<string name="screen_roomlist_filter_favourites_empty_state_title">"Você não tem nenhuma conversa favorita ainda"</string>
<string name="screen_roomlist_filter_low_priority">"Baixa prioridade"</string>
<string name="screen_roomlist_filter_mixed_empty_state_title">"Você não tem conversas para esta seleção"</string>
<string name="screen_roomlist_filter_people">"Pessoas"</string> <string name="screen_roomlist_filter_people">"Pessoas"</string>
<string name="screen_roomlist_filter_people_empty_state_title">"Você não tem nenhum conversa privada ainda"</string>
<string name="screen_roomlist_filter_rooms">"Salas"</string>
<string name="screen_roomlist_filter_rooms_empty_state_title">"Você não está em nenhuma sala ainda"</string>
<string name="screen_roomlist_filter_unreads">"Não lidos"</string>
<string name="screen_roomlist_filter_unreads_empty_state_title">"Parabéns!
Você não tem nenhuma mensagem não lida!"</string>
<string name="screen_roomlist_main_space_title">"Conversas"</string> <string name="screen_roomlist_main_space_title">"Conversas"</string>
<string name="screen_roomlist_mark_as_read">"Marcar como lido"</string>
<string name="screen_roomlist_mark_as_unread">"Marcar como não lido"</string>
<string name="screen_roomlist_room_directory_button_title">"Navegar por todas as salas"</string>
<string name="session_verification_banner_message">"Parece que você está usando um novo dispositivo. Verifique com outro dispositivo para acessar suas mensagens criptografadas."</string> <string name="session_verification_banner_message">"Parece que você está usando um novo dispositivo. Verifique com outro dispositivo para acessar suas mensagens criptografadas."</string>
<string name="session_verification_banner_title">"Verifique se é você"</string> <string name="session_verification_banner_title">"Verifique se é você"</string>
</resources> </resources>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_invites_decline_chat_message">"Haqiqatan ham qo\'shilish taklifini rad qilmoqchimisiz%1$s ?"</string>
<string name="screen_invites_decline_chat_title">"Taklifni rad etish"</string>
<string name="screen_invites_decline_direct_chat_message">"Haqiqatan ham bu shaxsiy chatni rad qilmoqchimisiz%1$s ?"</string>
<string name="screen_invites_decline_direct_chat_title">"Chatni rad etish"</string>
<string name="screen_invites_empty_list">"Takliflar yo\'q"</string>
<string name="screen_invites_invited_you">"%1$s(%2$s ) sizni taklif qildi"</string>
<string name="screen_migration_message">"Bu bir martalik jarayon, kutganingiz uchun rahmat."</string>
<string name="screen_migration_title">"Hisobingiz sozlanmoqda."</string>
<string name="screen_roomlist_a11y_create_message">"Yangi suhbat yoki xona yarating"</string>
<string name="screen_roomlist_empty_message">"Kimgadir xabar yuborishdan boshlang."</string>
<string name="screen_roomlist_empty_title">"Hozircha chatlar yoq."</string>
<string name="screen_roomlist_filter_people">"Odamlar"</string>
<string name="screen_roomlist_main_space_title">"Suhbatlar"</string>
<string name="session_verification_banner_message">"Siz yangi qurilmadan foydalanayotganga oxshaysiz. Shifrlangan xabarlaringizga kirish uchun boshqa qurilma bilan tasdiqlang."</string>
<string name="session_verification_banner_title">"Siz ekanligingizni tasdiqlang"</string>
</resources>

View file

@ -6,11 +6,18 @@
<string name="screen_chat_backup_key_backup_title">"Backup"</string> <string name="screen_chat_backup_key_backup_title">"Backup"</string>
<string name="screen_chat_backup_recovery_action_change">"Zmień klucz przywracania"</string> <string name="screen_chat_backup_recovery_action_change">"Zmień klucz przywracania"</string>
<string name="screen_chat_backup_recovery_action_confirm">"Wprowadź klucz przywracania"</string> <string name="screen_chat_backup_recovery_action_confirm">"Wprowadź klucz przywracania"</string>
<string name="screen_chat_backup_recovery_action_confirm_description">"Backup czatu nie jest zsynchronizowany."</string> <string name="screen_chat_backup_recovery_action_confirm_description">"Backup czatu jest niezsynchronizowany."</string>
<string name="screen_chat_backup_recovery_action_setup">"Skonfiguruj przywracanie"</string> <string name="screen_chat_backup_recovery_action_setup">"Skonfiguruj przywracanie"</string>
<string name="screen_chat_backup_recovery_action_setup_description">"Uzyskaj dostęp do swoich wiadomości szyfrowanych, jeśli utracisz wszystkie swoje urządzenia lub zostaniesz wylogowany z %1$s."</string> <string name="screen_chat_backup_recovery_action_setup_description">"Uzyskaj dostęp do swoich wiadomości szyfrowanych, jeśli utracisz wszystkie swoje urządzenia lub zostaniesz wylogowany z %1$s."</string>
<string name="screen_create_new_recovery_key_list_item_1">"Otwórz %1$s na urządzeniu stacjonarnym"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Zaloguj się ponownie na swoje konto"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Gdy pojawi się prośba o weryfikację urządzenia, wybierz %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"“Resetuj wszystko”"</string>
<string name="screen_create_new_recovery_key_list_item_4">"Postępuj zgodnie z instrukcjami, aby utworzyć nowy klucz przywracania"</string>
<string name="screen_create_new_recovery_key_list_item_5">"Zapisz nowy klucz przywracania w menedżerze haseł lub notatce szyfrowanej"</string>
<string name="screen_create_new_recovery_key_title">"Resetuj szyfrowanie swojego konta za pomocą drugiego urządzenia"</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Wyłącz"</string> <string name="screen_key_backup_disable_confirmation_action_turn_off">"Wyłącz"</string>
<string name="screen_key_backup_disable_confirmation_description">"Utracisz dostęp do wiadomości szyfrowanych, jeśli zostaniesz wylogowany ze wszystkich urządzeń."</string> <string name="screen_key_backup_disable_confirmation_description">"Jeśli wylogujesz się ze wszystkich urządzeń, stracisz wszystkie wiadomości szyfrowane."</string>
<string name="screen_key_backup_disable_confirmation_title">"Czy na pewno chcesz wyłączyć backup?"</string> <string name="screen_key_backup_disable_confirmation_title">"Czy na pewno chcesz wyłączyć backup?"</string>
<string name="screen_key_backup_disable_description">"Wyłączenie backupu spowoduje usunięcie kopii klucza szyfrowania i wyłączenie innych funkcji bezpieczeństwa. W takim przypadku będziesz:"</string> <string name="screen_key_backup_disable_description">"Wyłączenie backupu spowoduje usunięcie kopii klucza szyfrowania i wyłączenie innych funkcji bezpieczeństwa. W takim przypadku będziesz:"</string>
<string name="screen_key_backup_disable_description_point_1">"Posiadał historii wiadomości szyfrowanych na nowych urządzeniach"</string> <string name="screen_key_backup_disable_description_point_1">"Posiadał historii wiadomości szyfrowanych na nowych urządzeniach"</string>
@ -21,11 +28,14 @@
<string name="screen_recovery_key_change_generate_key_description">"Upewnij się, że klucz przywracania będzie trzymany w bezpiecznym miejscu"</string> <string name="screen_recovery_key_change_generate_key_description">"Upewnij się, że klucz przywracania będzie trzymany w bezpiecznym miejscu"</string>
<string name="screen_recovery_key_change_success">"Zmieniono klucz przywracania"</string> <string name="screen_recovery_key_change_success">"Zmieniono klucz przywracania"</string>
<string name="screen_recovery_key_change_title">"Zmienić klucz przywracania?"</string> <string name="screen_recovery_key_change_title">"Zmienić klucz przywracania?"</string>
<string name="screen_recovery_key_confirm_create_new_recovery_key">"Utwórz nowy klucz przywracania"</string>
<string name="screen_recovery_key_confirm_description">"Upewnij się, że nikt nie widzi tego ekranu!"</string> <string name="screen_recovery_key_confirm_description">"Upewnij się, że nikt nie widzi tego ekranu!"</string>
<string name="screen_recovery_key_confirm_error_content">"Spróbuj ponownie, aby potwierdzić dostęp do backupu czatu."</string> <string name="screen_recovery_key_confirm_error_content">"Spróbuj ponownie, aby potwierdzić dostęp do backupu czatu."</string>
<string name="screen_recovery_key_confirm_error_title">"Nieprawidłowy klucz przywracania"</string> <string name="screen_recovery_key_confirm_error_title">"Nieprawidłowy klucz przywracania"</string>
<string name="screen_recovery_key_confirm_key_description">"To też zadziała, jeśli posiadasz klucz lub frazę bezpieczeństwa."</string> <string name="screen_recovery_key_confirm_key_description">"To też zadziała, jeśli posiadasz klucz lub frazę bezpieczeństwa."</string>
<string name="screen_recovery_key_confirm_key_label">"Klucz przywracania lub hasło"</string>
<string name="screen_recovery_key_confirm_key_placeholder">"Wprowadź…"</string> <string name="screen_recovery_key_confirm_key_placeholder">"Wprowadź…"</string>
<string name="screen_recovery_key_confirm_lost_recovery_key">"Zgubiłeś swój kod przywracania?"</string>
<string name="screen_recovery_key_confirm_success">"Potwierdzono klucz przywracania"</string> <string name="screen_recovery_key_confirm_success">"Potwierdzono klucz przywracania"</string>
<string name="screen_recovery_key_confirm_title">"Wprowadź klucz przywracania"</string> <string name="screen_recovery_key_confirm_title">"Wprowadź klucz przywracania"</string>
<string name="screen_recovery_key_copied_to_clipboard">"Skopiowano klucz przywracania"</string> <string name="screen_recovery_key_copied_to_clipboard">"Skopiowano klucz przywracania"</string>

View file

@ -4,7 +4,7 @@
<string name="screen_chat_backup_key_backup_action_enable">"Ativar o backup"</string> <string name="screen_chat_backup_key_backup_action_enable">"Ativar o backup"</string>
<string name="screen_chat_backup_key_backup_description">"O backup garante que você não perca seu histórico de mensagens. %1$s."</string> <string name="screen_chat_backup_key_backup_description">"O backup garante que você não perca seu histórico de mensagens. %1$s."</string>
<string name="screen_chat_backup_key_backup_title">"Backup"</string> <string name="screen_chat_backup_key_backup_title">"Backup"</string>
<string name="screen_chat_backup_recovery_action_change">"Mudar chave de recuperação"</string> <string name="screen_chat_backup_recovery_action_change">"Alterar chave de recuperação"</string>
<string name="screen_chat_backup_recovery_action_confirm">"Insira a chave de recuperação"</string> <string name="screen_chat_backup_recovery_action_confirm">"Insira a chave de recuperação"</string>
<string name="screen_chat_backup_recovery_action_confirm_description">"Seu backup das conversas está atualmente fora de sincronia."</string> <string name="screen_chat_backup_recovery_action_confirm_description">"Seu backup das conversas está atualmente fora de sincronia."</string>
<string name="screen_chat_backup_recovery_action_setup">"Configurar a recuperação"</string> <string name="screen_chat_backup_recovery_action_setup">"Configurar a recuperação"</string>
@ -22,10 +22,13 @@
<string name="screen_recovery_key_change_success">"Chave de recuperação alterada"</string> <string name="screen_recovery_key_change_success">"Chave de recuperação alterada"</string>
<string name="screen_recovery_key_change_title">"Alterar chave de recuperação?"</string> <string name="screen_recovery_key_change_title">"Alterar chave de recuperação?"</string>
<string name="screen_recovery_key_confirm_description">"Certifique-se de que ninguém possa ver essa tela!"</string> <string name="screen_recovery_key_confirm_description">"Certifique-se de que ninguém possa ver essa tela!"</string>
<string name="screen_recovery_key_confirm_error_title">"Chave de recuperação incorreta"</string>
<string name="screen_recovery_key_confirm_key_description">"Se você tiver uma chave de segurança ou frase de segurança, isso também funcionará."</string> <string name="screen_recovery_key_confirm_key_description">"Se você tiver uma chave de segurança ou frase de segurança, isso também funcionará."</string>
<string name="screen_recovery_key_confirm_key_placeholder">"Inserir…"</string> <string name="screen_recovery_key_confirm_key_placeholder">"Inserir…"</string>
<string name="screen_recovery_key_confirm_success">"Chave de recuperação confirmada"</string> <string name="screen_recovery_key_confirm_success">"Chave de recuperação confirmada"</string>
<string name="screen_recovery_key_confirm_title">"Insira sua chave de recuperação"</string> <string name="screen_recovery_key_confirm_title">"Insira sua chave de recuperação"</string>
<string name="screen_recovery_key_copied_to_clipboard">"Chave de recuperação copiada"</string>
<string name="screen_recovery_key_generating_key">"Gerando…"</string>
<string name="screen_recovery_key_save_action">"Salvar chave de recuperação"</string> <string name="screen_recovery_key_save_action">"Salvar chave de recuperação"</string>
<string name="screen_recovery_key_save_description">"Anote sua chave de recuperação em algum lugar seguro ou salve-a em um gerenciador de senhas."</string> <string name="screen_recovery_key_save_description">"Anote sua chave de recuperação em algum lugar seguro ou salve-a em um gerenciador de senhas."</string>
<string name="screen_recovery_key_save_key_description">"Toque para copiar a chave de recuperação"</string> <string name="screen_recovery_key_save_key_description">"Toque para copiar a chave de recuperação"</string>

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_chat_backup_key_backup_action_disable">"Zaxiralashni o\'chirib qo\'ying"</string>
<string name="screen_chat_backup_key_backup_action_enable">"Zaxiralashni yoqing"</string>
<string name="screen_chat_backup_key_backup_description">"Zaxiralash xabarlar tarixini yo\'qotmaslikni ta\'minlaydi.%1$s."</string>
<string name="screen_chat_backup_key_backup_title">"Zaxira"</string>
<string name="screen_chat_backup_recovery_action_change">"Qayta tiklash kalitini o\'zgartiring"</string>
<string name="screen_chat_backup_recovery_action_confirm">"Qayta tiklash kalitini kiriting"</string>
<string name="screen_chat_backup_recovery_action_confirm_description">"Sizning chat zaxirangiz hozirda sinxronlashtirilmagan."</string>
<string name="screen_chat_backup_recovery_action_setup">"Qayta tiklashni sozlang"</string>
<string name="screen_chat_backup_recovery_action_setup_description">"Agar barcha qurilmalaringizni yoqotib qoysangiz yoki tizimdan chiqqan bolsangiz, shifrlangan xabarlaringizga ruxsat oling%1$s hamma joyda."</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"O\'chirish"</string>
<string name="screen_key_backup_disable_confirmation_description">"Agar barcha qurilmalardan chiqqan boʻlsangiz, shifrlangan xabarlaringizni yoʻqotasiz."</string>
<string name="screen_key_backup_disable_confirmation_title">"Haqiqatan ham zaxiralashni ochirib qoymoqchimisiz?"</string>
<string name="screen_key_backup_disable_description">"Zaxiralashni ochirib qoysangiz, joriy shifrlash kaliti zaxira nusxasi ochiriladi va boshqa xavfsizlik funksiyalari ochiriladi. Bunday holda siz:"</string>
<string name="screen_key_backup_disable_description_point_1">"Yangi qurilmalarda shifrlangan xabarlar tarixi mavjud emas"</string>
<string name="screen_key_backup_disable_description_point_2">"Agar tizimdan chiqqan boʻlsangiz, shifrlangan xabarlaringizga kirish huquqini yoʻqotasiz%1$s hamma joyda"</string>
<string name="screen_key_backup_disable_title">"Haqiqatan ham zaxiralashni ochirib qoymoqchimisiz?"</string>
<string name="screen_recovery_key_change_description">"Mavjud kalitingizni yo\'qotgan bo\'lsangiz, yangi tiklash kalitini oling. Qayta tiklash kalitini almashtirganingizdan so\'ng, eski kalitingiz ishlamaydi."</string>
<string name="screen_recovery_key_change_generate_key">"Yangi tiklash kalitini yarating"</string>
<string name="screen_recovery_key_change_generate_key_description">"Qayta tiklash kalitingizni xavfsiz joyda saqlashingiz mumkinligiga ishonch hosil qiling"</string>
<string name="screen_recovery_key_change_success">"Qayta tiklash kaliti oʻzgartirildi"</string>
<string name="screen_recovery_key_change_title">"Qayta tiklash kaliti almashtirilsinmi?"</string>
<string name="screen_recovery_key_confirm_description">"Hech kim bu ekranni kora olmasligiga ishonch hosil qiling!"</string>
<string name="screen_recovery_key_confirm_key_description">"Agar sizda xavfsizlik kaliti yoki xavfsizlik iborasi bolsa, bu ham ishlaydi."</string>
<string name="screen_recovery_key_confirm_key_placeholder">"Kirish…"</string>
<string name="screen_recovery_key_confirm_success">"Qayta tiklash kaliti tasdiqlandi"</string>
<string name="screen_recovery_key_confirm_title">"Qayta tiklash kalitingizni kiriting"</string>
<string name="screen_recovery_key_save_action">"Qayta tiklash kalitini saqlang"</string>
<string name="screen_recovery_key_save_description">"Qayta tiklash kalitingizni xavfsiz joyga yozing yoki parol menejerida saqlang."</string>
<string name="screen_recovery_key_save_key_description">"Qayta tiklash kalitidan nusxa olish uchun bosing"</string>
<string name="screen_recovery_key_save_title">"Zaxira kalitingizni saqlang"</string>
<string name="screen_recovery_key_setup_confirmation_description">"Ushbu qadamdan song siz yangi tiklash kalitingizga kira olmaysiz."</string>
<string name="screen_recovery_key_setup_confirmation_title">"Zaxira kalitingizni saqladingizmi?"</string>
<string name="screen_recovery_key_setup_description">"Suhbatingiz zaxira nusxasi tiklash kaliti bilan himoyalangan. Agar sozlashdan keyin sizga yangi tiklash kaliti kerak boʻlsa, “Qayta tiklash kalitini oʻzgartirish”ni tanlash orqali qayta yaratishingiz mumkin."</string>
<string name="screen_recovery_key_setup_generate_key">"Qayta tiklash kalitini yarating"</string>
<string name="screen_recovery_key_setup_generate_key_description">"Qayta tiklash kalitingizni xavfsiz joyda saqlashingiz mumkinligiga ishonch hosil qiling"</string>
<string name="screen_recovery_key_setup_success">"Qayta tiklash muvaffaqiyatli sozlandi"</string>
<string name="screen_recovery_key_setup_title">"Qayta tiklashni sozlang"</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_signed_out_reason_1">"Siz boshqa seansda parolingizni o\'zgartirdingiz"</string>
<string name="screen_signed_out_reason_2">"Siz seansni boshqa seansdan o\'chirib tashladingiz"</string>
<string name="screen_signed_out_reason_3">"Serveringiz administratori ruxsatingizni bekor qildi"</string>
<string name="screen_signed_out_subtitle">"Siz quyida sanab otilgan sabablardan biri tufayli tizimdan chiqqan bolishingiz mumkin. Foydalanishni davom ettirish uchun qayta kiring%s ."</string>
<string name="screen_signed_out_title">"Hisobingizdan chiqdingiz"</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Bloklash"</string>
<string name="screen_dm_details_block_alert_description">"Bloklangan foydalanuvchilar sizga xabar yubora olmaydi va ularning barcha xabarlari yashiriladi. Ularni istalgan vaqtda blokdan chiqarishingiz mumkin."</string>
<string name="screen_dm_details_block_user">"Foydalanuvchini bloklash"</string>
<string name="screen_dm_details_unblock_alert_action">"Blokdan chiqarish"</string>
<string name="screen_dm_details_unblock_alert_description">"Ulardan kelgan barcha xabarlarni yana koʻrishingiz mumkin boʻladi."</string>
<string name="screen_dm_details_unblock_user">"Foydalanuvchini blokdan chiqarish"</string>
<string name="screen_start_chat_error_starting_chat">"Suhbatni boshlashda xatolik yuz berdi"</string>
</resources>

View file

@ -1,11 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_identity_confirmation_create_new_recovery_key">"Utwórz nowy klucz przywracania"</string>
<string name="screen_identity_confirmation_subtitle">"Zweryfikuj to urządzenie, aby skonfigurować bezpieczne przesyłanie wiadomości."</string>
<string name="screen_identity_confirmation_title">"Potwierdź, że to Ty"</string>
<string name="screen_identity_confirmed_subtitle">"Teraz możesz bezpiecznie czytać i wysyłać wiadomości, każdy z kim czatujesz również może ufać temu urządzeniu."</string>
<string name="screen_identity_confirmed_title">"Urządzenie zweryfikowane"</string>
<string name="screen_identity_use_another_device">"Użyj innego urządzenia"</string>
<string name="screen_identity_waiting_on_other_device">"Oczekiwanie na inne urządzenie…"</string>
<string name="screen_session_verification_cancelled_subtitle">"Coś tu nie gra. Albo upłynął limit czasu, albo żądanie zostało odrzucone."</string> <string name="screen_session_verification_cancelled_subtitle">"Coś tu nie gra. Albo upłynął limit czasu, albo żądanie zostało odrzucone."</string>
<string name="screen_session_verification_compare_emojis_subtitle">"Upewnij się, że poniższe emotikony pasują do tych wyświetlanych na innej sesji."</string> <string name="screen_session_verification_compare_emojis_subtitle">"Upewnij się, że emoji poniżej pasują do tych pokazanych na innej sesji."</string>
<string name="screen_session_verification_compare_emojis_title">"Porównaj emotki"</string> <string name="screen_session_verification_compare_emojis_title">"Porównaj emotki"</string>
<string name="screen_session_verification_compare_numbers_subtitle">"Upewnij się, że liczby poniżej pasują do tych wyświetlanych na innej sesji."</string> <string name="screen_session_verification_compare_numbers_subtitle">"Upewnij się, że liczby poniżej pasują do tych wyświetlanych na innej sesji."</string>
<string name="screen_session_verification_compare_numbers_title">"Porównaj liczby"</string> <string name="screen_session_verification_compare_numbers_title">"Porównaj liczby"</string>
<string name="screen_session_verification_complete_subtitle">"Twoja nowa sesja jest teraz zweryfikowana. Ma ona dostęp do Twoich zaszyfrowanych wiadomości, a inni użytkownicy będą widzieć ją jako zaufaną."</string> <string name="screen_session_verification_complete_subtitle">"Twoja nowa sesja jest teraz zweryfikowana. Ma ona dostęp do Twoich zaszyfrowanych wiadomości, a inni użytkownicy będą widzieć ją jako zaufaną."</string>
<string name="screen_session_verification_enter_recovery_key">"Wprowadź klucz przywracania"</string>
<string name="screen_session_verification_open_existing_session_subtitle">"Udowodnij, że to ty, aby uzyskać dostęp do historii zaszyfrowanych wiadomości."</string> <string name="screen_session_verification_open_existing_session_subtitle">"Udowodnij, że to ty, aby uzyskać dostęp do historii zaszyfrowanych wiadomości."</string>
<string name="screen_session_verification_open_existing_session_title">"Otwórz istniejącą sesję"</string> <string name="screen_session_verification_open_existing_session_title">"Otwórz istniejącą sesję"</string>
<string name="screen_session_verification_positive_button_canceled">"Ponów weryfikację"</string> <string name="screen_session_verification_positive_button_canceled">"Ponów weryfikację"</string>

View file

@ -1,14 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_identity_confirmed_title">"Dispositivo verificado"</string>
<string name="screen_identity_use_another_device">"Usar outro dispositivo"</string>
<string name="screen_session_verification_cancelled_subtitle">"Algo não parece certo. Ou a solicitação atingiu o tempo limite ou a solicitação foi negada."</string> <string name="screen_session_verification_cancelled_subtitle">"Algo não parece certo. Ou a solicitação atingiu o tempo limite ou a solicitação foi negada."</string>
<string name="screen_session_verification_compare_emojis_subtitle">"Confirme se os emojis abaixo correspondem aos mostrados em sua outra sessão."</string> <string name="screen_session_verification_compare_emojis_subtitle">"Confirme se os emojis abaixo correspondem aos mostrados em sua outra sessão."</string>
<string name="screen_session_verification_compare_emojis_title">"Compare os emojis"</string> <string name="screen_session_verification_compare_emojis_title">"Compare os emojis"</string>
<string name="screen_session_verification_compare_numbers_subtitle">"Confirme se os números abaixo correspondem aos mostrados em sua outra sessão."</string>
<string name="screen_session_verification_compare_numbers_title">"Comparar números"</string>
<string name="screen_session_verification_complete_subtitle">"Sua nova sessão está agora verificada. Ela tem acesso às suas mensagens criptografadas e outros usuários a verão como confiável."</string> <string name="screen_session_verification_complete_subtitle">"Sua nova sessão está agora verificada. Ela tem acesso às suas mensagens criptografadas e outros usuários a verão como confiável."</string>
<string name="screen_session_verification_enter_recovery_key">"Insira a chave de recuperação"</string>
<string name="screen_session_verification_open_existing_session_subtitle">"Prove que é você para acessar seu histórico de mensagens criptografadas."</string> <string name="screen_session_verification_open_existing_session_subtitle">"Prove que é você para acessar seu histórico de mensagens criptografadas."</string>
<string name="screen_session_verification_open_existing_session_title">"Abrir uma sessão existente"</string> <string name="screen_session_verification_open_existing_session_title">"Abrir uma sessão existente"</string>
<string name="screen_session_verification_positive_button_canceled">"Repetir verificação"</string> <string name="screen_session_verification_positive_button_canceled">"Repetir verificação"</string>
<string name="screen_session_verification_positive_button_initial">"Estou pronto"</string> <string name="screen_session_verification_positive_button_initial">"Estou pronto"</string>
<string name="screen_session_verification_positive_button_verifying_ongoing">"Esperando para combinar"</string> <string name="screen_session_verification_positive_button_verifying_ongoing">"Esperando para combinar"</string>
<string name="screen_session_verification_ready_subtitle">"Compare um conjunto único de emojis."</string>
<string name="screen_session_verification_request_accepted_subtitle">"Compare os emojis únicos, garantindo que apareçam na mesma ordem."</string> <string name="screen_session_verification_request_accepted_subtitle">"Compare os emojis únicos, garantindo que apareçam na mesma ordem."</string>
<string name="screen_session_verification_they_dont_match">"Eles não combinam"</string> <string name="screen_session_verification_they_dont_match">"Eles não combinam"</string>
<string name="screen_session_verification_they_match">"Eles combinam"</string> <string name="screen_session_verification_they_match">"Eles combinam"</string>

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_session_verification_cancelled_subtitle">"Nimadir notogri korinadi. Yoki sorov muddati tugadi yoki sorov rad etildi."</string>
<string name="screen_session_verification_compare_emojis_subtitle">"Quyidagi kulgichlar boshqa seansda korsatilganlarga mos kelishini tasdiqlang."</string>
<string name="screen_session_verification_compare_emojis_title">"Emojilarni solishtiring"</string>
<string name="screen_session_verification_complete_subtitle">"Yangi seansingiz tasdiqlandi. U sizning shifrlangan xabarlaringizga kirish huquqiga ega va boshqa foydalanuvchilar uni ishonchli deb bilishadi."</string>
<string name="screen_session_verification_open_existing_session_subtitle">"Shifrlangan xabarlar tarixiga kirish uchun shaxsingizni tasdiqlang."</string>
<string name="screen_session_verification_open_existing_session_title">"Mavjud seansni oching"</string>
<string name="screen_session_verification_positive_button_canceled">"Tasdiqlashni qaytadan urining"</string>
<string name="screen_session_verification_positive_button_initial">"Men tayyorman"</string>
<string name="screen_session_verification_positive_button_verifying_ongoing">"Mos kelishi kutilmoqda"</string>
<string name="screen_session_verification_request_accepted_subtitle">"Noyob emojilarni solishtiring, ular bir xil tartibda paydo bo\'lishiga ishonch hosil qiling."</string>
<string name="screen_session_verification_they_dont_match">"Ular mos kelmaydi"</string>
<string name="screen_session_verification_they_match">"Ular mos keladi"</string>
<string name="screen_session_verification_waiting_to_accept_subtitle">"Davom etish uchun boshqa seansda tekshirish jarayonini boshlash soʻrovini qabul qiling."</string>
<string name="screen_session_verification_waiting_to_accept_title">"Soʻrovni qabul qilish kutilmoqda"</string>
</resources>

View file

@ -20,7 +20,7 @@ constraintlayout = "2.1.4"
constraintlayout_compose = "1.0.1" constraintlayout_compose = "1.0.1"
lifecycle = "2.7.0" lifecycle = "2.7.0"
activity = "1.9.1" activity = "1.9.1"
media3 = "1.3.1" media3 = "1.4.0"
camera = "1.3.4" camera = "1.3.4"
# Compose # Compose
@ -39,7 +39,7 @@ test_core = "1.6.1"
#other #other
coil = "2.7.0" coil = "2.7.0"
datetime = "0.6.0" datetime = "0.6.0"
dependencyAnalysis = "1.32.0" dependencyAnalysis = "1.33.0"
serialization_json = "1.6.3" serialization_json = "1.6.3"
showkase = "1.0.3" showkase = "1.0.3"
appyx = "1.4.0" appyx = "1.4.0"
@ -75,7 +75,7 @@ oss_licenses_plugin = "com.google.android.gms:oss-licenses-plugin:0.10.6"
# AndroidX # AndroidX
androidx_core = { module = "androidx.core:core", version.ref = "core" } androidx_core = { module = "androidx.core:core", version.ref = "core" }
androidx_corektx = { module = "androidx.core:core-ktx", version.ref = "core" } androidx_corektx = { module = "androidx.core:core-ktx", version.ref = "core" }
androidx_annotationjvm = "androidx.annotation:annotation-jvm:1.8.0" androidx_annotationjvm = "androidx.annotation:annotation-jvm:1.8.1"
androidx_datastore_preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" } androidx_datastore_preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" }
androidx_datastore_datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" } androidx_datastore_datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" }
androidx_exifinterface = "androidx.exifinterface:exifinterface:1.3.7" androidx_exifinterface = "androidx.exifinterface:exifinterface:1.3.7"
@ -163,7 +163,7 @@ jsoup = "org.jsoup:jsoup:1.18.1"
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0" molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0"
timber = "com.jakewharton.timber:timber:5.0.1" timber = "com.jakewharton.timber:timber:5.0.1"
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.34" matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.35"
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" } matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" } matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
@ -187,7 +187,7 @@ play_services_oss_licenses = "com.google.android.gms:play-services-oss-licenses:
# Analytics # Analytics
posthog = "com.posthog:posthog-android:3.4.2" posthog = "com.posthog:posthog-android:3.4.2"
sentry = "io.sentry:sentry-android:7.12.0" sentry = "io.sentry:sentry-android:7.12.1"
# main branch can be tested replacing the version with main-SNAPSHOT # main branch can be tested replacing the version with main-SNAPSHOT
matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.23.1" matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.23.1"

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="error_no_compatible_app_found">"Bu amalni bajarish uchun mos ilova topilmadi."</string>
</resources>

View file

@ -3,12 +3,16 @@
<string name="state_event_avatar_changed_too">"(zdjęcie profilowe też zostało zmienione)"</string> <string name="state_event_avatar_changed_too">"(zdjęcie profilowe też zostało zmienione)"</string>
<string name="state_event_avatar_url_changed">"%1$s zmienił swoje zdjęcie profilowe"</string> <string name="state_event_avatar_url_changed">"%1$s zmienił swoje zdjęcie profilowe"</string>
<string name="state_event_avatar_url_changed_by_you">"Zmieniłeś swoje zdjęcie profilowe"</string> <string name="state_event_avatar_url_changed_by_you">"Zmieniłeś swoje zdjęcie profilowe"</string>
<string name="state_event_demoted_to_member">"%1$s został zdegradowany do członka"</string>
<string name="state_event_demoted_to_moderator">"%1$s został zdegradowany do moderatora"</string>
<string name="state_event_display_name_changed_from">"%1$s zmienił swoją wyświetlaną nazwę z %2$s na %3$s"</string> <string name="state_event_display_name_changed_from">"%1$s zmienił swoją wyświetlaną nazwę z %2$s na %3$s"</string>
<string name="state_event_display_name_changed_from_by_you">"Zmieniłeś swoją wyświetlaną nazwę z %1$s na %2$s"</string> <string name="state_event_display_name_changed_from_by_you">"Zmieniłeś swoją wyświetlaną nazwę z %1$s na %2$s"</string>
<string name="state_event_display_name_removed">"%1$s usunął swoją wyświetlaną nazwę (byo to %2$s)"</string> <string name="state_event_display_name_removed">"%1$s usunął swoją wyświetlaną nazwę (byo to %2$s)"</string>
<string name="state_event_display_name_removed_by_you">"Usunąłeś swoją wyświetlaną nazwę (było to %1$s)"</string> <string name="state_event_display_name_removed_by_you">"Usunąłeś swoją wyświetlaną nazwę (było to %1$s)"</string>
<string name="state_event_display_name_set">"%1$s ustawił swoją wyświetlaną nazwę na %2$s"</string> <string name="state_event_display_name_set">"%1$s ustawił swoją wyświetlaną nazwę na %2$s"</string>
<string name="state_event_display_name_set_by_you">"Ustawiłeś swoją wyświetlaną nazwę na %1$s"</string> <string name="state_event_display_name_set_by_you">"Ustawiłeś swoją wyświetlaną nazwę na %1$s"</string>
<string name="state_event_promoted_to_administrator">"%1$s został awansowany na administratora"</string>
<string name="state_event_promoted_to_moderator">"%1$s został awansowany na moderatora"</string>
<string name="state_event_room_avatar_changed">"%1$s zmienił zdjęcie profilowe pokoju"</string> <string name="state_event_room_avatar_changed">"%1$s zmienił zdjęcie profilowe pokoju"</string>
<string name="state_event_room_avatar_changed_by_you">"Zmieniłeś zdjęcie profilowe pokoju"</string> <string name="state_event_room_avatar_changed_by_you">"Zmieniłeś zdjęcie profilowe pokoju"</string>
<string name="state_event_room_avatar_removed">"%1$s usunął zdjęcie profilowe pokoju"</string> <string name="state_event_room_avatar_removed">"%1$s usunął zdjęcie profilowe pokoju"</string>
@ -42,7 +46,7 @@
<string name="state_event_room_none">"%1$s nie wprowadził żadnych zmian"</string> <string name="state_event_room_none">"%1$s nie wprowadził żadnych zmian"</string>
<string name="state_event_room_none_by_you">"Nie wprowadzono żadnych zmian"</string> <string name="state_event_room_none_by_you">"Nie wprowadzono żadnych zmian"</string>
<string name="state_event_room_reject">"%1$s odrzucił zaproszenie"</string> <string name="state_event_room_reject">"%1$s odrzucił zaproszenie"</string>
<string name="state_event_room_reject_by_you">"Odrzuciłeś(aś) zaproszenie"</string> <string name="state_event_room_reject_by_you">"Odrzuciłeś zaproszenie"</string>
<string name="state_event_room_remove">"%1$s usunął %2$s"</string> <string name="state_event_room_remove">"%1$s usunął %2$s"</string>
<string name="state_event_room_remove_by_you">"Usunąłeś %1$s"</string> <string name="state_event_room_remove_by_you">"Usunąłeś %1$s"</string>
<string name="state_event_room_third_party_invite">"%1$s wysłał zaproszenie do %2$s, aby dołączył do pokoju"</string> <string name="state_event_room_third_party_invite">"%1$s wysłał zaproszenie do %2$s, aby dołączył do pokoju"</string>

View file

@ -3,12 +3,16 @@
<string name="state_event_avatar_changed_too">"(o avatar também foi alterado)"</string> <string name="state_event_avatar_changed_too">"(o avatar também foi alterado)"</string>
<string name="state_event_avatar_url_changed">"%1$s mudou seu avatar"</string> <string name="state_event_avatar_url_changed">"%1$s mudou seu avatar"</string>
<string name="state_event_avatar_url_changed_by_you">"Você mudou seu avatar"</string> <string name="state_event_avatar_url_changed_by_you">"Você mudou seu avatar"</string>
<string name="state_event_demoted_to_member">"%1$s foi rebaixado a membro"</string>
<string name="state_event_demoted_to_moderator">"%1$s foi rebaixado a moderador"</string>
<string name="state_event_display_name_changed_from">"%1$s mudou seu nome de exibição de %2$s para %3$s"</string> <string name="state_event_display_name_changed_from">"%1$s mudou seu nome de exibição de %2$s para %3$s"</string>
<string name="state_event_display_name_changed_from_by_you">"Você alterou seu nome de exibição de %1$s para %2$s"</string> <string name="state_event_display_name_changed_from_by_you">"Você alterou seu nome de exibição de %1$s para %2$s"</string>
<string name="state_event_display_name_removed">"%1$s removeu seu nome de exibição (era %2$s)"</string> <string name="state_event_display_name_removed">"%1$s removeu seu nome de exibição (era %2$s)"</string>
<string name="state_event_display_name_removed_by_you">"Você removeu seu nome de exibição (era %1$s)"</string> <string name="state_event_display_name_removed_by_you">"Você removeu seu nome de exibição (era %1$s)"</string>
<string name="state_event_display_name_set">"%1$s definiu seu nome de exibição como %2$s"</string> <string name="state_event_display_name_set">"%1$s definiu seu nome de exibição como %2$s"</string>
<string name="state_event_display_name_set_by_you">"Você definiu seu nome de exibição como %1$s"</string> <string name="state_event_display_name_set_by_you">"Você definiu seu nome de exibição como %1$s"</string>
<string name="state_event_promoted_to_administrator">"%1$s foi promovido a administrador"</string>
<string name="state_event_promoted_to_moderator">"%1$s foi promovido a moderador"</string>
<string name="state_event_room_avatar_changed">"%1$s mudou o avatar da sala"</string> <string name="state_event_room_avatar_changed">"%1$s mudou o avatar da sala"</string>
<string name="state_event_room_avatar_changed_by_you">"Você mudou o avatar da sala"</string> <string name="state_event_room_avatar_changed_by_you">"Você mudou o avatar da sala"</string>
<string name="state_event_room_avatar_removed">"%1$s removeu o avatar da sala"</string> <string name="state_event_room_avatar_removed">"%1$s removeu o avatar da sala"</string>
@ -39,6 +43,8 @@
<string name="state_event_room_name_changed_by_you">"Você mudou o nome da sala para: %1$s"</string> <string name="state_event_room_name_changed_by_you">"Você mudou o nome da sala para: %1$s"</string>
<string name="state_event_room_name_removed">"%1$s removeu o nome da sala"</string> <string name="state_event_room_name_removed">"%1$s removeu o nome da sala"</string>
<string name="state_event_room_name_removed_by_you">"Você removeu o nome da sala"</string> <string name="state_event_room_name_removed_by_you">"Você removeu o nome da sala"</string>
<string name="state_event_room_none">"%1$s não fez alterações"</string>
<string name="state_event_room_none_by_you">"Você não fez nenhuma alteração"</string>
<string name="state_event_room_reject">"%1$s rejeitou o convite"</string> <string name="state_event_room_reject">"%1$s rejeitou o convite"</string>
<string name="state_event_room_reject_by_you">"Você rejeitou o convite"</string> <string name="state_event_room_reject_by_you">"Você rejeitou o convite"</string>
<string name="state_event_room_remove">"%1$s removido %2$s"</string> <string name="state_event_room_remove">"%1$s removido %2$s"</string>

View file

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="state_event_avatar_changed_too">"(avatar ham o\'zgartirildi)"</string>
<string name="state_event_avatar_url_changed">"%1$s avatarini o\'zgartirdi"</string>
<string name="state_event_avatar_url_changed_by_you">"Siz avataringizni o\'zgartirdingiz"</string>
<string name="state_event_display_name_changed_from">"%1$s ko\'rsatiladigan nomini %2$sdan %3$sga o\'zgartirdi"</string>
<string name="state_event_display_name_changed_from_by_you">"Siz ko\'rsatiladigan nomingizni %1$s dan %2$s ga o\'zgartirdingiz"</string>
<string name="state_event_display_name_removed">"%1$s ko\'rinadigan nomini o\'chirib tashladi (avval %2$s bo\'lgan edi)"</string>
<string name="state_event_display_name_removed_by_you">"Siz ko\'rinadigan nomingizni o\'chirib tashladingiz (avval %1$s bo\'lgan edi)"</string>
<string name="state_event_display_name_set">"%1$s ularning ko\'rsatiladigan nomini o\'rnating %2$s"</string>
<string name="state_event_display_name_set_by_you">"Siz ko\'rsatiladigan nomingizni o\'rnating %1$s"</string>
<string name="state_event_room_avatar_changed">"%1$s xonani avatarini o\'zgartirdi"</string>
<string name="state_event_room_avatar_changed_by_you">"Siz xonani avatarini o\'zgartirdingiz"</string>
<string name="state_event_room_avatar_removed">"%1$s xonani avatarini o\'chirib tashladi"</string>
<string name="state_event_room_avatar_removed_by_you">"Siz xonani avatarini o\'chirib tashladingiz"</string>
<string name="state_event_room_ban">"%1$staqiqlangan%2$s"</string>
<string name="state_event_room_ban_by_you">"Siz taqiqlangansiz%1$s"</string>
<string name="state_event_room_created">"%1$sxonani yaratdi"</string>
<string name="state_event_room_created_by_you">"Siz xonani yaratdingiz"</string>
<string name="state_event_room_invite">"%1$staklif qilingan%2$s"</string>
<string name="state_event_room_invite_accepted">"%1$staklifni qabul qildi"</string>
<string name="state_event_room_invite_accepted_by_you">"Siz taklifni qabul qildingiz"</string>
<string name="state_event_room_invite_by_you">"Siz taklif qildingiz%1$s"</string>
<string name="state_event_room_invite_you">"%1$ssizni taklif qildi"</string>
<string name="state_event_room_join">"%1$sxonaga qo\'shildi"</string>
<string name="state_event_room_join_by_you">"Siz xonaga qo\'shildingiz"</string>
<string name="state_event_room_knock">"%1$s qo\'shilishni so\'radi"</string>
<string name="state_event_room_knock_accepted">"%1$s %2$sga qo\'shilishga ruxsat berdi"</string>
<string name="state_event_room_knock_accepted_by_you">"Siz %1$sga qo\'shilishaga ruxsat berdingiz"</string>
<string name="state_event_room_knock_by_you">"Siz qoʻshilishni soʻragansiz"</string>
<string name="state_event_room_knock_denied">"%1$s %2$sning qo\'shilish haqidagi iltimosini rad etdi"</string>
<string name="state_event_room_knock_denied_by_you">"Siz %1$sning qo\'shiliz iltimosini rad etdingiz"</string>
<string name="state_event_room_knock_denied_you">"%1$s sizni qo\'shilish iltimosingizni rad etdi"</string>
<string name="state_event_room_knock_retracted">"%1$ endi qo\'shilishdan manfaatdor emas"</string>
<string name="state_event_room_knock_retracted_by_you">"Siz qoʻshilish soʻrovingizni bekor qildingiz"</string>
<string name="state_event_room_leave">"%1$sxonani tark etdi"</string>
<string name="state_event_room_leave_by_you">"Siz xonani tark etdingiz"</string>
<string name="state_event_room_name_changed">"%1$s xonani nomini %2$s o\'zgartirdi"</string>
<string name="state_event_room_name_changed_by_you">"Siz xonani nomini %1$s ga o\'zgartirdingiz"</string>
<string name="state_event_room_name_removed">"%1$s xonani nomini o\'chirib tashladi"</string>
<string name="state_event_room_name_removed_by_you">"Siz xonani nomini o\'chirib tashladingiz"</string>
<string name="state_event_room_reject">"%1$staklifni rad etdi"</string>
<string name="state_event_room_reject_by_you">"Siz taklifni rad etdingiz"</string>
<string name="state_event_room_remove">"%1$o\'chirildi%2$s"</string>
<string name="state_event_room_remove_by_you">"siz o\'chirildingiz%1$s"</string>
<string name="state_event_room_third_party_invite">"%1$s taklifnoma yubordi %2$sga xonaga qo\'shilish uchun"</string>
<string name="state_event_room_third_party_invite_by_you">"Siz taklifnoma yubordingiz %1$sga xonaga qo\'shilishi uchun"</string>
<string name="state_event_room_third_party_revoked_invite">"%1$s taklifni %2$sga xonaga qo\'shilish uchun bekor qildi"</string>
<string name="state_event_room_third_party_revoked_invite_by_you">"Siz xonaga qo\'shilish taklifini $1$s ga bekor qildingiz"</string>
<string name="state_event_room_topic_changed">"%1$s mavzuni %2$s o\'zgartirdi"</string>
<string name="state_event_room_topic_changed_by_you">"Siz mavzuni %1$s ga o\'zgartirdingiz"</string>
<string name="state_event_room_topic_removed">"%1$s xonani mavzusini o\'chirib tashladi"</string>
<string name="state_event_room_topic_removed_by_you">"Siz xonani mavzusini o\'chirib tashladingiz"</string>
<string name="state_event_room_unban">"%1$staqiqlanmagan%2$s"</string>
<string name="state_event_room_unban_by_you">"Siz %1$s taqiqini bekor qildingiz"</string>
<string name="state_event_room_unknown_membership_change">"%1$s aʼzoligiga nomaʼlum oʻzgarishlar kiritdi"</string>
</resources>

View file

@ -120,4 +120,18 @@ enum class FeatureFlags(
defaultValue = { it.buildType != BuildType.RELEASE }, defaultValue = { it.buildType != BuildType.RELEASE },
isFinished = false, isFinished = false,
), ),
PinnedEvents(
key = "feature.pinnedEvents",
title = "Pinned Events",
description = "Allow user to pin events in a room",
defaultValue = { false },
isFinished = false,
),
SyncOnPush(
key = "feature.syncOnPush",
title = "Sync on push",
description = "Subscribe to room sync when a push is received",
defaultValue = { false },
isFinished = false,
),
} }

View file

@ -180,6 +180,8 @@ interface MatrixRoom : Closeable {
suspend fun canUserTriggerRoomNotification(userId: UserId): Result<Boolean> suspend fun canUserTriggerRoomNotification(userId: UserId): Result<Boolean>
suspend fun canUserPinUnpin(userId: UserId): Result<Boolean>
suspend fun canUserJoinCall(userId: UserId): Result<Boolean> = suspend fun canUserJoinCall(userId: UserId): Result<Boolean> =
canUserSendState(userId, StateEventType.CALL_MEMBER) canUserSendState(userId, StateEventType.CALL_MEMBER)

View file

@ -17,6 +17,7 @@
package io.element.android.libraries.matrix.api.room package io.element.android.libraries.matrix.api.room
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
@ -52,4 +53,5 @@ data class MatrixRoomInfo(
val hasRoomCall: Boolean, val hasRoomCall: Boolean,
val activeRoomCallParticipants: ImmutableList<String>, val activeRoomCallParticipants: ImmutableList<String>,
val heroes: ImmutableList<MatrixUser>, val heroes: ImmutableList<MatrixUser>,
val pinnedEventIds: ImmutableList<EventId>
) )

View file

@ -65,3 +65,8 @@ suspend fun MatrixRoom.canRedactOwn(): Result<Boolean> = canUserRedactOwn(sessio
* Shortcut for calling [MatrixRoom.canRedactOther] with our own user. * Shortcut for calling [MatrixRoom.canRedactOther] with our own user.
*/ */
suspend fun MatrixRoom.canRedactOther(): Result<Boolean> = canUserRedactOther(sessionId) suspend fun MatrixRoom.canRedactOther(): Result<Boolean> = canUserRedactOther(sessionId)
/**
* Shortcut for calling [MatrixRoom.canUserPinUnpin] with our own user.
*/
suspend fun MatrixRoom.canPinUnpin(): Result<Boolean> = canUserPinUnpin(sessionId)

View file

@ -169,4 +169,22 @@ interface Timeline : AutoCloseable {
): Result<MediaUploadHandler> ): Result<MediaUploadHandler>
suspend fun loadReplyDetails(eventId: EventId): InReplyTo suspend fun loadReplyDetails(eventId: EventId): InReplyTo
/**
* Adds a new pinned event by sending an updated `m.room.pinned_events`
* event containing the new event id.
*
* Returns `true` if we sent the request, `false` if the event was already
* pinned.
*/
suspend fun pinEvent(eventId: EventId): Result<Boolean>
/**
* Adds a new pinned event by sending an updated `m.room.pinned_events`
* event without the event id we want to remove.
*
* Returns `true` if we sent the request, `false` if the event wasn't
* pinned
*/
suspend fun unpinEvent(eventId: EventId): Result<Boolean>
} }

View file

@ -26,6 +26,7 @@ data class EventTimelineItem(
val eventId: EventId?, val eventId: EventId?,
val transactionId: TransactionId?, val transactionId: TransactionId?,
val isEditable: Boolean, val isEditable: Boolean,
val canBeRepliedTo: Boolean,
val isLocal: Boolean, val isLocal: Boolean,
val isOwn: Boolean, val isOwn: Boolean,
val isRemote: Boolean, val isRemote: Boolean,

View file

@ -16,6 +16,7 @@
package io.element.android.libraries.matrix.impl.room package io.element.android.libraries.matrix.impl.room
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
@ -58,7 +59,8 @@ class MatrixRoomInfoMapper {
userDefinedNotificationMode = it.userDefinedNotificationMode?.map(), userDefinedNotificationMode = it.userDefinedNotificationMode?.map(),
hasRoomCall = it.hasRoomCall, hasRoomCall = it.hasRoomCall,
activeRoomCallParticipants = it.activeRoomCallParticipants.toImmutableList(), activeRoomCallParticipants = it.activeRoomCallParticipants.toImmutableList(),
heroes = it.elementHeroes().toImmutableList() heroes = it.elementHeroes().toImmutableList(),
pinnedEventIds = it.pinnedEventIds.map(::EventId).toImmutableList(),
) )
} }
} }

View file

@ -403,6 +403,12 @@ class RustMatrixRoom(
} }
} }
override suspend fun canUserPinUnpin(userId: UserId): Result<Boolean> {
return runCatching {
innerRoom.canUserPinUnpin(userId.value)
}
}
override suspend fun sendImage( override suspend fun sendImage(
file: File, file: File,
thumbnailFile: File?, thumbnailFile: File?,

View file

@ -525,6 +525,18 @@ class RustTimeline(
} }
} }
override suspend fun pinEvent(eventId: EventId): Result<Boolean> = withContext(dispatcher) {
runCatching {
inner.pinEvent(eventId = eventId.value)
}
}
override suspend fun unpinEvent(eventId: EventId): Result<Boolean> = withContext(dispatcher) {
runCatching {
inner.unpinEvent(eventId = eventId.value)
}
}
private suspend fun fetchDetailsForEvent(eventId: EventId): Result<Unit> { private suspend fun fetchDetailsForEvent(eventId: EventId): Result<Unit> {
return runCatching { return runCatching {
inner.fetchDetailsForEvent(eventId.value) inner.fetchDetailsForEvent(eventId.value)

View file

@ -44,6 +44,7 @@ class EventTimelineItemMapper(private val contentMapper: TimelineEventContentMap
eventId = it.eventId()?.let(::EventId), eventId = it.eventId()?.let(::EventId),
transactionId = it.transactionId()?.let(::TransactionId), transactionId = it.transactionId()?.let(::TransactionId),
isEditable = it.isEditable(), isEditable = it.isEditable(),
canBeRepliedTo = it.canBeRepliedTo(),
isLocal = it.isLocal(), isLocal = it.isLocal(),
isOwn = it.isOwn(), isOwn = it.isOwn(),
isRemote = it.isRemote(), isRemote = it.isRemote(),

View file

@ -221,6 +221,7 @@ private fun aRustRoomInfo(
numUnreadMessages: ULong = 0uL, numUnreadMessages: ULong = 0uL,
numUnreadNotifications: ULong = 0uL, numUnreadNotifications: ULong = 0uL,
numUnreadMentions: ULong = 0uL, numUnreadMentions: ULong = 0uL,
pinnedEventIds: List<String> = listOf(),
) = RoomInfo( ) = RoomInfo(
id = id, id = id,
displayName = displayName, displayName = displayName,
@ -249,7 +250,8 @@ private fun aRustRoomInfo(
isMarkedUnread = isMarkedUnread, isMarkedUnread = isMarkedUnread,
numUnreadMessages = numUnreadMessages, numUnreadMessages = numUnreadMessages,
numUnreadNotifications = numUnreadNotifications, numUnreadNotifications = numUnreadNotifications,
numUnreadMentions = numUnreadMentions numUnreadMentions = numUnreadMentions,
pinnedEventIds = pinnedEventIds,
) )
class FakeRoomListItem( class FakeRoomListItem(

View file

@ -125,6 +125,7 @@ class FakeMatrixRoom(
private val getWidgetDriverResult: (MatrixWidgetSettings) -> Result<MatrixWidgetDriver> = { lambdaError() }, private val getWidgetDriverResult: (MatrixWidgetSettings) -> Result<MatrixWidgetDriver> = { lambdaError() },
private val canUserTriggerRoomNotificationResult: (UserId) -> Result<Boolean> = { lambdaError() }, private val canUserTriggerRoomNotificationResult: (UserId) -> Result<Boolean> = { lambdaError() },
private val canUserJoinCallResult: (UserId) -> Result<Boolean> = { lambdaError() }, private val canUserJoinCallResult: (UserId) -> Result<Boolean> = { lambdaError() },
private val canUserPinUnpinResult: (UserId) -> Result<Boolean> = { lambdaError() },
private val setIsFavoriteResult: (Boolean) -> Result<Unit> = { lambdaError() }, private val setIsFavoriteResult: (Boolean) -> Result<Unit> = { lambdaError() },
private val powerLevelsResult: () -> Result<MatrixRoomPowerLevels> = { lambdaError() }, private val powerLevelsResult: () -> Result<MatrixRoomPowerLevels> = { lambdaError() },
private val updatePowerLevelsResult: () -> Result<Unit> = { lambdaError() }, private val updatePowerLevelsResult: () -> Result<Unit> = { lambdaError() },
@ -289,6 +290,10 @@ class FakeMatrixRoom(
return canUserJoinCallResult(userId) return canUserJoinCallResult(userId)
} }
override suspend fun canUserPinUnpin(userId: UserId): Result<Boolean> {
return canUserPinUnpinResult(userId)
}
override suspend fun sendImage( override suspend fun sendImage(
file: File, file: File,
thumbnailFile: File?, thumbnailFile: File?,
@ -517,6 +522,7 @@ fun aRoomInfo(
userPowerLevels: ImmutableMap<UserId, Long> = persistentMapOf(), userPowerLevels: ImmutableMap<UserId, Long> = persistentMapOf(),
activeRoomCallParticipants: List<String> = emptyList(), activeRoomCallParticipants: List<String> = emptyList(),
heroes: List<MatrixUser> = emptyList(), heroes: List<MatrixUser> = emptyList(),
pinnedEventIds: List<EventId> = emptyList(),
) = MatrixRoomInfo( ) = MatrixRoomInfo(
id = id, id = id,
name = name, name = name,
@ -542,6 +548,7 @@ fun aRoomInfo(
userPowerLevels = userPowerLevels, userPowerLevels = userPowerLevels,
activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(), activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(),
heroes = heroes.toImmutableList(), heroes = heroes.toImmutableList(),
pinnedEventIds = pinnedEventIds.toImmutableList(),
) )
fun defaultRoomPowerLevels() = MatrixRoomPowerLevels( fun defaultRoomPowerLevels() = MatrixRoomPowerLevels(

View file

@ -33,6 +33,7 @@ import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
import io.element.android.tests.testutils.lambda.lambdaError
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -371,6 +372,16 @@ class FakeTimeline(
override suspend fun loadReplyDetails(eventId: EventId) = loadReplyDetailsLambda(eventId) override suspend fun loadReplyDetails(eventId: EventId) = loadReplyDetailsLambda(eventId)
var pinEventLambda: (eventId: EventId) -> Result<Boolean> = { lambdaError() }
override suspend fun pinEvent(eventId: EventId): Result<Boolean> {
return pinEventLambda(eventId)
}
var unpinEventLambda: (eventId: EventId) -> Result<Boolean> = { lambdaError() }
override suspend fun unpinEvent(eventId: EventId): Result<Boolean> {
return unpinEventLambda(eventId)
}
var closeCounter = 0 var closeCounter = 0
private set private set

View file

@ -46,6 +46,7 @@ fun anEventTimelineItem(
eventId: EventId = AN_EVENT_ID, eventId: EventId = AN_EVENT_ID,
transactionId: TransactionId? = null, transactionId: TransactionId? = null,
isEditable: Boolean = false, isEditable: Boolean = false,
canBeRepliedTo: Boolean = false,
isLocal: Boolean = false, isLocal: Boolean = false,
isOwn: Boolean = false, isOwn: Boolean = false,
isRemote: Boolean = false, isRemote: Boolean = false,
@ -61,6 +62,7 @@ fun anEventTimelineItem(
eventId = eventId, eventId = eventId,
transactionId = transactionId, transactionId = transactionId,
isEditable = isEditable, isEditable = isEditable,
canBeRepliedTo = canBeRepliedTo,
isLocal = isLocal, isLocal = isLocal,
isOwn = isOwn, isOwn = isOwn,
isRemote = isRemote, isRemote = isRemote,

View file

@ -56,6 +56,13 @@ fun MatrixRoom.canCall(updateKey: Long): State<Boolean> {
} }
} }
@Composable
fun MatrixRoom.canPinUnpin(updateKey: Long): State<Boolean> {
return produceState(initialValue = false, key1 = updateKey) {
value = canUserPinUnpin(sessionId).getOrElse { false }
}
}
@Composable @Composable
fun MatrixRoom.isOwnUserAdmin(): Boolean { fun MatrixRoom.isOwnUserAdmin(): Boolean {
val roomInfo by roomInfoFlow.collectAsState(initial = null) val roomInfo by roomInfoFlow.collectAsState(initial = null)

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_invites_invited_you">"%1$s(%2$s ) sizni taklif qildi"</string>
</resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="dialog_permission_camera">"Ilovaga kameradan foydalanishiga ruxsat berish uchun tizim sozlamalarida ruxsat bering."</string>
<string name="dialog_permission_generic">"Iltimos, tizim sozlamalarida ruxsat bering."</string>
<string name="dialog_permission_microphone">"Ilovaga mikrofondan foydalanishiga ruxsat berish uchun tizim sozlamalarida ruxsat bering."</string>
<string name="dialog_permission_notification">"Ilova bildirishnomalarni ko\'rsatishi uchun tizim sozlamalarida ruxsat bering."</string>
</resources>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="troubleshoot_notifications_test_check_permission_description">"Sprawdź, czy aplikacja może wyświetlać powiadomienia."</string>
<string name="troubleshoot_notifications_test_check_permission_title">"Sprawdź uprawnienia"</string>
</resources>

View file

@ -56,6 +56,7 @@ dependencies {
implementation(projects.libraries.uiStrings) implementation(projects.libraries.uiStrings)
implementation(projects.libraries.troubleshoot.api) implementation(projects.libraries.troubleshoot.api)
implementation(projects.features.call.api) implementation(projects.features.call.api)
implementation(projects.libraries.featureflag.api)
api(projects.libraries.pushproviders.api) api(projects.libraries.pushproviders.api)
api(projects.libraries.pushstore.api) api(projects.libraries.pushstore.api)
api(projects.libraries.push.api) api(projects.libraries.push.api)

Some files were not shown because too many files have changed in this diff Show more