From 3899814adbbe40783429cd17fe2a45a4207c243b Mon Sep 17 00:00:00 2001 From: Marco Antonio Alvarez Date: Thu, 11 Jan 2024 18:30:10 +0100 Subject: [PATCH 001/119] Adjusted the login flow buttons a little bit They were slightly smaller on the onboarding page so I used that everywhere Signed-off-by: Marco Antonio Alvarez --- .../loginpassword/LoginPasswordView.kt | 29 ++++++++++++------- .../onboarding/impl/OnBoardingView.kt | 2 +- .../atomic/pages/HeaderFooterPage.kt | 4 ++- .../atomic/pages/OnBoardingPage.kt | 3 +- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt index 2a6a752154..2a5fbe8657 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt @@ -16,6 +16,7 @@ package io.element.android.features.login.impl.screens.loginpassword +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets @@ -55,6 +56,7 @@ import io.element.android.features.login.impl.R import io.element.android.features.login.impl.error.isWaitListError import io.element.android.features.login.impl.error.loginError import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog @@ -114,7 +116,7 @@ fun LoginPasswordView( .padding(padding) .consumeWindowInsets(padding) .verticalScroll(state = scrollState) - .padding(horizontal = 16.dp), + .padding(start = 20.dp, end = 20.dp, bottom = 20.dp), ) { // Title IconTitleSubtitleMolecule( @@ -137,16 +139,23 @@ fun LoginPasswordView( // Flexible spacing to keep the submit button at the bottom Spacer(modifier = Modifier.weight(1f)) // Submit - Button( - text = stringResource(CommonStrings.action_continue), - showProgress = isLoading, - onClick = ::submit, - enabled = state.submitEnabled || isLoading, + Box( modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.loginContinue) - ) - Spacer(modifier = Modifier.height(60.dp)) + .padding(horizontal = 16.dp) + ) { + ButtonColumnMolecule { + Button( + text = stringResource(CommonStrings.action_continue), + showProgress = isLoading, + onClick = ::submit, + enabled = state.submitEnabled || isLoading, + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.loginContinue) + ) + Spacer(modifier = Modifier.height(48.dp)) + } + } if (state.loginAction is AsyncData.Failure) { when { diff --git a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt index a0d2e2ce7f..8ecef02b0e 100644 --- a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt +++ b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt @@ -186,7 +186,7 @@ private fun OnBoardingButtons( .fillMaxWidth() ) } - Spacer(modifier = Modifier.height(16.dp)) + Spacer(modifier = Modifier.height(48.dp)) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt index c08eb5149a..6b99537dc9 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt @@ -68,7 +68,9 @@ fun HeaderFooterPage( content() } // Footer - footer() + Box(modifier = Modifier.padding(horizontal = 16.dp)) { + footer() + } } } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/OnBoardingPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/OnBoardingPage.kt index 1b8aed72c2..9f028f5c5a 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/OnBoardingPage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/OnBoardingPage.kt @@ -67,13 +67,12 @@ fun OnBoardingPage( modifier = Modifier .fillMaxSize() .systemBarsPadding() - .padding(vertical = 16.dp), + .padding(all = 20.dp), ) { // Content Column( modifier = Modifier .weight(1f) - .padding(horizontal = 24.dp) .fillMaxWidth(), horizontalAlignment = contentAlignment, ) { From 4611536ee954f976837c2e883aee5260997e8f5a Mon Sep 17 00:00:00 2001 From: Marco Antonio Alvarez Date: Thu, 11 Jan 2024 18:34:52 +0100 Subject: [PATCH 002/119] added 825 changelog Signed-off-by: Marco Antonio Alvarez --- changelog.d/825.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/825.misc diff --git a/changelog.d/825.misc b/changelog.d/825.misc new file mode 100644 index 0000000000..a715b61217 --- /dev/null +++ b/changelog.d/825.misc @@ -0,0 +1 @@ +Adjusted the login flow buttons so the continue button is always at the same height From 5875995a954afaa5e87a545419d2c0b263860979 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 11 Jan 2024 17:49:29 +0000 Subject: [PATCH 003/119] Update screenshots --- ...null_AnalyticsOptInView-Day-0_1_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ll_AnalyticsOptInView-Night-0_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ..._NotificationsOptInView-Day-1_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...otificationsOptInView-Night-1_3_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...comeView_null_WelcomeView-Day-2_3_null,NEXUS_5,1.0,en].png | 4 ++-- ...meView_null_WelcomeView-Night-2_4_null,NEXUS_5,1.0,en].png | 4 ++-- ...null_SetupBiometricView-Day-2_3_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ll_SetupBiometricView-Night-2_4_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...firmAccountProviderView-Day-5_6_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...rmAccountProviderView-Night-5_7_null_0,NEXUS_5,1.0,en].png | 4 ++-- ..._null_LoginPasswordView-Day-6_7_null_0,NEXUS_5,1.0,en].png | 4 ++-- ..._null_LoginPasswordView-Day-6_7_null_1,NEXUS_5,1.0,en].png | 4 ++-- ..._null_LoginPasswordView-Day-6_7_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...ull_LoginPasswordView-Night-6_8_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ull_LoginPasswordView-Night-6_8_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...ull_LoginPasswordView-Night-6_8_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...outView_null_LogoutView-Day-0_1_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...outView_null_LogoutView-Day-0_1_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...outView_null_LogoutView-Day-0_1_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...outView_null_LogoutView-Day-0_1_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...outView_null_LogoutView-Day-0_1_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...outView_null_LogoutView-Day-0_1_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...outView_null_LogoutView-Day-0_1_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...outView_null_LogoutView-Day-0_1_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...outView_null_LogoutView-Day-0_1_null_8,NEXUS_5,1.0,en].png | 4 ++-- ...outView_null_LogoutView-Day-0_1_null_9,NEXUS_5,1.0,en].png | 4 ++-- ...tView_null_LogoutView-Night-0_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...tView_null_LogoutView-Night-0_2_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...tView_null_LogoutView-Night-0_2_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...tView_null_LogoutView-Night-0_2_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...tView_null_LogoutView-Night-0_2_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...tView_null_LogoutView-Night-0_2_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...tView_null_LogoutView-Night-0_2_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...tView_null_LogoutView-Night-0_2_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...tView_null_LogoutView-Night-0_2_null_8,NEXUS_5,1.0,en].png | 4 ++-- ...tView_null_LogoutView-Night-0_2_null_9,NEXUS_5,1.0,en].png | 4 ++-- ...n_null_OnBoardingScreen-Day-0_1_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...n_null_OnBoardingScreen-Day-0_1_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...n_null_OnBoardingScreen-Day-0_1_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...n_null_OnBoardingScreen-Day-0_1_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...n_null_OnBoardingScreen-Day-0_1_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...null_OnBoardingScreen-Night-0_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...null_OnBoardingScreen-Night-0_2_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...null_OnBoardingScreen-Night-0_2_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...null_OnBoardingScreen-Night-0_2_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...null_OnBoardingScreen-Night-0_2_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...SecureBackupDisableView-Day-0_1_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...SecureBackupDisableView-Day-0_1_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...SecureBackupDisableView-Day-0_1_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...SecureBackupDisableView-Day-0_1_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...cureBackupDisableView-Night-0_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...cureBackupDisableView-Night-0_2_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...cureBackupDisableView-Night-0_2_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...cureBackupDisableView-Night-0_2_null_3,NEXUS_5,1.0,en].png | 4 ++-- ..._SecureBackupEnableView-Day-1_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ..._SecureBackupEnableView-Day-1_2_null_1,NEXUS_5,1.0,en].png | 4 ++-- ..._SecureBackupEnableView-Day-1_2_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...ecureBackupEnableView-Night-1_3_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ecureBackupEnableView-Night-1_3_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...ecureBackupEnableView-Night-1_3_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...kupEnterRecoveryKeyView-Day-2_3_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...kupEnterRecoveryKeyView-Day-2_3_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...kupEnterRecoveryKeyView-Day-2_3_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...kupEnterRecoveryKeyView-Day-2_3_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...pEnterRecoveryKeyView-Night-2_4_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...pEnterRecoveryKeyView-Night-2_4_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...pEnterRecoveryKeyView-Night-2_4_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...pEnterRecoveryKeyView-Night-2_4_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...reBackupSetupViewChange-Day-5_6_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...reBackupSetupViewChange-Day-5_6_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...reBackupSetupViewChange-Day-5_6_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...reBackupSetupViewChange-Day-5_6_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...reBackupSetupViewChange-Day-5_6_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...BackupSetupViewChange-Night-5_7_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...BackupSetupViewChange-Night-5_7_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...BackupSetupViewChange-Night-5_7_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...BackupSetupViewChange-Night-5_7_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...BackupSetupViewChange-Night-5_7_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...l_SecureBackupSetupView-Day-4_5_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...l_SecureBackupSetupView-Day-4_5_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...l_SecureBackupSetupView-Day-4_5_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...l_SecureBackupSetupView-Day-4_5_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...l_SecureBackupSetupView-Day-4_5_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...SecureBackupSetupView-Night-4_6_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...SecureBackupSetupView-Night-4_6_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...SecureBackupSetupView-Night-4_6_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...SecureBackupSetupView-Night-4_6_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...SecureBackupSetupView-Night-4_6_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...View_null_SignedOutView-Day-0_1_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ew_null_SignedOutView-Night-0_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...l_VerifySelfSessionView-Day-0_1_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...l_VerifySelfSessionView-Day-0_1_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...l_VerifySelfSessionView-Day-0_1_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...l_VerifySelfSessionView-Day-0_1_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...l_VerifySelfSessionView-Day-0_1_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...l_VerifySelfSessionView-Day-0_1_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...VerifySelfSessionView-Night-0_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...VerifySelfSessionView-Night-0_2_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...VerifySelfSessionView-Night-0_2_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...VerifySelfSessionView-Night-0_2_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...VerifySelfSessionView-Night-0_2_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...VerifySelfSessionView-Night-0_2_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...ingPage_null_OnBoardingPage-Day_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...gPage_null_OnBoardingPage-Night_1_null,NEXUS_5,1.0,en].png | 4 ++-- 104 files changed, 208 insertions(+), 208 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Day-0_1_null_0,NEXUS_5,1.0,en].png index ef4f316ea1..f594008e01 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Day-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c6664fe1c30fee448945a238795a60f4ddbc8fefec0bc7e48e9345e6b5b429e5 -size 50702 +oid sha256:0707a78bd738d570d258d5e65a2b0256d0e76ed78a49d79bed49330defeb7cf2 +size 50637 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Night-0_2_null_0,NEXUS_5,1.0,en].png index 34a414aed1..fa6c0514d8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Night-0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:91e2590fa78c1dbc1a24eb13602df844259b5c9e7a14fd0d3cfbf2d9086e0453 -size 49543 +oid sha256:b8734b34987f4972b604a589856d03af8def1202fc57617e2e9833141c6b4a79 +size 49523 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Day-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Day-1_2_null_0,NEXUS_5,1.0,en].png index 10ec2fb2cb..11da2d3322 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Day-1_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Day-1_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:556ac89ad4a4f4a2f204f84634ce63ad8fef43bdb26edba8a78f7bc06d13b374 -size 37179 +oid sha256:779ce59665e92ed1419dbba576b8f887e11363bcef07c42608a8d3634bbc00d2 +size 37172 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Night-1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Night-1_3_null_0,NEXUS_5,1.0,en].png index 2fc8eaee08..bd19065570 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Night-1_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Night-1_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:636756772f8e4eba11e88a7903f04c545cc93045961c7747ddfa2fcb24533754 -size 33710 +oid sha256:699739584aab66a1f67ac8f5fb6a5ff535e3ca02696eacff05adf1c64d2d5646 +size 33709 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-2_3_null,NEXUS_5,1.0,en].png index ff92f02572..36be4e7755 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-2_3_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-2_3_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d56633c5bddf14c18ebab5859b0f2aaee9c37c1ff8a8923b508827880e708b3 -size 288239 +oid sha256:0ce8844085506f4efec8eb1c461f416d240545fc0119cfd28b71cf8fe4a92ae5 +size 286645 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-2_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-2_4_null,NEXUS_5,1.0,en].png index 3440c75136..846e49c61a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-2_4_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-2_4_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04b8af7d14aff6edd9db4e4c6187381c9efbdba1721e1688da5823038f1aa31f -size 391103 +oid sha256:0f88c69dbd38cdd2f6c8ed953901dc7655dc6157a01c73060ee1dc6e82ace110 +size 390198 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Day-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Day-2_3_null_0,NEXUS_5,1.0,en].png index 5ecc718c8f..12b8bc6a3a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Day-2_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Day-2_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb7cf95951b4cae20445929a7b0ce7d3fb4cb1ff0d6ce735e890106754485856 -size 37651 +oid sha256:c4338ca6512f08eee0bb3171b2d0cb0cbd813d4644be5500f14e47c9f071243b +size 37263 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Night-2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Night-2_4_null_0,NEXUS_5,1.0,en].png index 652efd1ba4..4be5500a4e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Night-2_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Night-2_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:38e9a9481637e9fafbc2bf4083b1024e735bd40f6b4bdcda4f50b403df475e96 -size 35595 +oid sha256:181c745a8d624eacaa85e43b2f98fd24e006d9d93155cf66480d51213064bf19 +size 35255 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_null_ConfirmAccountProviderView-Day-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_null_ConfirmAccountProviderView-Day-5_6_null_0,NEXUS_5,1.0,en].png index 6459387bca..5bace8e94e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_null_ConfirmAccountProviderView-Day-5_6_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_null_ConfirmAccountProviderView-Day-5_6_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e5b2bdd159234708bc988bedeafc62ef8bfadeba71211fb6838bdc522990acb -size 39654 +oid sha256:a2c1530f2b1d34dee8ebf59ba05165f195226ae77d6098d22f446e62799f90f5 +size 39577 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_null_ConfirmAccountProviderView-Night-5_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_null_ConfirmAccountProviderView-Night-5_7_null_0,NEXUS_5,1.0,en].png index 600bf55cbb..2e943e28c2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_null_ConfirmAccountProviderView-Night-5_7_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_null_ConfirmAccountProviderView-Night-5_7_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a87c902caeaae97acc949d5465f8abcba4bbed81abddea42dcb2b4485e7d29fa -size 37219 +oid sha256:7ce7ad067efe9b9158395982211077c1d13fbfb46c6b317900cd30fd5836ddfe +size 37196 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_0,NEXUS_5,1.0,en].png index 3bdb7f4d9c..4dd3f033ec 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cc2c0ee0df61c3b071185daeb2e20d64f129db2b3e9b5f2d9bb68dd2b04ca71 -size 39655 +oid sha256:f4df53da4792d6058a3fb3edda32652612185006b52efef772331e09dd5b1194 +size 40018 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_1,NEXUS_5,1.0,en].png index 54fb4fa2dd..95f9d62944 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27b8b17d2f628e9bfa5f0d97bf2cf68424261596967d1b2621a5bb879f2ce2d1 -size 40815 +oid sha256:57b400403ffc577cce65aede9880e023fb022ce0aae447d7c621308203a7f053 +size 41151 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_2,NEXUS_5,1.0,en].png index f198ed2c07..8f10ceed70 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec89cd219a741b98ae046b2c90db9d36d418570f05d0edf41cfe831cf71b1303 -size 30745 +oid sha256:77dbd62af4d2ee97e157cdf407c62807aef0979192d95d2fce9f880a1387714a +size 31171 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_0,NEXUS_5,1.0,en].png index 179219d286..78c5aecd8b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a240b641665c15d79663a4f35fa7af923bebf88eeac6355c9c1ff127794ba77 -size 37585 +oid sha256:50495c30c23802a747c9d423d8e227d0e034dbf3b403f64cb4607d9ab258607c +size 37952 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_1,NEXUS_5,1.0,en].png index 44a81090b6..df9f611f99 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e32c7c81e3def45b6df8aa8f1da70012f656a2289f0db562fd5df57c00ca104 -size 38574 +oid sha256:93773786e103f7f02d716906700f7f0e80a1d54d16ab6a31995703673d1df241 +size 38975 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_2,NEXUS_5,1.0,en].png index dfadb50fef..15e4d8478d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8a15fc7d605ed1c4ba2bbfa901ca01b44ce0fb5c678be6b5fb484b721f95c87 -size 27410 +oid sha256:0ae46f9ad861a896b8b6d7210908ada04ac387675d333b8528761790555a093d +size 27467 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_0,NEXUS_5,1.0,en].png index c930a5e486..a0db2c925d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78edb2f0cbb3a7c975999aa31f90e5a82e7b73a21f2923e420f364da3c0118fd -size 13706 +oid sha256:cab6223e7adc7db6f882f563a1398af3815d52e187557d3b7b294f9f4877e4b6 +size 13647 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_1,NEXUS_5,1.0,en].png index 4bd4efb227..1baec3dd08 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:186d3f45a3e8b294e523ede9787563399d97b26d7181ad687afcd8b48f8be506 -size 41247 +oid sha256:644a4f196e60808b48c1a582f5bf8b1930fcca68658b3acdd06c57546bc7ae9d +size 41107 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_2,NEXUS_5,1.0,en].png index 99ff03c0d8..61d189a9a0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c6252e151e747259945691f9461e4e4d893abd6f048be336df5fb75ff558be7 -size 30679 +oid sha256:bc64a63b73f24ad8ef0d77bd8c6f9a0659d1bcebce238f060bfb56642043c07f +size 30574 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_3,NEXUS_5,1.0,en].png index 4bd4efb227..1baec3dd08 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:186d3f45a3e8b294e523ede9787563399d97b26d7181ad687afcd8b48f8be506 -size 41247 +oid sha256:644a4f196e60808b48c1a582f5bf8b1930fcca68658b3acdd06c57546bc7ae9d +size 41107 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_4,NEXUS_5,1.0,en].png index 4e3d0eab1c..63d1ff7ae8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d8dfacb3767fe7e9de7996cd0fb9bcb7f208e49498bfdc7b599546669b7b396 -size 27632 +oid sha256:b87ac5dd5a9a24720a2dc587c16832709401129e3ee71fef60a32213fe7027df +size 27626 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_5,NEXUS_5,1.0,en].png index e9f9d8d5db..ca0009bf97 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51528fe646b2ac7f679861af28443b22fe1a710586ac70aa730d98cf7c1e8856 -size 21842 +oid sha256:0c457ab756a8b4edfd32ba3eb4379d96f2393b3e738452990cd284fd1c9782ea +size 21837 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_6,NEXUS_5,1.0,en].png index 72f4c9d66e..d00e196535 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:47f4ac4fb71f8a4adc3890101244c4ee9fbbf87d0da0e0381a85792b79896a5f -size 26372 +oid sha256:5a85634f4ee86bde474fb43df2f5583abf573e2c3fc939d815011aae399ed67f +size 26370 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_7,NEXUS_5,1.0,en].png index 4e8080e3cf..4997ef394b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:394d3da2d14c0c9438632d54c0999a6ecae9b2ca8cf8bb77986d761d2dfa0a1d -size 39116 +oid sha256:256f55b114ccfbc7cf464bad2670d02e6e201005d0bfacbafee6e57aff6c3edb +size 39002 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_8,NEXUS_5,1.0,en].png index b23d4ec921..e6a0eb8ed8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:87010d4aae66e1030d4b9557e4f065df777324495459c08513040e6793d2d00d -size 36675 +oid sha256:cb978a661027d7b390fdb2f129b5804246c2de03adbcaa25b94fe3b5339baf3d +size 36551 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_9,NEXUS_5,1.0,en].png index 00eb708f14..df32e11ac4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c2f057c86716407a4cd3491612f57b9d4b800cf4ef749c2e6dce3e12fbbf632 -size 37972 +oid sha256:de65298c0876e02a818de70cde17140e3526612e2a422eb52c350198a3149219 +size 37854 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_0,NEXUS_5,1.0,en].png index 5915ad7d40..d4b890a58d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd5acfa31ca9994634286aa59bdb67d86e438165296164db82e152cd56868585 -size 13044 +oid sha256:71074dc46126df100436179a90d1488ed39b642d046e19a0bfe2446cb465d188 +size 13005 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_1,NEXUS_5,1.0,en].png index b28650d35e..a7a4d01916 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49df4ac1a7ec108308b82d0fdb6f0e0d8ca2063960070fa9865562e59774ec21 -size 38623 +oid sha256:552ebfbd3a498b7ce008915846e5f2803450cfd83aa494fa8619a927abbbf7a7 +size 38528 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_2,NEXUS_5,1.0,en].png index 9fc78733b4..a5e98ce937 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8406ed0ada582c9436be5937d8cb010f3b4692962e5357c0a919471574aabb3 -size 28958 +oid sha256:5345a21405c403064db7691aff0f0071cb40fe62bc3baeb73b50a5a722bbda31 +size 28893 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_3,NEXUS_5,1.0,en].png index b28650d35e..a7a4d01916 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49df4ac1a7ec108308b82d0fdb6f0e0d8ca2063960070fa9865562e59774ec21 -size 38623 +oid sha256:552ebfbd3a498b7ce008915846e5f2803450cfd83aa494fa8619a927abbbf7a7 +size 38528 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_4,NEXUS_5,1.0,en].png index 1906882931..66f14bd622 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51af5a971e3bb5958646ec1866ba41fd4d46e281713573b005bc71b4762ec7b4 -size 24085 +oid sha256:53248519a1ed41bd21902e21b3dabf9ccb91d6e859c4d294ec79a9e417f36c05 +size 24040 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_5,NEXUS_5,1.0,en].png index 2c7916bd2b..f2368a5ea6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6efe41b16360b4cfc891a5ffc99ff147ea5c5324341d5f8b8a2f574d360b5aad -size 19117 +oid sha256:f34078246c32c14414523890609bd52d48cf5d6de6cefc561cccdf3f1531b9d3 +size 19078 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_6,NEXUS_5,1.0,en].png index 8de5498d31..b0dc94329b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a2a05a5d4f528302ca1a6f8bd799418f97d511bb808dadbfe806077ab2a9bab -size 22756 +oid sha256:9cb1578b4f6f11f395aa93c6e3a091f71d5ebf020477ddbfa4ebb70a70b9bde8 +size 22724 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_7,NEXUS_5,1.0,en].png index 968ebc3569..e8aa9de326 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b923a90e9e8f4c16aac3afdda71b60c52db6ab6a08d046be49c9c7a1e91277c0 -size 37107 +oid sha256:fc8e0ed5cd39c3118062931c9735324c84e28f0a0a80f4abd6de8ba1d33093dc +size 37054 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_8,NEXUS_5,1.0,en].png index b61e420d85..821f87b9cf 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3961ac676e29ac3d6f8dede3aed1c0c22d400e6a95ca8c4333691102f1616c42 -size 34922 +oid sha256:9d2b0f5732d4eaea745050d8f3aaa0c893aba732746bfa894924a8d10d4ffd15 +size 34824 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_9,NEXUS_5,1.0,en].png index 3a2db9c9ab..d5b7cf0706 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52bf6e6334dd36e2e39f6957a60c7d54839caffdf47a32a0bb463124fc2c27d4 -size 35820 +oid sha256:ee04bbd259ccc5d3a8accf8db7a8ea76b89a2a489159839b655636e9bf807cd4 +size 35721 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_0,NEXUS_5,1.0,en].png index 6e9b5a2034..cef3fc6c73 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:067d37cbe20e27b8e852f3eedaa3bcc64d94d062cb825df5b826a1b584e2aa1b -size 316186 +oid sha256:7eca99bfe466baa273ce4b485eb534b25d765b7409c9636d322603e96b9e3419 +size 313750 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_1,NEXUS_5,1.0,en].png index 708b07d88d..3606f7454a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08aedb3ff38729714de367a1b51b92f14907d1cd6f71a02a3b73ada46c2b86a6 -size 311794 +oid sha256:84bdea70c97fd350a5465632bdfb480f5cc441e8cdda4420b73e6f96a2a9846c +size 309062 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_2,NEXUS_5,1.0,en].png index 54aa98cef9..e5c16e58e6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94383700dd12a703f9e3773d231a18520b74037d7823d307c15c2c44ef9c4fd3 -size 315047 +oid sha256:2fc68b4e7b66cb0ee9ce86445c522de1c248173331e38291cc3f9c0a4d1dbb02 +size 315615 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_3,NEXUS_5,1.0,en].png index 92da16468b..15721984cb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:672c71bf95e048043af54904e3e14909a7af37efc8dbfa0c29ba3b027db2b70a -size 305960 +oid sha256:6a47c109da395d2ee7b4237c06b8169e5dc9e587b13fa18e520d9769d50ef904 +size 309218 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_4,NEXUS_5,1.0,en].png index 5d7e8bf7d7..b84f054f96 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aaf9e59966650af342740484f37b7e5da334a72a96faa318db0dc9ea012d891c -size 316954 +oid sha256:ae89eb426653b9fb34a309904e1b1d6f8502689a8712470a3d4f388c35219c20 +size 314617 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_0,NEXUS_5,1.0,en].png index d8891a6a48..595ebbc34d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6334be27e7a3c3fd2b000ffb8d912b84fdf3867211c261fcd6451f8321d91f9e -size 412644 +oid sha256:59bb85b2846fc6554715ba06f7aa30b35843ff494c0178bb815ce1a5939112e1 +size 408754 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_1,NEXUS_5,1.0,en].png index 0e7087356f..f9d5628574 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d84c6f511b9533118bf603d37e99f9a8670f8dc120c0b6ef85f0e488ffea43a -size 401150 +oid sha256:98437f10ee4ae9c51606e07e15918c67fa72ac2e78f581c7cb0cdeb672f51bbd +size 394881 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_2,NEXUS_5,1.0,en].png index 8c96d798de..a57a330934 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:162389f0543bec631064fa9da20bf4e76150cedacc594a3299b4da231fe62502 -size 404119 +oid sha256:19e49c236cd452f1d65bced61a7f7e56ee55ae508a3d9be95531f1ba4c2ea0b1 +size 401190 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_3,NEXUS_5,1.0,en].png index eb250c9c5e..7d985f2111 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f980d1c2a27b18237e04679bc8a507854ebb518eab7506d1210e34bd38087a4 -size 383009 +oid sha256:cba5c736930368a47a265a841e53072cd58aecd10a49982581c46ab19ee5f901 +size 379887 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_4,NEXUS_5,1.0,en].png index d40e2236b4..82e5112fdb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b19d128ca665799591b6a570be4643e482821bf9d632dc7704aacd13111d458a -size 413352 +oid sha256:38e645b8dcba672383b58435524a7ef188a2e0891234dcea7249e0b163330ab1 +size 409549 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_0,NEXUS_5,1.0,en].png index f6271e8460..c4ae2f066f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76e7306898cbc637a811110c7dd0ae6dece0842755d3f9c3a90fd0c7c07f9b2e -size 59005 +oid sha256:7e95a434bf45ede892d898dc1446da9b493826bf9bf3bf61ef9f00ccc6ded4f9 +size 58950 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_1,NEXUS_5,1.0,en].png index d059a1163e..6da6c6b540 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3148e7abdbf0b6d27d3fbebc9c237ecaa30a7724d35bd0421c89198b5e6165b -size 47784 +oid sha256:8153dddb52ae42e3ad07afc2048f99658f3c8a54a4b07f4fe69e217132b0573f +size 47788 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_2,NEXUS_5,1.0,en].png index 8491fdc798..e9850cab98 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd12f81dfc5856c5db3ae9421e05d38328ffc977aa9a778d0b4202d0e79b447e -size 59677 +oid sha256:d2eb5b3ac0e302c29bded27f39048c16d3eb1f31ea4a03b58775bb34d3c5f285 +size 59604 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_3,NEXUS_5,1.0,en].png index 178750d1cb..96c9d6f178 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6e4b21aaf3ff6f327041bcf169ba4d493c24f18d1b2f8fbafa20a20c19e78561 -size 30447 +oid sha256:236e68f772ab98458afa255ebce89ecf92949f07b346fa1d2e0c86a46f65f7d5 +size 30451 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_0,NEXUS_5,1.0,en].png index b41c7b1376..b6934ad81a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd7d552ceb9b86f4bacbd9bd0e20b7e895d29e17b113230e4de63a9680ced233 -size 57250 +oid sha256:a3d32339cf5f0044826083659b2040691a15e1dd3c1d92091b10b3b8ee02b99b +size 57203 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_1,NEXUS_5,1.0,en].png index 88031ebef1..f77060c7f7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:87be047661972a3fa72e069e79028cb64b4663c1188eb747593427209edcf924 -size 41941 +oid sha256:45e274f5a693439a3b05254aa366c8de7eedfa4f04fd69b92bc9b22144b5ad79 +size 41895 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_2,NEXUS_5,1.0,en].png index e4c1b50917..d78db00ed9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e35b127fc263420421bb48153aa7fad1a65fca043bfd5756c92988ed3b356302 -size 57740 +oid sha256:c32f1ef0076a7f1388b9389850601a5087f641b0eb8f4bc62239b048e237dfca +size 57686 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_3,NEXUS_5,1.0,en].png index b9a3c9c104..2c8cb25ffe 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ea77618be64e5cc97ccd0b80e9524b8653d5539c5e8fa0db265e202dbfd7802 -size 26831 +oid sha256:c9b47f6c36ab9e2edb7cdef8b10fe188d9dddd51d47a14e095025e192d4c526a +size 26788 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_0,NEXUS_5,1.0,en].png index feb7987b41..7246823443 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:849fa392721e9b4e2232a704f45cc6163e6357651d1069fb1ab2345440aac55c -size 15696 +oid sha256:48d9b98073f29075b695d3bf3aa82de865cf56287900ab8d092fe3093fb41cc6 +size 15643 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_1,NEXUS_5,1.0,en].png index 77982cc44f..42b8221a43 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1c1f211e73d9de972ab992a2177af2f004fd3a3372f935cf1a88a91fa3fd55d -size 16281 +oid sha256:208ca078799b9abfc532e4e96b4b27e2982a31a137c0c8097ea2d46543eaed07 +size 16199 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_2,NEXUS_5,1.0,en].png index 7be35c9986..70e76acd29 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:63678a4962dba8131e64b6951dfe93ca84785b245a2540ad83f46ad18233d0db -size 22278 +oid sha256:d1b54ade12447d53d48d41189574d577a538a8e2db0f1106f4adb0fd3d37f51d +size 22229 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_0,NEXUS_5,1.0,en].png index 77fa9498f2..c75e0a7727 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9cebf1580d95f89cf875c9c2787798107e8c932eaa990c9ef1d423188faacdb -size 14723 +oid sha256:25ddab1c45a5ebf26728d9f4374ea12ed3db57a4a9f826f3a815ee5da0195c2f +size 14738 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_1,NEXUS_5,1.0,en].png index 44ac77db85..0abd63b28d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:03ede18be1caac331973d48fd06c08c2914effc5aa6a20780b5606c59a5f2b6d -size 15286 +oid sha256:8c811b7f8f94c5af8e41fa7ea9c71c42394d04e172c578725b26ea7a4382423d +size 15272 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_2,NEXUS_5,1.0,en].png index f74055c74b..f1d9248af8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7eeb470b7327e6a662c9057858114f343410fe49e48c0cbcaf809c0e0d2b1681 -size 18985 +oid sha256:ae4d255ca1769d8436b552081b08f98806eca91cd255518e1171314b4dfc8251 +size 18951 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_0,NEXUS_5,1.0,en].png index f676e5c07b..37cc1b0ae5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66257f3f4d7d8178574b8b4deab2c7142f6c29aeb99c83930d5799f049dfc505 -size 34289 +oid sha256:c777028e4141f13301c87d9b0dd67622181b5e24e52f48ccbafec9c35321a4b4 +size 34189 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_1,NEXUS_5,1.0,en].png index b1b1f187c6..93c5ff1a69 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3db584d82b7d55dc3bfa938ebad7be835fb6f06f7475961db060eb8cc9b98405 -size 46326 +oid sha256:7315273c9b9d030c1fa6938b9963ee9efb574265128268638aae22023989d856 +size 46223 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_2,NEXUS_5,1.0,en].png index 99e28a0f87..4b59040c96 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23856a19db311a1b829dd05965154033cc4a7b4b143b73fde0cf7544bdaad9e2 -size 44649 +oid sha256:df70bdde9bc0b4299f7754923867ec8187a2daa05d56087b5ec2019ae666645b +size 44613 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_3,NEXUS_5,1.0,en].png index 1f4478dff8..458100b3ac 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:619bc51f59ea9011a23ad43a261ae73c0c1e09b476a67e3bdca3d9ec3f1a692e -size 43706 +oid sha256:821b60569968f6416318a62235cd4839067ef48f0fd21c9d219548da9ea2a526 +size 43672 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_0,NEXUS_5,1.0,en].png index 37728629fd..b53a7c0023 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc6c5fe6048fe1bcd5ee4c3a00561a705b3011b17c92cd8594f3a93f5d317533 -size 32511 +oid sha256:21bc9b421f1e88690664d264562076609a4c63d68666b6ebe1673ab9202f10a1 +size 32514 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_1,NEXUS_5,1.0,en].png index fb9bdcada0..cccf7049a8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e03b605d6dfabab4add9502e4e58185442d21f00f36f6ccb9f34dc49c9c98e03 -size 42992 +oid sha256:5efecc0c8b77341752cabb60a988458af292adb4da04274d5fe43e0ebfc5dad7 +size 42986 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_2,NEXUS_5,1.0,en].png index db94222ed0..3fafc59ea8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c055595664b4188a8098cfc999d4172091f046878106f6c1ce858eca46fc9f3 -size 41562 +oid sha256:4457774feb41b613e9a4598541e252ccbbea7f9806c0a4ca8fbe71c3eff716a6 +size 41577 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_3,NEXUS_5,1.0,en].png index de6d3a8cb2..df72fb1233 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12aa860192cf02e19dfba9dd8060e96568b23766dc5ef5db30f05d7eb19c403b -size 38641 +oid sha256:9b7dbb4c98ddf1e89af7e1ca3c84bc381755da135346c08cebad1b75c4903172 +size 38613 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_0,NEXUS_5,1.0,en].png index bc2ae46f25..d7d3fffc1e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce5bf5f221e71ef09f2467e160297db6a538ecaec7f423451edb521b3d743725 -size 50588 +oid sha256:87a301e27fbb5b3084791a5df778d1fc1b5788f9ec88fdc580d4791f56d993d7 +size 50523 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_1,NEXUS_5,1.0,en].png index 0cfd15d94e..4888c2bdda 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:934e4749c80b61c3b4b1fcfb7573ba12186f31260198c3b16595b5b23ab17fc9 -size 48481 +oid sha256:044ee40991f7d5812761520bb6347bfbfc18d7447151200bad244d0c1a677ea3 +size 48396 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_2,NEXUS_5,1.0,en].png index 4e5f1e7f9e..f94baa080b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7872cba8e1bf900d81288748b0e55e945bb551525707f20fe0711931a483c22b -size 53234 +oid sha256:8f48a7154d43b6e3b79a2767d80035b03a819ad7bd475174e67f63d40e3faa83 +size 53124 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_3,NEXUS_5,1.0,en].png index 4e5f1e7f9e..f94baa080b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7872cba8e1bf900d81288748b0e55e945bb551525707f20fe0711931a483c22b -size 53234 +oid sha256:8f48a7154d43b6e3b79a2767d80035b03a819ad7bd475174e67f63d40e3faa83 +size 53124 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_4,NEXUS_5,1.0,en].png index a8ed14d18d..b29bca1351 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef611386d451c8849c3c8cdcd2344d8c19aa66092cbdd52bea58a9cf882ab22a -size 48550 +oid sha256:ed606bf9bcda44b7dbc9ee3e7a4f27e8d1c5bf12bcdffc746fea7ed29ebcd8fd +size 48520 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_0,NEXUS_5,1.0,en].png index 6e8461a284..62d3da7f86 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ba7c55493f8c25285889c9c8f4e12bb838dd7827697da967d03de723e4ef766 -size 48555 +oid sha256:9604e1d8d263f3254fcdce7c79bae85510f42cee1d13fa3deb2b823a8252ac8a +size 48568 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_1,NEXUS_5,1.0,en].png index c8314ad20c..348ce584fa 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f60f9eb53c57170e063b296d60308214d442bda3039d570e511860babc6fff38 -size 46302 +oid sha256:f97d4a5adc6327b134d5cb4b43e45d543e7fe912b001b44deb040e6400efa675 +size 46320 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_2,NEXUS_5,1.0,en].png index 3a647c4592..275a6f6735 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5f3cbe3e3158a6958e12a9848dda9070b70e14a27fb5872a8d173673b93aeea -size 50434 +oid sha256:1f4eee3cd8e4f6d2586a0ffdcf061756ef443e8bbdb3672e768ace100c9e787b +size 50410 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_3,NEXUS_5,1.0,en].png index 3a647c4592..275a6f6735 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5f3cbe3e3158a6958e12a9848dda9070b70e14a27fb5872a8d173673b93aeea -size 50434 +oid sha256:1f4eee3cd8e4f6d2586a0ffdcf061756ef443e8bbdb3672e768ace100c9e787b +size 50410 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_4,NEXUS_5,1.0,en].png index 50b7e8170b..01bc072140 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c31a548db85548409db4071c9ae34411d6c93de7f23b514034c0a58679fdaa20 -size 42588 +oid sha256:8aafe986067ba8e3c8c6df67f9229280fe5b78819e296d01885563711eb3645a +size 42556 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_0,NEXUS_5,1.0,en].png index 6f3d6c5ae5..4bf7dcfa2f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f7cc54d19a41c6920c51d799f17ae5d2cf4d0bbeaf58d32c83dae52c24ac1f8 -size 51883 +oid sha256:2b15b3765fdf410748cf16c04f3de34f35f6228791c4117494dbbfc59584e6bc +size 51812 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_1,NEXUS_5,1.0,en].png index 2bd5b57a2b..8373a0047c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c73f15a0a31e0e6665fc3e278f64dad957d7f717afa4213094c88edbfdb728e -size 50018 +oid sha256:8edbf48dbf23e6b8265f6b1f3b247d765c44a23642b042db67ceb25966347830 +size 49949 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_2,NEXUS_5,1.0,en].png index 4e5f1e7f9e..f94baa080b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7872cba8e1bf900d81288748b0e55e945bb551525707f20fe0711931a483c22b -size 53234 +oid sha256:8f48a7154d43b6e3b79a2767d80035b03a819ad7bd475174e67f63d40e3faa83 +size 53124 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_3,NEXUS_5,1.0,en].png index 4e5f1e7f9e..f94baa080b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7872cba8e1bf900d81288748b0e55e945bb551525707f20fe0711931a483c22b -size 53234 +oid sha256:8f48a7154d43b6e3b79a2767d80035b03a819ad7bd475174e67f63d40e3faa83 +size 53124 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_4,NEXUS_5,1.0,en].png index a8ed14d18d..b29bca1351 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef611386d451c8849c3c8cdcd2344d8c19aa66092cbdd52bea58a9cf882ab22a -size 48550 +oid sha256:ed606bf9bcda44b7dbc9ee3e7a4f27e8d1c5bf12bcdffc746fea7ed29ebcd8fd +size 48520 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_0,NEXUS_5,1.0,en].png index 1ac830b344..4d7658bba5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:757ed00bc9ff04e2a4142daf475acb3ba28d1441765c1a8b322fa9328bb62676 -size 49979 +oid sha256:31df36227486eec2337ca723784ad1a3a37fc70c9a21734f626d80588e6b9ae8 +size 49992 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_1,NEXUS_5,1.0,en].png index 67cb5d4067..715adcf685 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15be1d03c579a5427493087f0097f76d03d6d36564d01cbd6d7149576e93f102 -size 47986 +oid sha256:ed46873416a6f26f42980a8b166914ba75a5bffb446c076f5647b2c01497d8db +size 48000 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_2,NEXUS_5,1.0,en].png index 3a647c4592..275a6f6735 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5f3cbe3e3158a6958e12a9848dda9070b70e14a27fb5872a8d173673b93aeea -size 50434 +oid sha256:1f4eee3cd8e4f6d2586a0ffdcf061756ef443e8bbdb3672e768ace100c9e787b +size 50410 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_3,NEXUS_5,1.0,en].png index 3a647c4592..275a6f6735 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5f3cbe3e3158a6958e12a9848dda9070b70e14a27fb5872a8d173673b93aeea -size 50434 +oid sha256:1f4eee3cd8e4f6d2586a0ffdcf061756ef443e8bbdb3672e768ace100c9e787b +size 50410 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_4,NEXUS_5,1.0,en].png index 50b7e8170b..01bc072140 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c31a548db85548409db4071c9ae34411d6c93de7f23b514034c0a58679fdaa20 -size 42588 +oid sha256:8aafe986067ba8e3c8c6df67f9229280fe5b78819e296d01885563711eb3645a +size 42556 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Day-0_1_null_0,NEXUS_5,1.0,en].png index ad273f4619..49dbf2265c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Day-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8830c2822cdc08b72cf6b45d5b9becd29b1824c01002336f69dc4b8a432e28e2 -size 60504 +oid sha256:75f40e29b9e471c0920cbbb1d1300b4b8eb89fc8041470a5ed72081e4ea2f75b +size 60457 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Night-0_2_null_0,NEXUS_5,1.0,en].png index 944fb938d8..b957e40f5b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Night-0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d0cf3d6480619228bace599debade40ccf67ecdeb545ba1a3115700e58db55e0 -size 58730 +oid sha256:ed8a17b524b38e2d979f3464f132a87f5c7479d2a935077c6eceaa4e475324a5 +size 58712 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_0,NEXUS_5,1.0,en].png index ad10e0a111..25be545a89 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ddfdd4089c3ef38fc0ad5435296490ea1bf82a33c0aa23c4d0b5217c341bb1f -size 28389 +oid sha256:3e5c225258a6b257afcfc935971df619199a8985ad87203528d27a166a6fda2a +size 28334 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_2,NEXUS_5,1.0,en].png index de0e46a071..f239a76249 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8425428904c58fd9a22f4505c7dbb47798efcf2f2e5007fbf3f402222cb0cf53 -size 51381 +oid sha256:c4545536311f20615a2809a067af9eabf1a9398f39a9df819dd3c0a4c3de64bd +size 51321 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_3,NEXUS_5,1.0,en].png index 258a528e6c..4e7f01ae48 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5318f2a7564c319c58971daf76a60a696d0835476d06666a190849a7222c98d -size 52689 +oid sha256:26759996939952786a94b9d860e02c40154c0903a49afb8e7be05a20787f0fa4 +size 52615 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_4,NEXUS_5,1.0,en].png index f718f8a03a..22ffbb29ae 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9f571f7767f7d1c41db37113346eaeb995b81a87e8957d83751b7cd6ef27058 -size 31678 +oid sha256:3ce368edd4536137fb4545fb21892c51af43d7325f21b6e34e5ac1a72b818b34 +size 31575 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_5,NEXUS_5,1.0,en].png index 799ac802fa..7dec5258da 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:70d489b8ff343e27f95065f4f201b44607837dda177eb1f331a2def661e121c1 -size 21009 +oid sha256:d4bff39ac46298be6b968326b64c632aa73b13a51f1efff512d5c6894c414a1b +size 20951 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_6,NEXUS_5,1.0,en].png index fb2edac72f..7908dfeabd 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22817cd770e9eb1d438128804007a33cc038f8da17788f0848ec5db26211a179 -size 35569 +oid sha256:0843965afef3d142709f377ab408866cacfb016f652107ee84dc2fd39770ff17 +size 35490 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_0,NEXUS_5,1.0,en].png index c9c0ad3e0b..0795fad021 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5657f3d4778f66d9ba1c6138845317a336fc0d51342d26650c317fca9bf2a1c2 -size 26851 +oid sha256:8ef7b72a829b4add1f21b5f7fc06d6a260227b9c065c08ad770d70656b28f815 +size 26848 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_2,NEXUS_5,1.0,en].png index 1c24f49ca9..128acd813b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:77668eb95611c9b72913d286956b1b5e6bafe1cf6a1facfc00a5c4fd0ceab3d2 -size 49133 +oid sha256:9fdcab5fa8620cb0ba0cc0cb822fe4a4807198dbbcd7de7dd7a230d2fe3030e0 +size 49141 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_3,NEXUS_5,1.0,en].png index dd78bc04ea..c8329b273e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:121d1b8f9a6abf9c50a951b2db6a7e52602ec34876a55e38b009f1fdffcd725d -size 50525 +oid sha256:06b8e6bce1573d8e39a1bf5823945733152fde066a394a3e90f2bf8dc7929ea3 +size 50526 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_4,NEXUS_5,1.0,en].png index 62205e2c19..2db49fef25 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:322ff3eb42407c0e188521ca32361819b6740d323a3ee4411441853abbe44b8a -size 30024 +oid sha256:10c16c9a29f898a6a33f8bfa10df24d2da9adb93eeb764e2b744c6efe531890e +size 30007 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_5,NEXUS_5,1.0,en].png index 5e3429e748..29ac2f3227 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:48a8536aa38e0bb678d87b835ace8c5811f97e2311a771ed37b04aabd142f69b -size 19981 +oid sha256:f4786a280d694ead76992dfa75f09bd2e20c292376509f86462a359d19be3e50 +size 19984 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_6,NEXUS_5,1.0,en].png index 7fb3f28454..b2be2a2397 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:83961b489de278edf3f2646f228e059e71f381363ffb751f44244a7d12604cb9 -size 33443 +oid sha256:59cabf858188557db3f2660f71cafc9b041977c656693dc5bc009da71a5c7648 +size 33457 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_OnBoardingPage_null_OnBoardingPage-Day_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_OnBoardingPage_null_OnBoardingPage-Day_0_null,NEXUS_5,1.0,en].png index b73b74bbcd..842430a4a0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_OnBoardingPage_null_OnBoardingPage-Day_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_OnBoardingPage_null_OnBoardingPage-Day_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:579b575f467e2d4b7adaab0c5ca7f3cc62e7a5ae18d8f67406b26df48ac0887e -size 233216 +oid sha256:ab46218fff44d542277c3a498ee7b66ecbe2d233fd302f7e58ca322363bac563 +size 233128 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_OnBoardingPage_null_OnBoardingPage-Night_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_OnBoardingPage_null_OnBoardingPage-Night_1_null,NEXUS_5,1.0,en].png index a030afbbd0..c499dd3801 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_OnBoardingPage_null_OnBoardingPage-Night_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_OnBoardingPage_null_OnBoardingPage-Night_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9771fe25cb79b032ce28bee8edf5bafe0decb3effcaa153eb7f5c7d83df01d23 -size 369766 +oid sha256:28aacc92d6ccad7586b3e5d76c2cd70cfbcc2101551ee75a213fbb269a29dbd2 +size 369703 From 9440fce23a68a037b2f2d1a949facd1546c6223f Mon Sep 17 00:00:00 2001 From: Marco Antonio Alvarez Date: Thu, 11 Jan 2024 19:23:36 +0100 Subject: [PATCH 004/119] empty commit From af4c9b3e6d004e524b068bdca24cbc734d3ccbec Mon Sep 17 00:00:00 2001 From: Marco Antonio Alvarez Date: Thu, 11 Jan 2024 19:23:54 +0100 Subject: [PATCH 005/119] empty commit From 8e9e9d8296bd9cbe59d9be856e64d807333cf0dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Wed, 31 Jan 2024 10:35:09 +0100 Subject: [PATCH 006/119] Remove session preferences on logout Observe the sessions in `DefaultSessionPreferencesFactory` and remove the session preferences from both the cache and disk when a session is finished. --- .../store/DefaultSessionPreferencesStoreFactory.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStoreFactory.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStoreFactory.kt index 84dcbac289..745dd42615 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStoreFactory.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStoreFactory.kt @@ -21,6 +21,8 @@ import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.SingleIn import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.sessionstorage.api.observer.SessionListener +import io.element.android.libraries.sessionstorage.api.observer.SessionObserver import kotlinx.coroutines.CoroutineScope import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject @@ -28,9 +30,20 @@ import javax.inject.Inject @SingleIn(AppScope::class) class DefaultSessionPreferencesStoreFactory @Inject constructor( @ApplicationContext private val context: Context, + sessionObserver: SessionObserver, ) { private val cache = ConcurrentHashMap() + init { + sessionObserver.addListener(object : SessionListener { + override suspend fun onSessionCreated(userId: String) = Unit + override suspend fun onSessionDeleted(userId: String) { + val sessionPreferences = cache.remove(SessionId(userId)) + sessionPreferences?.clear() + } + }) + } + fun get(sessionId: SessionId, sessionCoroutineScope: CoroutineScope): DefaultSessionPreferencesStore = cache.getOrPut(sessionId) { DefaultSessionPreferencesStore(context, sessionId, sessionCoroutineScope) } From 379da141ee7342927fbb204bf40960a123bfd571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Wed, 31 Jan 2024 10:38:27 +0100 Subject: [PATCH 007/119] Add changelog --- changelog.d/+remove-session-preferences-on-logout.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/+remove-session-preferences-on-logout.misc diff --git a/changelog.d/+remove-session-preferences-on-logout.misc b/changelog.d/+remove-session-preferences-on-logout.misc new file mode 100644 index 0000000000..6d6146d75f --- /dev/null +++ b/changelog.d/+remove-session-preferences-on-logout.misc @@ -0,0 +1 @@ +Remove session preferences on user log out. From e25b54eb1248de8acea781b367280bf808688022 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 31 Jan 2024 10:55:28 +0000 Subject: [PATCH 008/119] Update plugin detekt to v1.23.5 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ade161ca38..afee15da81 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -212,7 +212,7 @@ kotlin_serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } anvil = { id = "com.squareup.anvil", version.ref = "anvil" } -detekt = "io.gitlab.arturbosch.detekt:1.23.4" +detekt = "io.gitlab.arturbosch.detekt:1.23.5" ktlint = "org.jlleitschuh.gradle.ktlint:12.1.0" dependencygraph = "com.savvasdalkitsis.module-dependency-graph:0.12" dependencycheck = "org.owasp.dependencycheck:9.0.9" From 6a96bcb07a57688e83a30a1013590ea105009cb1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:34:52 +0000 Subject: [PATCH 009/119] Update gradle/gradle-build-action action to v3 --- .github/workflows/build.yml | 2 +- .github/workflows/nightlyReports.yml | 2 +- .github/workflows/quality.yml | 2 +- .github/workflows/recordScreenshots.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/sonar.yml | 2 +- .github/workflows/tests.yml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 09ba964a0c..9412c319d3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,7 +38,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.12.0 + uses: gradle/gradle-build-action@v3.0.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Assemble debug APK diff --git a/.github/workflows/nightlyReports.yml b/.github/workflows/nightlyReports.yml index 9b5a01a14e..e5f47543b0 100644 --- a/.github/workflows/nightlyReports.yml +++ b/.github/workflows/nightlyReports.yml @@ -62,7 +62,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.12.0 + uses: gradle/gradle-build-action@v3.0.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Dependency analysis diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index e1eb132ee7..a9b5acc8d1 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -40,7 +40,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.12.0 + uses: gradle/gradle-build-action@v3.0.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run code quality check suite diff --git a/.github/workflows/recordScreenshots.yml b/.github/workflows/recordScreenshots.yml index 5024bea3af..e5775477ae 100644 --- a/.github/workflows/recordScreenshots.yml +++ b/.github/workflows/recordScreenshots.yml @@ -39,7 +39,7 @@ jobs: java-version: '17' # Add gradle cache, this should speed up the process - name: Configure gradle - uses: gradle/gradle-build-action@v2.12.0 + uses: gradle/gradle-build-action@v3.0.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Record screenshots diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bf672d8315..d8e773bdf0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.12.0 + uses: gradle/gradle-build-action@v3.0.0 - name: Create app bundle env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index 130eb573d4..150551a9c3 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -32,7 +32,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.12.0 + uses: gradle/gradle-build-action@v3.0.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: 🔊 Publish results to Sonar diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3cbda49612..86c235dcf2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,7 +44,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.12.0 + uses: gradle/gradle-build-action@v3.0.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} From f7abfb2f5ae8459410a75623665dda487ca76b05 Mon Sep 17 00:00:00 2001 From: jmartinesp <480955+jmartinesp@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:38:42 +0000 Subject: [PATCH 010/119] Sync Strings from Localazy --- .../impl/src/main/res/values-cs/translations.xml | 11 +++++++++++ .../impl/src/main/res/values-fr/translations.xml | 9 +++++++++ .../impl/src/main/res/values-ru/translations.xml | 11 +++++++++++ .../messages/impl/src/main/res/values/localazy.xml | 9 +++++++++ .../impl/src/main/res/values-hu/translations.xml | 2 +- .../impl/src/main/res/values-ru/translations.xml | 2 ++ .../impl/src/main/res/values-hu/translations.xml | 2 +- .../impl/src/main/res/values-ru/translations.xml | 5 +++++ .../roomlist/impl/src/main/res/values/localazy.xml | 2 ++ .../src/main/res/values-hu/translations.xml | 6 +++--- 10 files changed, 54 insertions(+), 5 deletions(-) diff --git a/features/messages/impl/src/main/res/values-cs/translations.xml b/features/messages/impl/src/main/res/values-cs/translations.xml index 62a5ceb29b..1cb2a3f57f 100644 --- a/features/messages/impl/src/main/res/values-cs/translations.xml +++ b/features/messages/impl/src/main/res/values-cs/translations.xml @@ -13,6 +13,16 @@ "%1$d změny místnosti" "%1$d změn místnosti" + + "%1$s, %2$s a %3$d další" + "%1$s, %2$s a %3$d další" + "%1$s, %2$s a %3$d dalších" + + + "%1$s píše" + "%1$s píší" + "%1$s píše" + "Tato zpráva bude nahlášena správci vašeho domovského serveru. Nebude si moci přečíst žádné šifrované zprávy." "Důvod nahlášení tohoto obsahu" "Toto je začátek %1$s." @@ -54,6 +64,7 @@ "Vaši zprávu se nepodařilo odeslat" "Přidat emoji" "Zobrazit méně" + "%1$s a %2$s" "Držte pro nahrávání" "Všichni" "Zablokovat uživatele" diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml index 1358ce802b..51ea5ab07f 100644 --- a/features/messages/impl/src/main/res/values-fr/translations.xml +++ b/features/messages/impl/src/main/res/values-fr/translations.xml @@ -12,6 +12,14 @@ "%1$d changement dans le salon" "%1$d changements dans le salon" + + "%1$s, %2$s et %3$d autre" + "%1$s, %2$s et %3$d autres" + + + "%1$s écrit" + "%1$s écrivent" + "Ce message sera signalé à l’administrateur de votre serveur d’accueil. Il ne pourra lire aucun message chiffré." "Raison du signalement de ce contenu" "Ceci est le début de %1$s." @@ -53,6 +61,7 @@ "Votre message n’a pas pu être envoyé" "Ajouter un émoji" "Afficher moins" + "%1$s et %2$s" "Maintenir pour enregistrer" "Tout le monde" "Bloquer l’utilisateur" diff --git a/features/messages/impl/src/main/res/values-ru/translations.xml b/features/messages/impl/src/main/res/values-ru/translations.xml index c3175d9a53..a537ef01ac 100644 --- a/features/messages/impl/src/main/res/values-ru/translations.xml +++ b/features/messages/impl/src/main/res/values-ru/translations.xml @@ -13,6 +13,16 @@ "%1$d изменения в комнате" "%1$d изменений в комнате" + + "%1$s, %2$s и %3$d" + "%1$s, %2$s и другие %3$d" + "%1$s, %2$s и другие %3$d" + + + "%1$s набирает сообщение" + "%1$s набирают сообщения" + "%1$s набирают сообщения" + "Это сообщение будет передано администратору вашего домашнего сервера. Они не смогут прочитать зашифрованные сообщения." "Причина, по которой вы пожаловались на этот контент" "Это начало %1$s." @@ -54,6 +64,7 @@ "Не удалось отправить ваше сообщение" "Добавить эмодзи" "Показать меньше" + "%1$s и %2$s" "Удерживайте для записи" "Для всех" "Заблокировать пользователя" diff --git a/features/messages/impl/src/main/res/values/localazy.xml b/features/messages/impl/src/main/res/values/localazy.xml index d9c5fce030..3d781ab8cb 100644 --- a/features/messages/impl/src/main/res/values/localazy.xml +++ b/features/messages/impl/src/main/res/values/localazy.xml @@ -12,6 +12,14 @@ "%1$d room change" "%1$d room changes" + + "%1$s, %2$s and %3$d other" + "%1$s, %2$s and %3$d others" + + + "%1$s is typing" + "%1$s are typing" + "This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages." "Reason for reporting this content" "This is the beginning of %1$s." @@ -53,6 +61,7 @@ "Your message failed to send" "Add emoji" "Show less" + "%1$s and %2$s" "Hold to record" "Everyone" "Block user" diff --git a/features/preferences/impl/src/main/res/values-hu/translations.xml b/features/preferences/impl/src/main/res/values-hu/translations.xml index 5abf6a83f8..598c61da3d 100644 --- a/features/preferences/impl/src/main/res/values-hu/translations.xml +++ b/features/preferences/impl/src/main/res/values-hu/translations.xml @@ -6,7 +6,7 @@ "Fejlesztői mód" "Engedélyezd, hogy elérd a fejlesztőknek szánt funkciókat." "A formázott szöveges szerkesztő letiltása, hogy kézzel írhass Markdownt." - "Engedélyezze a beállítást az üzenet forrásának megjelenítéséhez az idővonalon." + "Engedélyezd a beállítást az üzenet forrásának megjelenítéséhez az idővonalon." "Megjelenítendő név" "Saját megjelenítendő név" "Ismeretlen hiba történt, és az információ módosítása nem sikerült." diff --git a/features/preferences/impl/src/main/res/values-ru/translations.xml b/features/preferences/impl/src/main/res/values-ru/translations.xml index 979644cec9..8c79b71df5 100644 --- a/features/preferences/impl/src/main/res/values-ru/translations.xml +++ b/features/preferences/impl/src/main/res/values-ru/translations.xml @@ -6,6 +6,8 @@ "Режим разработчика" "Предоставьте разработчикам доступ к функциям и функциональным возможностям." "Отключить редактор форматированного текста и включить Markdown." + "Уведомления о прочтении" + "Если этот параметр выключен, ваш статус о прочтении не будет отображаться. Вы по-прежнему будете видеть статус о прочтении от других пользователей." "Включить опцию просмотра источника сообщения в ленте." "Отображаемое имя" "Ваше отображаемое имя" diff --git a/features/roomlist/impl/src/main/res/values-hu/translations.xml b/features/roomlist/impl/src/main/res/values-hu/translations.xml index cd5dced50a..21c3e645a3 100644 --- a/features/roomlist/impl/src/main/res/values-hu/translations.xml +++ b/features/roomlist/impl/src/main/res/values-hu/translations.xml @@ -1,6 +1,6 @@ - "A csevegés biztonsági mentés nincs szinkronban. Meg kell erősítenie a helyreállítási kulcsát, hogy továbbra is hozzáférjen a csevegés biztonsági mentéséhez." + "A csevegés biztonsági mentése nincs szinkronban. Meg kell erősítened a helyreállítási kulcsát, hogy továbbra is hozzáférj a csevegés biztonsági mentéséhez." "Helyreállítási kulcs megerősítése" "Új beszélgetés vagy szoba létrehozása" "Kezdje azzal, hogy üzenetet küld valakinek." diff --git a/features/roomlist/impl/src/main/res/values-ru/translations.xml b/features/roomlist/impl/src/main/res/values-ru/translations.xml index 261feda31d..d752ef073a 100644 --- a/features/roomlist/impl/src/main/res/values-ru/translations.xml +++ b/features/roomlist/impl/src/main/res/values-ru/translations.xml @@ -5,6 +5,11 @@ "Создайте новую беседу или комнату" "Начните переписку с отправки сообщения." "Пока нет доступных чатов." + "Избранное" + "Низкий приоритет" + "Люди" + "Комнаты" + "Непрочитанные" "Все чаты" "Похоже, вы используете новое устройство. Чтобы получить доступ к зашифрованным сообщениям пройдите верификацию с другим устройством." "Подтвердите, что это вы" diff --git a/features/roomlist/impl/src/main/res/values/localazy.xml b/features/roomlist/impl/src/main/res/values/localazy.xml index 6f7332a091..660017d240 100644 --- a/features/roomlist/impl/src/main/res/values/localazy.xml +++ b/features/roomlist/impl/src/main/res/values/localazy.xml @@ -11,6 +11,8 @@ "Rooms" "Unreads" "All Chats" + "Mark as read" + "Mark as unread" "Looks like you’re using a new device. Verify with another device to access your encrypted messages." "Verify it’s you" diff --git a/libraries/ui-strings/src/main/res/values-hu/translations.xml b/libraries/ui-strings/src/main/res/values-hu/translations.xml index 102320bf83..18c981005f 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -46,7 +46,7 @@ "Szavazás szerkesztése" "Engedélyezés" "Szavazás lezárása" - "Adja meg a PIN-kódot" + "Add meg a PIN-kódot" "Elfelejtetted a jelszavadat?" "Tovább" "Meghívás" @@ -200,13 +200,13 @@ "Megerősítés" "Figyelmeztetés" "Nem sikerült létrehozni az állandó hivatkozást" - "Az %1$s nem tudta betölteni a térképet. Próbálja meg újra később." + "A(z) %1$s nem tudta betölteni a térképet. Próbáld meg újra később." "Nem sikerült betölteni az üzeneteket" "A(z) %1$s nem tudta elérni a tartózkodási helyét. Próbáld meg újra később." "Nem sikerült feltölteni a hangüzenetét." "Az %1$snek nincs engedélye, hogy hozzáférjen a tartózkodási helyedhez. Ezt a beállításokban engedélyezheted." "Az %1$snek nincs engedélye, hogy hozzáférjen a tartózkodási helyéhez. Engedélyezze alább az elérését." - "Az %1$snek nincs engedélye, hogy hozzáférjen a mikrofonjához. Engedélyezze, hogy tudjon hangüzenetet felvenni." + "Az %1$snek nincs engedélye, hogy hozzáférjen a mikrofonhoz. Engedélyezd, hogy tudjon hangüzenetet felvenni." "Néhány üzenet nem került elküldésre" "Elnézést, hiba történt" "🔐️ Csatlakozz hozzám itt: %1$s" From f582128250895b87b0980b34ab0794d59b828cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 1 Feb 2024 09:06:37 +0100 Subject: [PATCH 011/119] Remove Compose Foundation version pinning workaround --- changelog.d/+remove-compose-foundation-workaround.misc | 1 + gradle/libs.versions.toml | 1 - .../src/main/kotlin/extension/DependencyHandleScope.kt | 10 ---------- 3 files changed, 1 insertion(+), 11 deletions(-) create mode 100644 changelog.d/+remove-compose-foundation-workaround.misc diff --git a/changelog.d/+remove-compose-foundation-workaround.misc b/changelog.d/+remove-compose-foundation-workaround.misc new file mode 100644 index 0000000000..480a288426 --- /dev/null +++ b/changelog.d/+remove-compose-foundation-workaround.misc @@ -0,0 +1 @@ +Remove Compose Foundation version pinning workaround. This was done to avoid a bug introduced in the default foundation version used by the material3 library, but that has been already been fixed. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 591113eb1b..68c2119f55 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -93,7 +93,6 @@ androidx_preference = "androidx.preference:preference:1.2.1" androidx_webkit = "androidx.webkit:webkit:1.10.0" androidx_compose_bom = { module = "androidx.compose:compose-bom", version.ref = "compose_bom" } -# When Material3 updates its transitive Compose dependencies, take a look at the constraints in `DependencyHandleScope.kt`. We may be able to remove the workaround there. androidx_compose_material3 = "androidx.compose.material3:material3:1.2.0-rc01" androidx_compose_ui = { module = "androidx.compose.ui:ui" } androidx_compose_ui_tooling = { module = "androidx.compose.ui:ui-tooling" } diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt index 1ba4b60b4d..cc254511bd 100644 --- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt +++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt @@ -56,16 +56,6 @@ fun DependencyHandlerScope.composeDependencies(libs: LibrariesForLibs) { implementation(libs.androidx.compose.ui) implementation(libs.androidx.compose.material) implementation(libs.androidx.compose.material3) - - // Remove these constraints once `material3` updates its internal dependencies - constraints { - implementation("androidx.compose.foundation:foundation:1.6.0-beta02") { - because("The transitive version inside `material3` (1.6.0-beta01) causes a scrolling issue. " + - "See https://android.googlesource.com/platform/frameworks/support/+/2d15876146ccf201f7e15cacc78bfca762060624" - ) - } - } - implementation(libs.androidx.compose.material.icons) implementation(libs.androidx.compose.ui.tooling.preview) implementation(libs.androidx.activity.compose) From 5c59ecbaa670db6ba86b1de17f57107482ac6ec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 1 Feb 2024 11:49:17 +0100 Subject: [PATCH 012/119] Use `MatrixRoom.isEncrypted` value to enable encryption in room calls --- .../android/features/call/utils/DefaultCallWidgetProvider.kt | 2 +- .../libraries/matrix/api/widget/CallWidgetSettingsProvider.kt | 3 ++- .../matrix/impl/widget/DefaultCallWidgetSettingsProvider.kt | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/features/call/src/main/kotlin/io/element/android/features/call/utils/DefaultCallWidgetProvider.kt b/features/call/src/main/kotlin/io/element/android/features/call/utils/DefaultCallWidgetProvider.kt index cc6e2a299f..7fb6d3cb48 100644 --- a/features/call/src/main/kotlin/io/element/android/features/call/utils/DefaultCallWidgetProvider.kt +++ b/features/call/src/main/kotlin/io/element/android/features/call/utils/DefaultCallWidgetProvider.kt @@ -43,7 +43,7 @@ class DefaultCallWidgetProvider @Inject constructor( ): Result> = runCatching { val room = matrixClientsProvider.getOrRestore(sessionId).getOrThrow().getRoom(roomId) ?: error("Room not found") val baseUrl = appPreferencesStore.getCustomElementCallBaseUrlFlow().firstOrNull() ?: ElementCallConfig.DEFAULT_BASE_URL - val widgetSettings = callWidgetSettingsProvider.provide(baseUrl) + val widgetSettings = callWidgetSettingsProvider.provide(baseUrl, encrypted = room.isEncrypted) val callUrl = room.generateWidgetWebViewUrl(widgetSettings, clientId, languageTag, theme).getOrThrow() room.getWidgetDriver(widgetSettings).getOrThrow() to callUrl } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/CallWidgetSettingsProvider.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/CallWidgetSettingsProvider.kt index f0a22d0128..9e5d8a41ad 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/CallWidgetSettingsProvider.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/CallWidgetSettingsProvider.kt @@ -21,6 +21,7 @@ import java.util.UUID interface CallWidgetSettingsProvider { fun provide( baseUrl: String, - widgetId: String = UUID.randomUUID().toString() + widgetId: String = UUID.randomUUID().toString(), + encrypted: Boolean, ): MatrixWidgetSettings } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/widget/DefaultCallWidgetSettingsProvider.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/widget/DefaultCallWidgetSettingsProvider.kt index a337adff17..44c73ae9d3 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/widget/DefaultCallWidgetSettingsProvider.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/widget/DefaultCallWidgetSettingsProvider.kt @@ -27,7 +27,7 @@ import javax.inject.Inject @ContributesBinding(AppScope::class) class DefaultCallWidgetSettingsProvider @Inject constructor() : CallWidgetSettingsProvider { - override fun provide(baseUrl: String, widgetId: String): MatrixWidgetSettings { + override fun provide(baseUrl: String, widgetId: String, encrypted: Boolean): MatrixWidgetSettings { val options = VirtualElementCallWidgetOptions( elementCallUrl = baseUrl, widgetId = widgetId, @@ -40,7 +40,7 @@ class DefaultCallWidgetSettingsProvider @Inject constructor() : CallWidgetSettin confineToRoom = true, font = null, analyticsId = null, - encryption = EncryptionSystem.PerParticipantKeys, + encryption = if (encrypted) EncryptionSystem.PerParticipantKeys else EncryptionSystem.Unencrypted, ) val rustWidgetSettings = newVirtualElementCallWidget(options) return MatrixWidgetSettings.fromRustWidgetSettings(rustWidgetSettings) From f93c962509e3ff935e3d39bfeba396a41d5e1c4d Mon Sep 17 00:00:00 2001 From: SpiritCroc Date: Thu, 1 Feb 2024 11:54:12 +0100 Subject: [PATCH 013/119] Do not crash on UP unregistered --- changelog.d/2304.bugfix | 1 + .../unifiedpush/VectorUnifiedPushMessagingReceiver.kt | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 changelog.d/2304.bugfix diff --git a/changelog.d/2304.bugfix b/changelog.d/2304.bugfix new file mode 100644 index 0000000000..5f0c485ddd --- /dev/null +++ b/changelog.d/2304.bugfix @@ -0,0 +1 @@ +Fix crash after unregistering UnifiedPush distributor diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt index 05400e87f2..e28d67ecf7 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt @@ -105,7 +105,6 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() { */ override fun onUnregistered(context: Context, instance: String) { Timber.tag(loggerTag.value).d("Unifiedpush: Unregistered") - TODO() /* val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME pushDataStore.setFdroidSyncBackgroundMode(mode) From 90e640e738bb2b251781147a949446065911df16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 1 Feb 2024 12:19:25 +0100 Subject: [PATCH 014/119] Fix test fixtures --- .../matrix/test/widget/FakeCallWidgetSettingsProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/widget/FakeCallWidgetSettingsProvider.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/widget/FakeCallWidgetSettingsProvider.kt index d2be886ea6..ca198129f6 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/widget/FakeCallWidgetSettingsProvider.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/widget/FakeCallWidgetSettingsProvider.kt @@ -24,7 +24,7 @@ class FakeCallWidgetSettingsProvider( ) : CallWidgetSettingsProvider { val providedBaseUrls = mutableListOf() - override fun provide(baseUrl: String, widgetId: String): MatrixWidgetSettings { + override fun provide(baseUrl: String, widgetId: String, encrypted: Boolean): MatrixWidgetSettings { providedBaseUrls += baseUrl return provideFn(baseUrl, widgetId) } From 9f3724129ae6aeaf90cbf35a442cec81ac0f5bea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 1 Feb 2024 13:20:58 +0100 Subject: [PATCH 015/119] Add changelog --- changelog.d/2333.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2333.feature diff --git a/changelog.d/2333.feature b/changelog.d/2333.feature new file mode 100644 index 0000000000..305e47c5d3 --- /dev/null +++ b/changelog.d/2333.feature @@ -0,0 +1 @@ +Allow joinin unencrypted video calls in non encrypted rooms. From eba1571ad043e1d6bc9d96ef596a458de2880266 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 1 Feb 2024 14:53:28 +0100 Subject: [PATCH 016/119] Migrate form `gradle/gradle-build-action` to `gradle/actions/setup-gradle` https://github.com/gradle/gradle-build-action --- .github/workflows/build.yml | 2 +- .github/workflows/nightlyReports.yml | 2 +- .github/workflows/quality.yml | 2 +- .github/workflows/recordScreenshots.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/sonar.yml | 2 +- .github/workflows/tests.yml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9412c319d3..dba9677ae3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,7 +38,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v3.0.0 + uses: gradle/actions/setup-gradle@v3 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Assemble debug APK diff --git a/.github/workflows/nightlyReports.yml b/.github/workflows/nightlyReports.yml index e5f47543b0..8b429a4d13 100644 --- a/.github/workflows/nightlyReports.yml +++ b/.github/workflows/nightlyReports.yml @@ -62,7 +62,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v3.0.0 + uses: gradle/actions/setup-gradle@v3 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Dependency analysis diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index a9b5acc8d1..73bee00499 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -40,7 +40,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v3.0.0 + uses: gradle/actions/setup-gradle@v3 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run code quality check suite diff --git a/.github/workflows/recordScreenshots.yml b/.github/workflows/recordScreenshots.yml index e5775477ae..a9293f006e 100644 --- a/.github/workflows/recordScreenshots.yml +++ b/.github/workflows/recordScreenshots.yml @@ -39,7 +39,7 @@ jobs: java-version: '17' # Add gradle cache, this should speed up the process - name: Configure gradle - uses: gradle/gradle-build-action@v3.0.0 + uses: gradle/actions/setup-gradle@v3 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Record screenshots diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d8e773bdf0..274214a98b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v3.0.0 + uses: gradle/actions/setup-gradle@v3 - name: Create app bundle env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index 150551a9c3..ec1fa5eb8f 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -32,7 +32,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v3.0.0 + uses: gradle/actions/setup-gradle@v3 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: 🔊 Publish results to Sonar diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 86c235dcf2..089ba09508 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,7 +44,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v3.0.0 + uses: gradle/actions/setup-gradle@v3 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} From a44930adedcb1f773c891f22224e316bbfb81d21 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 1 Feb 2024 15:22:42 +0100 Subject: [PATCH 017/119] Update changelog.d/2333.feature Co-authored-by: Benoit Marty --- changelog.d/2333.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/2333.feature b/changelog.d/2333.feature index 305e47c5d3..5bde0d6734 100644 --- a/changelog.d/2333.feature +++ b/changelog.d/2333.feature @@ -1 +1 @@ -Allow joinin unencrypted video calls in non encrypted rooms. +Allow joining unencrypted video calls in non encrypted rooms. From 5202f732ccaf17a8a6745f3bc1ef7258dc7fdc66 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 1 Feb 2024 15:26:58 +0100 Subject: [PATCH 018/119] Try fixing contributor PR notice (#2335) * Try fixing contributor PR notice --- .github/workflows/fork-pr-notice.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/fork-pr-notice.yml b/.github/workflows/fork-pr-notice.yml index 42b1e54c3f..b11a81bfb1 100644 --- a/.github/workflows/fork-pr-notice.yml +++ b/.github/workflows/fork-pr-notice.yml @@ -11,7 +11,8 @@ jobs: welcome: runs-on: ubuntu-latest name: Welcome comment - if: github.event.pull_request.fork != null + # Only display it if base repo (upstream) is different from HEAD repo (possibly a fork) + if: github.event.pull_request.base.repo.full_name != github.event.pull_request.head.repo.full_name steps: - name: Add auto-generated commit warning uses: actions/github-script@v7 From 95d9b046169df3aeae587b1f920545fc3f5d85e1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 15:27:27 +0100 Subject: [PATCH 019/119] Update codecov/codecov-action action to v4 (#2331) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update codecov/codecov-action action to v4 * Add missing codecov token --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jorge Martín --- .github/workflows/tests.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3cbda49612..774a7b1e92 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -81,6 +81,8 @@ jobs: # https://github.com/codecov/codecov-action - name: ☂️ Upload coverage reports to codecov if: always() - uses: codecov/codecov-action@v3 - # with: - # files: build/reports/kover/xml/report.xml + uses: codecov/codecov-action@v4 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} +# with: +# files: build/reports/kover/xml/report.xml From 22cd74a170ef28b6ddcb06eb5e5cd18ce8bb615a Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 1 Feb 2024 15:27:49 +0100 Subject: [PATCH 020/119] Update changelog.d/+remove-compose-foundation-workaround.misc Co-authored-by: Benoit Marty --- changelog.d/+remove-compose-foundation-workaround.misc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/+remove-compose-foundation-workaround.misc b/changelog.d/+remove-compose-foundation-workaround.misc index 480a288426..91bb19be2e 100644 --- a/changelog.d/+remove-compose-foundation-workaround.misc +++ b/changelog.d/+remove-compose-foundation-workaround.misc @@ -1 +1 @@ -Remove Compose Foundation version pinning workaround. This was done to avoid a bug introduced in the default foundation version used by the material3 library, but that has been already been fixed. +Remove Compose Foundation version pinning workaround. This was done to avoid a bug introduced in the default foundation version used by the material3 library, but that has already been fixed. From f21c9a05e04fab627ddab8393a1c6289a9b98130 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 14:35:50 +0000 Subject: [PATCH 021/119] Update dependency com.posthog:posthog-android to v3.1.6 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 89513e4dd9..b6ea59157d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -172,7 +172,7 @@ opusencoder = "io.element.android:opusencoder:1.1.0" kotlinpoet = "com.squareup:kotlinpoet:1.16.0" # Analytics -posthog = "com.posthog:posthog-android:3.1.5" +posthog = "com.posthog:posthog-android:3.1.6" sentry = "io.sentry:sentry-android:7.3.0" matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:aa14cbcdf81af2746d20a71779ec751f971e1d7f" From 23b08093992cc0d34830c42102d6b0ef74dff53e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 14:35:57 +0000 Subject: [PATCH 022/119] Update plugin sonarqube to v4.4.1.3373 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 89513e4dd9..8a903fe54c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -224,4 +224,4 @@ knit = { id = "org.jetbrains.kotlinx.knit", version = "0.5.0" } # Version '4.3.1.3277' introduced some regressions in CI time (more than 2x slower), so make sure # this is no longer the case before upgrading. -sonarqube = "org.sonarqube:4.2.1.3168" +sonarqube = "org.sonarqube:4.4.1.3373" From c392477f4905711ec3c893b75170c9a985e748b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 1 Feb 2024 16:09:14 +0100 Subject: [PATCH 023/119] Remove sonarqube regression warning message --- gradle/libs.versions.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8a903fe54c..eb10ea0b09 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -221,7 +221,4 @@ kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } firebaseAppDistribution = { id = "com.google.firebase.appdistribution", version.ref = "firebaseAppDistribution" } knit = { id = "org.jetbrains.kotlinx.knit", version = "0.5.0" } - -# Version '4.3.1.3277' introduced some regressions in CI time (more than 2x slower), so make sure -# this is no longer the case before upgrading. sonarqube = "org.sonarqube:4.4.1.3373" From 857b47d69ec7c3ca5227e61078620d8aef9f9dc1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 15:14:09 +0000 Subject: [PATCH 024/119] Update wysiwyg to v2.27.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 89513e4dd9..ef7aad634b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -38,7 +38,7 @@ serialization_json = "1.6.2" showkase = "1.0.2" appyx = "1.4.0" sqldelight = "2.0.1" -wysiwyg = "2.25.0" +wysiwyg = "2.27.0" # DI dagger = "2.50" From 8351b89da85c917b4f0cd5cd5826a7f3d4a76e6a Mon Sep 17 00:00:00 2001 From: Marco Antonio Alvarez Date: Thu, 1 Feb 2024 18:09:35 +0100 Subject: [PATCH 025/119] fix spacing between continue and report problem Signed-off-by: Marco Antonio Alvarez --- .../element/android/features/onboarding/impl/OnBoardingView.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt index c4b4e2d6b4..50dce445ac 100644 --- a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt +++ b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt @@ -190,11 +190,10 @@ private fun OnBoardingButtons( .fillMaxWidth() ) } - Spacer(modifier = Modifier.height(16.dp)) // Add a report problem text button. Use a Text since we need a special theme here. Text( modifier = Modifier - .padding(8.dp) + .padding(16.dp) .clickable(onClick = onReportProblem), text = stringResource(id = CommonStrings.common_report_a_problem), style = ElementTheme.typography.fontBodySmRegular, From e99d28bbc1705076fbbb92cc5be9b9a148229f3b Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 1 Feb 2024 17:21:58 +0000 Subject: [PATCH 026/119] Update screenshots --- ...n_null_OnBoardingScreen-Day-0_1_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...n_null_OnBoardingScreen-Day-0_1_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...n_null_OnBoardingScreen-Day-0_1_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...n_null_OnBoardingScreen-Day-0_1_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...n_null_OnBoardingScreen-Day-0_1_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...null_OnBoardingScreen-Night-0_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...null_OnBoardingScreen-Night-0_2_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...null_OnBoardingScreen-Night-0_2_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...null_OnBoardingScreen-Night-0_2_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...null_OnBoardingScreen-Night-0_2_null_4,NEXUS_5,1.0,en].png | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_0,NEXUS_5,1.0,en].png index 1618c3e638..10061b344b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4595089f2c885b73cdbd79d626c3eab79da657fef1a2ce2390e761e2469a4e0b -size 313738 +oid sha256:380998b59449eeba94f716a5295a003f7fbafc43a79b55d42c8105526dde0d63 +size 316491 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_1,NEXUS_5,1.0,en].png index df970a3ce3..f87365fb1a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:452876b1189da12691bca146c63202324593c791e854a1f8918a71c556a2c454 -size 305809 +oid sha256:984a1146d31e908a9945719926cdbaa07811e8cf9d0a9dfa042041bed4f2769c +size 311287 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_2,NEXUS_5,1.0,en].png index ac4bd00348..119e802731 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f496246dd0186280943dbf3057956eef19fdfd64ccd8458c49edb32cb97f24d3 -size 315968 +oid sha256:a3eaf72d0716767f7acb7a796353138034f9234da86a7762d415b5edb6fb3ae5 +size 317991 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_3,NEXUS_5,1.0,en].png index 254fa69e4f..5de0fcb890 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a127838495bdd790e1eeb6632d9fcfb256edd8ebe5d83f222434458b9687679c -size 307141 +oid sha256:17d055188d8548d847c991f4eb4e944c682a569a59eb8b34dc0a46787c803f26 +size 311514 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_4,NEXUS_5,1.0,en].png index 148df5143d..0ff4445f75 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5c86f4f08460382392eb9404fecdc93b6ff056e0d4116f39fc6308d1061c163 -size 314537 +oid sha256:8b0feb213813e17ffef6a1a4c58bb14056ac28b953fc8ab38f47d27c15270230 +size 317281 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_0,NEXUS_5,1.0,en].png index 7cd11d4937..04064b98f7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9df131c8d7482b7b00df587cca8b385d0cd57954240368f349a24e8e6ae36322 -size 405021 +oid sha256:f9d815ff280835ec6ed5cf43ef61711a079142c1024b5b6597595ceb9b57552b +size 410811 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_1,NEXUS_5,1.0,en].png index b5ca95ff71..7958c0afb7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5070637e034d7ecc1db4944cb05ec056e0cbea342769a32ee030b47110f8e4b -size 386301 +oid sha256:dd31e46ae32037a519030d1264bdb923208ea0b0d4f27f1072d5923618b67b9b +size 396666 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_2,NEXUS_5,1.0,en].png index a6d71eb418..a5ab2c2a95 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:662e5beadd1baea7825ce047df87c4429f720ea7b4dd53f8ea576ada91814568 -size 399093 +oid sha256:18a918db52e87b21312b888e74732ad9483b5edd1b5fa3f82471ab7739a12618 +size 403275 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_3,NEXUS_5,1.0,en].png index 8791e1399c..f3a5fda140 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a76b73d9e5e94b8dd8b27addd07593bae43423b92048110b213413e1185100e -size 373027 +oid sha256:35c7dbdebc92344948be74a22d171827aba09bf93b8150a96469fcf31b01ab35 +size 381498 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_4,NEXUS_5,1.0,en].png index 36442edf97..768132db23 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1a672fea8d9f8b6a1c9646982438bf3dc66874a472c0c2269d74fccffed3a98 -size 405810 +oid sha256:601c93ac65d44b65e72cf99c2885ff76c5a1442fb67213622d0bd174d9b34146 +size 411641 From 3cc571609501fbe7c5964e8f38b1383e353de8ca Mon Sep 17 00:00:00 2001 From: Marco Antonio Alvarez Date: Thu, 1 Feb 2024 18:25:27 +0100 Subject: [PATCH 027/119] empty commit From 686d58ee0a2a3ba16f93f27ee1e62e5e9712b6da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 2 Feb 2024 11:34:12 +0100 Subject: [PATCH 028/119] Fix message forwarding after SDK API change Created `Room.fullRoomWithTimeline` helper to always get an initialized timeline with the room object. --- .../libraries/matrix/impl/RustMatrixClient.kt | 8 ++--- .../matrix/impl/room/RoomContentForwarder.kt | 5 +++- .../impl/roomlist/RoomListItemExtensions.kt | 29 +++++++++++++++++++ 3 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListItemExtensions.kt diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 26bc775e55..8886afab55 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -55,6 +55,7 @@ import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber import io.element.android.libraries.matrix.impl.room.RustMatrixRoom import io.element.android.libraries.matrix.impl.roomlist.RoomListFactory import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService +import io.element.android.libraries.matrix.impl.roomlist.fullRoomWithTimeline import io.element.android.libraries.matrix.impl.roomlist.roomOrNull import io.element.android.libraries.matrix.impl.sync.RustSyncService import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper @@ -270,12 +271,7 @@ class RustMatrixClient( private suspend fun pairOfRoom(roomId: RoomId): Pair? { val cachedRoomListItem = innerRoomListService.roomOrNull(roomId.value) - val fullRoom = cachedRoomListItem?.let { roomListItem -> - if (!roomListItem.isTimelineInitialized()) { - roomListItem.initTimeline(eventFilters) - } - roomListItem.fullRoom() - } + val fullRoom = cachedRoomListItem?.fullRoomWithTimeline(filter = eventFilters) return if (cachedRoomListItem == null || fullRoom == null) { Timber.d("No room cached for $roomId") null diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt index 1d610422f9..1688763d4c 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt @@ -20,6 +20,7 @@ import io.element.android.libraries.core.coroutine.parallelMap import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.ForwardEventException +import io.element.android.libraries.matrix.impl.roomlist.fullRoomWithTimeline import io.element.android.libraries.matrix.impl.roomlist.roomOrNull import io.element.android.libraries.matrix.impl.timeline.runWithTimelineListenerRegistered import kotlinx.coroutines.CancellationException @@ -50,7 +51,9 @@ class RoomContentForwarder( ) { val content = fromTimeline.getTimelineEventContentByEventId(eventId.value) val targetSlidingSyncRooms = toRoomIds.mapNotNull { roomId -> roomListService.roomOrNull(roomId.value) } - val targetRooms = targetSlidingSyncRooms.mapNotNull { slidingSyncRoom -> slidingSyncRoom.use { it.fullRoom() } } + val targetRooms = targetSlidingSyncRooms.map { slidingSyncRoom -> + slidingSyncRoom.use { it.fullRoomWithTimeline(null) } + } val failedForwardingTo = mutableSetOf() targetRooms.parallelMap { room -> room.use { targetRoom -> diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListItemExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListItemExtensions.kt new file mode 100644 index 0000000000..24f292b456 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListItemExtensions.kt @@ -0,0 +1,29 @@ +/* + * 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 + * + * http://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.libraries.matrix.impl.roomlist + +import org.matrix.rustcomponents.sdk.Room +import org.matrix.rustcomponents.sdk.RoomListItem +import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter + +/** Returns a `Room` with an initialized timeline using the given [filter]. */ +suspend fun RoomListItem.fullRoomWithTimeline(filter: TimelineEventTypeFilter? = null): Room { + if (!isTimelineInitialized()) { + initTimeline(filter) + } + return fullRoom() +} From b3538e9d962c7a7149a9a5c594b2ff9052eff8b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 2 Feb 2024 11:37:01 +0100 Subject: [PATCH 029/119] Add changelog --- changelog.d/+fix-message-forwarding-after-sdk-api-change.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/+fix-message-forwarding-after-sdk-api-change.bugfix diff --git a/changelog.d/+fix-message-forwarding-after-sdk-api-change.bugfix b/changelog.d/+fix-message-forwarding-after-sdk-api-change.bugfix new file mode 100644 index 0000000000..76765d60ac --- /dev/null +++ b/changelog.d/+fix-message-forwarding-after-sdk-api-change.bugfix @@ -0,0 +1 @@ +Fix message forwarding after SDK API change related to Timeline intitialization. From 49646f2bef6bb3328d77dcba39bfcf4096b03542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 2 Feb 2024 12:39:09 +0100 Subject: [PATCH 030/119] Add an empty state to the room list. - Make `RoomListDataSource.allRooms` a `SharedFlow` so we can know when we don't have a value yet. - Map its output in `RoomListPresenter` to `AsyncData`. - Display the new empty state when the room list has loaded and has no items. --- changelog.d/2330.feature | 1 + .../roomlist/impl/RoomListPresenter.kt | 6 +- .../features/roomlist/impl/RoomListState.kt | 3 +- .../roomlist/impl/RoomListStateProvider.kt | 5 +- .../features/roomlist/impl/RoomListView.kt | 132 ++++++++++++------ .../impl/datasource/RoomListDataSource.kt | 6 +- .../roomlist/impl/RoomListPresenterTests.kt | 20 +-- 7 files changed, 115 insertions(+), 58 deletions(-) create mode 100644 changelog.d/2330.feature diff --git a/changelog.d/2330.feature b/changelog.d/2330.feature new file mode 100644 index 0000000000..c501ded646 --- /dev/null +++ b/changelog.d/2330.feature @@ -0,0 +1 @@ +Add empty state to the room list. diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt index 8ab1bde3f4..421cdb0c62 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt @@ -23,6 +23,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue @@ -32,6 +33,7 @@ import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource import io.element.android.features.roomlist.impl.datasource.RoomListDataSource +import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState @@ -68,7 +70,9 @@ class RoomListPresenter @Inject constructor( val matrixUser: MutableState = rememberSaveable { mutableStateOf(null) } - val roomList by roomListDataSource.allRooms.collectAsState() + val roomList by produceState(initialValue = AsyncData.Loading()) { + roomListDataSource.allRooms.collect { value = AsyncData.Success(it) } + } val filteredRoomList by roomListDataSource.filteredRooms.collectAsState() val filter by roomListDataSource.filter.collectAsState() val networkConnectionStatus by networkMonitor.connectivity.collectAsState() diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt index 7003da36e5..b1180aecb3 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt @@ -19,6 +19,7 @@ package io.element.android.features.roomlist.impl import androidx.compose.runtime.Immutable import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.roomlist.impl.model.RoomListRoomSummary +import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.user.MatrixUser @@ -28,7 +29,7 @@ import kotlinx.collections.immutable.ImmutableList data class RoomListState( val matrixUser: MatrixUser?, val showAvatarIndicator: Boolean, - val roomList: ImmutableList, + val roomList: AsyncData>, val filter: String?, val filteredRoomList: ImmutableList, val displayVerificationPrompt: Boolean, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt index b1e43f147e..fd97725778 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.leaveroom.api.aLeaveRoomState import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.features.roomlist.impl.model.aRoomListRoomSummary +import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage @@ -49,13 +50,15 @@ open class RoomListStateProvider : PreviewParameterProvider { ) ), aRoomListState().copy(displayRecoveryKeyPrompt = true), + aRoomListState().copy(roomList = AsyncData.Success(persistentListOf())), + aRoomListState().copy(roomList = AsyncData.Loading()), ) } internal fun aRoomListState() = RoomListState( matrixUser = MatrixUser(userId = UserId("@id:domain"), displayName = "User#1"), showAvatarIndicator = false, - roomList = aRoomListRoomSummaryList(), + roomList = AsyncData.Success(aRoomListRoomSummaryList()), filter = "filter", filteredRoomList = aRoomListRoomSummaryList(), hasNetworkConnection = true, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt index 0a9f4f523b..cf4ee97c72 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt @@ -17,7 +17,9 @@ package io.element.android.features.roomlist.impl import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxSize @@ -35,6 +37,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.nestedScroll @@ -42,6 +45,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.features.leaveroom.api.LeaveRoomView import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorContainer import io.element.android.features.roomlist.impl.components.ConfirmRecoveryKeyBanner @@ -51,17 +55,22 @@ import io.element.android.features.roomlist.impl.components.RoomListTopBar import io.element.android.features.roomlist.impl.components.RoomSummaryRow import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.features.roomlist.impl.search.RoomListSearchResultView +import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.FloatingActionButton import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.Scaffold +import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.ui.strings.CommonStrings @Composable fun RoomListView( @@ -122,6 +131,35 @@ fun RoomListView( } } +@Composable +private fun EmptyRoomListView( + onCreateRoomClicked: () -> Unit, +) { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text( + text = stringResource(R.string.screen_roomlist_empty_title), + style = ElementTheme.typography.fontBodyLgRegular, + color = ElementTheme.colors.textSecondary, + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = stringResource(R.string.screen_roomlist_empty_message), + style = ElementTheme.typography.fontBodyLgRegular, + color = ElementTheme.colors.textSecondary, + ) + Spacer(modifier = Modifier.height(16.dp)) + Button( + text = stringResource(CommonStrings.action_start_chat), + leadingIcon = IconSource.Resource(CommonDrawables.ic_new_message), + onClick = onCreateRoomClicked, + ) + } +} + @OptIn(ExperimentalMaterial3Api::class) @Composable private fun RoomListContent( @@ -182,56 +220,62 @@ private fun RoomListContent( ) }, content = { padding -> - LazyColumn( - modifier = Modifier - .padding(padding) - .consumeWindowInsets(padding) - .nestedScroll(nestedScrollConnection), - state = lazyListState, - ) { - when { - state.displayVerificationPrompt -> { - item { - RequestVerificationHeader( - onVerifyClicked = onVerifyClicked, - onDismissClicked = { state.eventSink(RoomListEvents.DismissRequestVerificationPrompt) } - ) + println(state.roomList) + if (state.roomList is AsyncData.Success && state.roomList.data.isEmpty()) { + EmptyRoomListView(onCreateRoomClicked) + } else { + LazyColumn( + modifier = Modifier + .padding(padding) + .consumeWindowInsets(padding) + .nestedScroll(nestedScrollConnection), + state = lazyListState, + ) { + when { + state.displayVerificationPrompt -> { + item { + RequestVerificationHeader( + onVerifyClicked = onVerifyClicked, + onDismissClicked = { state.eventSink(RoomListEvents.DismissRequestVerificationPrompt) } + ) + } + } + state.displayRecoveryKeyPrompt -> { + item { + ConfirmRecoveryKeyBanner( + onContinueClicked = onOpenSettings, + onDismissClicked = { state.eventSink(RoomListEvents.DismissRecoveryKeyPrompt) } + ) + } } } - state.displayRecoveryKeyPrompt -> { - item { - ConfirmRecoveryKeyBanner( - onContinueClicked = onOpenSettings, - onDismissClicked = { state.eventSink(RoomListEvents.DismissRecoveryKeyPrompt) } - ) - } - } - } - if (state.invitesState != InvitesState.NoInvites) { + if (state.invitesState != InvitesState.NoInvites) { + item { + InvitesEntryPointView(onInvitesClicked, state.invitesState) + } + } + + val roomList = state.roomList.dataOrNull().orEmpty() + itemsIndexed( + items = roomList, + contentType = { _, room -> room.contentType() }, + ) { index, room -> + RoomSummaryRow( + room = room, + onClick = ::onRoomClicked, + onLongClick = onRoomLongClicked, + ) + if (index != roomList.lastIndex) { + HorizontalDivider() + } + } + // Add a last Spacer item to ensure that the FAB does not hide the last room item + // FAB height is 56dp, bottom padding is 16dp, we add 8dp as extra margin -> 56+16+8 = 80 item { - InvitesEntryPointView(onInvitesClicked, state.invitesState) + Spacer(modifier = Modifier.height(80.dp)) } } - - itemsIndexed( - items = state.roomList, - contentType = { _, room -> room.contentType() }, - ) { index, room -> - RoomSummaryRow( - room = room, - onClick = ::onRoomClicked, - onLongClick = onRoomLongClicked, - ) - if (index != state.roomList.lastIndex) { - HorizontalDivider() - } - } - // Add a last Spacer item to ensure that the FAB does not hide the last room item - // FAB height is 56dp, bottom padding is 16dp, we add 8dp as extra margin -> 56+16+8 = 80 - item { - Spacer(modifier = Modifier.height(80.dp)) - } } }, floatingActionButton = { diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt index a255767d7e..a9f584b565 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt @@ -28,7 +28,9 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce @@ -52,7 +54,7 @@ class RoomListDataSource @Inject constructor( } private val _filter = MutableStateFlow("") - private val _allRooms = MutableStateFlow>(persistentListOf()) + private val _allRooms = MutableSharedFlow>(replay = 1) private val _filteredRooms = MutableStateFlow>(persistentListOf()) private val lock = Mutex() @@ -90,7 +92,7 @@ class RoomListDataSource @Inject constructor( } val filter: StateFlow = _filter - val allRooms: StateFlow> = _allRooms + val allRooms: SharedFlow> = _allRooms val filteredRooms: StateFlow> = _filteredRooms @OptIn(FlowPreview::class) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index 839fa04cf7..09d6f12e0c 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -166,14 +166,16 @@ class RoomListPresenterTests { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = consumeItemsUntilPredicate { state -> state.roomList.size == 16 }.last() + val initialState = consumeItemsUntilPredicate { state -> state.roomList.dataOrNull()?.size == 16 }.last() // Room list is loaded with 16 placeholders - assertThat(initialState.roomList.size).isEqualTo(16) - assertThat(initialState.roomList.all { it.isPlaceholder }).isTrue() + val initialItems = initialState.roomList.dataOrNull().orEmpty() + assertThat(initialItems.size).isEqualTo(16) + assertThat(initialItems.all { it.isPlaceholder }).isTrue() roomListService.postAllRooms(listOf(aRoomSummaryFilled())) - val withRoomState = consumeItemsUntilPredicate { state -> state.roomList.size == 1 }.last() - assertThat(withRoomState.roomList.size).isEqualTo(1) - assertThat(withRoomState.roomList.first()) + val withRoomState = consumeItemsUntilPredicate { state -> state.roomList.dataOrNull()?.size == 1 }.last() + val withRoomStateItems = withRoomState.roomList.dataOrNull().orEmpty() + assertThat(withRoomStateItems.size).isEqualTo(1) + assertThat(withRoomStateItems.first()) .isEqualTo(aRoomListRoomSummary) scope.cancel() } @@ -194,7 +196,7 @@ class RoomListPresenterTests { skipItems(3) val loadedState = awaitItem() // Test filtering with result - assertThat(loadedState.roomList.size).isEqualTo(1) + assertThat(loadedState.roomList.dataOrNull().orEmpty().size).isEqualTo(1) loadedState.eventSink.invoke(RoomListEvents.UpdateFilter(A_ROOM_NAME.substring(0, 3))) skipItems(1) val withFilteredRoomState = awaitItem() @@ -384,10 +386,10 @@ class RoomListPresenterTests { notificationSettingsService.setRoomNotificationMode(A_ROOM_ID, userDefinedMode) val updatedState = consumeItemsUntilPredicate { state -> - state.roomList.any { it.id == A_ROOM_ID.value && it.userDefinedNotificationMode == userDefinedMode } + state.roomList.dataOrNull().orEmpty().any { it.id == A_ROOM_ID.value && it.userDefinedNotificationMode == userDefinedMode } }.last() - val room = updatedState.roomList.find { it.id == A_ROOM_ID.value } + val room = updatedState.roomList.dataOrNull()?.find { it.id == A_ROOM_ID.value } assertThat(room?.userDefinedNotificationMode).isEqualTo(userDefinedMode) cancelAndIgnoreRemainingEvents() scope.cancel() From aa32123c826a998c2d8fd4f7c53aac441e91b9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 2 Feb 2024 13:04:28 +0100 Subject: [PATCH 031/119] Make the `RoomListRoomSummaryFactory.createPlaceholder` and `RoomListRoomSummaryFactory.createFakeList` functions static ones --- .../impl/datasource/RoomListDataSource.kt | 4 +- .../datasource/RoomListRoomSummaryFactory.kt | 44 ++++++++++--------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt index a9f584b565..0d32239a3b 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt @@ -115,7 +115,7 @@ class RoomListDataSource @Inject constructor( private suspend fun buildAndEmitAllRooms(roomSummaries: List) { if (diffCache.isEmpty()) { _allRooms.emit( - roomListRoomSummaryFactory.createFakeList() + RoomListRoomSummaryFactory.createFakeList() ) } else { val roomListRoomSummaries = ArrayList() @@ -135,7 +135,7 @@ class RoomListDataSource @Inject constructor( private fun buildAndCacheItem(roomSummaries: List, index: Int): RoomListRoomSummary? { val roomListRoomSummary = when (val roomSummary = roomSummaries.getOrNull(index)) { - is RoomSummary.Empty -> roomListRoomSummaryFactory.createPlaceholder(roomSummary.identifier) + is RoomSummary.Empty -> RoomListRoomSummaryFactory.createPlaceholder(roomSummary.identifier) is RoomSummary.Filled -> roomListRoomSummaryFactory.create(roomSummary) null -> null } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt index 9f1416c701..2d47be35a3 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt @@ -32,28 +32,30 @@ class RoomListRoomSummaryFactory @Inject constructor( private val lastMessageTimestampFormatter: LastMessageTimestampFormatter, private val roomLastMessageFormatter: RoomLastMessageFormatter, ) { - fun createPlaceholder(id: String): RoomListRoomSummary { - return RoomListRoomSummary( - id = id, - roomId = RoomId("!aRoom:domain"), - isPlaceholder = true, - name = "Short name", - timestamp = "hh:mm", - lastMessage = "Last message for placeholder", - avatarData = AvatarData(id, "S", size = AvatarSize.RoomListItem), - numberOfUnreadMessages = 0, - numberOfUnreadMentions = 0, - numberOfUnreadNotifications = 0, - userDefinedNotificationMode = null, - hasRoomCall = false, - isDm = false, - ) - } + companion object { + fun createPlaceholder(id: String): RoomListRoomSummary { + return RoomListRoomSummary( + id = id, + roomId = RoomId(id), + isPlaceholder = true, + name = "Short name", + timestamp = "hh:mm", + lastMessage = "Last message for placeholder", + avatarData = AvatarData(id, "S", size = AvatarSize.RoomListItem), + numberOfUnreadMessages = 0, + numberOfUnreadMentions = 0, + numberOfUnreadNotifications = 0, + userDefinedNotificationMode = null, + hasRoomCall = false, + isDm = false, + ) + } - fun createFakeList(): ImmutableList { - return List(16) { - createPlaceholder("!fakeRoom$it:domain") - }.toImmutableList() + fun createFakeList(): ImmutableList { + return List(16) { + createPlaceholder("!fakeRoom$it:domain") + }.toImmutableList() + } } fun create(roomSummary: RoomSummary.Filled): RoomListRoomSummary { From e9c0aee96ee2698808163cc1ec349a7bbf3882d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 2 Feb 2024 13:04:56 +0100 Subject: [PATCH 032/119] Fix full placeholder screen --- .../android/features/roomlist/impl/RoomListStateProvider.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt index fd97725778..55cbcf925b 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt @@ -18,6 +18,7 @@ package io.element.android.features.roomlist.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.leaveroom.api.aLeaveRoomState +import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.features.roomlist.impl.model.aRoomListRoomSummary import io.element.android.libraries.architecture.AsyncData @@ -51,7 +52,7 @@ open class RoomListStateProvider : PreviewParameterProvider { ), aRoomListState().copy(displayRecoveryKeyPrompt = true), aRoomListState().copy(roomList = AsyncData.Success(persistentListOf())), - aRoomListState().copy(roomList = AsyncData.Loading()), + aRoomListState().copy(roomList = AsyncData.Loading(prevData = RoomListRoomSummaryFactory.createFakeList())), ) } From 02fe07a6bdf631a1a935eba08af491a855dddeab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 2 Feb 2024 13:05:37 +0100 Subject: [PATCH 033/119] Tweak room list behaviour: - Instead of an extra item, use `contentPadding`. - Make sure each item has an unique id. --- .../android/features/roomlist/impl/RoomListView.kt | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt index cf4ee97c72..929d2550a4 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt @@ -20,6 +20,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxSize @@ -220,7 +221,6 @@ private fun RoomListContent( ) }, content = { padding -> - println(state.roomList) if (state.roomList is AsyncData.Success && state.roomList.data.isEmpty()) { EmptyRoomListView(onCreateRoomClicked) } else { @@ -230,6 +230,8 @@ private fun RoomListContent( .consumeWindowInsets(padding) .nestedScroll(nestedScrollConnection), state = lazyListState, + // FAB height is 56dp, bottom padding is 16dp, we add 8dp as extra margin -> 56+16+8 = 80 + contentPadding = PaddingValues(bottom = 80.dp) ) { when { state.displayVerificationPrompt -> { @@ -260,6 +262,7 @@ private fun RoomListContent( itemsIndexed( items = roomList, contentType = { _, room -> room.contentType() }, + key = { _, room -> room.roomId.value } ) { index, room -> RoomSummaryRow( room = room, @@ -270,11 +273,6 @@ private fun RoomListContent( HorizontalDivider() } } - // Add a last Spacer item to ensure that the FAB does not hide the last room item - // FAB height is 56dp, bottom padding is 16dp, we add 8dp as extra margin -> 56+16+8 = 80 - item { - Spacer(modifier = Modifier.height(80.dp)) - } } } }, From 8060c870f1c1247212d186be3a5bfc7e70ba9d25 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 2 Feb 2024 12:12:03 +0000 Subject: [PATCH 034/119] Update screenshots --- ...tView_null_RoomListView-Day-3_4_null_10,NEXUS_5,1.0,en].png | 3 +++ ...tView_null_RoomListView-Day-3_4_null_11,NEXUS_5,1.0,en].png | 3 +++ ...iew_null_RoomListView-Night-3_5_null_10,NEXUS_5,1.0,en].png | 3 +++ ...iew_null_RoomListView-Night-3_5_null_11,NEXUS_5,1.0,en].png | 3 +++ 4 files changed, 12 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_10,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_11,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_10,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_11,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_10,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..15095f8707 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_10,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f0dfd19b21ff2ba1fdbb423578f71e4d28a01d84bc7e1a41a1027b1b2d09db3 +size 55268 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_11,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..cc18d97b3a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_11,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2fb4ca138fefc4d4c4e09d3c1c403bea3f54aa226bba6a836711f21093e06b6 +size 51707 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_10,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3b31d08513 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_10,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ab5f276868ad19ac4c5f6bbce92286bd6a846c77b91cb8c76d12a9d6aeca31c +size 57171 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_11,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6c07e38044 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_11,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aecfc26c04a2c1d16ae9d00a85388ac6497190c743b8b6b37b40c4337c216216 +size 53371 From 5f064a4b271c837ce1cc82411062cca36d8526e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 2 Feb 2024 13:28:30 +0100 Subject: [PATCH 035/119] Only display placeholder list when the room list hasn't loaded yet --- .../roomlist/impl/datasource/RoomListDataSource.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt index 0d32239a3b..c55a47596b 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt @@ -21,6 +21,7 @@ import io.element.android.libraries.androidutils.diff.DiffCacheUpdater import io.element.android.libraries.androidutils.diff.MutableListDiffCache import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService +import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.RoomSummary import kotlinx.collections.immutable.ImmutableList @@ -113,10 +114,9 @@ class RoomListDataSource @Inject constructor( } private suspend fun buildAndEmitAllRooms(roomSummaries: List) { - if (diffCache.isEmpty()) { - _allRooms.emit( - RoomListRoomSummaryFactory.createFakeList() - ) + if (diffCache.isEmpty() && roomListService.allRooms.loadingState.value is RoomList.LoadingState.NotLoaded) { + // If the room list is not loaded, we emit a fake placeholders list + _allRooms.emit(RoomListRoomSummaryFactory.createFakeList()) } else { val roomListRoomSummaries = ArrayList() for (index in diffCache.indices()) { From fee8cda04a1e56347e905d6c1d53c02a23916df5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 22:17:18 +0000 Subject: [PATCH 036/119] Update dependency com.google.truth:truth to v1.4.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f61a28edd2..c5f504934d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -132,7 +132,7 @@ test_junitext = "androidx.test.ext:junit:1.1.5" test_mockk = "io.mockk:mockk:1.13.9" test_konsist = "com.lemonappdev:konsist:0.13.0" test_turbine = "app.cash.turbine:turbine:1.0.0" -test_truth = "com.google.truth:truth:1.3.0" +test_truth = "com.google.truth:truth:1.4.0" test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.15" test_robolectric = "org.robolectric:robolectric:4.11.1" test_appyx_junit = { module = "com.bumble.appyx:testing-junit4", version.ref = "appyx" } From 03c95555e31a4d4d1bac195783e73250707e0ff5 Mon Sep 17 00:00:00 2001 From: bmarty <3940906+bmarty@users.noreply.github.com> Date: Mon, 5 Feb 2024 00:09:59 +0000 Subject: [PATCH 037/119] Sync Strings from Localazy --- .../src/main/res/values-be/translations.xml | 7 + .../src/main/res/values-be/translations.xml | 10 + .../src/main/res/values-be/translations.xml | 6 + .../src/main/res/values-be/translations.xml | 15 ++ .../src/main/res/values-cs/translations.xml | 2 +- .../src/main/res/values-de/translations.xml | 2 +- .../src/main/res/values-es/translations.xml | 2 +- .../src/main/res/values-fr/translations.xml | 2 +- .../src/main/res/values-hu/translations.xml | 2 +- .../src/main/res/values-in/translations.xml | 2 +- .../src/main/res/values-it/translations.xml | 2 +- .../src/main/res/values-ro/translations.xml | 2 +- .../src/main/res/values-ru/translations.xml | 2 +- .../src/main/res/values-sk/translations.xml | 2 +- .../main/res/values-zh-rTW/translations.xml | 2 +- .../impl/src/main/res/values/localazy.xml | 2 +- .../src/main/res/values-be/translations.xml | 13 + .../src/main/res/values-be/translations.xml | 9 + .../src/main/res/values-be/translations.xml | 6 + .../src/main/res/values-be/translations.xml | 39 +++ .../src/main/res/values-be/translations.xml | 41 +++ .../src/main/res/values-cs/translations.xml | 4 +- .../src/main/res/values-de/translations.xml | 4 +- .../src/main/res/values-es/translations.xml | 4 +- .../src/main/res/values-fr/translations.xml | 4 +- .../src/main/res/values-hu/translations.xml | 6 +- .../src/main/res/values-in/translations.xml | 4 +- .../src/main/res/values-it/translations.xml | 4 +- .../src/main/res/values-ro/translations.xml | 4 +- .../src/main/res/values-ru/translations.xml | 4 +- .../src/main/res/values-sk/translations.xml | 4 +- .../main/res/values-zh-rTW/translations.xml | 4 +- .../impl/src/main/res/values/localazy.xml | 4 +- .../src/main/res/values-be/translations.xml | 18 ++ .../src/main/res/values-be/translations.xml | 62 +++++ .../src/main/res/values-cs/translations.xml | 2 +- .../src/main/res/values-de/translations.xml | 2 +- .../src/main/res/values-es/translations.xml | 2 +- .../src/main/res/values-fr/translations.xml | 2 +- .../src/main/res/values-hu/translations.xml | 4 +- .../src/main/res/values-in/translations.xml | 2 +- .../src/main/res/values-it/translations.xml | 11 +- .../src/main/res/values-ro/translations.xml | 2 +- .../src/main/res/values-ru/translations.xml | 2 +- .../src/main/res/values-sk/translations.xml | 2 +- .../main/res/values-zh-rTW/translations.xml | 2 +- .../impl/src/main/res/values/localazy.xml | 2 +- .../src/main/res/values-be/translations.xml | 7 + .../src/main/res/values-be/translations.xml | 19 ++ .../src/main/res/values-cs/translations.xml | 4 +- .../src/main/res/values-de/translations.xml | 4 +- .../src/main/res/values-es/translations.xml | 4 +- .../src/main/res/values-fr/translations.xml | 4 +- .../src/main/res/values-hu/translations.xml | 4 +- .../src/main/res/values-in/translations.xml | 4 +- .../src/main/res/values-it/translations.xml | 4 +- .../src/main/res/values-ru/translations.xml | 4 +- .../src/main/res/values-sk/translations.xml | 4 +- .../impl/src/main/res/values/localazy.xml | 4 +- .../src/main/res/values-be/translations.xml | 43 +++ .../src/main/res/values-it/translations.xml | 2 + .../src/main/res/values-be/translations.xml | 6 + .../src/main/res/values-be/translations.xml | 15 ++ .../src/main/res/values-it/translations.xml | 1 + .../src/main/res/values-be/translations.xml | 55 ++++ .../src/main/res/values-cs/translations.xml | 2 +- .../src/main/res/values-de/translations.xml | 2 +- .../src/main/res/values-es/translations.xml | 2 +- .../src/main/res/values-fr/translations.xml | 2 +- .../src/main/res/values-hu/translations.xml | 2 +- .../src/main/res/values-in/translations.xml | 2 +- .../src/main/res/values-it/translations.xml | 2 +- .../src/main/res/values-ro/translations.xml | 2 +- .../src/main/res/values-ru/translations.xml | 2 +- .../src/main/res/values-sk/translations.xml | 2 +- .../main/res/values-zh-rTW/translations.xml | 2 +- .../impl/src/main/res/values/localazy.xml | 2 +- .../src/main/res/values-be/translations.xml | 11 + .../src/main/res/values-cs/translations.xml | 4 +- .../src/main/res/values-de/translations.xml | 1 + .../src/main/res/values-es/translations.xml | 1 + .../src/main/res/values-fr/translations.xml | 4 +- .../src/main/res/values-hu/translations.xml | 1 + .../src/main/res/values-in/translations.xml | 1 + .../src/main/res/values-it/translations.xml | 7 + .../src/main/res/values-ro/translations.xml | 1 + .../src/main/res/values-ru/translations.xml | 4 +- .../src/main/res/values-sk/translations.xml | 2 +- .../main/res/values-zh-rTW/translations.xml | 1 + .../impl/src/main/res/values/localazy.xml | 2 +- .../src/main/res/values-be/translations.xml | 44 ++++ .../src/main/res/values-be/translations.xml | 8 + .../src/main/res/values-be/translations.xml | 20 ++ .../src/main/res/values-be/translations.xml | 4 + .../src/main/res/values-be/translations.xml | 57 ++++ .../src/main/res/values-hu/translations.xml | 2 + .../src/main/res/values-be/translations.xml | 7 + .../src/main/res/values-be/translations.xml | 58 +++++ .../src/main/res/values-cs/translations.xml | 4 +- .../src/main/res/values-de/translations.xml | 3 +- .../src/main/res/values-es/translations.xml | 3 +- .../src/main/res/values-fr/translations.xml | 4 +- .../src/main/res/values-hu/translations.xml | 5 +- .../src/main/res/values-in/translations.xml | 3 +- .../src/main/res/values-it/translations.xml | 6 +- .../src/main/res/values-ro/translations.xml | 2 - .../src/main/res/values-ru/translations.xml | 4 +- .../src/main/res/values-sk/translations.xml | 3 +- .../main/res/values-zh-rTW/translations.xml | 3 +- .../impl/src/main/res/values/localazy.xml | 4 +- .../src/main/res/values-be/translations.xml | 25 ++ .../src/main/res/values-be/translations.xml | 246 ++++++++++++++++++ .../src/main/res/values-cs/translations.xml | 1 + .../src/main/res/values-fr/translations.xml | 1 + .../src/main/res/values-hu/translations.xml | 2 +- .../src/main/res/values-it/translations.xml | 1 + .../src/main/res/values-ru/translations.xml | 1 + .../src/main/res/values/localazy.xml | 1 + 118 files changed, 996 insertions(+), 113 deletions(-) create mode 100644 features/analytics/api/src/main/res/values-be/translations.xml create mode 100644 features/analytics/impl/src/main/res/values-be/translations.xml create mode 100644 features/call/src/main/res/values-be/translations.xml create mode 100644 features/createroom/impl/src/main/res/values-be/translations.xml create mode 100644 features/ftue/impl/src/main/res/values-be/translations.xml create mode 100644 features/invitelist/impl/src/main/res/values-be/translations.xml create mode 100644 features/leaveroom/api/src/main/res/values-be/translations.xml create mode 100644 features/lockscreen/impl/src/main/res/values-be/translations.xml create mode 100644 features/login/impl/src/main/res/values-be/translations.xml create mode 100644 features/logout/impl/src/main/res/values-be/translations.xml create mode 100644 features/messages/impl/src/main/res/values-be/translations.xml create mode 100644 features/onboarding/impl/src/main/res/values-be/translations.xml create mode 100644 features/poll/impl/src/main/res/values-be/translations.xml create mode 100644 features/preferences/impl/src/main/res/values-be/translations.xml create mode 100644 features/rageshake/api/src/main/res/values-be/translations.xml create mode 100644 features/rageshake/impl/src/main/res/values-be/translations.xml create mode 100644 features/roomdetails/impl/src/main/res/values-be/translations.xml create mode 100644 features/roomlist/impl/src/main/res/values-be/translations.xml create mode 100644 features/securebackup/impl/src/main/res/values-be/translations.xml create mode 100644 features/signedout/impl/src/main/res/values-be/translations.xml create mode 100644 features/verifysession/impl/src/main/res/values-be/translations.xml create mode 100644 libraries/androidutils/src/main/res/values-be/translations.xml create mode 100644 libraries/eventformatter/impl/src/main/res/values-be/translations.xml create mode 100644 libraries/permissions/api/src/main/res/values-be/translations.xml create mode 100644 libraries/push/impl/src/main/res/values-be/translations.xml create mode 100644 libraries/textcomposer/impl/src/main/res/values-be/translations.xml create mode 100644 libraries/ui-strings/src/main/res/values-be/translations.xml diff --git a/features/analytics/api/src/main/res/values-be/translations.xml b/features/analytics/api/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..34f69b49b4 --- /dev/null +++ b/features/analytics/api/src/main/res/values-be/translations.xml @@ -0,0 +1,7 @@ + + + "Дзяліцеся дадзенымі аналітыкі" + "Даваць ананімныя дадзеныя аб выкарыстанні, каб дапамагчы нам выявіць праблемы." + "Вы можаце азнаёміцца з усімі нашымі ўмовамі %1$s." + "тут" + diff --git a/features/analytics/impl/src/main/res/values-be/translations.xml b/features/analytics/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..6d322781c3 --- /dev/null +++ b/features/analytics/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,10 @@ + + + "Мы не будзем запісваць або прафіляваць любыя асабістыя даныя" + "Даваць ананімныя дадзеныя аб выкарыстанні, каб дапамагчы нам выявіць праблемы." + "Вы можаце азнаёміцца з усімі нашымі ўмовамі %1$s." + "тут" + "Вы можаце адключыць гэта ў любы час" + "Мы не будзем перадаваць вашыя дадзеныя трэцім асобам" + "Дапамажыце палепшыць %1$s" + diff --git a/features/call/src/main/res/values-be/translations.xml b/features/call/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..8355f3af9b --- /dev/null +++ b/features/call/src/main/res/values-be/translations.xml @@ -0,0 +1,6 @@ + + + "Бягучы выклік" + "Націсніце, каб вярнуцца да выкліка" + "☎️ Выконваецца выклік" + diff --git a/features/createroom/impl/src/main/res/values-be/translations.xml b/features/createroom/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..d838edaa3a --- /dev/null +++ b/features/createroom/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,15 @@ + + + "Новы пакой" + "Запрасіце сяброў у Element" + "Запрасіць людзей" + "Пры стварэнні пакоя адбылася памылка" + "Паведамленні ў гэтым пакоі зашыфраваны. Гэта шыфраванне нельга адключыць." + "Прыватны пакой (толькі па запрашэнні)" + "Паведамленні не зашыфраваны, і кожны можа іх прачытаць. Вы можаце ўключыць шыфраванне пазней." + "Адкрыты пакой (для ўсіх)" + "Тэма (неабавязкова)" + "Пры спробе пачаць чат адбылася памылка" + "Назва пакоя" + "Стварыце пакой" + diff --git a/features/createroom/impl/src/main/res/values-cs/translations.xml b/features/createroom/impl/src/main/res/values-cs/translations.xml index febd535cb0..7131c871f7 100644 --- a/features/createroom/impl/src/main/res/values-cs/translations.xml +++ b/features/createroom/impl/src/main/res/values-cs/translations.xml @@ -8,8 +8,8 @@ "Soukromá místnost (jen pro pozvané)" "Zprávy nejsou šifrované a může si je přečíst kdokoli. Šifrování můžete povolit později." "Veřejná místnost (kdokoli)" - "Název místnosti" "Téma (nepovinné)" "Při pokusu o zahájení chatu došlo k chybě" + "Název místnosti" "Vytvořit místnost" diff --git a/features/createroom/impl/src/main/res/values-de/translations.xml b/features/createroom/impl/src/main/res/values-de/translations.xml index 59ebe82ff1..94a4baccba 100644 --- a/features/createroom/impl/src/main/res/values-de/translations.xml +++ b/features/createroom/impl/src/main/res/values-de/translations.xml @@ -8,8 +8,8 @@ "Privater Raum (nur auf Einladung)" "Die Nachrichten sind nicht verschlüsselt und können von jedem gelesen werden. Die Verschlüsselung kann zu einem späteren Zeitpunkt aktiviert werden." "Öffentlicher Raum (für alle)" - "Raumname" "Thema (optional)" "Beim Versuch, einen Chat zu starten, ist ein Fehler aufgetreten" + "Raumname" "Raum erstellen" diff --git a/features/createroom/impl/src/main/res/values-es/translations.xml b/features/createroom/impl/src/main/res/values-es/translations.xml index 1c4c687d31..c5bae8be29 100644 --- a/features/createroom/impl/src/main/res/values-es/translations.xml +++ b/features/createroom/impl/src/main/res/values-es/translations.xml @@ -8,8 +8,8 @@ "Sala privada (sólo con invitación)" "Los mensajes no están cifrados y cualquiera puede leerlos. Puedes activar la encriptación más adelante." "Sala pública (cualquiera)" - "Nombre de la sala" "Tema (opcional)" "Se ha producido un error al intentar iniciar un chat" + "Nombre de la sala" "Crear una sala" diff --git a/features/createroom/impl/src/main/res/values-fr/translations.xml b/features/createroom/impl/src/main/res/values-fr/translations.xml index e060b99ed8..d584410e8a 100644 --- a/features/createroom/impl/src/main/res/values-fr/translations.xml +++ b/features/createroom/impl/src/main/res/values-fr/translations.xml @@ -8,8 +8,8 @@ "Salon privé (sur invitation seulement)" "Les messages ne sont pas chiffrés et n’importe qui peut les lire. Vous pouvez activer le chiffrement ultérieurement." "Salon public (tout le monde)" - "Nom du salon" "Sujet (facultatif)" "Une erreur s’est produite lors de la tentative de création de la discussion" + "Nom du salon" "Créer un salon" diff --git a/features/createroom/impl/src/main/res/values-hu/translations.xml b/features/createroom/impl/src/main/res/values-hu/translations.xml index 29a403a091..68af83d977 100644 --- a/features/createroom/impl/src/main/res/values-hu/translations.xml +++ b/features/createroom/impl/src/main/res/values-hu/translations.xml @@ -8,8 +8,8 @@ "Privát szoba (csak meghívással)" "Az üzenetek nincsenek titkosítva, és bárki elolvashatja őket. A titkosítást később is engedélyezheti." "Nyilvános szoba (bárki)" - "Szoba neve" "Téma (nem kötelező)" "Hiba történt a csevegés indításakor" + "Szoba neve" "Szoba létrehozása" diff --git a/features/createroom/impl/src/main/res/values-in/translations.xml b/features/createroom/impl/src/main/res/values-in/translations.xml index 775eef7fda..421362fff3 100644 --- a/features/createroom/impl/src/main/res/values-in/translations.xml +++ b/features/createroom/impl/src/main/res/values-in/translations.xml @@ -8,8 +8,8 @@ "Ruangan pribadi (hanya undangan)" "Pesan tidak dienkripsi dan siapa pun dapat membacanya. Anda dapat mengaktifkan enkripsi di kemudian hari." "Ruang publik (siapa saja)" - "Nama ruangan" "Topik (opsional)" "Terjadi kesalahan saat mencoba memulai obrolan" + "Nama ruangan" "Buat ruangan" diff --git a/features/createroom/impl/src/main/res/values-it/translations.xml b/features/createroom/impl/src/main/res/values-it/translations.xml index 836cff3452..00793c2bd1 100644 --- a/features/createroom/impl/src/main/res/values-it/translations.xml +++ b/features/createroom/impl/src/main/res/values-it/translations.xml @@ -8,8 +8,8 @@ "Stanza privata (solo su invito)" "I messaggi non sono cifrati e chiunque può leggerli. Puoi attivare la crittografia in un secondo momento." "Stanza pubblica (chiunque)" - "Nome stanza" "Argomento (facoltativo)" "Si è verificato un errore durante il tentativo di avviare una chat" + "Nome stanza" "Crea una stanza" diff --git a/features/createroom/impl/src/main/res/values-ro/translations.xml b/features/createroom/impl/src/main/res/values-ro/translations.xml index 9f68a006e5..ad97c81fbc 100644 --- a/features/createroom/impl/src/main/res/values-ro/translations.xml +++ b/features/createroom/impl/src/main/res/values-ro/translations.xml @@ -8,8 +8,8 @@ "Cameră privată (doar pe bază de invitație)" "Mesajele nu sunt criptate și oricine le poate citi. Puteți activa criptarea la o dată ulterioară." "Cameră publică (oricine)" - "Numele camerei" "Subiect (opțional)" "A apărut o eroare la încercarea începerii conversației" + "Numele camerei" "Creați o cameră" diff --git a/features/createroom/impl/src/main/res/values-ru/translations.xml b/features/createroom/impl/src/main/res/values-ru/translations.xml index 1557931e85..ce16a9fab3 100644 --- a/features/createroom/impl/src/main/res/values-ru/translations.xml +++ b/features/createroom/impl/src/main/res/values-ru/translations.xml @@ -8,8 +8,8 @@ "Приватная комната (только по приглашению)" "Сообщения не зашифрованы, каждый может их прочитать. Вы можете включить шифрование позже." "Публичная комната (любой)" - "Название комнаты" "Тема (необязательно)" "Произошла ошибка при попытке открытия комнаты" + "Название комнаты" "Создать комнату" diff --git a/features/createroom/impl/src/main/res/values-sk/translations.xml b/features/createroom/impl/src/main/res/values-sk/translations.xml index 15fa779609..730297c17c 100644 --- a/features/createroom/impl/src/main/res/values-sk/translations.xml +++ b/features/createroom/impl/src/main/res/values-sk/translations.xml @@ -8,8 +8,8 @@ "Súkromná miestnosť (len pre pozvaných)" "Správy nie sú šifrované a môže si ich prečítať ktokoľvek. Šifrovanie môžete zapnúť neskôr." "Verejná miestnosť (ktokoľvek)" - "Názov miestnosti" "Téma (voliteľné)" "Pri pokuse o spustenie konverzácie sa vyskytla chyba" + "Názov miestnosti" "Vytvoriť miestnosť" diff --git a/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml b/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml index a078802c36..226bd4eb93 100644 --- a/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml @@ -8,7 +8,7 @@ "私密聊天室(僅限邀請)" "訊息未加密,任何人都可以查看。您可以在之後啟用加密功能。" "公開聊天室(任何人)" - "聊天室名稱" "主題(非必填)" + "聊天室名稱" "建立聊天室" diff --git a/features/createroom/impl/src/main/res/values/localazy.xml b/features/createroom/impl/src/main/res/values/localazy.xml index eadcf49f34..e3607275f2 100644 --- a/features/createroom/impl/src/main/res/values/localazy.xml +++ b/features/createroom/impl/src/main/res/values/localazy.xml @@ -8,8 +8,8 @@ "Private room (invite only)" "Messages are not encrypted and anyone can read them. You can enable encryption at a later date." "Public room (anyone)" - "Room name" "Topic (optional)" "An error occurred when trying to start a chat" + "Room name" "Create a room" diff --git a/features/ftue/impl/src/main/res/values-be/translations.xml b/features/ftue/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..3d785bbbdc --- /dev/null +++ b/features/ftue/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,13 @@ + + + "Гэта аднаразовы працэс, дзякуем за чаканне." + "Налада ўліковага запісу." + "Вы можаце змяніць налады пазней." + "Дазвольце апавяшчэнні і ніколі не прапускайце іх" + "Званкі, апытанні, пошук і многае іншае будзе дададзена пазней у гэтым годзе." + "Гісторыя паведамленняў для зашыфраваных пакояў пакуль недаступна." + "Мы будзем рады пачуць вашае меркаванне, паведаміце нам аб гэтым праз старонку налад." + "Пачнём!" + "Вось што вам трэба ведаць:" + "Вітаем у %1$s!" + diff --git a/features/invitelist/impl/src/main/res/values-be/translations.xml b/features/invitelist/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..36c895dcb9 --- /dev/null +++ b/features/invitelist/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,9 @@ + + + "Вы ўпэўненыя, што жадаеце адхіліць запрашэнне ў %1$s?" + "Адхіліць запрашэнне" + "Вы ўпэўненыя, што жадаеце адмовіцца ад прыватных зносін з %1$s?" + "Адхіліць чат" + "Няма запрашэнняў" + "%1$s (%2$s) запрасіў вас" + diff --git a/features/leaveroom/api/src/main/res/values-be/translations.xml b/features/leaveroom/api/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..f7d4ed17df --- /dev/null +++ b/features/leaveroom/api/src/main/res/values-be/translations.xml @@ -0,0 +1,6 @@ + + + "Вы ўпэўнены, што жадаеце пакінуць гэты пакой? Вы тут адзіны карыстальнік. Калі вы выйдзеце, ніхто не зможа далучыцца ў будучыні, у тым ліку і вы." + "Вы ўпэўнены, што жадаеце пакінуць гэты пакой? Гэты пакой не агульнадаступны, і вы не зможаце далучыцца да яго зноў без запрашэння." + "Вы ўпэўнены, што жадаеце пакінуць пакой?" + diff --git a/features/lockscreen/impl/src/main/res/values-be/translations.xml b/features/lockscreen/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..508ddc97cd --- /dev/null +++ b/features/lockscreen/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,39 @@ + + + + "У вас %1$d спроба разблакіроўкі" + "У вас %1$d спроб разблакіроўкі" + "У вас %1$d спроб разблакіроўкі" + + + "Няправільны PIN-код. У вас застаўся %1$d шанец" + "Няправільны PIN-код. У вас застаўася %1$d шанцаў" + "Няправільны PIN-код. У вас застаўася %1$d шанцаў" + + "біяметрычная аўтэнтыфікацыя" + "біяметрычная разблакіроўка" + "Разблакіроўка з дапамогай біяметрыі" + "Забыліся PIN-код?" + "Змяніць PIN-код" + "Дазволіць біяметрычную разблакіроўку" + "Выдаліць PIN-код" + "Вы ўпэўнены, што жадаеце выдаліць PIN-код?" + "Выдаліць PIN-код?" + "Дазволіць %1$s" + "Я хацеў бы выкарыстоўваць PIN-код" + "Эканомце час і выкарыстоўвайце %1$s для разблакіроўкі праграмы" + "Выберыце PIN-код" + "Пацвярджэнне PIN-кода" + "Вы не можаце выбраць гэты PIN-код з меркаванняў бяспекі" + "Выберыце іншы PIN-код" + "Заблакіруйце %1$s, каб павялічыць бяспеку вашых чатаў. + +Абярыце што-небудзь незабыўнае. Калі вы забудзецеся гэты PIN-код, вы выйдзеце з праграмы." + "Увядзіце адзін і той жа PIN двойчы" + "PIN-коды не супадаюць" + "Каб працягнуць, вам неабходна паўторна ўвайсці ў сістэму і стварыць новы PIN-код" + "Вы выходзіце з сістэмы" + "Выкарыстоўваць біяметрыю" + "Выкарыстоўваць PIN-код" + "Выхад…" + diff --git a/features/login/impl/src/main/res/values-be/translations.xml b/features/login/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..1e4bd1f7af --- /dev/null +++ b/features/login/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,41 @@ + + + "Змяніць правайдара ўліковага запісу" + "Адрас хатняга сервера" + "Увядзіце пошукавы запыт або адрас дамена." + "Пошук кампаніі, супольнасці або прыватнага сервера." + "Знайдзіце правайдара ўліковых запісаў" + "Вы збіраецеся ўвайсці ў %s" + "Вы збіраецеся стварыць уліковы запіс на %s" + "Matrix.org - гэта вялікі бясплатны сервер у агульнадаступнай сетцы Matrix для бяспечнай дэцэнтралізаванай сувязі, якім кіруе фонд Matrix.org." + "Іншае" + "Выкарыстоўвайце іншага правайдара ўліковых запісаў, напрыклад, уласны прыватны сервер або працоўны ўліковы запіс." + "Змяніць правайдара ўліковага запісу" + "Нам не ўдалося звязацца з гэтым хатнім серверам. Упэўніцеся, што вы правільна ўвялі URL-адрас хатняга сервера. Калі URL-адрас пазначаны правільна, звярніцеся да адміністратара хатняга сервера за дадатковай дапамогай." + "На жаль, гэты сервер не падтрымлівае sliding sync." + "URL хатняга сервера" + "Вы можаце падключыцца толькі да існуючага сервера, які падтрымлівае sliding sync. Адміністратару хатняга сервера запатрабуецца наладзіць яго. %1$s" + "Які адрас вашага сервера?" + "Выберыце свой сервер" + "Гэты ўліковы запіс быў дэактываваны." + "Няправільнае імя карыстальніка і/або пароль" + "Гэта несапраўдны ідэнтыфікатар карыстальніка. Чаканы фармат: ‘@user:homeserver.org’" + "Выбраны хатні сервер не падтрымлівае пароль або ўваход у OIDC. Калі ласка, звярніцеся да адміністратара або абярыце іншы хатні сервер." + "Увядзіце свае даныя" + "Сардэчна запрашаем!" + "Увайдзіце ў %1$s" + "Змяніць правайдара ўліковага запісу" + "Прыватны сервер для супрацоўнікаў Element." + "Matrix - гэта адкрытая сетка для бяспечнай, дэцэнтралізаванай сувязі." + "Тут будуць захоўвацца вашыя размовы - сапраўды гэтак жа, як вы выкарыстоўваеце паштовага правайдара для захоўвання сваіх лістоў." + "Вы збіраецеся ўвайсці ў %1$s" + "Вы збіраецеся стварыць уліковы запіс на %1$s" + "Зараз існуе высокі попыт на %1$s на %2$s. Калі ласка, вярніцеся ў дадатак праз некалькі дзён і паспрабуйце зноў. + +Дзякуй за цярпенне!" + "Амаль гатова." + "Тут будуць захоўвацца вашыя размовы - сапраўды гэтак жа, як вы выкарыстоўваеце паштовага правайдара для захоўвання сваіх лістоў." + "Тут будуць захоўвацца вашыя размовы - сапраўды гэтак жа, як вы выкарыстоўваеце паштовага правайдара для захоўвання сваіх лістоў." + "Matrix - гэта адкрытая сетка для бяспечнай, дэцэнтралізаванай сувязі." + "Вітаем у %1$s!" + diff --git a/features/login/impl/src/main/res/values-cs/translations.xml b/features/login/impl/src/main/res/values-cs/translations.xml index 0094479b12..8295b140c3 100644 --- a/features/login/impl/src/main/res/values-cs/translations.xml +++ b/features/login/impl/src/main/res/values-cs/translations.xml @@ -5,9 +5,7 @@ "Zadejte hledaný výraz nebo adresu domény." "Vyhledejte společnost, komunitu nebo soukromý server." "Najít poskytovatele účtu" - "Zde budou uloženy vaše konverzace - podobně jako u poskytovatele e-mailových služeb uchováváte své e-maily." "Chystáte se přihlásit do %s" - "Zde budou uloženy vaše konverzace - podobně jako u poskytovatele e-mailových služeb uchováváte své e-maily." "Chystáte se vytvořit účet na %s" "Matrix.org je velký bezplatný server ve veřejné síti Matrix pro bezpečnou decentralizovanou komunikaci, který provozuje nadace Matrix.org." "Jiný" @@ -37,6 +35,8 @@ Díky za trpělivost!" "Jste v pořadníku!" "Jdete do toho!" + "Zde budou uloženy vaše konverzace - podobně jako u poskytovatele e-mailových služeb uchováváte své e-maily." + "Zde budou uloženy vaše konverzace - podobně jako u poskytovatele e-mailových služeb uchováváte své e-maily." "Matrix je otevřená síť pro bezpečnou a decentralizovanou komunikaci." "Vítá vás %1$s!" diff --git a/features/login/impl/src/main/res/values-de/translations.xml b/features/login/impl/src/main/res/values-de/translations.xml index 2603548cab..2dbf83ad8a 100644 --- a/features/login/impl/src/main/res/values-de/translations.xml +++ b/features/login/impl/src/main/res/values-de/translations.xml @@ -5,9 +5,7 @@ "Gib einen Suchbegriff oder eine Domainadresse ein." "Suche nach einem Unternehmen, einer Community oder einem privaten Server." "Kontoanbieter finden" - "Hier werden deine Gespräche gespeichert – genau so, wie du einen E-Mail-Anbieter nutzen würdest, um deine E-Mails aufzubewahren." "Du bist dabei, dich bei %s anzumelden" - "Hier werden deine Gespräche gespeichert – genau so, wie du einen E-Mail-Anbieter nutzen würdest, um deine E-Mails aufzubewahren." "Du bist dabei, ein Konto bei %s zu erstellen" "Matrix.org ist ein großer, kostenloser Server im öffentlichen Matrix-Netzwerk für eine sichere, dezentralisierte Kommunikation, der von der Matrix.org Foundation betrieben wird." "Sonstige" @@ -37,6 +35,8 @@ Danke für deine Geduld!" "Du bist fast am Ziel." "Du bist dabei." + "Hier werden deine Gespräche gespeichert - so wie du deine E-Mails bei einem E-Mail-Anbieter aufbewahren würden." + "Hier werden deine Gespräche gespeichert - so wie du deine E-Mails bei einem E-Mail-Anbieter aufbewahren würden." "Matrix ist ein offenes Netzwerk für eine sichere, dezentrale Kommunikation." "Willkommen bei %1$s!" diff --git a/features/login/impl/src/main/res/values-es/translations.xml b/features/login/impl/src/main/res/values-es/translations.xml index a9020cbd22..e51f908654 100644 --- a/features/login/impl/src/main/res/values-es/translations.xml +++ b/features/login/impl/src/main/res/values-es/translations.xml @@ -5,9 +5,7 @@ "Introduzca un término de búsqueda o una dirección de dominio." "Busca una empresa, comunidad o servidor privado." "Encontrar un proveedor de cuenta" - "Aquí es donde se alojarán tus conversaciones — justo como si utilizaras un proveedor de correo electrónico para guardar tus correos electrónicos." "Estás a punto de iniciar sesión en %s" - "Aquí es donde se alojarán tus conversaciones — justo como si utilizaras un proveedor de correo electrónico para guardar tus correos electrónicos." "Estás a punto de crear una cuenta en %s" "Matrix.org es un servidor grande y gratuito en la red pública Matrix para una comunicación segura y descentralizada, administrado por la Fundación Matrix.org." "Otro" @@ -37,6 +35,8 @@ ¡Gracias por tu paciencia!" "Ya casi has terminado." "Estás dentro." + "Aquí es donde se alojarán tus conversaciones — justo como utilizarías un proveedor de correo electrónico para guardar tus correos electrónicos." + "Aquí es donde se alojarán tus conversaciones — justo como utilizarías un proveedor de correo electrónico para guardar tus correos electrónicos." "Matrix es una red abierta para una comunicación segura y descentralizada." "¡Bienvenido a %1$s!" diff --git a/features/login/impl/src/main/res/values-fr/translations.xml b/features/login/impl/src/main/res/values-fr/translations.xml index eedba864da..b07b674a97 100644 --- a/features/login/impl/src/main/res/values-fr/translations.xml +++ b/features/login/impl/src/main/res/values-fr/translations.xml @@ -5,9 +5,7 @@ "Entrez un terme de recherche ou une adresse de domaine." "Recherchez une entreprise, une communauté ou un serveur privé." "Trouver un fournisseur de comptes" - "C’est ici que vos conversations seront enregistrées, comme vous le feriez avec un fournisseur de messagerie pour conserver vos e-mails." "Vous êtes sur le point de vous connecter à %s" - "C’est ici que vos conversations seront enregistrées, comme vous le feriez avec un fournisseur de messagerie pour conserver vos e-mails." "Vous êtes sur le point de créer un compte sur %s" "Matrix.org est un grand serveur gratuit sur le réseau public Matrix pour une communication sécurisée et décentralisée, géré par la Fondation Matrix.org." "Autres" @@ -37,6 +35,8 @@ Merci pour votre patience !" "Vous y êtes presque." "Vous y êtes." + "C’est ici que vos conversations seront enregistrées, comme vous le feriez avec un fournisseur de messagerie pour conserver vos e-mails." + "C’est ici que vos conversations seront enregistrées, comme vous le feriez avec un fournisseur de messagerie pour conserver vos e-mails." "Matrix est un réseau ouvert pour une communication sécurisée et décentralisée." "Bienvenue dans %1$s !" diff --git a/features/login/impl/src/main/res/values-hu/translations.xml b/features/login/impl/src/main/res/values-hu/translations.xml index e41ff77592..1c3957f028 100644 --- a/features/login/impl/src/main/res/values-hu/translations.xml +++ b/features/login/impl/src/main/res/values-hu/translations.xml @@ -5,10 +5,8 @@ "Adjon meg egy keresési kifejezést vagy egy tartománycímet." "Keresés egy cégre, közösségre vagy privát kiszolgálóra." "Fiókszolgáltató keresése" - "Itt lesznek a beszélgetései – ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez." "Hamarosan bejelentkezik ide: %s" - "Itt lesznek a beszélgetéseid – ahogyan egy e-mail-szolgáltatást is használnál a leveleid kezeléséhez." - "Hamarosan létrehoz egy fiókot itt: %s" + "Hamarosan létrehozol egy fiókot itt: %s" "A Matrix.org egy nagy, ingyenes kiszolgáló a nyilvános Matrix-hálózaton, a biztonságos, decentralizált kommunikáció érdekében, amelyet a Matrix.org Alapítvány üzemeltet." "Egyéb" "Másik fiókszolgáltató, például a saját privát kiszolgáló vagy egy munkahelyi fiók használata." @@ -37,6 +35,8 @@ Köszönjük a türelmét!" "Már majdnem kész van." "Bent van." + "Itt lesznek a beszélgetései – ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez." + "Itt lesznek a beszélgetései – ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez." "A Matrix egy nyitott hálózat a biztonságos, decentralizált kommunikációhoz." "Üdvözli az %1$s!" diff --git a/features/login/impl/src/main/res/values-in/translations.xml b/features/login/impl/src/main/res/values-in/translations.xml index 6d4cb91baa..ed98a36efa 100644 --- a/features/login/impl/src/main/res/values-in/translations.xml +++ b/features/login/impl/src/main/res/values-in/translations.xml @@ -5,9 +5,7 @@ "Masukkan istilah pencarian atau alamat domain." "Cari perusahaan, komunitas, atau server pribadi." "Cari penyedia akun" - "Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda." "Anda akan masuk ke %s" - "Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda." "Anda akan membuat akun di %s" "Matrix.org adalah server besar dan gratis di jaringan Matrix publik untuk komunikasi yang aman dan terdesentralisasi, disediakan oleh Yayasan Matrix.org." "Lainnya" @@ -37,6 +35,8 @@ Terima kasih atas kesabaran Anda!" "Anda hampir selesai." "Anda sudah masuk." + "Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda." + "Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda." "Matrix adalah jaringan terbuka untuk komunikasi yang aman dan terdesentralisasi." "Selamat datang di %1$s!" diff --git a/features/login/impl/src/main/res/values-it/translations.xml b/features/login/impl/src/main/res/values-it/translations.xml index c3e43e5933..0036cf25d3 100644 --- a/features/login/impl/src/main/res/values-it/translations.xml +++ b/features/login/impl/src/main/res/values-it/translations.xml @@ -5,9 +5,7 @@ "Inserisci un termine di ricerca o un indirizzo di dominio." "Cerca un\' azienda, una comunità o un server privato." "Trova un fornitore di account" - "Qui è dove vivranno le tue conversazioni - proprio come useresti un fornitore di posta elettronica per conservare le tue email." "Stai per accedere a %s" - "Qui è dove vivranno le tue conversazioni - proprio come useresti un fornitore di posta elettronica per conservare le tue email." "Stai per creare un account su %s" "Matrix.org è un grande server gratuito nella rete pubblica Matrix per una comunicazione sicura e decentralizzata, gestito dalla Fondazione Matrix.org." "Altro" @@ -37,6 +35,8 @@ Grazie per la pazienza!" "Ci sei quasi." "Sei dentro." + "Qui è dove vivranno le tue conversazioni — proprio come useresti un fornitore di posta elettronica per conservare le tue email." + "Qui è dove vivranno le tue conversazioni — proprio come useresti un fornitore di posta elettronica per conservare le tue email." "Matrix è una rete aperta per comunicazioni sicure e decentralizzate." "Benvenuti in %1$s!" diff --git a/features/login/impl/src/main/res/values-ro/translations.xml b/features/login/impl/src/main/res/values-ro/translations.xml index 49e701558a..bc4ac6d0f8 100644 --- a/features/login/impl/src/main/res/values-ro/translations.xml +++ b/features/login/impl/src/main/res/values-ro/translations.xml @@ -5,9 +5,7 @@ "Introduceţi un termen de căutare sau o adresă de domeniu." "Căutați o companie, o comunitate sau un server privat." "Găsiți un furnizor de cont" - "Aici vor trăi conversațiile dumneavoastră - la fel cum ați folosi un furnizor de e-mail pentru a vă păstra e-mailurile." "Sunteți pe cale să vă conectați la %s" - "Aici vor trăi conversațiile dumneavoastră - la fel cum ați folosi un furnizor de e-mail pentru a vă păstra e-mailurile." "Sunteți pe cale să creați un cont pe %s" "Matrix.org este un server mare și gratuit din rețeaua publică Matrix pentru comunicații sigure și descentralizate, administrat de Fundația Matrix.org." "Altul" @@ -36,6 +34,8 @@ Vă mulțumim pentru răbdare!" "Sunteți pe lista de așteptare" "Sunteți conectat!" + "Aici vor trăi conversațiile dvs. - la fel cum ați folosi un furnizor de e-mail pentru a vă păstra e-mailurile." + "Aici vor trăi conversațiile dvs. - la fel cum ați folosi un furnizor de e-mail pentru a vă păstra e-mailurile." "Matrix este o rețea deschisă pentru o comunicare sigură și descentralizată." "Bun venit la%1$s!" diff --git a/features/login/impl/src/main/res/values-ru/translations.xml b/features/login/impl/src/main/res/values-ru/translations.xml index 0917813950..1451bcd85c 100644 --- a/features/login/impl/src/main/res/values-ru/translations.xml +++ b/features/login/impl/src/main/res/values-ru/translations.xml @@ -5,9 +5,7 @@ "Введите поисковый запрос или адрес домена." "Поиск компании, сообщества или частного сервера." "Поиск сервера учетной записи" - "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем." "Вы собираетесь войти в %s" - "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем." "Вы собираетесь создать учетную запись на %s" "Matrix.org — это большой бесплатный сервер в общедоступной сети Matrix для безопасной децентрализованной связи, управляемый Matrix.org Foundation." "Другое" @@ -37,6 +35,8 @@ Спасибо за терпение!" "Почти готово." "Вы зарегистрированы." + "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем." + "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем." "Matrix — это открытая сеть для безопасной децентрализованной связи." "Добро пожаловать в %1$s!" diff --git a/features/login/impl/src/main/res/values-sk/translations.xml b/features/login/impl/src/main/res/values-sk/translations.xml index b2e41b5a43..016aed6786 100644 --- a/features/login/impl/src/main/res/values-sk/translations.xml +++ b/features/login/impl/src/main/res/values-sk/translations.xml @@ -5,9 +5,7 @@ "Zadajte hľadaný výraz alebo adresu domény." "Vyhľadať spoločnosť, komunitu alebo súkromný server." "Nájsť poskytovateľa účtu" - "Tu budú žiť vaše konverzácie — podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov." "Chystáte sa prihlásiť do %s" - "Tu budú žiť vaše konverzácie — podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov." "Chystáte sa vytvoriť účet na %s" "Matrix.org je veľký bezplatný server vo verejnej sieti Matrix na bezpečnú, decentralizovanú komunikáciu, ktorý prevádzkuje nadácia Matrix.org." "Iný" @@ -37,6 +35,8 @@ Ďakujeme za trpezlivosť!" "Ste na čakanej listine!" "Ste dnu!" + "Tu budú žiť vaše konverzácie - podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov." + "Tu budú žiť vaše konverzácie - podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov." "Matrix je otvorená sieť pre bezpečnú a decentralizovanú komunikáciu." "Vitajte v %1$s!" diff --git a/features/login/impl/src/main/res/values-zh-rTW/translations.xml b/features/login/impl/src/main/res/values-zh-rTW/translations.xml index 7fd944aac2..cdf9a9d9d6 100644 --- a/features/login/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/login/impl/src/main/res/values-zh-rTW/translations.xml @@ -5,9 +5,7 @@ "輸入關鍵字或網域名稱。" "搜尋公司、社群、私有伺服器。" "尋找帳號提供者" - "您的所有對話將保存於此,就如同您的電子郵件供應商會保存您的電子郵件一樣。" "您即將登入 %s" - "您的所有對話將保存於此,就如同您的電子郵件供應商會保存您的電子郵件一樣。" "您即將在 %s 建立帳號" "Matrix.org 由 Matrix.org 基金會營運,是用於安全、去中心化通訊的公共 Matrix 網路上的大型免費伺服器。" "其他" @@ -27,6 +25,8 @@ "您的所有對話將保存於此,就如同您的電子郵件供應商會保存您的電子郵件一樣。" "您即將登入 %1$s" "您即將在 %1$s 建立帳號" + "您的所有對話將保存於此,就如同您的電子郵件供應商會保存您的電子郵件一樣。" + "您的所有對話將保存於此,就如同您的電子郵件供應商會保存您的電子郵件一樣。" "Matrix 是一個開放網路,為了安全且去中心化的通訊而生。" "歡迎使用 %1$s!" diff --git a/features/login/impl/src/main/res/values/localazy.xml b/features/login/impl/src/main/res/values/localazy.xml index 2149318670..bec21c85dc 100644 --- a/features/login/impl/src/main/res/values/localazy.xml +++ b/features/login/impl/src/main/res/values/localazy.xml @@ -5,9 +5,7 @@ "Enter a search term or a domain address." "Search for a company, community, or private server." "Find an account provider" - "This is where your conversations will live — just like you would use an email provider to keep your emails." "You’re about to sign in to %s" - "This is where your conversations will live — just like you would use an email provider to keep your emails." "You’re about to create an account on %s" "Matrix.org is a large, free server on the public Matrix network for secure, decentralised communication, run by the Matrix.org Foundation." "Other" @@ -37,6 +35,8 @@ Thanks for your patience!" "You’re almost there." "You\'re in." + "This is where your conversations will live — just like you would use an email provider to keep your emails." + "This is where your conversations will live — just like you would use an email provider to keep your emails." "Matrix is an open network for secure, decentralised communication." "Welcome to %1$s!" diff --git a/features/logout/impl/src/main/res/values-be/translations.xml b/features/logout/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..237c7ff2b0 --- /dev/null +++ b/features/logout/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,18 @@ + + + "Вы ўпэўнены, што жадаеце выйсці?" + "Выхад…" + "Вы збіраецеся выйсці з апошняга сеанса. Калі вы выйдзеце з сістэмы зараз, вы страціце доступ да зашыфраваных паведамленняў." + "Вы адключылі рэзервовае капіраванне" + "Вашы ключы ўсё яшчэ захоўваліся, калі вы выйшлі з сеткі. Паўторна падключыцеся, каб можна было стварыць рэзервовую копію вашых ключоў перад выхадам." + "Рэзервовае капіраванне ключоў усё яшчэ працягваецца" + "Калі ласка, дачакайцеся завяршэння працэсу, перш чым выходзіць з сістэмы." + "Вашы ключы ўсё яшчэ ствараюцца" + "Вы збіраецеся выйсці з апошняга сеанса. Калі вы выйдзеце з сістэмы зараз, вы страціце доступ да зашыфраваных паведамленняў." + "Аднаўленне не наладжана" + "Вы збіраецеся выйсці з апошняга сеанса. Калі вы выйдзеце з сістэмы зараз, вы страціце доступ да зашыфраваных паведамленняў." + "Вы захавалі свой ключ аднаўлення?" + "Выйсці" + "Выйсці" + "Выйсці" + diff --git a/features/messages/impl/src/main/res/values-be/translations.xml b/features/messages/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..5bef3e7fac --- /dev/null +++ b/features/messages/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,62 @@ + + + "Актыўнасці" + "Сцягі" + "Ежа & Напоі" + "Жывёлы & Прырода" + "Аб\'екты" + "Усмешкі & Людзі" + "Падарожжы & Месцы" + "Сімвалы" + + "%1$d змена ў пакоі" + "%1$d змен у пакоі" + "%1$d змен у пакоі" + + "Гэтае паведамленне будзе перададзена адміністратару вашага хатняга сервера. Яны не змогуць прачытаць зашыфраваныя паведамленні." + "Прычына, па якой вы паскардзіліся на гэты змест" + "Гэта пачатак %1$s." + "Гэта пачатак гэтай размовы." + "Новы" + "Апавясціць увесь пакой" + "Адзначце, ці жадаеце вы схаваць усе бягучыя і будучыя паведамленні ад гэтага карыстальніка" + "Камера" + "Запісаць відэа" + "Далучэнне" + "Фота & Відэа Бібліятэка" + "Месцазнаходжанне" + "Апытанне" + "Фармаціраванне тэксту" + "Гісторыя паведамленняў зараз недаступна." + "Гісторыя паведамленняў у гэтым пакоі недаступная. Праверце гэтую прыладу, каб убачыць гісторыю паведамленняў." + "Не ўдалося атрымаць інфармацыю пра карыстальніка" + "Вы жадаеце запрасіць іх назад?" + "Вы адзін у гэтым чаце" + "Паведамленне скапіравана" + "У вас няма дазволу публікаваць паведамленні ў гэтым пакоі" + "Дазволіць карыстальніцкую наладу" + "Калі гэта ўключыць, ваша налада па змаўчанні будзе адменена" + "Апавяшчаць мяне ў гэтым чаце для" + "Вы можаце змяніць яго ў сваім %1$s." + "глабальныя налады" + "Налада па змаўчанні" + "Выдаліць карыстальніцкую наладу" + "Падчас загрузкі налад апавяшчэнняў адбылася памылка." + "Не атрымалася аднавіць рэжым па змаўчанні, паспрабуйце яшчэ раз." + "Не ўдалося наладзіць рэжым, паспрабуйце яшчэ раз." + "Ваш хатні сервер не падтрымлівае гэту опцыю ў зашыфраваных пакоях, вы не атрымаеце апавяшчэнне ў гэтым пакоі." + "Усе паведамленні" + "У гэтым пакоі паведаміце мяне пра" + "Паказаць менш" + "Паказаць больш" + "Адправіць зноў" + "Не ўдалося адправіць ваша паведамленне" + "Дадаць эмодзі" + "Паказаць менш" + "Утрымлівайце для запісу" + "Усе" + "Заблакіраваць карыстальніка" + "Зрабіць фота" + "Не атрымалася апрацаваць медыяфайл для загрузкі, паспрабуйце яшчэ раз." + "Толькі згадванні і ключавыя словы" + diff --git a/features/messages/impl/src/main/res/values-cs/translations.xml b/features/messages/impl/src/main/res/values-cs/translations.xml index 1cb2a3f57f..bad37ba26b 100644 --- a/features/messages/impl/src/main/res/values-cs/translations.xml +++ b/features/messages/impl/src/main/res/values-cs/translations.xml @@ -31,7 +31,6 @@ "Informujte celou místnost" "Zaškrtněte, pokud chcete skrýt všechny aktuální a budoucí zprávy od tohoto uživatele" "Fotoaparát" - "Vyfotit" "Natočit video" "Příloha" "Knihovna fotografií a videí" @@ -68,6 +67,7 @@ "Držte pro nahrávání" "Všichni" "Zablokovat uživatele" + "Vyfotit" "Nahrání média se nezdařilo, zkuste to prosím znovu." "Pouze zmínky a klíčová slova" diff --git a/features/messages/impl/src/main/res/values-de/translations.xml b/features/messages/impl/src/main/res/values-de/translations.xml index 2ecb2a31a8..8d72d7ffb2 100644 --- a/features/messages/impl/src/main/res/values-de/translations.xml +++ b/features/messages/impl/src/main/res/values-de/translations.xml @@ -20,7 +20,6 @@ "Den ganzen Raum benachrichtigen" "Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest" "Kamera" - "Foto aufnehmen" "Video aufnehmen" "Anhang" "Foto- und Videobibliothek" @@ -56,6 +55,7 @@ "Zum Aufnehmen gedrückt halten" "Alle" "Benutzer blockieren" + "Foto aufnehmen" "Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut." "Nur Erwähnungen und Schlüsselwörter" diff --git a/features/messages/impl/src/main/res/values-es/translations.xml b/features/messages/impl/src/main/res/values-es/translations.xml index 02710138f4..d61d7c1c5a 100644 --- a/features/messages/impl/src/main/res/values-es/translations.xml +++ b/features/messages/impl/src/main/res/values-es/translations.xml @@ -20,7 +20,6 @@ "Notificar a toda la sala" "Marque si quieres ocultar todos los mensajes actuales y futuros de este usuario" "Cámara" - "Hacer foto" "Grabar video" "Archivo adjunto" "Biblioteca de fotos y vídeos" @@ -56,6 +55,7 @@ "Mantén pulsado para grabar" "Todos" "Bloquear usuario" + "Hacer foto" "Error al procesar el contenido multimedia, por favor inténtalo de nuevo." "Únicamente Menciones y Palabras clave" diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml index 51ea5ab07f..e6f6f0e0d7 100644 --- a/features/messages/impl/src/main/res/values-fr/translations.xml +++ b/features/messages/impl/src/main/res/values-fr/translations.xml @@ -28,7 +28,6 @@ "Notifier tout le salon" "Cochez si vous souhaitez masquer tous les messages actuels et futurs de cet utilisateur." "Appareil photo" - "Prendre une photo" "Enregistrer une vidéo" "Pièce jointe" "Galerie Photo et Vidéo" @@ -65,6 +64,7 @@ "Maintenir pour enregistrer" "Tout le monde" "Bloquer l’utilisateur" + "Prendre une photo" "Échec du traitement des médias à télécharger, veuillez réessayer." "Mentions et mots clés uniquement" diff --git a/features/messages/impl/src/main/res/values-hu/translations.xml b/features/messages/impl/src/main/res/values-hu/translations.xml index 4917430141..78e078c3c8 100644 --- a/features/messages/impl/src/main/res/values-hu/translations.xml +++ b/features/messages/impl/src/main/res/values-hu/translations.xml @@ -17,10 +17,9 @@ "Ez a(z) %1$s kezdete." "Ez a beszélgetés kezdete." "Új" - "Teljes szoba értesítése" + "Az egész szoba értesítése" "Jelölje be, ha el akarja rejteni az összes jelenlegi és jövőbeli üzenetet ettől a felhasználótól" "Kamera" - "Fénykép készítése" "Videó rögzítése" "Melléklet" "Fénykép- és videótár" @@ -56,6 +55,7 @@ "Tartsa a rögzítéshez" "Mindenki" "Felhasználó letiltása" + "Fénykép készítése" "Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra." "Csak említések és kulcsszavak" diff --git a/features/messages/impl/src/main/res/values-in/translations.xml b/features/messages/impl/src/main/res/values-in/translations.xml index 3e792da48f..af2a60da68 100644 --- a/features/messages/impl/src/main/res/values-in/translations.xml +++ b/features/messages/impl/src/main/res/values-in/translations.xml @@ -19,7 +19,6 @@ "Beri tahu seluruh ruangan" "Centang jika Anda ingin menyembunyikan semua pesan saat ini dan yang akan datang dari pengguna ini" "Kamera" - "Ambil foto" "Rekam video" "Lampiran" "Pustaka Foto & Video" @@ -55,6 +54,7 @@ "Tahan untuk merekam" "Semua orang" "Blokir pengguna" + "Ambil foto" "Gagal memproses media untuk diunggah, silakan coba lagi." "Sebutan dan Kata Kunci saja" diff --git a/features/messages/impl/src/main/res/values-it/translations.xml b/features/messages/impl/src/main/res/values-it/translations.xml index 0dd03afae2..9bccc57825 100644 --- a/features/messages/impl/src/main/res/values-it/translations.xml +++ b/features/messages/impl/src/main/res/values-it/translations.xml @@ -12,6 +12,14 @@ "%1$d modifica alla stanza" "%1$d modifiche alla stanza" + + "%1$s, %2$s e %3$d altro" + "%1$s, %2$s e altri %3$d" + + + "%1$s sta scrivendo" + "%1$s stanno scrivendo" + "Questo messaggio verrà segnalato all\'amministratore dell\'homeserver. Questi non sarà in grado di leggere i messaggi criptati." "Motivo della segnalazione di questo contenuto" "Questo è l\'inizio di %1$s." @@ -20,7 +28,6 @@ "Avvisa l\'intera stanza" "Seleziona se vuoi nascondere tutti i messaggi attuali e futuri di questo utente" "Fotocamera" - "Scatta foto" "Registra video" "Allegato" "Libreria di foto e video" @@ -53,9 +60,11 @@ "Il tuo messaggio non è stato inviato" "Aggiungi emoji" "Mostra meno" + "%1$s e %2$s" "Tieni premuto per registrare" "Tutti" "Blocca utente" + "Scatta foto" "Elaborazione del file multimediale da caricare fallita, riprova." "Solo menzioni e parole chiave" diff --git a/features/messages/impl/src/main/res/values-ro/translations.xml b/features/messages/impl/src/main/res/values-ro/translations.xml index c15a465976..07b43375af 100644 --- a/features/messages/impl/src/main/res/values-ro/translations.xml +++ b/features/messages/impl/src/main/res/values-ro/translations.xml @@ -20,7 +20,6 @@ "Nou" "Confirmați că doriți să ascundeți toate mesajele curente și viitoare de la acest utilizator" "Cameră foto" - "Faceți o fotografie" "Înregistrați un videoclip" "Atașament" "Bibliotecă foto și video" @@ -52,6 +51,7 @@ "Adăugați emoji" "Afișați mai puțin" "Blocați utilizatorul" + "Faceți o fotografie" "Procesarea datelor media a eșuat, vă rugăm să încercați din nou." "Numai mențiuni și cuvinte cheie" diff --git a/features/messages/impl/src/main/res/values-ru/translations.xml b/features/messages/impl/src/main/res/values-ru/translations.xml index a537ef01ac..8d797c6b09 100644 --- a/features/messages/impl/src/main/res/values-ru/translations.xml +++ b/features/messages/impl/src/main/res/values-ru/translations.xml @@ -31,7 +31,6 @@ "Уведомить всю комнату" "Отметьте, хотите ли вы скрыть все текущие и будущие сообщения от этого пользователя" "Камера" - "Сделать фото" "Записать видео" "Вложение" "Фото и видео" @@ -68,6 +67,7 @@ "Удерживайте для записи" "Для всех" "Заблокировать пользователя" + "Сделать фото" "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." "Только упоминания и ключевые слова" diff --git a/features/messages/impl/src/main/res/values-sk/translations.xml b/features/messages/impl/src/main/res/values-sk/translations.xml index 0bdf008fc2..7d2b0b903c 100644 --- a/features/messages/impl/src/main/res/values-sk/translations.xml +++ b/features/messages/impl/src/main/res/values-sk/translations.xml @@ -21,7 +21,6 @@ "Informovať celú miestnosť" "Označte, či chcete skryť všetky aktuálne a budúce správy od tohto používateľa" "Kamera" - "Odfotiť" "Nahrať video" "Príloha" "Knižnica fotografií a videí" @@ -57,6 +56,7 @@ "Podržaním nahrajte" "Všetci" "Zablokovať používateľa" + "Urobiť fotku" "Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova." "Iba zmienky a kľúčové slová" diff --git a/features/messages/impl/src/main/res/values-zh-rTW/translations.xml b/features/messages/impl/src/main/res/values-zh-rTW/translations.xml index 4f75bf4b0d..71ca37eed4 100644 --- a/features/messages/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/messages/impl/src/main/res/values-zh-rTW/translations.xml @@ -14,7 +14,6 @@ "檢舉這個內容的原因" "新訊息" "照相機" - "拍照" "錄影" "附件" "照片與影片庫" @@ -38,5 +37,6 @@ "較少" "所有人" "封鎖使用者" + "拍照" "僅限提及與關鍵字" diff --git a/features/messages/impl/src/main/res/values/localazy.xml b/features/messages/impl/src/main/res/values/localazy.xml index 3d781ab8cb..ae91b144da 100644 --- a/features/messages/impl/src/main/res/values/localazy.xml +++ b/features/messages/impl/src/main/res/values/localazy.xml @@ -28,7 +28,6 @@ "Notify the whole room" "Check if you want to hide all current and future messages from this user" "Camera" - "Take photo" "Record video" "Attachment" "Photo & Video Library" @@ -65,6 +64,7 @@ "Hold to record" "Everyone" "Block user" + "Take photo" "Failed processing media to upload, please try again." "Mentions and Keywords only" diff --git a/features/onboarding/impl/src/main/res/values-be/translations.xml b/features/onboarding/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..f39880e97e --- /dev/null +++ b/features/onboarding/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,7 @@ + + + "Увайдзіце ўручную" + "Увайдзіце з QR-кодам" + "Стварыць уліковы запіс" + "Сардэчна запрашаем у самы хуткі Element. Перавага ў хуткасці і прастаце." + diff --git a/features/poll/impl/src/main/res/values-be/translations.xml b/features/poll/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..474de08d6e --- /dev/null +++ b/features/poll/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,19 @@ + + + "Дадаць варыянт" + "Паказаць вынікі толькі пасля заканчэння апытання" + "Схаваць галасы" + "Варыянт %1$d" + "Вашы змены не былі захаваны. Вы ўпэўнены, што жадаеце вярнуцца?" + "Пытанне або тэма" + "Пра што апытанне?" + "Стварэнне апытання" + "Вы ўпэўнены, што жадаеце выдаліць гэтае апытанне?" + "Немагчыма знайсці бягучыя апытанні." + "Немагчыма знайсці мінулыя апытанні." + "Бягучыя" + "Мінулыя" + "Апытанні" + "Выдаліць апытанне" + "Рэдагаваць апытанне" + diff --git a/features/poll/impl/src/main/res/values-cs/translations.xml b/features/poll/impl/src/main/res/values-cs/translations.xml index ac1ebfb8dd..43da9e54be 100644 --- a/features/poll/impl/src/main/res/values-cs/translations.xml +++ b/features/poll/impl/src/main/res/values-cs/translations.xml @@ -9,11 +9,11 @@ "Čeho se hlasování týká?" "Vytvořit hlasování" "Opravdu chcete odstranit toto hlasování?" - "Odstranit hlasování" - "Upravit hlasování" "Nelze najít žádná probíhající hlasování." "Nelze najít žádná minulá hlasování." "Probíhající" "Minulé" "Hlasování" + "Odstranit hlasování" + "Upravit hlasování" diff --git a/features/poll/impl/src/main/res/values-de/translations.xml b/features/poll/impl/src/main/res/values-de/translations.xml index 99520079e6..3bdd314a8c 100644 --- a/features/poll/impl/src/main/res/values-de/translations.xml +++ b/features/poll/impl/src/main/res/values-de/translations.xml @@ -9,11 +9,11 @@ "Worum geht es bei der Umfrage?" "Umfrage erstellen" "Bist du dir sicher, dass du diese Umfrage löschen möchtest?" - "Umfrage löschen" - "Umfrage bearbeiten" "Keine laufenden Umfragen vorhanden." "Keine vergangenen Umfragen vorhanden." "Aktuell" "Vergangene" "Umfragen" + "Umfrage löschen" + "Umfrage bearbeiten" diff --git a/features/poll/impl/src/main/res/values-es/translations.xml b/features/poll/impl/src/main/res/values-es/translations.xml index 2789740859..f48255bf31 100644 --- a/features/poll/impl/src/main/res/values-es/translations.xml +++ b/features/poll/impl/src/main/res/values-es/translations.xml @@ -9,11 +9,11 @@ "¿De qué trata la encuesta?" "Crear una Encuesta" "¿Seguro que quieres eliminar esta encuesta?" - "Eliminar encuesta" - "Editar encuesta" "No se pudo encontrar ninguna encuesta en curso." "No se pudo encontrar ninguna encuesta anterior." "En curso" "Anteriores" "Encuestas" + "Eliminar encuesta" + "Editar encuesta" diff --git a/features/poll/impl/src/main/res/values-fr/translations.xml b/features/poll/impl/src/main/res/values-fr/translations.xml index 081b5e53b1..b8050c2067 100644 --- a/features/poll/impl/src/main/res/values-fr/translations.xml +++ b/features/poll/impl/src/main/res/values-fr/translations.xml @@ -9,11 +9,11 @@ "Quel est le sujet du sondage ?" "Créer un sondage" "Êtes-vous certain de vouloir supprimer ce sondage?" - "Supprimer le sondage" - "Modifier le sondage" "Impossible de trouver des sondages en cours." "Impossible de trouver des sondages terminés." "En cours" "Terminés" "Sondages" + "Supprimer le sondage" + "Modifier le sondage" diff --git a/features/poll/impl/src/main/res/values-hu/translations.xml b/features/poll/impl/src/main/res/values-hu/translations.xml index 74b83c8942..142044c486 100644 --- a/features/poll/impl/src/main/res/values-hu/translations.xml +++ b/features/poll/impl/src/main/res/values-hu/translations.xml @@ -9,11 +9,11 @@ "Miről szól ez a szavazás?" "Szavazás létrehozása" "Biztos, hogy törli ezt a szavazást?" - "Szavazás törlése" - "Szavazás szerkesztése" "Nem találhatók folyamatban lévő szavazások." "Nem található korábbi szavazás." "Folyamatban" "Korábbi" "Szavazások" + "Szavazás törlése" + "Szavazás szerkesztése" diff --git a/features/poll/impl/src/main/res/values-in/translations.xml b/features/poll/impl/src/main/res/values-in/translations.xml index f55dccd75d..c947c56295 100644 --- a/features/poll/impl/src/main/res/values-in/translations.xml +++ b/features/poll/impl/src/main/res/values-in/translations.xml @@ -9,11 +9,11 @@ "Tentang apa pemungutan suara ini?" "Buat pemungutan suara" "Apakah Anda yakin ingin menghapus pemungutan suara ini?" - "Hapus pemungutan suara" - "Sunting pemungutan suara" "Tidak dapat menemukan pemungutan suara yang sedang berlangsung." "Tidak dapat menemukan pemungutan suara sebelumnya." "Sedang berlangsung" "Masa lalu" "Pemungutan suara" + "Hapus pemungutan suara" + "Sunting pemungutan suara" diff --git a/features/poll/impl/src/main/res/values-it/translations.xml b/features/poll/impl/src/main/res/values-it/translations.xml index bf7190db23..25a7cda088 100644 --- a/features/poll/impl/src/main/res/values-it/translations.xml +++ b/features/poll/impl/src/main/res/values-it/translations.xml @@ -9,11 +9,11 @@ "Di cosa parla il sondaggio?" "Crea sondaggio" "Vuoi davvero eliminare questo sondaggio?" - "Elimina sondaggio" - "Modifica sondaggio" "Impossibile trovare sondaggi in corso." "Impossibile trovare sondaggi passati." "In corso" "Passato" "Sondaggi" + "Elimina sondaggio" + "Modifica sondaggio" diff --git a/features/poll/impl/src/main/res/values-ru/translations.xml b/features/poll/impl/src/main/res/values-ru/translations.xml index 2ed319bbeb..2035cc2988 100644 --- a/features/poll/impl/src/main/res/values-ru/translations.xml +++ b/features/poll/impl/src/main/res/values-ru/translations.xml @@ -9,11 +9,11 @@ "Тема опроса?" "Создать опрос" "Вы уверены, что хотите удалить этот опрос?" - "Удалить опрос" - "Редактировать опрос" "Не найдено текущих опросов." "Не найдено предыдущих опросов." "Текущие" "Прошлые" "Опросы" + "Удалить опрос" + "Редактировать опрос" diff --git a/features/poll/impl/src/main/res/values-sk/translations.xml b/features/poll/impl/src/main/res/values-sk/translations.xml index cbc6b8582c..bb42d0d684 100644 --- a/features/poll/impl/src/main/res/values-sk/translations.xml +++ b/features/poll/impl/src/main/res/values-sk/translations.xml @@ -9,11 +9,11 @@ "O čom je anketa?" "Vytvoriť anketu" "Ste si istý, že chcete odstrániť túto anketu?" - "Odstrániť anketu" - "Upraviť anketu" "Nepodarilo sa nájsť žiadne prebiehajúce ankety." "Nie je možné nájsť žiadne predchádzajúce ankety." "Prebiehajúce" "Minulé" "Ankety" + "Odstrániť anketu" + "Upraviť anketu" diff --git a/features/poll/impl/src/main/res/values/localazy.xml b/features/poll/impl/src/main/res/values/localazy.xml index 7a7a15ea3c..9c2ca6449c 100644 --- a/features/poll/impl/src/main/res/values/localazy.xml +++ b/features/poll/impl/src/main/res/values/localazy.xml @@ -9,11 +9,11 @@ "What is the poll about?" "Create Poll" "Are you sure you want to delete this poll?" - "Delete Poll" - "Edit poll" "Can\'t find any ongoing polls." "Can\'t find any past polls." "Ongoing" "Past" "Polls" + "Delete Poll" + "Edit poll" diff --git a/features/preferences/impl/src/main/res/values-be/translations.xml b/features/preferences/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..2e5ced17ad --- /dev/null +++ b/features/preferences/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,43 @@ + + + "Базавы URL сервера званкоў Element" + "Задайце свой сервер Element Call." + "Адрас пазначаны няправільна, пераканайцеся, што вы ўказалі пратакол (http/https) і правільны адрас." + "Рэжым распрацоўніка" + "Падайце распрацоўнікам доступ да функцый і функцыянальным магчымасцям." + "Адключыць рэдактар фарматаванага тэксту і ўключыць Markdown." + "Уключыце опцыю для прагляду крыніцы паведамлення на часовай шкале." + "Бачнае імя" + "Ваша бачнае імя" + "Узнікла невядомая памылка, і інфармацыю не ўдалося змяніць." + "Немагчыма абнавіць профіль" + "Рэдагаваць профіль" + "Абнаўленне профілю…" + "Дадатковыя налады" + "Аўдыё і відэа званкі" + "Неадпаведнасць канфігурацыі" + "Мы спрасцілі налады апавяшчэнняў, каб спрасціць пошук опцый. Некаторыя карыстальніцкія наладкі, абраныя вамі раней, не адлюстроўваюцца ў дадзеным меню, але яны ўсё яшчэ актыўныя. + +Калі вы працягнеце, некаторыя налады могуць быць зменены." + "Прамыя чаты" + "Карыстальніцкія налады для кожнага чата" + "Пры абнаўленні налад апавяшчэнняў адбылася памылка." + "Усе паведамленні" + "Толькі згадванні і ключавыя словы" + "Апавяшчаць мяне ў асабістых чатах" + "Апавяшчаць мяне ў групавых чатах" + "Уключыць апавяшчэнні на гэтай прыладзе" + "Канфігурацыя не была выпраўлена, паспрабуйце яшчэ раз." + "Групавыя чаты" + "Запрашэнні" + "Ваш хатні сервер не падтрымлівае гэтую опцыю ў зашыфраваных пакоях, вы можаце не атрымаць апавяшчэнне ў некаторых пакоях." + "Згадванні" + "Усе" + "Згадванні" + "Апавясціць мяне" + "Апавясціць мяне ў @room" + "Каб атрымліваць апавяшчэнні, змяніце свой %1$s." + "налады сістэмы" + "Сістэмныя апавяшчэнні выключаны" + "Апавяшчэнні" + diff --git a/features/preferences/impl/src/main/res/values-it/translations.xml b/features/preferences/impl/src/main/res/values-it/translations.xml index a77e35738a..26c7cacd2d 100644 --- a/features/preferences/impl/src/main/res/values-it/translations.xml +++ b/features/preferences/impl/src/main/res/values-it/translations.xml @@ -6,6 +6,8 @@ "Modalità sviluppatore" "Attiva per avere accesso a caratteristiche e funzionalità per sviluppatori." "Disattiva l\'editor in rich text per digitare Markdown manualmente." + "Conferme di lettura" + "Se disattivato, le tue conferme di lettura non verranno inviate a nessuno. Riceverai comunque conferme di lettura da altri utenti." "Attiva l\'opzione per visualizzare il sorgente del messaggio nella linea temporale." "Nome da mostrare" "Il tuo nome da mostrare" diff --git a/features/rageshake/api/src/main/res/values-be/translations.xml b/features/rageshake/api/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..eccddc73b8 --- /dev/null +++ b/features/rageshake/api/src/main/res/values-be/translations.xml @@ -0,0 +1,6 @@ + + + "Пры апошнім выкарыстанні %1$s адбыўся збой. Жадаеце падзяліцца справаздачай аб збоі?" + "Падобна, што вы трасеце тэлефон. Жадаеце адкрыць экран паведамлення пра памылку?" + "Парог выяўлення" + diff --git a/features/rageshake/impl/src/main/res/values-be/translations.xml b/features/rageshake/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..7d8c8b5327 --- /dev/null +++ b/features/rageshake/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,15 @@ + + + "Далучыць здымак экрана" + "Вы можаце звязацца са мной, калі ў Вас узнікнуць якія-небудзь дадатковыя пытанні." + "Звяжыцеся са мной" + "Рэдагаваць здымак экрана" + "Калі ласка, апішыце памылку. Што вы зрабілі? Якія паводзіны вы чакалі? Што адбылося насамрэч. Калі ласка, апішыце ўсё як магчыма падрабязней." + "Апішыце праблему…" + "Калі магчыма, калі ласка, напішыце апісанне на англійскай мове." + "Адправіць часопісы збояў" + "Дазволіць часопісы" + "Адправіць здымак экрана" + "Каб пераканацца, што ўсё працуе правільна, у паведамленне будуць уключаны часопісы. Каб адправіць паведамленне без часопісаў, адключыце гэтую наладу." + "Пры апошнім выкарыстанні %1$s адбыўся збой. Жадаеце падзяліцца справаздачай аб збоі?" + diff --git a/features/rageshake/impl/src/main/res/values-it/translations.xml b/features/rageshake/impl/src/main/res/values-it/translations.xml index 852a8f68ff..703e882ad2 100644 --- a/features/rageshake/impl/src/main/res/values-it/translations.xml +++ b/features/rageshake/impl/src/main/res/values-it/translations.xml @@ -11,5 +11,6 @@ "Consenti i log" "Invia istantanea schermo" "Per verificare che le cose funzionino come previsto, i log verranno inviati con il tuo messaggio. Per inviare solo il tuo messaggio, disattiva questa impostazione." + "Visualizza i log" "%1$s si è chiuso inaspettatamente l\'ultima volta che è stato usato. Vuoi condividere con noi un rapporto sull\'arresto anomalo?" diff --git a/features/roomdetails/impl/src/main/res/values-be/translations.xml b/features/roomdetails/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..6119feaff9 --- /dev/null +++ b/features/roomdetails/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,55 @@ + + + + "%1$d карыстальнік" + "%1$d карыстальнікаў" + "%1$d карыстальнікаў" + + "Пры абнаўленні налад апавяшчэнняў адбылася памылка." + "Ваш хатні сервер не падтрымлівае гэтую опцыю ў зашыфраваных пакоях, вы можаце не атрымаць апавяшчэнне ў некаторых пакоях." + "Апытанні" + "Дадаць тэму" + "Ужо ўдзельнік" + "Ужо запрасілі" + "Рэдагаваць пакой" + "Адбылася невядомая памылка, і інфармацыю нельга было змяніць." + "Немагчыма абнавіць пакой" + "Паведамленні абаронены замкамі. Толькі ў вас і атрымальнікаў ёсць унікальныя ключы, каб разблакіраваць іх." + "Шыфраванне паведамленняў уключана" + "Пры загрузцы налад апавяшчэнняў адбылася памылка." + "Не атрымалася адключыць гук у гэтым пакоі, паўтарыце спробу." + "Не ўдалося ўключыць гук у гэтым пакоі. Паўтарыце спробу." + "Запрасіць людзей" + "Карыстальніцкі" + "Па змаўчанні" + "Апавяшчэнні" + "Падзяліцца пакоем" + "Ідзе абнаўленне пакоя…" + "У чаканні" + "Карыстальнікі пакоя" + "Дазволіць карыстальніцкую наладу" + "Калі гэта ўключыць, ваша налада па змаўчанні будзе адменена" + "Апавяшчаць мяне ў гэтым чаце для" + "Вы можаце змяніць яго ў сваім %1$s." + "глабальныя налады" + "Налада па змаўчанні" + "Выдаліць карыстальніцкую наладу" + "Падчас загрузкі налад апавяшчэнняў адбылася памылка." + "Не атрымалася аднавіць рэжым па змаўчанні, паспрабуйце яшчэ раз." + "Не ўдалося наладзіць рэжым, паспрабуйце яшчэ раз." + "Ваш хатні сервер не падтрымлівае гэту опцыю ў зашыфраваных пакоях, вы не атрымаеце апавяшчэнне ў гэтым пакоі." + "Усе паведамленні" + "У гэтым пакоі паведаміце мяне пра" + "Пры спробе пачаць чат адбылася памылка" + "Заблакіраваць" + "Заблакіраваныя карыстальнікі не змогуць адпраўляць вам паведамленні, і ўсе іх паведамленні будуць схаваны. Вы можаце разблакіраваць іх у любы час." + "Заблакіраваць карыстальніка" + "Разблакіраваць" + "Вы зноў зможаце ўбачыць усе паведамленні." + "Разблакіраваць карыстальніка" + "Пакінуць пакой" + "Назва пакоя" + "Бяспека" + "Тэма" + "Толькі згадванні і ключавыя словы" + diff --git a/features/roomdetails/impl/src/main/res/values-cs/translations.xml b/features/roomdetails/impl/src/main/res/values-cs/translations.xml index e6316cfec1..f8658bd86b 100644 --- a/features/roomdetails/impl/src/main/res/values-cs/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-cs/translations.xml @@ -23,7 +23,6 @@ "Vlastní" "Výchozí" "Oznámení" - "Název místnosti" "Sdílet místnost" "Aktualizace místnosti…" "Nevyřízeno" @@ -50,6 +49,7 @@ "Odblokovat uživatele" "Opustit konverzaci" "Opustit místnost" + "Název místnosti" "Zabezpečení" "Téma" "Pouze zmínky a klíčová slova" diff --git a/features/roomdetails/impl/src/main/res/values-de/translations.xml b/features/roomdetails/impl/src/main/res/values-de/translations.xml index dbf2742e10..1f7501eb25 100644 --- a/features/roomdetails/impl/src/main/res/values-de/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-de/translations.xml @@ -22,7 +22,6 @@ "Benutzerdefiniert" "Standard" "Benachrichtigungen" - "Raumname" "Raum teilen" "Raum wird aktualisiert…" "Ausstehend" @@ -48,6 +47,7 @@ "Du kannst dann wieder alle Nachrichten von ihnen sehen." "Blockierung aufheben" "Raum verlassen" + "Raumname" "Sicherheit" "Thema" "Nur Erwähnungen und Schlüsselwörter" diff --git a/features/roomdetails/impl/src/main/res/values-es/translations.xml b/features/roomdetails/impl/src/main/res/values-es/translations.xml index f4e598ed71..2a119d013c 100644 --- a/features/roomdetails/impl/src/main/res/values-es/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-es/translations.xml @@ -22,7 +22,6 @@ "Personalizado" "Por defecto" "Notificaciones" - "Nombre de la sala" "Compartir sala" "Actualizando la sala…" "Pendiente" @@ -48,6 +47,7 @@ "Podrás ver todos sus mensajes de nuevo." "Desbloquear usuario" "Salir de la sala" + "Nombre de la sala" "Seguridad" "Tema" "Únicamente Menciones y Palabras clave" diff --git a/features/roomdetails/impl/src/main/res/values-fr/translations.xml b/features/roomdetails/impl/src/main/res/values-fr/translations.xml index 76e3ea81ec..51d40233d4 100644 --- a/features/roomdetails/impl/src/main/res/values-fr/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fr/translations.xml @@ -22,7 +22,6 @@ "Personnalisé" "Défaut" "Notifications" - "Nom du salon" "Partager le salon" "Mise à jour du salon…" "En attente" @@ -49,6 +48,7 @@ "Débloquer l’utilisateur" "Quitter la discussion" "Quitter le salon" + "Nom du salon" "Sécurité" "Sujet" "Mentions et mots clés uniquement" diff --git a/features/roomdetails/impl/src/main/res/values-hu/translations.xml b/features/roomdetails/impl/src/main/res/values-hu/translations.xml index 803b493585..586719a6cc 100644 --- a/features/roomdetails/impl/src/main/res/values-hu/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-hu/translations.xml @@ -22,7 +22,6 @@ "Egyéni" "Alapértelmezett" "Értesítések" - "Szoba neve" "Szoba megosztása" "Szoba frissítése…" "Függőben" @@ -48,6 +47,7 @@ "Újra láthatja az összes üzenetét." "Felhasználó kitiltásának feloldása" "Szoba elhagyása" + "Szoba neve" "Biztonság" "Téma" "Csak említések és kulcsszavak" diff --git a/features/roomdetails/impl/src/main/res/values-in/translations.xml b/features/roomdetails/impl/src/main/res/values-in/translations.xml index d44f24342b..a5f415decc 100644 --- a/features/roomdetails/impl/src/main/res/values-in/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-in/translations.xml @@ -21,7 +21,6 @@ "Khusus" "Bawaan" "Pemberitahuan" - "Nama ruangan" "Bagikan ruangan" "Memperbarui ruangan…" "Tertunda" @@ -47,6 +46,7 @@ "Anda akan dapat melihat semua pesan dari mereka lagi." "Buka blokir pengguna" "Tinggalkan ruangan" + "Nama ruangan" "Keamanan" "Topik" "Sebutan dan Kata Kunci saja" diff --git a/features/roomdetails/impl/src/main/res/values-it/translations.xml b/features/roomdetails/impl/src/main/res/values-it/translations.xml index 664c2dbd85..269ee39a2b 100644 --- a/features/roomdetails/impl/src/main/res/values-it/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-it/translations.xml @@ -22,7 +22,6 @@ "Personalizzato" "Predefinito" "Notifiche" - "Nome stanza" "Condividi stanza" "Aggiornamento della stanza…" "In attesa" @@ -49,6 +48,7 @@ "Sblocca utente" "Abbandona la conversazione" "Esci dalla stanza" + "Nome stanza" "Sicurezza" "Oggetto" "Solo menzioni e parole chiave" diff --git a/features/roomdetails/impl/src/main/res/values-ro/translations.xml b/features/roomdetails/impl/src/main/res/values-ro/translations.xml index be6217d5e9..99581bed6b 100644 --- a/features/roomdetails/impl/src/main/res/values-ro/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ro/translations.xml @@ -20,7 +20,6 @@ "Personalizat" "Implicit" "Notificări" - "Numele camerei" "Partajați camera" "Se actualizează camera…" "În așteptare" @@ -45,6 +44,7 @@ "La deblocarea utilizatorului, veți putea vedea din nou toate mesajele de la acesta." "Deblocați utilizatorul" "Părăsiți camera" + "Numele camerei" "Securitate" "Subiect" "Numai mențiuni și cuvinte cheie" diff --git a/features/roomdetails/impl/src/main/res/values-ru/translations.xml b/features/roomdetails/impl/src/main/res/values-ru/translations.xml index 4412d7be4d..3562a9ff70 100644 --- a/features/roomdetails/impl/src/main/res/values-ru/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ru/translations.xml @@ -23,7 +23,6 @@ "Пользовательский" "По умолчанию" "Уведомления" - "Название комнаты" "Поделиться комнатой" "Обновление комнаты…" "В ожидании" @@ -50,6 +49,7 @@ "Разблокировать пользователя" "Покинуть беседу" "Покинуть комнату" + "Название комнаты" "Безопасность" "Тема" "Только упоминания и ключевые слова" diff --git a/features/roomdetails/impl/src/main/res/values-sk/translations.xml b/features/roomdetails/impl/src/main/res/values-sk/translations.xml index c9afe72b80..4108c25083 100644 --- a/features/roomdetails/impl/src/main/res/values-sk/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-sk/translations.xml @@ -23,7 +23,6 @@ "Vlastné" "Predvolené" "Oznámenia" - "Názov miestnosti" "Zdieľať miestnosť" "Aktualizácia miestnosti…" "Čaká sa" @@ -50,6 +49,7 @@ "Odblokovať používateľa" "Opustiť konverzáciu" "Opustiť miestnosť" + "Názov miestnosti" "Bezpečnosť" "Téma" "Iba zmienky a kľúčové slová" diff --git a/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml index 50202c0e81..1cd51322e5 100644 --- a/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml @@ -17,7 +17,6 @@ "自訂" "預設" "通知" - "聊天室名稱" "分享聊天室" "正在更新聊天室…" "待定" @@ -32,6 +31,7 @@ "解除封鎖" "解除封鎖使用者" "離開聊天室" + "聊天室名稱" "安全性" "主題" "僅限提及與關鍵字" diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index 2693b40edb..b79ad0e8b2 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -22,7 +22,6 @@ "Custom" "Default" "Notifications" - "Room name" "Share room" "Updating room…" "Pending" @@ -49,6 +48,7 @@ "Unblock user" "Leave conversation" "Leave room" + "Room name" "Security" "Topic" "Mentions and Keywords only" diff --git a/features/roomlist/impl/src/main/res/values-be/translations.xml b/features/roomlist/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..b7449b98bc --- /dev/null +++ b/features/roomlist/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,11 @@ + + + "Ваша рэзервовая копія чата зараз не сінхранізавана. Вам трэба пацвердзіць ключ аднаўлення, каб захаваць доступ да рэзервовай копіі чата." + "Пацвердзіце ключ аднаўлення" + "Стварыце новую размову або пакой" + "Пачніце з паведамлення каму-небудзь." + "Пакуль няма чатаў." + "Усе чаты" + "Здаецца, вы карыстаецеся новай прыладай. Праверце з дапамогай іншай прылады, каб атрымаць доступ да зашыфраваных паведамленняў." + "Пацвердзіце, што гэта вы" + diff --git a/features/roomlist/impl/src/main/res/values-cs/translations.xml b/features/roomlist/impl/src/main/res/values-cs/translations.xml index 506c0c60b4..b5858e5767 100644 --- a/features/roomlist/impl/src/main/res/values-cs/translations.xml +++ b/features/roomlist/impl/src/main/res/values-cs/translations.xml @@ -7,10 +7,12 @@ "Zatím žádné konverzace." "Oblíbené" "Nízká priorita" - "Lidé" "Místnosti" "Nepřečtené" "Všechny chaty" + "Označit jako přečtené" + "Označit jako nepřečtené" "Zdá se, že používáte nové zařízení. Ověřte přihlášení, abyste měli přístup k zašifrovaným zprávám." "Ověřte, že jste to vy" + "Lidé" diff --git a/features/roomlist/impl/src/main/res/values-de/translations.xml b/features/roomlist/impl/src/main/res/values-de/translations.xml index d471bd3c7b..edd5b61c0d 100644 --- a/features/roomlist/impl/src/main/res/values-de/translations.xml +++ b/features/roomlist/impl/src/main/res/values-de/translations.xml @@ -8,4 +8,5 @@ "Alle Chats" "Es sieht aus, als würdest du ein neues Gerät verwenden. Verifiziere es mit einem anderen Gerät, damit du auf deine verschlüsselten Nachrichten zugreifen kannst." "Bestätige deine Identität" + "Personen" diff --git a/features/roomlist/impl/src/main/res/values-es/translations.xml b/features/roomlist/impl/src/main/res/values-es/translations.xml index c3f8b23398..dceb9e3b2b 100644 --- a/features/roomlist/impl/src/main/res/values-es/translations.xml +++ b/features/roomlist/impl/src/main/res/values-es/translations.xml @@ -8,4 +8,5 @@ "Todos los chats" "Parece que estás usando un nuevo dispositivo. Verifica que eres tú para acceder a tus mensajes cifrados." "Verifica que eres tú" + "Personas" diff --git a/features/roomlist/impl/src/main/res/values-fr/translations.xml b/features/roomlist/impl/src/main/res/values-fr/translations.xml index 70795bcd5f..85593ed0f5 100644 --- a/features/roomlist/impl/src/main/res/values-fr/translations.xml +++ b/features/roomlist/impl/src/main/res/values-fr/translations.xml @@ -7,10 +7,12 @@ "Aucune discussion pour le moment." "Favoris" "Priorité basse" - "Discussions" "Salons" "Non-lus" "Conversations" + "Marquer comme lu" + "Marquer comme non lu" "Il semblerait que vous utilisiez un nouvel appareil. Vérifiez la session avec un autre de vos appareils pour accéder à vos messages chiffrés." "Vérifier que c’est bien vous" + "Personnes" diff --git a/features/roomlist/impl/src/main/res/values-hu/translations.xml b/features/roomlist/impl/src/main/res/values-hu/translations.xml index 21c3e645a3..d6d8b82210 100644 --- a/features/roomlist/impl/src/main/res/values-hu/translations.xml +++ b/features/roomlist/impl/src/main/res/values-hu/translations.xml @@ -8,4 +8,5 @@ "Összes csevegés" "Úgy tűnik, hogy új eszközt használ. Ellenőrizze egy másik eszközzel, hogy a továbbiakban elérje a titkosított üzeneteket." "Ellenőrizze, hogy Ön az" + "Emberek" diff --git a/features/roomlist/impl/src/main/res/values-in/translations.xml b/features/roomlist/impl/src/main/res/values-in/translations.xml index 58994e4772..09a83b3370 100644 --- a/features/roomlist/impl/src/main/res/values-in/translations.xml +++ b/features/roomlist/impl/src/main/res/values-in/translations.xml @@ -8,4 +8,5 @@ "Semua Obrolan" "Sepertinya Anda menggunakan perangkat baru. Verifikasi dengan perangkat lain untuk mengakses pesan terenkripsi Anda selanjutnya." "Verifikasi bahwa ini Anda" + "Orang" diff --git a/features/roomlist/impl/src/main/res/values-it/translations.xml b/features/roomlist/impl/src/main/res/values-it/translations.xml index 2d186453a9..60ec4f6dbd 100644 --- a/features/roomlist/impl/src/main/res/values-it/translations.xml +++ b/features/roomlist/impl/src/main/res/values-it/translations.xml @@ -5,7 +5,14 @@ "Crea una nuova conversazione o stanza" "Inizia inviando un messaggio a qualcuno." "Ancora nessuna chat." + "Preferiti" + "Bassa priorità" + "Stanze" + "Non letti" "Tutte le conversazioni" + "Segna come letto" + "Segna come non letto" "Sembra che tu stia usando un nuovo dispositivo. Verificati con un altro dispositivo per accedere ai tuoi messaggi cifrati." "Verifica che sei tu" + "Persone" diff --git a/features/roomlist/impl/src/main/res/values-ro/translations.xml b/features/roomlist/impl/src/main/res/values-ro/translations.xml index e1f43a02ca..9bb61c8442 100644 --- a/features/roomlist/impl/src/main/res/values-ro/translations.xml +++ b/features/roomlist/impl/src/main/res/values-ro/translations.xml @@ -6,4 +6,5 @@ "Toate conversatiile" "Se pare că folosiți un dispozitiv nou. Verificați-vă identitatea cu un alt dispozitiv pentru a accesa mesajele dumneavoastră criptate." "Verificați că sunteți dumneavoastră" + "Persoane" diff --git a/features/roomlist/impl/src/main/res/values-ru/translations.xml b/features/roomlist/impl/src/main/res/values-ru/translations.xml index d752ef073a..6b915e7325 100644 --- a/features/roomlist/impl/src/main/res/values-ru/translations.xml +++ b/features/roomlist/impl/src/main/res/values-ru/translations.xml @@ -7,10 +7,12 @@ "Пока нет доступных чатов." "Избранное" "Низкий приоритет" - "Люди" "Комнаты" "Непрочитанные" "Все чаты" + "Пометить как прочитанное" + "Пометить как непрочитанное" "Похоже, вы используете новое устройство. Чтобы получить доступ к зашифрованным сообщениям пройдите верификацию с другим устройством." "Подтвердите, что это вы" + "Люди" diff --git a/features/roomlist/impl/src/main/res/values-sk/translations.xml b/features/roomlist/impl/src/main/res/values-sk/translations.xml index 705d31cee3..3ba59ce857 100644 --- a/features/roomlist/impl/src/main/res/values-sk/translations.xml +++ b/features/roomlist/impl/src/main/res/values-sk/translations.xml @@ -7,10 +7,10 @@ "Zatiaľ žiadne konverzácie." "Obľúbené" "Nízka priorita" - "Ľudia" "Miestnosti" "Neprečítané" "Všetky konverzácie" "Vyzerá to tak, že používate nové zariadenie. Overte svoj prístup k zašifrovaným správam pomocou vášho druhého zariadenia." "Overte, že ste to vy" + "Ľudia" diff --git a/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml index 4ad446a0bf..3a4a5f6585 100644 --- a/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml @@ -4,4 +4,5 @@ "所有聊天室" "您似乎正在使用新的裝置。請使用另一個裝置進行驗證,以存取您的加密訊息。" "驗證這是您本人" + "夥伴" diff --git a/features/roomlist/impl/src/main/res/values/localazy.xml b/features/roomlist/impl/src/main/res/values/localazy.xml index 660017d240..7f0c4cce9c 100644 --- a/features/roomlist/impl/src/main/res/values/localazy.xml +++ b/features/roomlist/impl/src/main/res/values/localazy.xml @@ -7,7 +7,6 @@ "No chats yet." "Favourites" "Low Priority" - "People" "Rooms" "Unreads" "All Chats" @@ -15,4 +14,5 @@ "Mark as unread" "Looks like you’re using a new device. Verify with another device to access your encrypted messages." "Verify it’s you" + "People" diff --git a/features/securebackup/impl/src/main/res/values-be/translations.xml b/features/securebackup/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..d16399978d --- /dev/null +++ b/features/securebackup/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,44 @@ + + + "Выключыць рэзервовае капіраванне" + "Уключыце рэзервовае капіраванне" + "Рэзервовае капіраванне гарантуе, што вы не страціце сваю гісторыю паведамленняў. %1$s." + "Рэзервовая копія" + "Змяніць ключ аднаўлення" + "Пацвердзіце ключ аднаўлення" + "Ваша рэзервовая копія чата зараз не сінхранізавана." + "Наладзьце аднаўленне" + "Атрымайце доступ да зашыфраваных паведамленняў, калі вы страціце ўсе свае прылады або выйдзеце з сістэмы %1$s усюды." + "Адключыць" + "Вы страціце зашыфраваныя паведамленні, калі выйдзеце з усіх прылад." + "Вы ўпэўнены, што жадаеце адключыць рэзервовае капіраванне?" + "Адключэнне рэзервовага капіравання прывядзе да выдалення бягучай рэзервовай копіі ключа шыфравання і адключэння іншых функцый бяспекі. У гэтым выпадку вы:" + "Няма зашыфраванай гісторыі паведамленняў на новых прыладах" + "Калі вы выходзіце з сістэмы, то губляеце доступ да зашыфраваных паведамленняў %1$s усюды" + "Вы ўпэўнены, што жадаеце адключыць рэзервовае капіраванне?" + "Атрымайце новы ключ аднаўлення, калі вы страцілі існуючы. Пасля змены ключа аднаўлення ваш стары больш не будзе працаваць." + "Стварыць новы ключ аднаўлення" + "Пераканайцеся, што вы можаце захаваць ключ аднаўлення ў бяспечным месцы" + "Ключ аднаўлення зменены" + "Змяніць ключ аднаўлення?" + "Увядзіце ключ аднаўлення, каб пацвердзіць доступ да рэзервовай копіі чата." + "Паўтарыце спробу, каб пацвердзіць доступ да рэзервовай копіі чата." + "Няправільны ключ аднаўлення" + "Увядзіце код з 48 сімвалаў." + "Увесці…" + "Ключ аднаўлення пацверджаны" + "Пацвердзіце ключ аднаўлення" + "Ключ аднаўлення скапіраваны" + "Стварэнне…" + "Захаваць ключ аднаўлення" + "Запішыце ключ аднаўлення ў бяспечным месцы або захавайце яго ў менеджары пароляў." + "Націсніце, каб скапіяваць ключ аднаўлення" + "Захавайце ключ аднаўлення" + "Пасля гэтага кроку вы не зможаце атрымаць доступ да новага ключа аднаўлення." + "Вы захавалі свой ключ аднаўлення?" + "Рэзервовая копія чата абаронена ключом аднаўлення. Калі пасля настройкі вам спатрэбіцца новы ключ аднаўлення, вы можаце стварыць яго нанова, выбраўшы \"Змяніць ключ аднаўлення\"." + "Стварыце ключ аднаўлення" + "Пераканайцеся, што вы можаце захаваць ключ аднаўлення ў бяспечным месцы" + "Наладка аднаўлення прайшла паспяхова" + "Наладзьце аднаўленне" + diff --git a/features/signedout/impl/src/main/res/values-be/translations.xml b/features/signedout/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..42cb5e41ff --- /dev/null +++ b/features/signedout/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,8 @@ + + + "Вы змянілі свой пароль на іншым сеансе" + "Вы выдалілі сеанс з іншага сеансу" + "Адміністратар вашага сервера ануляваў ваш доступ" + "Магчыма, вы выйшлі з сістэмы па адной з прычын, пералічаных ніжэй. Калі ласка, увайдзіце яшчэ раз, каб працягнуць выкарыстанне %s." + "Вы выйшлі з сістэмы" + diff --git a/features/verifysession/impl/src/main/res/values-be/translations.xml b/features/verifysession/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..5605dbc962 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,20 @@ + + + "Здаецца, нешта не так. Альбо час чакання запыту скончыўся, альбо запыт быў адхілены." + "Пераканайцеся, што прыведзеныя ніжэй эмодзі супадаюць з эмодзі, паказанымі ў вашым іншым сеансе." + "Параўнайце эмодзі" + "Пераканайцеся, што прыведзеныя ніжэй лічбы супадаюць з лічбамі, паказанымі ў іншым сеансе." + "Параўнайце лічбы" + "Ваш новы сеанс пацверджаны. Ён мае доступ да вашых зашыфраваных паведамленняў, і іншыя карыстальнікі будуць лічыць яго давераным." + "Дакажыце, што гэта вы, каб атрымаць доступ да вашай зашыфраванай гісторыі паведамленняў." + "Адкрыйце існуючы сеанс" + "Паўтарыце праверку" + "Я гатовы" + "Чаканне супадзення" + "Параўнайце ўнікальны набор эмодзі." + "Параўнайце ўнікальныя эмодзі, пераканаўшыся, што яны размешчаны ў тым жа парадку." + "Яны не супадаюць" + "Яны супадаюць" + "Для працягу працы прыміце запыт на запуск працэсу праверкі ў іншым сеансе." + "Чаканне прыняцця запыту" + diff --git a/libraries/androidutils/src/main/res/values-be/translations.xml b/libraries/androidutils/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..699c2537d1 --- /dev/null +++ b/libraries/androidutils/src/main/res/values-be/translations.xml @@ -0,0 +1,4 @@ + + + "Не знойдзена сумяшчальная праграма для выканання гэтага дзеяння." + diff --git a/libraries/eventformatter/impl/src/main/res/values-be/translations.xml b/libraries/eventformatter/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..d46abb9041 --- /dev/null +++ b/libraries/eventformatter/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,57 @@ + + + "(аватар таксама быў зменены)" + "%1$s змяніў аватар" + "Вы змянілі свой аватар" + "%1$s змяніў сваё адлюстраванае імя з %2$s на %3$s" + "Вы змянілі сваё адлюстраванае імя з %1$s на %2$s" + "%1$s выдаліў сваё адлюстраванае імя (яно было %2$s)" + "Вы выдалілі сваё адлюстраванае імя (яно было %1$s)" + "%1$s усталявалі сваё адлюстраванае імя на %2$s" + "Вы ўстанавілі адлюстраванае імя на %1$s" + "%1$s змяніў аватар пакоя" + "Вы змянілі аватар пакоя" + "%1$s выдаліў(-ла) аватар пакоя" + "Вы выдалілі аватар пакоя" + "%1$s заблакіраваў %2$s" + "Вы заблакіравалі %1$s" + "%1$s стварыў пакой" + "Вы стварылі пакой" + "%1$s запрасіў %2$s" + "%1$s прыняў(-ла) запрашэнне" + "Вы прынялі запрашэнне" + "Вы запрасілі %1$s" + "%1$s запрасіў вас" + "%1$s далучыўся да пакоя" + "Вы далучыліся да пакоя" + "%1$s прасіў(-ла) далучыцца" + "%1$s дазволіў(-ла) %2$s далучыцца" + "%1$s дазволіў(-ла) вам далучыцца" + "Вы прасілі далучыцца" + "%1$s адхіліў(-ла) %2$s запыт на далучэнне" + "Вы адхілілі %1$s запыт на далучэнне" + "%1$s адхіліў(-ла) ваш запыт на далучэнне" + "%1$s больш не зацікаўлены(-на) у далучэнні" + "Вы адмянілі запыт на далучэнне" + "%1$s выйшаў з пакоя" + "Вы выйшлі з пакоя" + "%1$s змяніў назву пакоя на: %2$s" + "Вы змянілі назву пакоя на: %1$s" + "%1$s выдаліў(-ла) назву пакоя" + "Вы выдалілі назву пакоя" + "%1$s адхіліў запрашэнне" + "Вы адхілілі запрашэнне" + "%1$s выдаліў %2$s" + "Вы выдалілі %1$s" + "%1$s адправіў(-ла) запрашэнне %2$s далучыцца да пакоя" + "Вы адправілі запрашэнне %1$s далучыцца да пакоя" + "%1$s адклікаў(-ла) запрашэнне для %2$s далучыцца да пакоя" + "Вы адклікалі запрашэнне для %1$s далучыцца да пакоя" + "%1$s змяніў тэму на: %2$s" + "Вы змянілі тэму на: %1$s" + "%1$s выдаліў(-ла) тэму пакоя" + "Вы выдалілі тэму пакоя" + "%1$s разблакіраваў %2$s" + "Вы разблакіравалі %1$s" + "%1$s унеслі невядомую змену ў сяброўства" + diff --git a/libraries/eventformatter/impl/src/main/res/values-hu/translations.xml b/libraries/eventformatter/impl/src/main/res/values-hu/translations.xml index dd38dcbbe4..167b695698 100644 --- a/libraries/eventformatter/impl/src/main/res/values-hu/translations.xml +++ b/libraries/eventformatter/impl/src/main/res/values-hu/translations.xml @@ -39,6 +39,8 @@ "Megváltoztatta a szoba nevét: %1$s" "%1$s eltávolította a szoba nevét" "Eltávolította a szoba nevét" + "%1$s nem változtatott semmin" + "Nem változtatott semmin" "%1$s elutasította a meghívást" "Elutasította a meghívást" "%1$s eltávolította: %2$s" diff --git a/libraries/permissions/api/src/main/res/values-be/translations.xml b/libraries/permissions/api/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..5a489ba844 --- /dev/null +++ b/libraries/permissions/api/src/main/res/values-be/translations.xml @@ -0,0 +1,7 @@ + + + "Каб дазволіць праграме выкарыстоўваць камеру, дайце дазвол у наладах сістэмы." + "Калі ласка, дайце дазвол у наладах сістэмы." + "Каб дазволіць праграме выкарыстоўваць мікрафон, дайце дазвол у наладах сістэмы." + "Каб дазволіць праграме паказваць апавяшчэнні, дайце дазвол у наладах сістэмы." + diff --git a/libraries/push/impl/src/main/res/values-be/translations.xml b/libraries/push/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..bdeaf9b5f6 --- /dev/null +++ b/libraries/push/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,58 @@ + + + "Выклік" + "Праслухоўванне падзей" + "Шумныя апавяшчэнні" + "Ціхія апавяшчэнні" + "** Не атрымалася даслаць - калі ласка, адкрыйце пакой" + "Адхіліць" + "Запрасіў вас у чат" + "Згадаў вас: %1$s" + "Новыя паведамленні" + "Адрэагаваў на %1$s" + "Запрасіў вас далучыцца да пакоя" + "Я" + "Вы праглядаеце апавяшчэнне! Націсніце мяне!" + "%1$s: %2$s" + "%1$s: %2$s %3$s" + "%1$s і %2$s" + "%1$s у %2$s" + "%1$s у %2$s і %3$s" + + "%1$s: %2$d паведамленне" + "%1$s: %2$d паведамленняў" + "%1$s: %2$d паведамленняў" + + + "%d апавяшчэнне" + "%d апавяшчэнняў" + "%d апавяшчэнняў" + + + "%d запрашэнне" + "%d запрашэнняў" + "%d запрашэнняў" + + + "%d новае паведамленне" + "%d новых паведамленняў" + "%d новых паведамленняў" + + + "%d непрачытанае апавяшчэнне" + "%d непрачытаных апавяшчэнняў" + "%d непрачытаных апавяшчэнняў" + + + "%d пакой" + "%d пакояў" + "%d пакояў" + + "Выберыце спосаб атрымання апавяшчэнняў" + "Фонавая сінхранізацыя" + "Сэрвісы Google" + "Службы Google Play не знойдзены. Апавяшчэнні могуць не працаваць належным чынам." + "Апавяшчэнне" + "Далучыцца" + "Хуткі адказ" + diff --git a/libraries/push/impl/src/main/res/values-cs/translations.xml b/libraries/push/impl/src/main/res/values-cs/translations.xml index 2b76d7b93e..22a0ba078c 100644 --- a/libraries/push/impl/src/main/res/values-cs/translations.xml +++ b/libraries/push/impl/src/main/res/values-cs/translations.xml @@ -5,13 +5,11 @@ "Hlasitá oznámení" "Tichá oznámení" "** Nepodařilo se odeslat - otevřete prosím místnost" - "Vstoupit" "Odmítnout" "Vás pozval(a) do chatu" "Zmínili vás: %1$s" "Nové zprávy" "Reagoval(a) s %1$s" - "Označit jako přečtené" "Vás pozval(a) do místnosti" "Já" "Prohlížíte si oznámení! Klikněte na mě!" @@ -55,5 +53,7 @@ "Služby Google" "Nebyly nalezeny žádné funkční služby Google Play. Oznámení nemusí fungovat správně." "Oznámení" + "Vstoupit" + "Označit jako přečtené" "Rychlá odpověď" diff --git a/libraries/push/impl/src/main/res/values-de/translations.xml b/libraries/push/impl/src/main/res/values-de/translations.xml index b807d7548e..c641eda4c3 100644 --- a/libraries/push/impl/src/main/res/values-de/translations.xml +++ b/libraries/push/impl/src/main/res/values-de/translations.xml @@ -5,13 +5,11 @@ "Laute Benachrichtigungen" "Stumme Benachrichtigungen" "** Fehler beim Senden - bitte Raum öffnen" - "Beitreten" "Ablehnen" "Du wurdest zu einem Chat eingeladen" "Hat Dich erwähnt: %1$s" "Neue Nachrichten" "Reagiert mit %1$s" - "Als gelesen markieren" "Du wurdest eingeladen, den Raum zu betreten" "Ich" "Du siehst dir die Benachrichtigung an! Klicke hier!" @@ -49,5 +47,6 @@ "Google-Dienste" "Keine gültigen Google Play-Dienste gefunden. Benachrichtigungen funktionieren möglicherweise nicht richtig." "Mitteilung" + "Beitreten" "Schnelle Antwort" diff --git a/libraries/push/impl/src/main/res/values-es/translations.xml b/libraries/push/impl/src/main/res/values-es/translations.xml index 6f5baf70d7..362614f6d8 100644 --- a/libraries/push/impl/src/main/res/values-es/translations.xml +++ b/libraries/push/impl/src/main/res/values-es/translations.xml @@ -5,13 +5,11 @@ "Notificaciones ruidosas" "Notificaciones silenciosas" "** No se ha podido enviar - por favor, abre la sala" - "Unirse" "Rechazar" "Te invitó a chatear" "Te mencionó: %1$s" "Mensajes nuevos" "Reaccionó con %1$s" - "Marcar como leído" "Te invitó a unirte a la sala" "Yo" "¡Estás viendo la notificación! ¡Haz clic en mí!" @@ -49,5 +47,6 @@ "Servicios de Google" "No se han encontrado Servicios de Google Play válidos. Es posible que las notificaciones no funcionen correctamente." "Notificación" + "Unirse" "Respuesta rápida" diff --git a/libraries/push/impl/src/main/res/values-fr/translations.xml b/libraries/push/impl/src/main/res/values-fr/translations.xml index daf8a092a6..2a1328503f 100644 --- a/libraries/push/impl/src/main/res/values-fr/translations.xml +++ b/libraries/push/impl/src/main/res/values-fr/translations.xml @@ -5,13 +5,11 @@ "Notifications bruyantes" "Notifications silencieuses" "** Échec de l’envoi - veuillez ouvrir le salon" - "Rejoindre" "Rejeter" "Vous a invité(e) à discuter" "Mentionné(e): %1$s" "Nouveaux messages" "A réagi avec %1$s" - "Marquer comme lu" "Vous a invité(e) à rejoindre le salon" "Moi" "Vous êtes en train de voir la notification ! Cliquez-moi !" @@ -49,5 +47,7 @@ "Services Google" "Aucun service Google Play valide n’a été trouvé. Les notifications peuvent ne pas fonctionner correctement." "Notification" + "Rejoindre" + "Marquer comme lu" "Réponse rapide" diff --git a/libraries/push/impl/src/main/res/values-hu/translations.xml b/libraries/push/impl/src/main/res/values-hu/translations.xml index 23c3998bb8..c7c1f209c2 100644 --- a/libraries/push/impl/src/main/res/values-hu/translations.xml +++ b/libraries/push/impl/src/main/res/values-hu/translations.xml @@ -5,14 +5,12 @@ "Zajos értesítések" "Csendes értesítések" "** Nem sikerült elküldeni – kérlek nyisd meg a szobát" - "Csatlakozás" "Elutasítás" "Meghívta, hogy csevegjen" "Megemlítette Önt: %1$s" "Új üzenetek" "Ezzel reagált: %1$s" - "Megjelölés olvasottként" - "Meghívta, hogy csatlakozzon a szobához" + "Meghívott, hogy csatlakozz a szobához" "Én" "Az értesítést nézed! Kattints ide!" "%1$s: %2$s" @@ -49,5 +47,6 @@ "Google szolgáltatások" "A Google Play szolgáltatások nem találhatók. Előfordulhat, hogy az értesítések nem működnek megfelelően." "Értesítés" + "Csatlakozás" "Gyors válasz" diff --git a/libraries/push/impl/src/main/res/values-in/translations.xml b/libraries/push/impl/src/main/res/values-in/translations.xml index 0e3f3699f0..1698df3597 100644 --- a/libraries/push/impl/src/main/res/values-in/translations.xml +++ b/libraries/push/impl/src/main/res/values-in/translations.xml @@ -5,13 +5,11 @@ "Pemberitahuan berisik" "Pemberitahuan diam" "** Gagal mengirim — silakan buka ruangan" - "Bergabung" "Tolak" "Mengundang Anda untuk mengobrol" "Menyebutkan Anda: %1$s" "Pesan Baru" "Menghapus dengan %1$s" - "Tandai sebagai dibaca" "Mengundang Anda untuk bergabung ke ruangan" "Saya" "Anda sedang melihat pemberitahuan ini! Klik saya!" @@ -43,5 +41,6 @@ "Layanan Google" "Tidak ditemukan Layanan Google Play yang valid. Pemberitahuan mungkin tidak berfungsi dengan baik." "Notifikasi" + "Gabung" "Balas cepat" diff --git a/libraries/push/impl/src/main/res/values-it/translations.xml b/libraries/push/impl/src/main/res/values-it/translations.xml index 9e512001be..b167dd2299 100644 --- a/libraries/push/impl/src/main/res/values-it/translations.xml +++ b/libraries/push/impl/src/main/res/values-it/translations.xml @@ -1,15 +1,15 @@ "Chiamata" + "Ascolto degli eventi" + "Notifiche con suono" "Notifiche silenziose" "** Invio fallito - si prega di aprire la stanza" - "Entra" "Rifiuta" "Ti ha invitato a chattare" "Ti ha menzionato: %1$s" "Nuovi messaggi" "Ha reagito con %1$s" - "Segna come letto" "Ti ha invitato ad entrare nella stanza" "Io" "Stai visualizzando la notifica! Cliccami!" @@ -47,5 +47,7 @@ "Servizi Google" "Google Play Services non trovato. Le notifiche non funzioneranno bene." "Notifica" + "Entra" + "Segna come letto" "Risposta rapida" diff --git a/libraries/push/impl/src/main/res/values-ro/translations.xml b/libraries/push/impl/src/main/res/values-ro/translations.xml index 8cabe414a2..4241ff1572 100644 --- a/libraries/push/impl/src/main/res/values-ro/translations.xml +++ b/libraries/push/impl/src/main/res/values-ro/translations.xml @@ -5,12 +5,10 @@ "Notificări zgomotoase" "Notificări silențioase" "** Trimiterea eșuată - vă rugăm să deschideți camera" - "Alăturați-vă" "Respingeți" "V-a invitat la o discuție" "Mesaje noi" "A reacționat cu %1$s" - "Marcați ca citit" "V-a invitat să vă alăturați camerei" "Eu" "Vizualizați o notificare! Faceți clic pe mine!" diff --git a/libraries/push/impl/src/main/res/values-ru/translations.xml b/libraries/push/impl/src/main/res/values-ru/translations.xml index f183396dbc..171b8a3298 100644 --- a/libraries/push/impl/src/main/res/values-ru/translations.xml +++ b/libraries/push/impl/src/main/res/values-ru/translations.xml @@ -5,13 +5,11 @@ "Шумные уведомления" "Бесшумные уведомления" "** Не удалось отправить - пожалуйста, откройте комнату" - "Присоединиться" "Отклонить" "Пригласил вас в чат" "Упомянул вас: %1$s" "Новые сообщения" "Отреагировал на %1$s" - "Отметить как прочитанное" "Пригласил вас в комнату" "Я" "Вы просматриваете уведомление! Нажмите на меня!" @@ -55,5 +53,7 @@ "Сервисы Google" "Не найдены действующие службы Google Play. Уведомления могут работать некорректно." "Уведомление" + "Присоединиться" + "Пометить как прочитанное" "Быстрый ответ" diff --git a/libraries/push/impl/src/main/res/values-sk/translations.xml b/libraries/push/impl/src/main/res/values-sk/translations.xml index bd79b42fc3..8917d57e6a 100644 --- a/libraries/push/impl/src/main/res/values-sk/translations.xml +++ b/libraries/push/impl/src/main/res/values-sk/translations.xml @@ -5,13 +5,11 @@ "Hlasité oznámenia" "Tiché oznámenia" "** Nepodarilo sa odoslať - prosím otvorte miestnosť" - "Pripojiť sa" "Zamietnuť" "Vás pozval/a na konverzáciu" "Spomenul/a vás: %1$s" "Nové správy" "Reagoval/a s %1$s" - "Označiť ako prečítané" "Vás pozval do miestnosti" "Ja" "Prezeráte si oznámenie! Kliknite na mňa!" @@ -55,5 +53,6 @@ "Služby Google" "Nenašli sa žiadne platné služby Google Play. Oznámenia nemusia fungovať správne." "Oznámenie" + "Pripojiť sa" "Rýchla odpoveď" diff --git a/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml b/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml index 76a2ada95b..3239ea6d4b 100644 --- a/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml @@ -3,13 +3,11 @@ "通話" "無聲通知" "** 無法傳送,請開啟聊天室" - "加入" "拒絕" "邀請您聊天" "提及您:%1$s" "新訊息" "回應 %1$s" - "標示為已讀" "邀請您加入聊天室" "我" "您正在查看通知!點我!" @@ -34,5 +32,6 @@ "背景同步" "Google 服務" "通知" + "加入" "快速回覆" diff --git a/libraries/push/impl/src/main/res/values/localazy.xml b/libraries/push/impl/src/main/res/values/localazy.xml index 7d50cd2864..a4fb529b7a 100644 --- a/libraries/push/impl/src/main/res/values/localazy.xml +++ b/libraries/push/impl/src/main/res/values/localazy.xml @@ -5,13 +5,11 @@ "Noisy notifications" "Silent notifications" "** Failed to send - please open room" - "Join" "Reject" "Invited you to chat" "Mentioned you: %1$s" "New Messages" "Reacted with %1$s" - "Mark as read" "Invited you to join the room" "Me" "You are viewing the notification! Click me!" @@ -49,5 +47,7 @@ "Google Services" "No valid Google Play Services found. Notifications may not work properly." "Notification" + "Join" + "Mark as read" "Quick reply" diff --git a/libraries/textcomposer/impl/src/main/res/values-be/translations.xml b/libraries/textcomposer/impl/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..10aa56cf99 --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/values-be/translations.xml @@ -0,0 +1,25 @@ + + + "Пераключыць маркіраваны спіс" + "Закрыць параметры фарматавання" + "Пераключыць блок кода" + "Паведамленне…" + "Стварыце спасылку" + "Рэдагаваць спасылку" + "Ужыць тоўсты шрыфт" + "Ужыць курсіўны фармат" + "Ужыць фармат закрэслівання" + "Ужыць фармат падкрэслення" + "Пераключэнне поўнаэкраннага рэжыму" + "Водступ" + "Ужыць убудаваны фармат кода" + "Усталяваць спасылку" + "Пераключыць нумараваны спіс" + "Адкрыйце параметры кампазіцыі" + "Пераключыць цытату" + "Выдаліць спасылку" + "Без водступу" + "Спасылка" + "Дадаць далучэнне" + "Утрымлівайце для запісу" + diff --git a/libraries/ui-strings/src/main/res/values-be/translations.xml b/libraries/ui-strings/src/main/res/values-be/translations.xml new file mode 100644 index 0000000000..9dd4e336ca --- /dev/null +++ b/libraries/ui-strings/src/main/res/values-be/translations.xml @@ -0,0 +1,246 @@ + + + "Выдаліць" + "Схаваць пароль" + "Перайсці ўніз" + "Толькі згадкі" + "Гук адключаны" + "Старонка %1$d" + "Паўза" + "Поле PIN-кода" + "Прайграць" + "Апытанне" + "Апытанне скончана" + "Рэагаваць з %1$s" + "Рэагаваць з іншымі эмодзі" + "Прачытана %1$s і %2$s" + "Прачытана %1$s" + "Націсніце, каб паказаць усе" + "Выдаліць рэакцыю з %1$s" + "Адправіць файлы" + "Паказаць пароль" + "Пазваніць" + "Меню карыстальніка" + "Запісаць галасавое паведамленне." + "Спыніць запіс" + "Прыняць" + "Дадаць на часовую шкалу" + "Назад" + "Скасаваць" + "Выбраць фота" + "Ачысціць" + "Закрыць" + "Поўная праверка" + "Пацвердзіць" + "Працягнуць" + "Капіраваць" + "Скапіраваць спасылку" + "Скапіраваць спасылку на паведамленне" + "Стварыць" + "Стварыце пакой" + "Адхіліць" + "Выдаліць апытанне" + "Адключыць" + "Гатова" + "Рэдагаваць" + "Рэдагаваць апытанне" + "Уключыць" + "Скончыць апытанне" + "Увядзіце PIN-код" + "Забылі пароль?" + "Пераслаць" + "Запрасіць" + "Запрасіць карыстальникаў" + "Запрасіць карыстальнікаў у %1$s" + "Запрасіць карыстальнікаў у %1$s" + "Запрашэнні" + "Далучыцца" + "Падрабязней" + "Пакінуць" + "Пакінуць пакой" + "Кіраванне ўліковым запісам" + "Кіраванне прыладамі" + "Далей" + "Не" + "Не зараз" + "Добра" + "Налады" + "Адкрыць з дапамогай" + "Хуткі адказ" + "Цытата" + "Рэакцыя" + "Выдаліць" + "Адказаць" + "Адказаць у гутаркі" + "Паведаміць пра памылку" + "Паскардзіцца на змест" + "Паўтарыць" + "Паўтарыце расшыфроўку" + "Захаваць" + "Пошук" + "Адправіць" + "Адправіць паведамленне" + "Падзяліцца" + "Абагуліць спасылку" + "Увайдзіце яшчэ раз" + "Выйсці" + "Усё роўна выйсці" + "Прапусціць" + "Пачаць" + "Пачаць чат" + "Пачаць праверку" + "Націсніце, каб загрузіць карту" + "Зрабіць фота" + "Дакраніцеся, каб убачыць параметры" + "Паўтарыць спробу" + "Паказаць крыніцу" + "Так" + "Загрузіць больш" + "Аб праграме" + "Палітыка дапушчальнага выкарыстання" + "Пашыраныя налады" + "Аналітыка" + "Аўдыё" + "Бурбалкі" + "Рэзервовае капіраванне чата" + "Аўтарскае права" + "Стварэнне пакоя…" + "Выйшаў з пакоя" + "Цёмная" + "Памылка расшыфроўкі" + "Параметры распрацоўшчыка" + "Прамы чат" + "(Адрэдагавана)" + "Рэдагаванне" + "* %1$s %2$s" + "Шыфраванне ўключана" + "Увядзіце свой PIN-код" + "Памылка" + "Усе" + "Файл" + "Файл захаваны ў папку Спампоўкі" + "Перасылка паведамлення" + "GIF" + "Выява" + "У адказ на %1$s" + "Усталяваць APK" + "Гэты Matrix ID не знойдзены, таму запрашэнне можа быць не атрымана." + "Пакінуць пакой" + "Светлая" + "Спасылка скапіравана ў буфер абмену" + "Загрузка…" + "Паведамленне" + "Дзеянні з паведамленням" + "Паведамленне выдалена" + "Сучасны" + "Адключыць гук" + "Вынікаў няма" + "Па-за сеткай" + "Пароль" + "Пастаянная спасылка" + "Дазвол" + "Усяго галасоў: %1$s" + "Вынікі будуць паказаны пасля завяршэння апытання" + "Палітыка прыватнасці" + "Рэакцыя" + "Рэакцыі" + "Ключ аднаўлення" + "Абнаўленне…" + "Адказвае на %1$s" + "Паведаміць пра памылку" + "Паведаміць аб праблеме" + "Скарга прынята" + "Рэдактар фарматаванага тэксту" + "Пакой" + "Назва пакоя" + "напрыклад, назва вашага праекта" + "Блакіроўка экрана" + "Шукаць карыстальніка" + "Вынікі пошуку" + "Бяспека" + "Прагледжана" + "Адпраўка…" + "Памылка адпраўкі" + "Адпраўлена" + "Сервер не падтрымліваецца" + "URL-адрас сервера" + "Налады" + "Выхад" + "Пачатак чата…" + "Стыкер" + "Поспех" + "Прапановы" + "Сінхранізацыя" + "Сістэма" + "Тэкст" + "Паведамленні трэціх асоб" + "Гутарка" + "Тэма" + "Пра што гэты пакой?" + "Немагчыма расшыфраваць" + "Не ўдалося адправіць запрашэнні аднаму або некалькім карыстальнікам." + "Немагчыма адправіць запрашэнне(я)" + "Разблакіраваць" + "Уключыць гук" + "Падзея не падтрымліваецца" + "Імя карыстальніка" + "Праверка адменена" + "Праверка завершана" + "Відэа" + "Галасавое паведамленне" + "Чакаем…" + "Чакаю гэтага паведамлення" + "Вы ўпэўнены, што хочаце скончыць гэтае апытанне?" + "Апытанне: %1$s" + "Праверце прыладу" + "Пацвярджэнне" + "Папярэджанне" + "Не атрымалася стварыць пастаянную спасылку" + "%1$s не атрымалася загрузіць карту. Калі ласка паспрабуйце зноў пазней." + "Не ўдалося загрузіць паведамленні" + "%1$s не магчыма атрымаць доступ да вашага месцазнаходжання. Калі ласка паспрабуйце зноў пазней." + "Не ўдалося загрузіць ваша галасавое паведамленне." + "У %1$s няма дазволу на доступ да вашага месцазнаходжання. Вы можаце даць доступ у Наладах." + "У %1$s няма дазволу на доступ да вашага месцазнаходжання. Дазвольце доступ ніжэй." + "%1$s не мае дазволу на доступ да вашага мікрафона. Дазвольце доступ да запісу галасавога паведамлення." + "Некаторыя паведамленні не былі адпраўлены" + "На жаль, адбылася памылка" + "🔐️ Далучайцеся да мяне %1$s" + "Гэй, пагавары са мной у %1$s: %2$s" + "%1$s Android" + + "Уведзеная лічба %1$d" + "Уведзена %1$d лічбы" + "Уведзена %1$d лічб" + + + "Прачытана %1$s і %2$d іншым" + "Прачытана %1$s і %2$d іншымі" + "Прачытана %1$s і %2$d іншымі" + + + "%1$d карыстальнік" + "%1$d карыстальнікаў" + "%1$d карыстальнікаў" + + + "%d голас" + "%d галасоў" + "%d галасоў" + + "Rageshake паведаміць пра памылку" + "Не ўдалося выбраць носьбіт, паўтарыце спробу." + "Не атрымалася апрацаваць медыяфайл для загрузкі, паспрабуйце яшчэ раз." + "Не атрымалася загрузіць медыяфайлы, паспрабуйце яшчэ раз." + "Падзяліцца месцазнаходжаннем" + "Падзяліцца маім месцазнаходжаннем" + "Адкрыць у Apple Maps" + "Адкрыць у Google Maps" + "Адкрыць у OpenStreetMap" + "Падзяліцеся гэтым месцазнаходжаннем" + "Месцазнаходжанне" + "Версія: %1$s (%2$s)" + "be" + "Памылка" + "Поспех" + diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index 3145967bf0..ae71ff19b0 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -119,6 +119,7 @@ "Zadejte svůj PIN" "Chyba" "Všichni" + "Oblíbené" "Soubor" "Soubor byl uložen do složky Stažené soubory" "Přeposlat zprávu" diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index c56ad20a88..740138e116 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -119,6 +119,7 @@ "Saisissez votre code PIN" "Erreur" "Tout le monde" + "Favori" "Fichier" "Fichier enregistré dans Téléchargements" "Transférer le message" diff --git a/libraries/ui-strings/src/main/res/values-hu/translations.xml b/libraries/ui-strings/src/main/res/values-hu/translations.xml index 18c981005f..169d5a6877 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -91,7 +91,7 @@ "Ellenőrzés elindítása" "Koppints a térkép betöltéséhez" "Fénykép készítése" - "Koppintson a lehetőségekért" + "Koppints a beállításokért" "Próbáld újra" "Forrás megtekintése" "Igen" diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml index ad2738557c..50c0f5b545 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -119,6 +119,7 @@ "Inserisci il PIN" "Errore" "Tutti" + "Preferiti" "File" "File salvato in Download" "Inoltra messaggio" diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index 5477f03af9..b75bd7680e 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -119,6 +119,7 @@ "Введите свой PIN-код" "Ошибка" "Для всех" + "Избранное" "Файл" "Файл сохранен в «Загрузки»" "Переслать сообщение" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 26874d4a19..4865698362 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -119,6 +119,7 @@ "Enter your PIN" "Error" "Everyone" + "Favourite" "File" "File saved to Downloads" "Forward message" From da1e86a51b1e2370b257770d3e8eeefc3551141f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Feb 2024 11:21:01 +0100 Subject: [PATCH 038/119] Upgrade gradle wrapper to 8.6 using command line: ./gradlew wrapper --gradle-version 8.6 --distribution-type all --gradle-distribution-sha256-sum 85719317abd2112f021d4f41f09ec370534ba288432065f4b477b6a3b652910d --- gradle/wrapper/gradle-wrapper.jar | Bin 63721 -> 43462 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c49b765f8051ef9d0a6055ff8e46073d8..d64cd4917707c1f8861d8cb53dd15194d4248596 100644 GIT binary patch literal 43462 zcma&NWl&^owk(X(xVyW%ySuwf;qI=D6|RlDJ2cR^yEKh!@I- zp9QeisK*rlxC>+~7Dk4IxIRsKBHqdR9b3+fyL=ynHmIDe&|>O*VlvO+%z5;9Z$|DJ zb4dO}-R=MKr^6EKJiOrJdLnCJn>np?~vU-1sSFgPu;pthGwf}bG z(1db%xwr#x)r+`4AGu$j7~u2MpVs3VpLp|mx&;>`0p0vH6kF+D2CY0fVdQOZ@h;A` z{infNyvmFUiu*XG}RNMNwXrbec_*a3N=2zJ|Wh5z* z5rAX$JJR{#zP>KY**>xHTuw?|-Rg|o24V)74HcfVT;WtQHXlE+_4iPE8QE#DUm%x0 zEKr75ur~W%w#-My3Tj`hH6EuEW+8K-^5P62$7Sc5OK+22qj&Pd1;)1#4tKihi=~8C zHiQSst0cpri6%OeaR`PY>HH_;CPaRNty%WTm4{wDK8V6gCZlG@U3$~JQZ;HPvDJcT1V{ z?>H@13MJcCNe#5z+MecYNi@VT5|&UiN1D4ATT+%M+h4c$t;C#UAs3O_q=GxK0}8%8 z8J(_M9bayxN}69ex4dzM_P3oh@ZGREjVvn%%r7=xjkqxJP4kj}5tlf;QosR=%4L5y zWhgejO=vao5oX%mOHbhJ8V+SG&K5dABn6!WiKl{|oPkq(9z8l&Mm%(=qGcFzI=eLu zWc_oCLyf;hVlB@dnwY98?75B20=n$>u3b|NB28H0u-6Rpl((%KWEBOfElVWJx+5yg z#SGqwza7f}$z;n~g%4HDU{;V{gXIhft*q2=4zSezGK~nBgu9-Q*rZ#2f=Q}i2|qOp z!!y4p)4o=LVUNhlkp#JL{tfkhXNbB=Ox>M=n6soptJw-IDI|_$is2w}(XY>a=H52d z3zE$tjPUhWWS+5h=KVH&uqQS=$v3nRs&p$%11b%5qtF}S2#Pc`IiyBIF4%A!;AVoI zXU8-Rpv!DQNcF~(qQnyyMy=-AN~U>#&X1j5BLDP{?K!%h!;hfJI>$mdLSvktEr*89 zdJHvby^$xEX0^l9g$xW-d?J;L0#(`UT~zpL&*cEh$L|HPAu=P8`OQZV!-}l`noSp_ zQ-1$q$R-gDL)?6YaM!=8H=QGW$NT2SeZlb8PKJdc=F-cT@j7Xags+Pr*jPtlHFnf- zh?q<6;)27IdPc^Wdy-mX%2s84C1xZq9Xms+==F4);O`VUASmu3(RlgE#0+#giLh-& zcxm3_e}n4{%|X zJp{G_j+%`j_q5}k{eW&TlP}J2wtZ2^<^E(O)4OQX8FDp6RJq!F{(6eHWSD3=f~(h} zJXCf7=r<16X{pHkm%yzYI_=VDP&9bmI1*)YXZeB}F? z(%QsB5fo*FUZxK$oX~X^69;x~j7ms8xlzpt-T15e9}$4T-pC z6PFg@;B-j|Ywajpe4~bk#S6(fO^|mm1hKOPfA%8-_iGCfICE|=P_~e;Wz6my&)h_~ zkv&_xSAw7AZ%ThYF(4jADW4vg=oEdJGVOs>FqamoL3Np8>?!W#!R-0%2Bg4h?kz5I zKV-rKN2n(vUL%D<4oj@|`eJ>0i#TmYBtYmfla;c!ATW%;xGQ0*TW@PTlGG><@dxUI zg>+3SiGdZ%?5N=8uoLA|$4isK$aJ%i{hECP$bK{J#0W2gQ3YEa zZQ50Stn6hqdfxJ*9#NuSLwKFCUGk@c=(igyVL;;2^wi4o30YXSIb2g_ud$ zgpCr@H0qWtk2hK8Q|&wx)}4+hTYlf;$a4#oUM=V@Cw#!$(nOFFpZ;0lc!qd=c$S}Z zGGI-0jg~S~cgVT=4Vo)b)|4phjStD49*EqC)IPwyeKBLcN;Wu@Aeph;emROAwJ-0< z_#>wVm$)ygH|qyxZaet&(Vf%pVdnvKWJn9`%DAxj3ot;v>S$I}jJ$FLBF*~iZ!ZXE zkvui&p}fI0Y=IDX)mm0@tAd|fEHl~J&K}ZX(Mm3cm1UAuwJ42+AO5@HwYfDH7ipIc zmI;1J;J@+aCNG1M`Btf>YT>~c&3j~Qi@Py5JT6;zjx$cvOQW@3oQ>|}GH?TW-E z1R;q^QFjm5W~7f}c3Ww|awg1BAJ^slEV~Pk`Kd`PS$7;SqJZNj->it4DW2l15}xP6 zoCl$kyEF%yJni0(L!Z&14m!1urXh6Btj_5JYt1{#+H8w?5QI%% zo-$KYWNMJVH?Hh@1n7OSu~QhSswL8x0=$<8QG_zepi_`y_79=nK=_ZP_`Em2UI*tyQoB+r{1QYZCpb?2OrgUw#oRH$?^Tj!Req>XiE#~B|~ z+%HB;=ic+R@px4Ld8mwpY;W^A%8%l8$@B@1m5n`TlKI6bz2mp*^^^1mK$COW$HOfp zUGTz-cN9?BGEp}5A!mDFjaiWa2_J2Iq8qj0mXzk; z66JBKRP{p%wN7XobR0YjhAuW9T1Gw3FDvR5dWJ8ElNYF94eF3ebu+QwKjtvVu4L zI9ip#mQ@4uqVdkl-TUQMb^XBJVLW(-$s;Nq;@5gr4`UfLgF$adIhd?rHOa%D);whv z=;krPp~@I+-Z|r#s3yCH+c1US?dnm+C*)r{m+86sTJusLdNu^sqLrfWed^ndHXH`m zd3#cOe3>w-ga(Dus_^ppG9AC>Iq{y%%CK+Cro_sqLCs{VLuK=dev>OL1dis4(PQ5R zcz)>DjEkfV+MO;~>VUlYF00SgfUo~@(&9$Iy2|G0T9BSP?&T22>K46D zL*~j#yJ?)^*%J3!16f)@Y2Z^kS*BzwfAQ7K96rFRIh>#$*$_Io;z>ux@}G98!fWR@ zGTFxv4r~v)Gsd|pF91*-eaZ3Qw1MH$K^7JhWIdX%o$2kCbvGDXy)a?@8T&1dY4`;L z4Kn+f%SSFWE_rpEpL9bnlmYq`D!6F%di<&Hh=+!VI~j)2mfil03T#jJ_s?}VV0_hp z7T9bWxc>Jm2Z0WMU?`Z$xE74Gu~%s{mW!d4uvKCx@WD+gPUQ zV0vQS(Ig++z=EHN)BR44*EDSWIyT~R4$FcF*VEY*8@l=218Q05D2$|fXKFhRgBIEE zdDFB}1dKkoO^7}{5crKX!p?dZWNz$m>1icsXG2N+((x0OIST9Zo^DW_tytvlwXGpn zs8?pJXjEG;T@qrZi%#h93?FP$!&P4JA(&H61tqQi=opRzNpm zkrG}$^t9&XduK*Qa1?355wd8G2CI6QEh@Ua>AsD;7oRUNLPb76m4HG3K?)wF~IyS3`fXuNM>${?wmB zpVz;?6_(Fiadfd{vUCBM*_kt$+F3J+IojI;9L(gc9n3{sEZyzR9o!_mOwFC#tQ{Q~ zP3-`#uK#tP3Q7~Q;4H|wjZHO8h7e4IuBxl&vz2w~D8)w=Wtg31zpZhz%+kzSzL*dV zwp@{WU4i;hJ7c2f1O;7Mz6qRKeASoIv0_bV=i@NMG*l<#+;INk-^`5w@}Dj~;k=|}qM1vq_P z|GpBGe_IKq|LNy9SJhKOQ$c=5L{Dv|Q_lZl=-ky*BFBJLW9&y_C|!vyM~rQx=!vun z?rZJQB5t}Dctmui5i31C_;_}CEn}_W%>oSXtt>@kE1=JW*4*v4tPp;O6 zmAk{)m!)}34pTWg8{i>($%NQ(Tl;QC@J@FfBoc%Gr&m560^kgSfodAFrIjF}aIw)X zoXZ`@IsMkc8_=w%-7`D6Y4e*CG8k%Ud=GXhsTR50jUnm+R*0A(O3UKFg0`K;qp1bl z7``HN=?39ic_kR|^R^~w-*pa?Vj#7|e9F1iRx{GN2?wK!xR1GW!qa=~pjJb-#u1K8 zeR?Y2i-pt}yJq;SCiVHODIvQJX|ZJaT8nO+(?HXbLefulKKgM^B(UIO1r+S=7;kLJ zcH}1J=Px2jsh3Tec&v8Jcbng8;V-`#*UHt?hB(pmOipKwf3Lz8rG$heEB30Sg*2rx zV<|KN86$soN(I!BwO`1n^^uF2*x&vJ$2d$>+`(romzHP|)K_KkO6Hc>_dwMW-M(#S zK(~SiXT1@fvc#U+?|?PniDRm01)f^#55;nhM|wi?oG>yBsa?~?^xTU|fX-R(sTA+5 zaq}-8Tx7zrOy#3*JLIIVsBmHYLdD}!0NP!+ITW+Thn0)8SS!$@)HXwB3tY!fMxc#1 zMp3H?q3eD?u&Njx4;KQ5G>32+GRp1Ee5qMO0lZjaRRu&{W<&~DoJNGkcYF<5(Ab+J zgO>VhBl{okDPn78<%&e2mR{jwVCz5Og;*Z;;3%VvoGo_;HaGLWYF7q#jDX=Z#Ml`H z858YVV$%J|e<1n`%6Vsvq7GmnAV0wW4$5qQ3uR@1i>tW{xrl|ExywIc?fNgYlA?C5 zh$ezAFb5{rQu6i7BSS5*J-|9DQ{6^BVQ{b*lq`xS@RyrsJN?-t=MTMPY;WYeKBCNg z^2|pN!Q^WPJuuO4!|P@jzt&tY1Y8d%FNK5xK(!@`jO2aEA*4 zkO6b|UVBipci?){-Ke=+1;mGlND8)6+P;8sq}UXw2hn;fc7nM>g}GSMWu&v&fqh

iViYT=fZ(|3Ox^$aWPp4a8h24tD<|8-!aK0lHgL$N7Efw}J zVIB!7=T$U`ao1?upi5V4Et*-lTG0XvExbf!ya{cua==$WJyVG(CmA6Of*8E@DSE%L z`V^$qz&RU$7G5mg;8;=#`@rRG`-uS18$0WPN@!v2d{H2sOqP|!(cQ@ zUHo!d>>yFArLPf1q`uBvY32miqShLT1B@gDL4XoVTK&@owOoD)OIHXrYK-a1d$B{v zF^}8D3Y^g%^cnvScOSJR5QNH+BI%d|;J;wWM3~l>${fb8DNPg)wrf|GBP8p%LNGN# z3EaIiItgwtGgT&iYCFy9-LG}bMI|4LdmmJt@V@% zb6B)1kc=T)(|L@0;wr<>=?r04N;E&ef+7C^`wPWtyQe(*pD1pI_&XHy|0gIGHMekd zF_*M4yi6J&Z4LQj65)S zXwdM{SwUo%3SbPwFsHgqF@V|6afT|R6?&S;lw=8% z3}@9B=#JI3@B*#4s!O))~z zc>2_4Q_#&+5V`GFd?88^;c1i7;Vv_I*qt!_Yx*n=;rj!82rrR2rQ8u5(Ejlo{15P% zs~!{%XJ>FmJ})H^I9bn^Re&38H{xA!0l3^89k(oU;bZWXM@kn$#aoS&Y4l^-WEn-fH39Jb9lA%s*WsKJQl?n9B7_~P z-XM&WL7Z!PcoF6_D>V@$CvUIEy=+Z&0kt{szMk=f1|M+r*a43^$$B^MidrT0J;RI` z(?f!O<8UZkm$_Ny$Hth1J#^4ni+im8M9mr&k|3cIgwvjAgjH z8`N&h25xV#v*d$qBX5jkI|xOhQn!>IYZK7l5#^P4M&twe9&Ey@@GxYMxBZq2e7?`q z$~Szs0!g{2fGcp9PZEt|rdQ6bhAgpcLHPz?f-vB?$dc*!9OL?Q8mn7->bFD2Si60* z!O%y)fCdMSV|lkF9w%x~J*A&srMyYY3{=&$}H zGQ4VG_?$2X(0|vT0{=;W$~icCI{b6W{B!Q8xdGhF|D{25G_5_+%s(46lhvNLkik~R z>nr(&C#5wwOzJZQo9m|U<;&Wk!_#q|V>fsmj1g<6%hB{jGoNUPjgJslld>xmODzGjYc?7JSuA?A_QzjDw5AsRgi@Y|Z0{F{!1=!NES-#*f^s4l0Hu zz468))2IY5dmD9pa*(yT5{EyP^G>@ZWumealS-*WeRcZ}B%gxq{MiJ|RyX-^C1V=0 z@iKdrGi1jTe8Ya^x7yyH$kBNvM4R~`fbPq$BzHum-3Zo8C6=KW@||>zsA8-Y9uV5V z#oq-f5L5}V<&wF4@X@<3^C%ptp6+Ce)~hGl`kwj)bsAjmo_GU^r940Z-|`<)oGnh7 zFF0Tde3>ui?8Yj{sF-Z@)yQd~CGZ*w-6p2U<8}JO-sRsVI5dBji`01W8A&3$?}lxBaC&vn0E$c5tW* zX>5(zzZ=qn&!J~KdsPl;P@bmA-Pr8T*)eh_+Dv5=Ma|XSle6t(k8qcgNyar{*ReQ8 zTXwi=8vr>!3Ywr+BhggHDw8ke==NTQVMCK`$69fhzEFB*4+H9LIvdt-#IbhZvpS}} zO3lz;P?zr0*0$%-Rq_y^k(?I{Mk}h@w}cZpMUp|ucs55bcloL2)($u%mXQw({Wzc~ z;6nu5MkjP)0C(@%6Q_I_vsWrfhl7Zpoxw#WoE~r&GOSCz;_ro6i(^hM>I$8y>`!wW z*U^@?B!MMmb89I}2(hcE4zN2G^kwyWCZp5JG>$Ez7zP~D=J^LMjSM)27_0B_X^C(M z`fFT+%DcKlu?^)FCK>QzSnV%IsXVcUFhFdBP!6~se&xxrIxsvySAWu++IrH;FbcY$ z2DWTvSBRfLwdhr0nMx+URA$j3i7_*6BWv#DXfym?ZRDcX9C?cY9sD3q)uBDR3uWg= z(lUIzB)G$Hr!){>E{s4Dew+tb9kvToZp-1&c?y2wn@Z~(VBhqz`cB;{E4(P3N2*nJ z_>~g@;UF2iG{Kt(<1PyePTKahF8<)pozZ*xH~U-kfoAayCwJViIrnqwqO}7{0pHw$ zs2Kx?s#vQr7XZ264>5RNKSL8|Ty^=PsIx^}QqOOcfpGUU4tRkUc|kc7-!Ae6!+B{o~7nFpm3|G5^=0#Bnm6`V}oSQlrX(u%OWnC zoLPy&Q;1Jui&7ST0~#+}I^&?vcE*t47~Xq#YwvA^6^} z`WkC)$AkNub|t@S!$8CBlwbV~?yp&@9h{D|3z-vJXgzRC5^nYm+PyPcgRzAnEi6Q^gslXYRv4nycsy-SJu?lMps-? zV`U*#WnFsdPLL)Q$AmD|0`UaC4ND07+&UmOu!eHruzV|OUox<+Jl|Mr@6~C`T@P%s zW7sgXLF2SSe9Fl^O(I*{9wsFSYb2l%-;&Pi^dpv!{)C3d0AlNY6!4fgmSgj_wQ*7Am7&$z;Jg&wgR-Ih;lUvWS|KTSg!&s_E9_bXBkZvGiC6bFKDWZxsD$*NZ#_8bl zG1P-#@?OQzED7@jlMJTH@V!6k;W>auvft)}g zhoV{7$q=*;=l{O>Q4a@ ziMjf_u*o^PsO)#BjC%0^h>Xp@;5$p{JSYDt)zbb}s{Kbt!T*I@Pk@X0zds6wsefuU zW$XY%yyRGC94=6mf?x+bbA5CDQ2AgW1T-jVAJbm7K(gp+;v6E0WI#kuACgV$r}6L? zd|Tj?^%^*N&b>Dd{Wr$FS2qI#Ucs1yd4N+RBUQiSZGujH`#I)mG&VKoDh=KKFl4=G z&MagXl6*<)$6P}*Tiebpz5L=oMaPrN+caUXRJ`D?=K9!e0f{@D&cZLKN?iNP@X0aF zE(^pl+;*T5qt?1jRC=5PMgV!XNITRLS_=9{CJExaQj;lt!&pdzpK?8p>%Mb+D z?yO*uSung=-`QQ@yX@Hyd4@CI^r{2oiu`%^bNkz+Nkk!IunjwNC|WcqvX~k=><-I3 zDQdbdb|!v+Iz01$w@aMl!R)koD77Xp;eZwzSl-AT zr@Vu{=xvgfq9akRrrM)}=!=xcs+U1JO}{t(avgz`6RqiiX<|hGG1pmop8k6Q+G_mv zJv|RfDheUp2L3=^C=4aCBMBn0aRCU(DQwX-W(RkRwmLeuJYF<0urcaf(=7)JPg<3P zQs!~G)9CT18o!J4{zX{_e}4eS)U-E)0FAt}wEI(c0%HkxgggW;(1E=>J17_hsH^sP z%lT0LGgbUXHx-K*CI-MCrP66UP0PvGqM$MkeLyqHdbgP|_Cm!7te~b8p+e6sQ_3k| zVcwTh6d83ltdnR>D^)BYQpDKlLk3g0Hdcgz2}%qUs9~~Rie)A-BV1mS&naYai#xcZ z(d{8=-LVpTp}2*y)|gR~;qc7fp26}lPcLZ#=JpYcn3AT9(UIdOyg+d(P5T7D&*P}# zQCYplZO5|7+r19%9e`v^vfSS1sbX1c%=w1;oyruXB%Kl$ACgKQ6=qNWLsc=28xJjg zwvsI5-%SGU|3p>&zXVl^vVtQT3o-#$UT9LI@Npz~6=4!>mc431VRNN8od&Ul^+G_kHC`G=6WVWM z%9eWNyy(FTO|A+@x}Ou3CH)oi;t#7rAxdIXfNFwOj_@Y&TGz6P_sqiB`Q6Lxy|Q{`|fgmRG(k+!#b*M+Z9zFce)f-7;?Km5O=LHV9f9_87; zF7%R2B+$?@sH&&-$@tzaPYkw0;=i|;vWdI|Wl3q_Zu>l;XdIw2FjV=;Mq5t1Q0|f< zs08j54Bp`3RzqE=2enlkZxmX6OF+@|2<)A^RNQpBd6o@OXl+i)zO%D4iGiQNuXd+zIR{_lb96{lc~bxsBveIw6umhShTX+3@ZJ=YHh@ zWY3(d0azg;7oHn>H<>?4@*RQbi>SmM=JrHvIG(~BrvI)#W(EAeO6fS+}mxxcc+X~W6&YVl86W9WFSS}Vz-f9vS?XUDBk)3TcF z8V?$4Q)`uKFq>xT=)Y9mMFVTUk*NIA!0$?RP6Ig0TBmUFrq*Q-Agq~DzxjStQyJ({ zBeZ;o5qUUKg=4Hypm|}>>L=XKsZ!F$yNTDO)jt4H0gdQ5$f|d&bnVCMMXhNh)~mN z@_UV6D7MVlsWz+zM+inZZp&P4fj=tm6fX)SG5H>OsQf_I8c~uGCig$GzuwViK54bcgL;VN|FnyQl>Ed7(@>=8$a_UKIz|V6CeVSd2(P z0Uu>A8A+muM%HLFJQ9UZ5c)BSAv_zH#1f02x?h9C}@pN@6{>UiAp>({Fn(T9Q8B z^`zB;kJ5b`>%dLm+Ol}ty!3;8f1XDSVX0AUe5P#@I+FQ-`$(a;zNgz)4x5hz$Hfbg z!Q(z26wHLXko(1`;(BAOg_wShpX0ixfWq3ponndY+u%1gyX)_h=v1zR#V}#q{au6; z!3K=7fQwnRfg6FXtNQmP>`<;!N137paFS%y?;lb1@BEdbvQHYC{976l`cLqn;b8lp zIDY>~m{gDj(wfnK!lpW6pli)HyLEiUrNc%eXTil|F2s(AY+LW5hkKb>TQ3|Q4S9rr zpDs4uK_co6XPsn_z$LeS{K4jFF`2>U`tbgKdyDne`xmR<@6AA+_hPNKCOR-Zqv;xk zu5!HsBUb^!4uJ7v0RuH-7?l?}b=w5lzzXJ~gZcxRKOovSk@|#V+MuX%Y+=;14i*%{)_gSW9(#4%)AV#3__kac1|qUy!uyP{>?U#5wYNq}y$S9pCc zFc~4mgSC*G~j0u#qqp9 z${>3HV~@->GqEhr_Xwoxq?Hjn#=s2;i~g^&Hn|aDKpA>Oc%HlW(KA1?BXqpxB;Ydx)w;2z^MpjJ(Qi(X!$5RC z*P{~%JGDQqojV>2JbEeCE*OEu!$XJ>bWA9Oa_Hd;y)F%MhBRi*LPcdqR8X`NQ&1L# z5#9L*@qxrx8n}LfeB^J{%-?SU{FCwiWyHp682F+|pa+CQa3ZLzBqN1{)h4d6+vBbV zC#NEbQLC;}me3eeYnOG*nXOJZEU$xLZ1<1Y=7r0(-U0P6-AqwMAM`a(Ed#7vJkn6plb4eI4?2y3yOTGmmDQ!z9`wzbf z_OY#0@5=bnep;MV0X_;;SJJWEf^E6Bd^tVJ9znWx&Ks8t*B>AM@?;D4oWUGc z!H*`6d7Cxo6VuyS4Eye&L1ZRhrRmN6Lr`{NL(wDbif|y&z)JN>Fl5#Wi&mMIr5i;x zBx}3YfF>>8EC(fYnmpu~)CYHuHCyr5*`ECap%t@y=jD>!_%3iiE|LN$mK9>- zHdtpy8fGZtkZF?%TW~29JIAfi2jZT8>OA7=h;8T{{k?c2`nCEx9$r zS+*&vt~2o^^J+}RDG@+9&M^K*z4p{5#IEVbz`1%`m5c2};aGt=V?~vIM}ZdPECDI)47|CWBCfDWUbxBCnmYivQ*0Nu_xb*C>~C9(VjHM zxe<*D<#dQ8TlpMX2c@M<9$w!RP$hpG4cs%AI){jp*Sj|*`m)5(Bw*A0$*i-(CA5#%>a)$+jI2C9r6|(>J8InryENI z$NohnxDUB;wAYDwrb*!N3noBTKPpPN}~09SEL18tkG zxgz(RYU_;DPT{l?Q$+eaZaxnsWCA^ds^0PVRkIM%bOd|G2IEBBiz{&^JtNsODs;5z zICt_Zj8wo^KT$7Bg4H+y!Df#3mbl%%?|EXe!&(Vmac1DJ*y~3+kRKAD=Ovde4^^%~ zw<9av18HLyrf*_>Slp;^i`Uy~`mvBjZ|?Ad63yQa#YK`4+c6;pW4?XIY9G1(Xh9WO8{F-Aju+nS9Vmv=$Ac0ienZ+p9*O%NG zMZKy5?%Z6TAJTE?o5vEr0r>f>hb#2w2U3DL64*au_@P!J!TL`oH2r*{>ffu6|A7tv zL4juf$DZ1MW5ZPsG!5)`k8d8c$J$o;%EIL0va9&GzWvkS%ZsGb#S(?{!UFOZ9<$a| zY|a+5kmD5N&{vRqkgY>aHsBT&`rg|&kezoD)gP0fsNYHsO#TRc_$n6Lf1Z{?+DLziXlHrq4sf(!>O{?Tj;Eh@%)+nRE_2VxbN&&%%caU#JDU%vL3}Cb zsb4AazPI{>8H&d=jUaZDS$-0^AxE@utGs;-Ez_F(qC9T=UZX=>ok2k2 ziTn{K?y~a5reD2A)P${NoI^>JXn>`IeArow(41c-Wm~)wiryEP(OS{YXWi7;%dG9v zI?mwu1MxD{yp_rrk!j^cKM)dc4@p4Ezyo%lRN|XyD}}>v=Xoib0gOcdXrQ^*61HNj z=NP|pd>@yfvr-=m{8$3A8TQGMTE7g=z!%yt`8`Bk-0MMwW~h^++;qyUP!J~ykh1GO z(FZ59xuFR$(WE;F@UUyE@Sp>`aVNjyj=Ty>_Vo}xf`e7`F;j-IgL5`1~-#70$9_=uBMq!2&1l zomRgpD58@)YYfvLtPW}{C5B35R;ZVvB<<#)x%srmc_S=A7F@DW8>QOEGwD6suhwCg z>Pa+YyULhmw%BA*4yjDp|2{!T98~<6Yfd(wo1mQ!KWwq0eg+6)o1>W~f~kL<-S+P@$wx*zeI|1t7z#Sxr5 zt6w+;YblPQNplq4Z#T$GLX#j6yldXAqj>4gAnnWtBICUnA&-dtnlh=t0Ho_vEKwV` z)DlJi#!@nkYV#$!)@>udAU*hF?V`2$Hf=V&6PP_|r#Iv*J$9)pF@X3`k;5})9^o4y z&)~?EjX5yX12O(BsFy-l6}nYeuKkiq`u9145&3Ssg^y{5G3Pse z9w(YVa0)N-fLaBq1`P!_#>SS(8fh_5!f{UrgZ~uEdeMJIz7DzI5!NHHqQtm~#CPij z?=N|J>nPR6_sL7!f4hD_|KH`vf8(Wpnj-(gPWH+ZvID}%?~68SwhPTC3u1_cB`otq z)U?6qo!ZLi5b>*KnYHWW=3F!p%h1;h{L&(Q&{qY6)_qxNfbP6E3yYpW!EO+IW3?@J z);4>g4gnl^8klu7uA>eGF6rIGSynacogr)KUwE_R4E5Xzi*Qir@b-jy55-JPC8c~( zo!W8y9OGZ&`xmc8;=4-U9=h{vCqfCNzYirONmGbRQlR`WWlgnY+1wCXbMz&NT~9*| z6@FrzP!LX&{no2!Ln_3|I==_4`@}V?4a;YZKTdw;vT<+K+z=uWbW(&bXEaWJ^W8Td z-3&1bY^Z*oM<=M}LVt>_j+p=2Iu7pZmbXrhQ_k)ysE9yXKygFNw$5hwDn(M>H+e1&9BM5!|81vd%r%vEm zqxY3?F@fb6O#5UunwgAHR9jp_W2zZ}NGp2%mTW@(hz7$^+a`A?mb8|_G*GNMJ) zjqegXQio=i@AINre&%ofexAr95aop5C+0MZ0m-l=MeO8m3epm7U%vZB8+I+C*iNFM z#T3l`gknX;D$-`2XT^Cg*vrv=RH+P;_dfF++cP?B_msQI4j+lt&rX2)3GaJx%W*Nn zkML%D{z5tpHH=dksQ*gzc|}gzW;lwAbxoR07VNgS*-c3d&8J|;@3t^ zVUz*J*&r7DFRuFVDCJDK8V9NN5hvpgGjwx+5n)qa;YCKe8TKtdnh{I7NU9BCN!0dq zczrBk8pE{{@vJa9ywR@mq*J=v+PG;?fwqlJVhijG!3VmIKs>9T6r7MJpC)m!Tc#>g zMtVsU>wbwFJEfwZ{vB|ZlttNe83)$iz`~#8UJ^r)lJ@HA&G#}W&ZH*;k{=TavpjWE z7hdyLZPf*X%Gm}i`Y{OGeeu^~nB8=`{r#TUrM-`;1cBvEd#d!kPqIgYySYhN-*1;L z^byj%Yi}Gx)Wnkosi337BKs}+5H5dth1JA{Ir-JKN$7zC)*}hqeoD(WfaUDPT>0`- z(6sa0AoIqASwF`>hP}^|)a_j2s^PQn*qVC{Q}htR z5-)duBFXT_V56-+UohKXlq~^6uf!6sA#ttk1o~*QEy_Y-S$gAvq47J9Vtk$5oA$Ct zYhYJ@8{hsC^98${!#Ho?4y5MCa7iGnfz}b9jE~h%EAAv~Qxu)_rAV;^cygV~5r_~?l=B`zObj7S=H=~$W zPtI_m%g$`kL_fVUk9J@>EiBH zOO&jtn~&`hIFMS5S`g8w94R4H40mdNUH4W@@XQk1sr17b{@y|JB*G9z1|CrQjd+GX z6+KyURG3;!*BQrentw{B2R&@2&`2}n(z-2&X7#r!{yg@Soy}cRD~j zj9@UBW+N|4HW4AWapy4wfUI- zZ`gSL6DUlgj*f1hSOGXG0IVH8HxK?o2|3HZ;KW{K+yPAlxtb)NV_2AwJm|E)FRs&& z=c^e7bvUsztY|+f^k7NXs$o1EUq>cR7C0$UKi6IooHWlK_#?IWDkvywnzg&ThWo^? z2O_N{5X39#?eV9l)xI(>@!vSB{DLt*oY!K1R8}_?%+0^C{d9a%N4 zoxHVT1&Lm|uDX%$QrBun5e-F`HJ^T$ zmzv)p@4ZHd_w9!%Hf9UYNvGCw2TTTbrj9pl+T9%-_-}L(tES>Or-}Z4F*{##n3~L~TuxjirGuIY#H7{%$E${?p{Q01 zi6T`n;rbK1yIB9jmQNycD~yZq&mbIsFWHo|ZAChSFPQa<(%d8mGw*V3fh|yFoxOOiWJd(qvVb!Z$b88cg->N=qO*4k~6;R==|9ihg&riu#P~s4Oap9O7f%crSr^rljeIfXDEg>wi)&v*a%7zpz<9w z*r!3q9J|390x`Zk;g$&OeN&ctp)VKRpDSV@kU2Q>jtok($Y-*x8_$2piTxun81@vt z!Vj?COa0fg2RPXMSIo26T=~0d`{oGP*eV+$!0I<(4azk&Vj3SiG=Q!6mX0p$z7I}; z9BJUFgT-K9MQQ-0@Z=^7R<{bn2Fm48endsSs`V7_@%8?Bxkqv>BDoVcj?K#dV#uUP zL1ND~?D-|VGKe3Rw_7-Idpht>H6XRLh*U7epS6byiGvJpr%d}XwfusjH9g;Z98H`x zyde%%5mhGOiL4wljCaWCk-&uE4_OOccb9c!ZaWt4B(wYl!?vyzl%7n~QepN&eFUrw zFIOl9c({``6~QD+43*_tzP{f2x41h(?b43^y6=iwyB)2os5hBE!@YUS5?N_tXd=h( z)WE286Fbd>R4M^P{!G)f;h<3Q>Fipuy+d2q-)!RyTgt;wr$(?9ox3;q+{E*ZQHhOn;lM`cjnu9 zXa48ks-v(~b*;MAI<>YZH(^NV8vjb34beE<_cwKlJoR;k6lJNSP6v}uiyRD?|0w+X@o1ONrH8a$fCxXpf? z?$DL0)7|X}Oc%h^zrMKWc-NS9I0Utu@>*j}b@tJ=ixQSJ={4@854wzW@E>VSL+Y{i z#0b=WpbCZS>kUCO_iQz)LoE>P5LIG-hv9E+oG}DtlIDF>$tJ1aw9^LuhLEHt?BCj& z(O4I8v1s#HUi5A>nIS-JK{v!7dJx)^Yg%XjNmlkWAq2*cv#tHgz`Y(bETc6CuO1VkN^L-L3j_x<4NqYb5rzrLC-7uOv z!5e`GZt%B782C5-fGnn*GhDF$%(qP<74Z}3xx+{$4cYKy2ikxI7B2N+2r07DN;|-T->nU&!=Cm#rZt%O_5c&1Z%nlWq3TKAW0w zQqemZw_ue--2uKQsx+niCUou?HjD`xhEjjQd3%rrBi82crq*~#uA4+>vR<_S{~5ce z-2EIl?~s z1=GVL{NxP1N3%=AOaC}j_Fv=ur&THz zyO!d9kHq|c73kpq`$+t+8Bw7MgeR5~`d7ChYyGCBWSteTB>8WAU(NPYt2Dk`@#+}= zI4SvLlyk#pBgVigEe`?NG*vl7V6m+<}%FwPV=~PvvA)=#ths==DRTDEYh4V5}Cf$z@#;< zyWfLY_5sP$gc3LLl2x+Ii)#b2nhNXJ{R~vk`s5U7Nyu^3yFg&D%Txwj6QezMX`V(x z=C`{76*mNb!qHHs)#GgGZ_7|vkt9izl_&PBrsu@}L`X{95-2jf99K)0=*N)VxBX2q z((vkpP2RneSIiIUEnGb?VqbMb=Zia+rF~+iqslydE34cSLJ&BJW^3knX@M;t*b=EA zNvGzv41Ld_T+WT#XjDB840vovUU^FtN_)G}7v)1lPetgpEK9YS^OWFkPoE{ovj^=@ zO9N$S=G$1ecndT_=5ehth2Lmd1II-PuT~C9`XVePw$y8J#dpZ?Tss<6wtVglm(Ok7 z3?^oi@pPio6l&!z8JY(pJvG=*pI?GIOu}e^EB6QYk$#FJQ%^AIK$I4epJ+9t?KjqA+bkj&PQ*|vLttme+`9G=L% ziadyMw_7-M)hS(3E$QGNCu|o23|%O+VN7;Qggp?PB3K-iSeBa2b}V4_wY`G1Jsfz4 z9|SdB^;|I8E8gWqHKx!vj_@SMY^hLEIbSMCuE?WKq=c2mJK z8LoG-pnY!uhqFv&L?yEuxo{dpMTsmCn)95xanqBrNPTgXP((H$9N${Ow~Is-FBg%h z53;|Y5$MUN)9W2HBe2TD`ct^LHI<(xWrw}$qSoei?}s)&w$;&!14w6B6>Yr6Y8b)S z0r71`WmAvJJ`1h&poLftLUS6Ir zC$bG9!Im_4Zjse)#K=oJM9mHW1{%l8sz$1o?ltdKlLTxWWPB>Vk22czVt|1%^wnN@*!l)}?EgtvhC>vlHm^t+ogpgHI1_$1ox9e;>0!+b(tBrmXRB`PY1vp-R**8N7 zGP|QqI$m(Rdu#=(?!(N}G9QhQ%o!aXE=aN{&wtGP8|_qh+7a_j_sU5|J^)vxq;# zjvzLn%_QPHZZIWu1&mRAj;Sa_97p_lLq_{~j!M9N^1yp3U_SxRqK&JnR%6VI#^E12 z>CdOVI^_9aPK2eZ4h&^{pQs}xsijXgFYRIxJ~N7&BB9jUR1fm!(xl)mvy|3e6-B3j zJn#ajL;bFTYJ2+Q)tDjx=3IklO@Q+FFM}6UJr6km7hj7th9n_&JR7fnqC!hTZoM~T zBeaVFp%)0cbPhejX<8pf5HyRUj2>aXnXBqDJe73~J%P(2C?-RT{c3NjE`)om! zl$uewSgWkE66$Kb34+QZZvRn`fob~Cl9=cRk@Es}KQm=?E~CE%spXaMO6YmrMl%9Q zlA3Q$3|L1QJ4?->UjT&CBd!~ru{Ih^in&JXO=|<6J!&qp zRe*OZ*cj5bHYlz!!~iEKcuE|;U4vN1rk$xq6>bUWD*u(V@8sG^7>kVuo(QL@Ki;yL zWC!FT(q{E8#on>%1iAS0HMZDJg{Z{^!De(vSIq&;1$+b)oRMwA3nc3mdTSG#3uYO_ z>+x;7p4I;uHz?ZB>dA-BKl+t-3IB!jBRgdvAbW!aJ(Q{aT>+iz?91`C-xbe)IBoND z9_Xth{6?(y3rddwY$GD65IT#f3<(0o#`di{sh2gm{dw*#-Vnc3r=4==&PU^hCv$qd zjw;>i&?L*Wq#TxG$mFIUf>eK+170KG;~+o&1;Tom9}}mKo23KwdEM6UonXgc z!6N(@k8q@HPw{O8O!lAyi{rZv|DpgfU{py+j(X_cwpKqcalcqKIr0kM^%Br3SdeD> zHSKV94Yxw;pjzDHo!Q?8^0bb%L|wC;4U^9I#pd5O&eexX+Im{ z?jKnCcsE|H?{uGMqVie_C~w7GX)kYGWAg%-?8|N_1#W-|4F)3YTDC+QSq1s!DnOML3@d`mG%o2YbYd#jww|jD$gotpa)kntakp#K;+yo-_ZF9qrNZw<%#C zuPE@#3RocLgPyiBZ+R_-FJ_$xP!RzWm|aN)S+{$LY9vvN+IW~Kf3TsEIvP+B9Mtm! zpfNNxObWQpLoaO&cJh5>%slZnHl_Q~(-Tfh!DMz(dTWld@LG1VRF`9`DYKhyNv z2pU|UZ$#_yUx_B_|MxUq^glT}O5Xt(Vm4Mr02><%C)@v;vPb@pT$*yzJ4aPc_FZ3z z3}PLoMBIM>q_9U2rl^sGhk1VUJ89=*?7|v`{!Z{6bqFMq(mYiA?%KbsI~JwuqVA9$H5vDE+VocjX+G^%bieqx->s;XWlKcuv(s%y%D5Xbc9+ zc(_2nYS1&^yL*ey664&4`IoOeDIig}y-E~_GS?m;D!xv5-xwz+G`5l6V+}CpeJDi^ z%4ed$qowm88=iYG+(`ld5Uh&>Dgs4uPHSJ^TngXP_V6fPyl~>2bhi20QB%lSd#yYn zO05?KT1z@?^-bqO8Cg`;ft>ilejsw@2%RR7;`$Vs;FmO(Yr3Fp`pHGr@P2hC%QcA|X&N2Dn zYf`MqXdHi%cGR@%y7Rg7?d3?an){s$zA{!H;Ie5exE#c~@NhQUFG8V=SQh%UxUeiV zd7#UcYqD=lk-}sEwlpu&H^T_V0{#G?lZMxL7ih_&{(g)MWBnCZxtXg znr#}>U^6!jA%e}@Gj49LWG@*&t0V>Cxc3?oO7LSG%~)Y5}f7vqUUnQ;STjdDU}P9IF9d9<$;=QaXc zL1^X7>fa^jHBu_}9}J~#-oz3Oq^JmGR#?GO7b9a(=R@fw@}Q{{@`Wy1vIQ#Bw?>@X z-_RGG@wt|%u`XUc%W{J z>iSeiz8C3H7@St3mOr_mU+&bL#Uif;+Xw-aZdNYUpdf>Rvu0i0t6k*}vwU`XNO2he z%miH|1tQ8~ZK!zmL&wa3E;l?!!XzgV#%PMVU!0xrDsNNZUWKlbiOjzH-1Uoxm8E#r`#2Sz;-o&qcqB zC-O_R{QGuynW14@)7&@yw1U}uP(1cov)twxeLus0s|7ayrtT8c#`&2~Fiu2=R;1_4bCaD=*E@cYI>7YSnt)nQc zohw5CsK%m?8Ack)qNx`W0_v$5S}nO|(V|RZKBD+btO?JXe|~^Qqur%@eO~<8-L^9d z=GA3-V14ng9L29~XJ>a5k~xT2152zLhM*@zlp2P5Eu}bywkcqR;ISbas&#T#;HZSf z2m69qTV(V@EkY(1Dk3`}j)JMo%ZVJ*5eB zYOjIisi+igK0#yW*gBGj?@I{~mUOvRFQR^pJbEbzFxTubnrw(Muk%}jI+vXmJ;{Q6 zrSobKD>T%}jV4Ub?L1+MGOD~0Ir%-`iTnWZN^~YPrcP5y3VMAzQ+&en^VzKEb$K!Q z<7Dbg&DNXuow*eD5yMr+#08nF!;%4vGrJI++5HdCFcGLfMW!KS*Oi@=7hFwDG!h2< zPunUEAF+HncQkbfFj&pbzp|MU*~60Z(|Ik%Tn{BXMN!hZOosNIseT?R;A`W?=d?5X zK(FB=9mZusYahp|K-wyb={rOpdn=@;4YI2W0EcbMKyo~-#^?h`BA9~o285%oY zfifCh5Lk$SY@|2A@a!T2V+{^!psQkx4?x0HSV`(w9{l75QxMk!)U52Lbhn{8ol?S) zCKo*7R(z!uk<6*qO=wh!Pul{(qq6g6xW;X68GI_CXp`XwO zxuSgPRAtM8K7}5E#-GM!*ydOOG_{A{)hkCII<|2=ma*71ci_-}VPARm3crFQjLYV! z9zbz82$|l01mv`$WahE2$=fAGWkd^X2kY(J7iz}WGS z@%MyBEO=A?HB9=^?nX`@nh;7;laAjs+fbo!|K^mE!tOB>$2a_O0y-*uaIn8k^6Y zSbuv;5~##*4Y~+y7Z5O*3w4qgI5V^17u*ZeupVGH^nM&$qmAk|anf*>r zWc5CV;-JY-Z@Uq1Irpb^O`L_7AGiqd*YpGUShb==os$uN3yYvb`wm6d=?T*it&pDk zo`vhw)RZX|91^^Wa_ti2zBFyWy4cJu#g)_S6~jT}CC{DJ_kKpT`$oAL%b^!2M;JgT zM3ZNbUB?}kP(*YYvXDIH8^7LUxz5oE%kMhF!rnPqv!GiY0o}NR$OD=ITDo9r%4E>E0Y^R(rS^~XjWyVI6 zMOR5rPXhTp*G*M&X#NTL`Hu*R+u*QNoiOKg4CtNPrjgH>c?Hi4MUG#I917fx**+pJfOo!zFM&*da&G_x)L(`k&TPI*t3e^{crd zX<4I$5nBQ8Ax_lmNRa~E*zS-R0sxkz`|>7q_?*e%7bxqNm3_eRG#1ae3gtV9!fQpY z+!^a38o4ZGy9!J5sylDxZTx$JmG!wg7;>&5H1)>f4dXj;B+@6tMlL=)cLl={jLMxY zbbf1ax3S4>bwB9-$;SN2?+GULu;UA-35;VY*^9Blx)Jwyb$=U!D>HhB&=jSsd^6yw zL)?a|>GxU!W}ocTC(?-%z3!IUhw^uzc`Vz_g>-tv)(XA#JK^)ZnC|l1`@CdX1@|!| z_9gQ)7uOf?cR@KDp97*>6X|;t@Y`k_N@)aH7gY27)COv^P3ya9I{4z~vUjLR9~z1Z z5=G{mVtKH*&$*t0@}-i_v|3B$AHHYale7>E+jP`ClqG%L{u;*ff_h@)al?RuL7tOO z->;I}>%WI{;vbLP3VIQ^iA$4wl6@0sDj|~112Y4OFjMs`13!$JGkp%b&E8QzJw_L5 zOnw9joc0^;O%OpF$Qp)W1HI!$4BaXX84`%@#^dk^hFp^pQ@rx4g(8Xjy#!X%+X5Jd@fs3amGT`}mhq#L97R>OwT5-m|h#yT_-v@(k$q7P*9X~T*3)LTdzP!*B} z+SldbVWrrwQo9wX*%FyK+sRXTa@O?WM^FGWOE?S`R(0P{<6p#f?0NJvnBia?k^fX2 zNQs7K-?EijgHJY}&zsr;qJ<*PCZUd*x|dD=IQPUK_nn)@X4KWtqoJNHkT?ZWL_hF? zS8lp2(q>;RXR|F;1O}EE#}gCrY~#n^O`_I&?&z5~7N;zL0)3Tup`%)oHMK-^r$NT% zbFg|o?b9w(q@)6w5V%si<$!U<#}s#x@0aX-hP>zwS#9*75VXA4K*%gUc>+yzupTDBOKH8WR4V0pM(HrfbQ&eJ79>HdCvE=F z|J>s;;iDLB^3(9}?biKbxf1$lI!*Z%*0&8UUq}wMyPs_hclyQQi4;NUY+x2qy|0J; zhn8;5)4ED1oHwg+VZF|80<4MrL97tGGXc5Sw$wAI#|2*cvQ=jB5+{AjMiDHmhUC*a zlmiZ`LAuAn_}hftXh;`Kq0zblDk8?O-`tnilIh|;3lZp@F_osJUV9`*R29M?7H{Fy z`nfVEIDIWXmU&YW;NjU8)EJpXhxe5t+scf|VXM!^bBlwNh)~7|3?fWwo_~ZFk(22% zTMesYw+LNx3J-_|DM~`v93yXe=jPD{q;li;5PD?Dyk+b? zo21|XpT@)$BM$%F=P9J19Vi&1#{jM3!^Y&fr&_`toi`XB1!n>sbL%U9I5<7!@?t)~ z;&H%z>bAaQ4f$wIzkjH70;<8tpUoxzKrPhn#IQfS%9l5=Iu))^XC<58D!-O z{B+o5R^Z21H0T9JQ5gNJnqh#qH^na|z92=hONIM~@_iuOi|F>jBh-?aA20}Qx~EpDGElELNn~|7WRXRFnw+Wdo`|# zBpU=Cz3z%cUJ0mx_1($X<40XEIYz(`noWeO+x#yb_pwj6)R(__%@_Cf>txOQ74wSJ z0#F3(zWWaR-jMEY$7C*3HJrohc79>MCUu26mfYN)f4M~4gD`}EX4e}A!U}QV8!S47 z6y-U-%+h`1n`*pQuKE%Av0@)+wBZr9mH}@vH@i{v(m-6QK7Ncf17x_D=)32`FOjjo zg|^VPf5c6-!FxN{25dvVh#fog=NNpXz zfB$o+0jbRkHH{!TKhE709f+jI^$3#v1Nmf80w`@7-5$1Iv_`)W^px8P-({xwb;D0y z7LKDAHgX<84?l!I*Dvi2#D@oAE^J|g$3!)x1Ua;_;<@#l1fD}lqU2_tS^6Ht$1Wl} zBESo7o^)9-Tjuz$8YQSGhfs{BQV6zW7dA?0b(Dbt=UnQs&4zHfe_sj{RJ4uS-vQpC zX;Bbsuju4%!o8?&m4UZU@~ZZjeFF6ex2ss5_60_JS_|iNc+R0GIjH1@Z z=rLT9%B|WWgOrR7IiIwr2=T;Ne?30M!@{%Qf8o`!>=s<2CBpCK_TWc(DX51>e^xh8 z&@$^b6CgOd7KXQV&Y4%}_#uN*mbanXq(2=Nj`L7H7*k(6F8s6{FOw@(DzU`4-*77{ zF+dxpv}%mFpYK?>N_2*#Y?oB*qEKB}VoQ@bzm>ptmVS_EC(#}Lxxx730trt0G)#$b zE=wVvtqOct1%*9}U{q<)2?{+0TzZzP0jgf9*)arV)*e!f`|jgT{7_9iS@e)recI#z zbzolURQ+TOzE!ymqvBY7+5NnAbWxvMLsLTwEbFqW=CPyCsmJ}P1^V30|D5E|p3BC5 z)3|qgw@ra7aXb-wsa|l^in~1_fm{7bS9jhVRkYVO#U{qMp z)Wce+|DJ}4<2gp8r0_xfZpMo#{Hl2MfjLcZdRB9(B(A(f;+4s*FxV{1F|4d`*sRNd zp4#@sEY|?^FIJ;tmH{@keZ$P(sLh5IdOk@k^0uB^BWr@pk6mHy$qf&~rI>P*a;h0C{%oA*i!VjWn&D~O#MxN&f@1Po# zKN+ zrGrkSjcr?^R#nGl<#Q722^wbYcgW@{+6CBS<1@%dPA8HC!~a`jTz<`g_l5N1M@9wn9GOAZ>nqNgq!yOCbZ@1z`U_N`Z>}+1HIZxk*5RDc&rd5{3qjRh8QmT$VyS;jK z;AF+r6XnnCp=wQYoG|rT2@8&IvKq*IB_WvS%nt%e{MCFm`&W*#LXc|HrD?nVBo=(8*=Aq?u$sDA_sC_RPDUiQ+wnIJET8vx$&fxkW~kP9qXKt zozR)@xGC!P)CTkjeWvXW5&@2?)qt)jiYWWBU?AUtzAN}{JE1I)dfz~7$;}~BmQF`k zpn11qmObXwRB8&rnEG*#4Xax3XBkKlw(;tb?Np^i+H8m(Wyz9k{~ogba@laiEk;2! zV*QV^6g6(QG%vX5Um#^sT&_e`B1pBW5yVth~xUs#0}nv?~C#l?W+9Lsb_5)!71rirGvY zTIJ$OPOY516Y|_014sNv+Z8cc5t_V=i>lWV=vNu#!58y9Zl&GsMEW#pPYPYGHQ|;vFvd*9eM==$_=vc7xnyz0~ zY}r??$<`wAO?JQk@?RGvkWVJlq2dk9vB(yV^vm{=NVI8dhsX<)O(#nr9YD?I?(VmQ z^r7VfUBn<~p3()8yOBjm$#KWx!5hRW)5Jl7wY@ky9lNM^jaT##8QGVsYeaVywmpv>X|Xj7gWE1Ezai&wVLt3p)k4w~yrskT-!PR!kiyQlaxl(( zXhF%Q9x}1TMt3~u@|#wWm-Vq?ZerK={8@~&@9r5JW}r#45#rWii};t`{5#&3$W)|@ zbAf2yDNe0q}NEUvq_Quq3cTjcw z@H_;$hu&xllCI9CFDLuScEMg|x{S7GdV8<&Mq=ezDnRZAyX-8gv97YTm0bg=d)(>N z+B2FcqvI9>jGtnK%eO%y zoBPkJTk%y`8TLf4)IXPBn`U|9>O~WL2C~C$z~9|0m*YH<-vg2CD^SX#&)B4ngOSG$ zV^wmy_iQk>dfN@Pv(ckfy&#ak@MLC7&Q6Ro#!ezM*VEh`+b3Jt%m(^T&p&WJ2Oqvj zs-4nq0TW6cv~(YI$n0UkfwN}kg3_fp?(ijSV#tR9L0}l2qjc7W?i*q01=St0eZ=4h zyGQbEw`9OEH>NMuIe)hVwYHsGERWOD;JxEiO7cQv%pFCeR+IyhwQ|y@&^24k+|8fD zLiOWFNJ2&vu2&`Jv96_z-Cd5RLgmeY3*4rDOQo?Jm`;I_(+ejsPM03!ly!*Cu}Cco zrQSrEDHNyzT(D5s1rZq!8#?f6@v6dB7a-aWs(Qk>N?UGAo{gytlh$%_IhyL7h?DLXDGx zgxGEBQoCAWo-$LRvM=F5MTle`M})t3vVv;2j0HZY&G z22^iGhV@uaJh(XyyY%} zd4iH_UfdV#T=3n}(Lj^|n;O4|$;xhu*8T3hR1mc_A}fK}jfZ7LX~*n5+`8N2q#rI$ z@<_2VANlYF$vIH$ zl<)+*tIWW78IIINA7Rr7i{<;#^yzxoLNkXL)eSs=%|P>$YQIh+ea_3k z_s7r4%j7%&*NHSl?R4k%1>Z=M9o#zxY!n8sL5>BO-ZP;T3Gut>iLS@U%IBrX6BA3k z)&@q}V8a{X<5B}K5s(c(LQ=%v1ocr`t$EqqY0EqVjr65usa=0bkf|O#ky{j3)WBR(((L^wmyHRzoWuL2~WTC=`yZ zn%VX`L=|Ok0v7?s>IHg?yArBcync5rG#^+u)>a%qjES%dRZoIyA8gQ;StH z1Ao7{<&}6U=5}4v<)1T7t!J_CL%U}CKNs-0xWoTTeqj{5{?Be$L0_tk>M9o8 zo371}S#30rKZFM{`H_(L`EM9DGp+Mifk&IP|C2Zu_)Ghr4Qtpmkm1osCf@%Z$%t+7 zYH$Cr)Ro@3-QDeQJ8m+x6%;?YYT;k6Z0E-?kr>x33`H%*ueBD7Zx~3&HtWn0?2Wt} zTG}*|v?{$ajzt}xPzV%lL1t-URi8*Zn)YljXNGDb>;!905Td|mpa@mHjIH%VIiGx- zd@MqhpYFu4_?y5N4xiHn3vX&|e6r~Xt> zZG`aGq|yTNjv;9E+Txuoa@A(9V7g?1_T5FzRI;!=NP1Kqou1z5?%X~Wwb{trRfd>i z8&y^H)8YnKyA_Fyx>}RNmQIczT?w2J4SNvI{5J&}Wto|8FR(W;Qw#b1G<1%#tmYzQ zQ2mZA-PAdi%RQOhkHy9Ea#TPSw?WxwL@H@cbkZwIq0B!@ns}niALidmn&W?!Vd4Gj zO7FiuV4*6Mr^2xlFSvM;Cp_#r8UaqIzHJQg_z^rEJw&OMm_8NGAY2)rKvki|o1bH~ z$2IbfVeY2L(^*rMRU1lM5Y_sgrDS`Z??nR2lX;zyR=c%UyGb*%TC-Dil?SihkjrQy~TMv6;BMs7P8il`H7DmpVm@rJ;b)hW)BL)GjS154b*xq-NXq2cwE z^;VP7ua2pxvCmxrnqUYQMH%a%nHmwmI33nJM(>4LznvY*k&C0{8f*%?zggpDgkuz&JBx{9mfb@wegEl2v!=}Sq2Gaty0<)UrOT0{MZtZ~j5y&w zXlYa_jY)I_+VA-^#mEox#+G>UgvM!Ac8zI<%JRXM_73Q!#i3O|)lOP*qBeJG#BST0 zqohi)O!|$|2SeJQo(w6w7%*92S})XfnhrH_Z8qe!G5>CglP=nI7JAOW?(Z29;pXJ9 zR9`KzQ=WEhy*)WH>$;7Cdz|>*i>=##0bB)oU0OR>>N<21e4rMCHDemNi2LD>Nc$;& zQRFthpWniC1J6@Zh~iJCoLOxN`oCKD5Q4r%ynwgUKPlIEd#?QViIqovY|czyK8>6B zSP%{2-<;%;1`#0mG^B(8KbtXF;Nf>K#Di72UWE4gQ%(_26Koiad)q$xRL~?pN71ZZ zujaaCx~jXjygw;rI!WB=xrOJO6HJ!!w}7eiivtCg5K|F6$EXa)=xUC za^JXSX98W`7g-tm@uo|BKj39Dl;sg5ta;4qjo^pCh~{-HdLl6qI9Ix6f$+qiZ$}s= zNguKrU;u+T@ko(Vr1>)Q%h$?UKXCY>3se%&;h2osl2D zE4A9bd7_|^njDd)6cI*FupHpE3){4NQ*$k*cOWZ_?CZ>Z4_fl@n(mMnYK62Q1d@+I zr&O))G4hMihgBqRIAJkLdk(p(D~X{-oBUA+If@B}j& zsHbeJ3RzTq96lB7d($h$xTeZ^gP0c{t!Y0c)aQE;$FY2!mACg!GDEMKXFOPI^)nHZ z`aSPJpvV0|bbrzhWWkuPURlDeN%VT8tndV8?d)eN*i4I@u zVKl^6{?}A?P)Fsy?3oi#clf}L18t;TjNI2>eI&(ezDK7RyqFxcv%>?oxUlonv(px) z$vnPzRH`y5A(x!yOIfL0bmgeMQB$H5wenx~!ujQK*nUBW;@Em&6Xv2%s(~H5WcU2R z;%Nw<$tI)a`Ve!>x+qegJnQsN2N7HaKzrFqM>`6R*gvh%O*-%THt zrB$Nk;lE;z{s{r^PPm5qz(&lM{sO*g+W{sK+m3M_z=4=&CC>T`{X}1Vg2PEfSj2x_ zmT*(x;ov%3F?qoEeeM>dUn$a*?SIGyO8m806J1W1o+4HRhc2`9$s6hM#qAm zChQ87b~GEw{ADfs+5}FJ8+|bIlIv(jT$Ap#hSHoXdd9#w<#cA<1Rkq^*EEkknUd4& zoIWIY)sAswy6fSERVm&!SO~#iN$OgOX*{9@_BWFyJTvC%S++ilSfCrO(?u=Dc?CXZ zzCG&0yVR{Z`|ZF0eEApWEo#s9osV>F{uK{QA@BES#&;#KsScf>y zvs?vIbI>VrT<*!;XmQS=bhq%46-aambZ(8KU-wOO2=en~D}MCToB_u;Yz{)1ySrPZ z@=$}EvjTdzTWU7c0ZI6L8=yP+YRD_eMMos}b5vY^S*~VZysrkq<`cK3>>v%uy7jgq z0ilW9KjVDHLv0b<1K_`1IkbTOINs0=m-22c%M~l=^S}%hbli-3?BnNq?b`hx^HX2J zIe6ECljRL0uBWb`%{EA=%!i^4sMcj+U_TaTZRb+~GOk z^ZW!nky0n*Wb*r+Q|9H@ml@Z5gU&W`(z4-j!OzC1wOke`TRAYGZVl$PmQ16{3196( zO*?`--I}Qf(2HIwb2&1FB^!faPA2=sLg(@6P4mN)>Dc3i(B0;@O-y2;lM4akD>@^v z=u>*|!s&9zem70g7zfw9FXl1bpJW(C#5w#uy5!V?Q(U35A~$dR%LDVnq@}kQm13{} zd53q3N(s$Eu{R}k2esbftfjfOITCL;jWa$}(mmm}d(&7JZ6d3%IABCapFFYjdEjdK z&4Edqf$G^MNAtL=uCDRs&Fu@FXRgX{*0<(@c3|PNHa>L%zvxWS={L8%qw`STm+=Rd zA}FLspESSIpE_^41~#5yI2bJ=9`oc;GIL!JuW&7YetZ?0H}$$%8rW@*J37L-~Rsx!)8($nI4 zZhcZ2^=Y+p4YPl%j!nFJA|*M^gc(0o$i3nlphe+~-_m}jVkRN{spFs(o0ajW@f3K{ zDV!#BwL322CET$}Y}^0ixYj2w>&Xh12|R8&yEw|wLDvF!lZ#dOTHM9pK6@Nm-@9Lnng4ZHBgBSrr7KI8YCC9DX5Kg|`HsiwJHg2(7#nS;A{b3tVO?Z% za{m5b3rFV6EpX;=;n#wltDv1LE*|g5pQ+OY&*6qCJZc5oDS6Z6JD#6F)bWxZSF@q% z+1WV;m!lRB!n^PC>RgQCI#D1br_o^#iPk>;K2hB~0^<~)?p}LG%kigm@moD#q3PE+ zA^Qca)(xnqw6x>XFhV6ku9r$E>bWNrVH9fum0?4s?Rn2LG{Vm_+QJHse6xa%nzQ?k zKug4PW~#Gtb;#5+9!QBgyB@q=sk9=$S{4T>wjFICStOM?__fr+Kei1 z3j~xPqW;W@YkiUM;HngG!;>@AITg}vAE`M2Pj9Irl4w1fo4w<|Bu!%rh%a(Ai^Zhi zs92>v5;@Y(Zi#RI*ua*h`d_7;byQSa*v9E{2x$<-_=5Z<7{%)}4XExANcz@rK69T0x3%H<@frW>RA8^swA+^a(FxK| zFl3LD*ImHN=XDUkrRhp6RY5$rQ{bRgSO*(vEHYV)3Mo6Jy3puiLmU&g82p{qr0F?ohmbz)f2r{X2|T2 z$4fdQ=>0BeKbiVM!e-lIIs8wVTuC_m7}y4A_%ikI;Wm5$9j(^Y z(cD%U%k)X>_>9~t8;pGzL6L-fmQO@K; zo&vQzMlgY95;1BSkngY)e{`n0!NfVgf}2mB3t}D9@*N;FQ{HZ3Pb%BK6;5#-O|WI( zb6h@qTLU~AbVW#_6?c!?Dj65Now7*pU{h!1+eCV^KCuPAGs28~3k@ueL5+u|Z-7}t z9|lskE`4B7W8wMs@xJa{#bsCGDFoRSNSnmNYB&U7 zVGKWe%+kFB6kb)e;TyHfqtU6~fRg)f|>=5(N36)0+C z`hv65J<$B}WUc!wFAb^QtY31yNleq4dzmG`1wHTj=c*=hay9iD071Hc?oYoUk|M*_ zU1GihAMBsM@5rUJ(qS?9ZYJ6@{bNqJ`2Mr+5#hKf?doa?F|+^IR!8lq9)wS3tF_9n zW_?hm)G(M+MYb?V9YoX^_mu5h-LP^TL^!Q9Z7|@sO(rg_4+@=PdI)WL(B7`!K^ND- z-uIuVDCVEdH_C@c71YGYT^_Scf_dhB8Z2Xy6vGtBSlYud9vggOqv^L~F{BraSE_t} zIkP+Hp2&nH^-MNEs}^`oMLy11`PQW$T|K(`Bu*(f@)mv1-qY(_YG&J2M2<7k;;RK~ zL{Fqj9yCz8(S{}@c)S!65aF<=&eLI{hAMErCx&>i7OeDN>okvegO87OaG{Jmi<|}D zaT@b|0X{d@OIJ7zvT>r+eTzgLq~|Dpu)Z&db-P4z*`M$UL51lf>FLlq6rfG)%doyp z)3kk_YIM!03eQ8Vu_2fg{+osaEJPtJ-s36R+5_AEG12`NG)IQ#TF9c@$99%0iye+ zUzZ57=m2)$D(5Nx!n)=5Au&O0BBgwxIBaeI(mro$#&UGCr<;C{UjJVAbVi%|+WP(a zL$U@TYCxJ=1{Z~}rnW;7UVb7+ZnzgmrogDxhjLGo>c~MiJAWs&&;AGg@%U?Y^0JhL ze(x6Z74JG6FlOFK(T}SXQfhr}RIFl@QXKnIcXYF)5|V~e-}suHILKT-k|<*~Ij|VF zC;t@=uj=hot~*!C68G8hTA%8SzOfETOXQ|3FSaIEjvBJp(A)7SWUi5!Eu#yWgY+;n zlm<$+UDou*V+246_o#V4kMdto8hF%%Lki#zPh}KYXmMf?hrN0;>Mv%`@{0Qn`Ujp) z=lZe+13>^Q!9zT);H<(#bIeRWz%#*}sgUX9P|9($kexOyKIOc`dLux}c$7It4u|Rl z6SSkY*V~g_B-hMPo_ak>>z@AVQ(_N)VY2kB3IZ0G(iDUYw+2d7W^~(Jq}KY=JnWS( z#rzEa&0uNhJ>QE8iiyz;n2H|SV#Og+wEZv=f2%1ELX!SX-(d3tEj$5$1}70Mp<&eI zCkfbByL7af=qQE@5vDVxx1}FSGt_a1DoE3SDI+G)mBAna)KBG4p8Epxl9QZ4BfdAN zFnF|Y(umr;gRgG6NLQ$?ZWgllEeeq~z^ZS7L?<(~O&$5|y)Al^iMKy}&W+eMm1W z7EMU)u^ke(A1#XCV>CZ71}P}0x)4wtHO8#JRG3MA-6g=`ZM!FcICCZ{IEw8Dm2&LQ z1|r)BUG^0GzI6f946RrBlfB1Vs)~8toZf~7)+G;pv&XiUO(%5bm)pl=p>nV^o*;&T z;}@oZSibzto$arQgfkp|z4Z($P>dTXE{4O=vY0!)kDO* zGF8a4wq#VaFpLfK!iELy@?-SeRrdz%F*}hjKcA*y@mj~VD3!it9lhRhX}5YOaR9$} z3mS%$2Be7{l(+MVx3 z(4?h;P!jnRmX9J9sYN#7i=iyj_5q7n#X(!cdqI2lnr8T$IfOW<_v`eB!d9xY1P=2q&WtOXY=D9QYteP)De?S4}FK6#6Ma z=E*V+#s8>L;8aVroK^6iKo=MH{4yEZ_>N-N z`(|;aOATba1^asjxlILk<4}f~`39dBFlxj>Dw(hMYKPO3EEt1@S`1lxFNM+J@uB7T zZ8WKjz7HF1-5&2=l=fqF-*@>n5J}jIxdDwpT?oKM3s8Nr`x8JnN-kCE?~aM1H!hAE z%%w(3kHfGwMnMmNj(SU(w42OrC-euI>Dsjk&jz3ts}WHqmMpzQ3vZrsXrZ|}+MHA7 z068obeXZTsO*6RS@o3x80E4ok``rV^Y3hr&C1;|ZZ0|*EKO`$lECUYG2gVFtUTw)R z4Um<0ZzlON`zTdvVdL#KFoMFQX*a5wM0Czp%wTtfK4Sjs)P**RW&?lP$(<}q%r68Z zS53Y!d@&~ne9O)A^tNrXHhXBkj~$8j%pT1%%mypa9AW5E&s9)rjF4@O3ytH{0z6riz|@< zB~UPh*wRFg2^7EbQrHf0y?E~dHlkOxof_a?M{LqQ^C!i2dawHTPYUE=X@2(3<=OOxs8qn_(y>pU>u^}3y&df{JarR0@VJn0f+U%UiF=$Wyq zQvnVHESil@d|8&R<%}uidGh7@u^(%?$#|&J$pvFC-n8&A>utA=n3#)yMkz+qnG3wd zP7xCnF|$9Dif@N~L)Vde3hW8W!UY0BgT2v(wzp;tlLmyk2%N|0jfG$%<;A&IVrOI< z!L)o>j>;dFaqA3pL}b-Je(bB@VJ4%!JeX@3x!i{yIeIso^=n?fDX`3bU=eG7sTc%g%ye8$v8P@yKE^XD=NYxTb zbf!Mk=h|otpqjFaA-vs5YOF-*GwWPc7VbaOW&stlANnCN8iftFMMrUdYNJ_Bnn5Vt zxfz@Ah|+4&P;reZxp;MmEI7C|FOv8NKUm8njF7Wb6Gi7DeODLl&G~}G4be&*Hi0Qw z5}77vL0P+7-B%UL@3n1&JPxW^d@vVwp?u#gVcJqY9#@-3X{ok#UfW3<1fb%FT`|)V~ggq z(3AUoUS-;7)^hCjdT0Kf{i}h)mBg4qhtHHBti=~h^n^OTH5U*XMgDLIR@sre`AaB$ zg)IGBET_4??m@cx&c~bA80O7B8CHR7(LX7%HThkeC*@vi{-pL%e)yXp!B2InafbDF zjPXf1mko3h59{lT6EEbxKO1Z5GF71)WwowO6kY|6tjSVSWdQ}NsK2x{>i|MKZK8%Q zfu&_0D;CO-Jg0#YmyfctyJ!mRJp)e#@O0mYdp|8x;G1%OZQ3Q847YWTyy|%^cpA;m zze0(5p{tMu^lDkpe?HynyO?a1$_LJl2L&mpeKu%8YvgRNr=%2z${%WThHG=vrWY@4 zsA`OP#O&)TetZ>s%h!=+CE15lOOls&nvC~$Qz0Ph7tHiP;O$i|eDwpT{cp>+)0-|; zY$|bB+Gbel>5aRN3>c0x)4U=|X+z+{ zn*_p*EQoquRL+=+p;=lm`d71&1NqBz&_ph)MXu(Nv6&XE7(RsS)^MGj5Q?Fwude-(sq zjJ>aOq!7!EN>@(fK7EE#;i_BGvli`5U;r!YA{JRodLBc6-`n8K+Fjgwb%sX;j=qHQ z7&Tr!)!{HXoO<2BQrV9Sw?JRaLXV8HrsNevvnf>Y-6|{T!pYLl7jp$-nEE z#X!4G4L#K0qG_4Z;Cj6=;b|Be$hi4JvMH!-voxqx^@8cXp`B??eFBz2lLD8RRaRGh zn7kUfy!YV~p(R|p7iC1Rdgt$_24i0cd-S8HpG|`@my70g^y`gu%#Tf_L21-k?sRRZHK&at(*ED0P8iw{7?R$9~OF$Ko;Iu5)ur5<->x!m93Eb zFYpIx60s=Wxxw=`$aS-O&dCO_9?b1yKiPCQmSQb>T)963`*U+Ydj5kI(B(B?HNP8r z*bfSBpSu)w(Z3j7HQoRjUG(+d=IaE~tv}y14zHHs|0UcN52fT8V_<@2ep_ee{QgZG zmgp8iv4V{k;~8@I%M3<#B;2R>Ef(Gg_cQM7%}0s*^)SK6!Ym+~P^58*wnwV1BW@eG z4sZLqsUvBbFsr#8u7S1r4teQ;t)Y@jnn_m5jS$CsW1um!p&PqAcc8!zyiXHVta9QC zY~wCwCF0U%xiQPD_INKtTb;A|Zf29(mu9NI;E zc-e>*1%(LSXB`g}kd`#}O;veb<(sk~RWL|f3ljxCnEZDdNSTDV6#Td({6l&y4IjKF z^}lIUq*ZUqgTPumD)RrCN{M^jhY>E~1pn|KOZ5((%F)G|*ZQ|r4zIbrEiV%42hJV8 z3xS)=!X1+=olbdGJ=yZil?oXLct8FM{(6ikLL3E%=q#O6(H$p~gQu6T8N!plf!96| z&Q3=`L~>U0zZh;z(pGR2^S^{#PrPxTRHD1RQOON&f)Siaf`GLj#UOk&(|@0?zm;Sx ztsGt8=29-MZs5CSf1l1jNFtNt5rFNZxJPvkNu~2}7*9468TWm>nN9TP&^!;J{-h)_ z7WsHH9|F%I`Pb!>KAS3jQWKfGivTVkMJLO-HUGM_a4UQ_%RgL6WZvrW+Z4ujZn;y@ zz9$=oO!7qVTaQAA^BhX&ZxS*|5dj803M=k&2%QrXda`-Q#IoZL6E(g+tN!6CA!CP* zCpWtCujIea)ENl0liwVfj)Nc<9mV%+e@=d`haoZ*`B7+PNjEbXBkv=B+Pi^~L#EO$D$ZqTiD8f<5$eyb54-(=3 zh)6i8i|jp(@OnRrY5B8t|LFXFQVQ895n*P16cEKTrT*~yLH6Z4e*bZ5otpRDri&+A zfNbK1D5@O=sm`fN=WzWyse!za5n%^+6dHPGX#8DyIK>?9qyX}2XvBWVqbP%%D)7$= z=#$WulZlZR<{m#gU7lwqK4WS1Ne$#_P{b17qe$~UOXCl>5b|6WVh;5vVnR<%d+Lnp z$uEmML38}U4vaW8>shm6CzB(Wei3s#NAWE3)a2)z@i{4jTn;;aQS)O@l{rUM`J@K& l00vQ5JBs~;vo!vr%%-k{2_Fq1Mn4QF81S)AQ99zk{{c4yR+0b! literal 63721 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鯊%1Et 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⁢ zv5o9#E7!G0XmdzfsS{efPNi`1b44~SZ4Z8fuX!I}#8g+(wxzQwUT#Xb2(tbY1+EUhGKoT@KEU9Ktl>_0 z%bjDJg;#*gtJZv!-Zs`?^}v5eKmnbjqlvnSzE@_SP|LG_PJ6CYU+6zY6>92%E+ z=j@TZf-iW4(%U{lnYxQA;7Q!b;^brF8n0D>)`q5>|WDDXLrqYU_tKN2>=#@~OE7grMnNh?UOz-O~6 z6%rHy{#h9K0AT+lDC7q4{hw^|q6*Ry;;L%Q@)Ga}$60_q%D)rv(CtS$CQbpq9|y1e zRSrN4;$Jyl{m5bZw`$8TGvb}(LpY{-cQ)fcyJv7l3S52TLXVDsphtv&aPuDk1OzCA z4A^QtC(!11`IsNx_HnSy?>EKpHJWT^wmS~hc^p^zIIh@9f6U@I2 zC=Mve{j2^)mS#U$e{@Q?SO6%LDsXz@SY+=cK_QMmXBIU)j!$ajc-zLx3V60EXJ!qC zi<%2x8Q24YN+&8U@CIlN zrZkcT9yh%LrlGS9`G)KdP(@9Eo-AQz@8GEFWcb7U=a0H^ZVbLmz{+&M7W(nXJ4sN8 zJLR7eeK(K8`2-}j(T7JsO`L!+CvbueT%izanm-^A1Dn{`1Nw`9P?cq;7no+XfC`K(GO9?O^5zNIt4M+M8LM0=7Gz8UA@Z0N+lg+cX)NfazRu z5D)~HA^(u%w^cz+@2@_#S|u>GpB+j4KzQ^&Wcl9f z&hG#bCA(Yk0D&t&aJE^xME^&E-&xGHhXn%}psEIj641H+Nl-}boj;)Zt*t(4wZ5DN z@GXF$bL=&pBq-#vkTkh>7hl%K5|3 z{`Vn9b$iR-SoGENp}bn4;fR3>9sA%X2@1L3aE9yTra;Wb#_`xWwLSLdfu+PAu+o3| zGVnpzPr=ch{uuoHjtw7+_!L_2;knQ!DuDl0R`|%jr+}jFzXtrHIKc323?JO{l&;VF z*L1+}JU7%QJOg|5|Tc|D8fN zJORAg=_vsy{ak|o);@)Yh8Lkcg@$FG3k@ep36BRa^>~UmnRPziS>Z=`Jb2x*Q#`%A zU*i3&Vg?TluO@X0O;r2Jl6LKLUOVhSqg1*qOt^|8*c7 zo(298@+r$k_wQNGHv{|$tW(T8L+4_`FQ{kEW5Jgg{yf7ey4ss_(SNKfz(N9lx&a;< je(UuV8hP?p&}TPdm1I$XmG#(RzlD&B2izSj9sl%y5~4qc diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a7a990ab2a..865f1ba80d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=c16d517b50dd28b3f5838f0e844b7520b8f1eb610f2f29de7e4e04a1b7c9c79b -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip +distributionSha256Sum=85719317abd2112f021d4f41f09ec370534ba288432065f4b477b6a3b652910d +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From a3716d0e0dcf910b174f7802b162b612e104c01b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Feb 2024 12:27:04 +0100 Subject: [PATCH 039/119] SessionPreferencesStore: add entries for `sharePresence`, `renderReadReceipts`, `sendTypingNotifications`, `renderTypingNotifications`. `sharePresence` should take existing value of `sendPublicReadReceipts`, which has been added first. --- .../api/store/SessionPreferencesStore.kt | 12 +++++++ .../store/DefaultSessionPreferencesStore.kt | 31 ++++++++++++++++ .../test/InMemorySessionPreferencesStore.kt | 35 +++++++++++++++++-- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/store/SessionPreferencesStore.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/store/SessionPreferencesStore.kt index 0174d8d1eb..948e9acb75 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/store/SessionPreferencesStore.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/store/SessionPreferencesStore.kt @@ -19,8 +19,20 @@ package io.element.android.features.preferences.api.store import kotlinx.coroutines.flow.Flow interface SessionPreferencesStore { + suspend fun setSharePresence(enabled: Boolean) + fun isSharePresenceEnabled(): Flow + suspend fun setSendPublicReadReceipts(enabled: Boolean) fun isSendPublicReadReceiptsEnabled(): Flow + suspend fun setRenderReadReceipts(enabled: Boolean) + fun isRenderReadReceiptsEnabled(): Flow + + suspend fun setSendTypingNotifications(enabled: Boolean) + fun isSendTypingNotificationsEnabled(): Flow + + suspend fun setRenderTypingNotifications(enabled: Boolean) + fun isRenderTypingNotificationsEnabled(): Flow + suspend fun clear() } diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt index eb2fffb045..b6283d2c69 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt @@ -29,7 +29,9 @@ import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.matrix.api.core.SessionId import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map +import kotlinx.coroutines.runBlocking import java.io.File class DefaultSessionPreferencesStore( @@ -43,14 +45,43 @@ class DefaultSessionPreferencesStore( return context.preferencesDataStoreFile("session_${hashedUserId}_preferences") } } + + private val sharePresenceKey = booleanPreferencesKey("sharePresence") private val sendPublicReadReceiptsKey = booleanPreferencesKey("sendPublicReadReceipts") + private val renderReadReceiptsKey = booleanPreferencesKey("renderReadReceipts") + private val sendTypingNotificationsKey = booleanPreferencesKey("sendTypingNotifications") + private val renderTypingNotificationsKey = booleanPreferencesKey("renderTypingNotifications") private val dataStoreFile = storeFile(context, sessionId) private val store = PreferenceDataStoreFactory.create(scope = sessionCoroutineScope) { dataStoreFile } + override suspend fun setSharePresence(enabled: Boolean) { + update(sharePresenceKey, enabled) + // Also update all the other settings + setSendPublicReadReceipts(enabled) + setRenderReadReceipts(enabled) + setSendTypingNotifications(enabled) + setRenderTypingNotifications(enabled) + } + + override fun isSharePresenceEnabled(): Flow { + // Migration, if sendPublicReadReceiptsKey was false, consider that sharing presence is false. + val defaultValue = runBlocking { isSendPublicReadReceiptsEnabled().first() } + return get(sharePresenceKey, defaultValue) + } + override suspend fun setSendPublicReadReceipts(enabled: Boolean) = update(sendPublicReadReceiptsKey, enabled) override fun isSendPublicReadReceiptsEnabled(): Flow = get(sendPublicReadReceiptsKey, true) + override suspend fun setRenderReadReceipts(enabled: Boolean) = update(renderReadReceiptsKey, enabled) + override fun isRenderReadReceiptsEnabled(): Flow = get(renderReadReceiptsKey, true) + + override suspend fun setSendTypingNotifications(enabled: Boolean) = update(sendTypingNotificationsKey, enabled) + override fun isSendTypingNotificationsEnabled(): Flow = get(sendTypingNotificationsKey, true) + + override suspend fun setRenderTypingNotifications(enabled: Boolean) = update(renderTypingNotificationsKey, enabled) + override fun isRenderTypingNotificationsEnabled(): Flow = get(renderTypingNotificationsKey, true) + override suspend fun clear() { dataStoreFile.safeDelete() } diff --git a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/featureflag/test/InMemorySessionPreferencesStore.kt b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/featureflag/test/InMemorySessionPreferencesStore.kt index 1f6f7a6724..9e9fc7ba2a 100644 --- a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/featureflag/test/InMemorySessionPreferencesStore.kt +++ b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/featureflag/test/InMemorySessionPreferencesStore.kt @@ -21,19 +21,50 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow class InMemorySessionPreferencesStore( + isSharePresenceEnabled: Boolean = true, isSendPublicReadReceiptsEnabled: Boolean = true, + isRenderReadReceiptsEnabled: Boolean = true, + isSendTypingNotificationsEnabled: Boolean = true, + isRenderTypingNotificationsEnabled: Boolean = true, ) : SessionPreferencesStore { + private val isSharePresenceEnabled = MutableStateFlow(isSharePresenceEnabled) private val isSendPublicReadReceiptsEnabled = MutableStateFlow(isSendPublicReadReceiptsEnabled) + private val isRenderReadReceiptsEnabled = MutableStateFlow(isRenderReadReceiptsEnabled) + private val isSendTypingNotificationsEnabled = MutableStateFlow(isSendTypingNotificationsEnabled) + private val isRenderTypingNotificationsEnabled = MutableStateFlow(isRenderTypingNotificationsEnabled) var clearCallCount = 0 private set + override suspend fun setSharePresence(enabled: Boolean) { + isSharePresenceEnabled.tryEmit(enabled) + } + + override fun isSharePresenceEnabled(): Flow = isSharePresenceEnabled + override suspend fun setSendPublicReadReceipts(enabled: Boolean) { isSendPublicReadReceiptsEnabled.tryEmit(enabled) } - override fun isSendPublicReadReceiptsEnabled(): Flow { - return isSendPublicReadReceiptsEnabled + + override fun isSendPublicReadReceiptsEnabled(): Flow = isSendPublicReadReceiptsEnabled + + override suspend fun setRenderReadReceipts(enabled: Boolean) { + isRenderReadReceiptsEnabled.tryEmit(enabled) } + override fun isRenderReadReceiptsEnabled(): Flow = isRenderReadReceiptsEnabled + + override suspend fun setSendTypingNotifications(enabled: Boolean) { + isSendTypingNotificationsEnabled.tryEmit(enabled) + } + + override fun isSendTypingNotificationsEnabled(): Flow = isSendTypingNotificationsEnabled + + override suspend fun setRenderTypingNotifications(enabled: Boolean) { + isRenderTypingNotificationsEnabled.tryEmit(enabled) + } + + override fun isRenderTypingNotificationsEnabled(): Flow = isRenderTypingNotificationsEnabled + override suspend fun clear() { clearCallCount++ isSendPublicReadReceiptsEnabled.tryEmit(true) From 9d1bc5925c3d91b61e9c1b20d1358d3121f1702d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Feb 2024 12:29:36 +0100 Subject: [PATCH 040/119] Avoid computing default value if it's not necessary. --- .../impl/store/DefaultSessionPreferencesStore.kt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt index b6283d2c69..770ca699fe 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt @@ -66,21 +66,20 @@ class DefaultSessionPreferencesStore( override fun isSharePresenceEnabled(): Flow { // Migration, if sendPublicReadReceiptsKey was false, consider that sharing presence is false. - val defaultValue = runBlocking { isSendPublicReadReceiptsEnabled().first() } - return get(sharePresenceKey, defaultValue) + return get(sharePresenceKey) { runBlocking { isSendPublicReadReceiptsEnabled().first() } } } override suspend fun setSendPublicReadReceipts(enabled: Boolean) = update(sendPublicReadReceiptsKey, enabled) - override fun isSendPublicReadReceiptsEnabled(): Flow = get(sendPublicReadReceiptsKey, true) + override fun isSendPublicReadReceiptsEnabled(): Flow = get(sendPublicReadReceiptsKey) { true } override suspend fun setRenderReadReceipts(enabled: Boolean) = update(renderReadReceiptsKey, enabled) - override fun isRenderReadReceiptsEnabled(): Flow = get(renderReadReceiptsKey, true) + override fun isRenderReadReceiptsEnabled(): Flow = get(renderReadReceiptsKey) { true } override suspend fun setSendTypingNotifications(enabled: Boolean) = update(sendTypingNotificationsKey, enabled) - override fun isSendTypingNotificationsEnabled(): Flow = get(sendTypingNotificationsKey, true) + override fun isSendTypingNotificationsEnabled(): Flow = get(sendTypingNotificationsKey) { true } override suspend fun setRenderTypingNotifications(enabled: Boolean) = update(renderTypingNotificationsKey, enabled) - override fun isRenderTypingNotificationsEnabled(): Flow = get(renderTypingNotificationsKey, true) + override fun isRenderTypingNotificationsEnabled(): Flow = get(renderTypingNotificationsKey) { true } override suspend fun clear() { dataStoreFile.safeDelete() @@ -90,7 +89,7 @@ class DefaultSessionPreferencesStore( store.edit { prefs -> prefs[key] = value } } - private fun get(key: Preferences.Key, default: T): Flow { - return store.data.map { prefs -> prefs[key] ?: default } + private fun get(key: Preferences.Key, default: () -> T): Flow { + return store.data.map { prefs -> prefs[key] ?: default() } } } From 23fb7811f35aed90fb84bd2148cbbbaf9afd5ab9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Feb 2024 13:06:50 +0100 Subject: [PATCH 041/119] Update advanced settings screen. Replace Read Receipt private mode by Share presence. --- .../impl/advanced/AdvancedSettingsEvents.kt | 2 +- .../impl/advanced/AdvancedSettingsPresenter.kt | 10 +++++----- .../impl/advanced/AdvancedSettingsState.kt | 2 +- .../impl/advanced/AdvancedSettingsStateProvider.kt | 2 +- .../impl/advanced/AdvancedSettingsView.kt | 8 ++++---- .../impl/src/main/res/values/localazy.xml | 2 ++ .../impl/advanced/AdvancedSettingsPresenterTest.kt | 14 +++++++------- 7 files changed, 21 insertions(+), 19 deletions(-) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt index d67a594dd2..8ee433f630 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt @@ -21,7 +21,7 @@ import io.element.android.compound.theme.Theme sealed interface AdvancedSettingsEvents { data class SetRichTextEditorEnabled(val enabled: Boolean) : AdvancedSettingsEvents data class SetDeveloperModeEnabled(val enabled: Boolean) : AdvancedSettingsEvents - data class SetSendPublicReadReceiptsEnabled(val enabled: Boolean) : AdvancedSettingsEvents + data class SetSharePresenceEnabled(val enabled: Boolean) : AdvancedSettingsEvents data object ChangeTheme : AdvancedSettingsEvents data object CancelChangeTheme : AdvancedSettingsEvents data class SetTheme(val theme: Theme) : AdvancedSettingsEvents diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt index 2ac5b664b5..2f0c2b7417 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt @@ -44,8 +44,8 @@ class AdvancedSettingsPresenter @Inject constructor( val isDeveloperModeEnabled by appPreferencesStore .isDeveloperModeEnabledFlow() .collectAsState(initial = false) - val isSendPublicReadReceiptsEnabled by sessionPreferencesStore - .isSendPublicReadReceiptsEnabled() + val isSharePresenceEnabled by sessionPreferencesStore + .isSharePresenceEnabled() .collectAsState(initial = true) val theme by remember { appPreferencesStore.getThemeFlow().mapToTheme() @@ -60,8 +60,8 @@ class AdvancedSettingsPresenter @Inject constructor( is AdvancedSettingsEvents.SetDeveloperModeEnabled -> localCoroutineScope.launch { appPreferencesStore.setDeveloperModeEnabled(event.enabled) } - is AdvancedSettingsEvents.SetSendPublicReadReceiptsEnabled -> localCoroutineScope.launch { - sessionPreferencesStore.setSendPublicReadReceipts(event.enabled) + is AdvancedSettingsEvents.SetSharePresenceEnabled -> localCoroutineScope.launch { + sessionPreferencesStore.setSharePresence(event.enabled) } AdvancedSettingsEvents.CancelChangeTheme -> showChangeThemeDialog = false AdvancedSettingsEvents.ChangeTheme -> showChangeThemeDialog = true @@ -75,7 +75,7 @@ class AdvancedSettingsPresenter @Inject constructor( return AdvancedSettingsState( isRichTextEditorEnabled = isRichTextEditorEnabled, isDeveloperModeEnabled = isDeveloperModeEnabled, - isSendPublicReadReceiptsEnabled = isSendPublicReadReceiptsEnabled, + isSharePresenceEnabled = isSharePresenceEnabled, theme = theme, showChangeThemeDialog = showChangeThemeDialog, eventSink = { handleEvents(it) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt index 0ea04185f7..469f8a630c 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt @@ -21,7 +21,7 @@ import io.element.android.compound.theme.Theme data class AdvancedSettingsState( val isRichTextEditorEnabled: Boolean, val isDeveloperModeEnabled: Boolean, - val isSendPublicReadReceiptsEnabled: Boolean, + val isSharePresenceEnabled: Boolean, val theme: Theme, val showChangeThemeDialog: Boolean, val eventSink: (AdvancedSettingsEvents) -> Unit diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt index acfd9bb026..5e255fc091 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt @@ -38,7 +38,7 @@ fun aAdvancedSettingsState( ) = AdvancedSettingsState( isRichTextEditorEnabled = isRichTextEditorEnabled, isDeveloperModeEnabled = isDeveloperModeEnabled, - isSendPublicReadReceiptsEnabled = isSendPublicReadReceiptsEnabled, + isSharePresenceEnabled = isSendPublicReadReceiptsEnabled, theme = Theme.System, showChangeThemeDialog = showChangeThemeDialog, eventSink = {} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt index 7079a4daea..9e739c3a00 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt @@ -83,15 +83,15 @@ fun AdvancedSettingsView( ) ListItem( headlineContent = { - Text(text = stringResource(id = R.string.screen_advanced_settings_send_read_receipts)) + Text(text = stringResource(id = R.string.screen_advanced_settings_share_presence)) }, supportingContent = { - Text(text = stringResource(id = R.string.screen_advanced_settings_send_read_receipts_description)) + Text(text = stringResource(id = R.string.screen_advanced_settings_share_presence_description)) }, trailingContent = ListItemContent.Switch( - checked = state.isSendPublicReadReceiptsEnabled, + checked = state.isSharePresenceEnabled, ), - onClick = { state.eventSink(AdvancedSettingsEvents.SetSendPublicReadReceiptsEnabled(!state.isSendPublicReadReceiptsEnabled)) } + onClick = { state.eventSink(AdvancedSettingsEvents.SetSharePresenceEnabled(!state.isSharePresenceEnabled)) } ) } diff --git a/features/preferences/impl/src/main/res/values/localazy.xml b/features/preferences/impl/src/main/res/values/localazy.xml index 607329332f..70a1b032a9 100644 --- a/features/preferences/impl/src/main/res/values/localazy.xml +++ b/features/preferences/impl/src/main/res/values/localazy.xml @@ -8,6 +8,8 @@ "Disable the rich text editor to type Markdown manually." "Read receipts" "If turned off, your read receipts won\'t be sent to anyone. You will still receive read receipts from other users." + "Share presence" + "If turned off, you won’t be able to send or receive read receipts or typing notifications" "Enable option to view message source in the timeline." "Display name" "Your display name" diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt index a042d792b8..1745d57396 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt @@ -43,7 +43,7 @@ class AdvancedSettingsPresenterTest { assertThat(initialState.isDeveloperModeEnabled).isFalse() assertThat(initialState.isRichTextEditorEnabled).isFalse() assertThat(initialState.showChangeThemeDialog).isFalse() - assertThat(initialState.isSendPublicReadReceiptsEnabled).isTrue() + assertThat(initialState.isSharePresenceEnabled).isTrue() assertThat(initialState.theme).isEqualTo(Theme.System) } } @@ -79,17 +79,17 @@ class AdvancedSettingsPresenterTest { } @Test - fun `present - send public read receipts off on`() = runTest { + fun `present - share presence off on`() = runTest { val presenter = createAdvancedSettingsPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitLastSequentialItem() - assertThat(initialState.isSendPublicReadReceiptsEnabled).isTrue() - initialState.eventSink.invoke(AdvancedSettingsEvents.SetSendPublicReadReceiptsEnabled(false)) - assertThat(awaitItem().isSendPublicReadReceiptsEnabled).isFalse() - initialState.eventSink.invoke(AdvancedSettingsEvents.SetSendPublicReadReceiptsEnabled(true)) - assertThat(awaitItem().isSendPublicReadReceiptsEnabled).isTrue() + assertThat(initialState.isSharePresenceEnabled).isTrue() + initialState.eventSink.invoke(AdvancedSettingsEvents.SetSharePresenceEnabled(false)) + assertThat(awaitItem().isSharePresenceEnabled).isFalse() + initialState.eventSink.invoke(AdvancedSettingsEvents.SetSharePresenceEnabled(true)) + assertThat(awaitItem().isSharePresenceEnabled).isTrue() } } From 5747453505b0a08258a1adedf972710ca2df495b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Feb 2024 12:45:48 +0100 Subject: [PATCH 042/119] Take into account the setting `isSendTypingNotificationsEnabled` to not send typing notification when it's been disabled by the user. --- .../MessageComposerPresenter.kt | 15 ++++++++++--- .../messages/impl/MessagesPresenterTest.kt | 1 + .../MessageComposerPresenterTest.kt | 22 +++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index 4fea2ee046..b98f5fcb39 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -23,6 +23,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf @@ -37,6 +38,7 @@ import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError import io.element.android.features.messages.impl.mentions.MentionSuggestion import io.element.android.features.messages.impl.mentions.MentionSuggestionsProcessor +import io.element.android.features.preferences.api.store.SessionPreferencesStore import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage @@ -86,6 +88,7 @@ class MessageComposerPresenter @Inject constructor( private val room: MatrixRoom, private val mediaPickerProvider: PickerProvider, private val featureFlagService: FeatureFlagService, + private val sessionPreferencesStore: SessionPreferencesStore, private val localMediaFactory: LocalMediaFactory, private val mediaSender: MediaSender, private val snackbarDispatcher: SnackbarDispatcher, @@ -147,6 +150,8 @@ class MessageComposerPresenter @Inject constructor( var showAttachmentSourcePicker: Boolean by remember { mutableStateOf(false) } var showTextFormatting: Boolean by remember { mutableStateOf(false) } + val sendTypingNotifications by sessionPreferencesStore.isSendTypingNotificationsEnabled().collectAsState(initial = true) + LaunchedEffect(messageComposerContext.composerMode) { when (val modeValue = messageComposerContext.composerMode) { is MessageComposerMode.Edit -> @@ -212,7 +217,9 @@ class MessageComposerPresenter @Inject constructor( // Declare that the user is not typing anymore when the composer is disposed onDispose { appCoroutineScope.launch { - room.typingNotice(false) + if (sendTypingNotifications) { + room.typingNotice(false) + } } } } @@ -310,8 +317,10 @@ class MessageComposerPresenter @Inject constructor( analyticsService.trackError(event.error) } is MessageComposerEvents.TypingNotice -> { - localCoroutineScope.launch { - room.typingNotice(event.isTyping) + if (sendTypingNotifications) { + localCoroutineScope.launch { + room.typingNotice(event.isTyping) + } } } is MessageComposerEvents.SuggestionReceived -> { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 1c10cb97e8..9dd9ff5d88 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -675,6 +675,7 @@ class MessagesPresenterTest { room = matrixRoom, mediaPickerProvider = FakePickerProvider(), featureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.NotificationSettings.key to true)), + sessionPreferencesStore = InMemorySessionPreferencesStore(), localMediaFactory = FakeLocalMediaFactory(mockMediaUrl), mediaSender = mediaSender, snackbarDispatcher = SnackbarDispatcher(), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenterTest.kt index d4b3c33cb1..c189e28c13 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenterTest.kt @@ -32,11 +32,13 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter import io.element.android.features.messages.impl.messagecomposer.MessageComposerState +import io.element.android.features.preferences.api.store.SessionPreferencesStore import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService +import io.element.android.libraries.featureflag.test.InMemorySessionPreferencesStore import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.media.ImageInfo @@ -888,6 +890,24 @@ class MessageComposerPresenterTest { } } + @Test + fun `present - handle typing notice event when sending typing notice is disabled`() = runTest { + val room = FakeMatrixRoom() + val store = InMemorySessionPreferencesStore( + isSendTypingNotificationsEnabled = false + ) + val presenter = createPresenter(room = room, sessionPreferencesStore = store, coroutineScope = this) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitFirstItem() + assertThat(room.typingRecord).isEmpty() + initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(true)) + initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(false)) + assertThat(room.typingRecord).isEmpty() + } + } + private suspend fun ReceiveTurbine.backToNormalMode(state: MessageComposerState, skipCount: Int = 0): MessageComposerState { state.eventSink.invoke(MessageComposerEvents.CloseSpecialMode) skipItems(skipCount) @@ -901,6 +921,7 @@ class MessageComposerPresenterTest { room: MatrixRoom = FakeMatrixRoom(), pickerProvider: PickerProvider = this.pickerProvider, featureFlagService: FeatureFlagService = this.featureFlagService, + sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(), mediaPreProcessor: MediaPreProcessor = this.mediaPreProcessor, snackbarDispatcher: SnackbarDispatcher = this.snackbarDispatcher, permissionPresenter: PermissionsPresenter = FakePermissionsPresenter(), @@ -909,6 +930,7 @@ class MessageComposerPresenterTest { room, pickerProvider, featureFlagService, + sessionPreferencesStore, localMediaFactory, MediaSender(mediaPreProcessor, room), snackbarDispatcher, From cbf7e9cfcefd1406e3eee81cda83f0b73cb9fc73 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Feb 2024 12:58:04 +0100 Subject: [PATCH 043/119] Hide Read Receipt if the user set Rended Read Receipt to false (actually disabled the "Share presence" toggle) --- .../impl/timeline/TimelinePresenter.kt | 2 ++ .../messages/impl/timeline/TimelineState.kt | 1 + .../impl/timeline/TimelineStateProvider.kt | 1 + .../messages/impl/timeline/TimelineView.kt | 1 + .../components/ATimelineItemEventRow.kt | 2 ++ .../components/TimelineItemEventRow.kt | 2 ++ .../TimelineItemEventRowWithRRPreview.kt | 3 +++ .../TimelineItemGroupedEventsRow.kt | 9 ++++++- .../timeline/components/TimelineItemRow.kt | 4 ++++ .../components/TimelineItemStateEventRow.kt | 3 +++ .../receipt/TimelineItemReadReceiptView.kt | 24 +++++++++++-------- 11 files changed, 41 insertions(+), 11 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index a0d7ebf771..cefc58d92f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -106,6 +106,7 @@ class TimelinePresenter @AssistedInject constructor( val keyBackupState by encryptionService.backupStateStateFlow.collectAsState() val isSendPublicReadReceiptsEnabled by sessionPreferencesStore.isSendPublicReadReceiptsEnabled().collectAsState(initial = true) + val renderReadReceipts by sessionPreferencesStore.isRenderReadReceiptsEnabled().collectAsState(initial = true) val sessionState by remember { derivedStateOf { @@ -183,6 +184,7 @@ class TimelinePresenter @AssistedInject constructor( highlightedEventId = highlightedEventId.value, paginationState = paginationState, timelineItems = timelineItems, + renderReadReceipts = renderReadReceipts, newEventState = newItemState.value, sessionState = sessionState, eventSink = { handleEvents(it) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt index ef25d94ebc..a709aa932f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt @@ -28,6 +28,7 @@ import kotlinx.collections.immutable.ImmutableList data class TimelineState( val timelineItems: ImmutableList, val timelineRoomInfo: TimelineRoomInfo, + val renderReadReceipts: Boolean, val highlightedEventId: EventId?, val paginationState: MatrixTimeline.PaginationState, val newEventState: NewEventState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index 3012a095e8..cb7ce3c938 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -48,6 +48,7 @@ import kotlin.random.Random fun aTimelineState(timelineItems: ImmutableList = persistentListOf()) = TimelineState( timelineItems = timelineItems, timelineRoomInfo = aTimelineRoomInfo(), + renderReadReceipts = false, paginationState = MatrixTimeline.PaginationState( isBackPaginating = false, hasMoreToLoadBackwards = true, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index 563eb78d78..72cf2be504 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -120,6 +120,7 @@ fun TimelineView( TimelineItemRow( timelineItem = timelineItem, timelineRoomInfo = state.timelineRoomInfo, + renderReadReceipts = state.renderReadReceipts, isLastOutgoingMessage = (timelineItem as? TimelineItem.Event)?.isMine == true && state.timelineItems.first().identifier() == timelineItem.identifier(), highlightedItem = state.highlightedEventId?.value, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ATimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ATimelineItemEventRow.kt index a7c860f46e..a86095b3fc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ATimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ATimelineItemEventRow.kt @@ -26,11 +26,13 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem internal fun ATimelineItemEventRow( event: TimelineItem.Event, timelineRoomInfo: TimelineRoomInfo = aTimelineRoomInfo(), + renderReadReceipts: Boolean = false, isLastOutgoingMessage: Boolean = false, isHighlighted: Boolean = false, ) = TimelineItemEventRow( event = event, timelineRoomInfo = timelineRoomInfo, + renderReadReceipts = renderReadReceipts, isLastOutgoingMessage = isLastOutgoingMessage, isHighlighted = isHighlighted, onClick = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index 3e23114729..bbc36b3d3f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -114,6 +114,7 @@ import kotlin.math.roundToInt fun TimelineItemEventRow( event: TimelineItem.Event, timelineRoomInfo: TimelineRoomInfo, + renderReadReceipts: Boolean, isLastOutgoingMessage: Boolean, isHighlighted: Boolean, onClick: () -> Unit, @@ -223,6 +224,7 @@ fun TimelineItemEventRow( isLastOutgoingMessage = isLastOutgoingMessage, receipts = event.readReceiptState.receipts, ), + renderReadReceipts = renderReadReceipts, onReadReceiptsClicked = { onReadReceiptClick(event) }, modifier = Modifier.padding(top = 4.dp), ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithRRPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithRRPreview.kt index 0e43e34670..85ff3a77bf 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithRRPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithRRPreview.kt @@ -47,6 +47,7 @@ internal fun TimelineItemEventRowWithRRPreview( timelineItemReactions = aTimelineItemReactions(count = 0), readReceiptState = TimelineItemReadReceipts(state.receipts), ), + renderReadReceipts = true, isLastOutgoingMessage = false, ) // A message from current user @@ -60,6 +61,7 @@ internal fun TimelineItemEventRowWithRRPreview( timelineItemReactions = aTimelineItemReactions(count = 0), readReceiptState = TimelineItemReadReceipts(state.receipts), ), + renderReadReceipts = true, isLastOutgoingMessage = false, ) // Another message from current user @@ -73,6 +75,7 @@ internal fun TimelineItemEventRowWithRRPreview( timelineItemReactions = aTimelineItemReactions(count = 0), readReceiptState = TimelineItemReadReceipts(state.receipts), ), + renderReadReceipts = true, isLastOutgoingMessage = true, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt index 5ca78adee6..c3d1f32ef2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt @@ -43,6 +43,7 @@ import io.element.android.libraries.matrix.api.core.UserId fun TimelineItemGroupedEventsRow( timelineItem: TimelineItem.GroupedEvents, timelineRoomInfo: TimelineRoomInfo, + renderReadReceipts: Boolean, isLastOutgoingMessage: Boolean, highlightedItem: String?, sessionState: SessionState, @@ -70,6 +71,7 @@ fun TimelineItemGroupedEventsRow( timelineItem = timelineItem, timelineRoomInfo = timelineRoomInfo, highlightedItem = highlightedItem, + renderReadReceipts = renderReadReceipts, isLastOutgoingMessage = isLastOutgoingMessage, sessionState = sessionState, onClick = onClick, @@ -93,6 +95,7 @@ private fun TimelineItemGroupedEventsRowContent( timelineItem: TimelineItem.GroupedEvents, timelineRoomInfo: TimelineRoomInfo, highlightedItem: String?, + renderReadReceipts: Boolean, isLastOutgoingMessage: Boolean, sessionState: SessionState, onClick: (TimelineItem.Event) -> Unit, @@ -124,6 +127,7 @@ private fun TimelineItemGroupedEventsRowContent( TimelineItemRow( timelineItem = subGroupEvent, timelineRoomInfo = timelineRoomInfo, + renderReadReceipts = renderReadReceipts, isLastOutgoingMessage = isLastOutgoingMessage, highlightedItem = highlightedItem, sessionState = sessionState, @@ -141,13 +145,14 @@ private fun TimelineItemGroupedEventsRowContent( ) } } - } else { + } else if (renderReadReceipts) { TimelineItemReadReceiptView( state = ReadReceiptViewState( sendState = null, isLastOutgoingMessage = false, receipts = timelineItem.aggregatedReadReceipts, ), + renderReadReceipts = true, onReadReceiptsClicked = onExpandGroupClick ) } @@ -163,6 +168,7 @@ internal fun TimelineItemGroupedEventsRowContentExpandedPreview() = ElementPrevi timelineItem = aGroupedEvents(withReadReceipts = true), timelineRoomInfo = aTimelineRoomInfo(), highlightedItem = null, + renderReadReceipts = true, isLastOutgoingMessage = false, sessionState = aSessionState(), onClick = {}, @@ -187,6 +193,7 @@ internal fun TimelineItemGroupedEventsRowContentCollapsePreview() = ElementPrevi timelineItem = aGroupedEvents(withReadReceipts = true), timelineRoomInfo = aTimelineRoomInfo(), highlightedItem = null, + renderReadReceipts = true, isLastOutgoingMessage = false, sessionState = aSessionState(), onClick = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt index 2fe1080743..899d38eb12 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt @@ -30,6 +30,7 @@ import io.element.android.libraries.matrix.api.core.UserId internal fun TimelineItemRow( timelineItem: TimelineItem, timelineRoomInfo: TimelineRoomInfo, + renderReadReceipts: Boolean, isLastOutgoingMessage: Boolean, highlightedItem: String?, sessionState: SessionState, @@ -58,6 +59,7 @@ internal fun TimelineItemRow( if (timelineItem.content is TimelineItemStateContent) { TimelineItemStateEventRow( event = timelineItem, + renderReadReceipts = renderReadReceipts, isLastOutgoingMessage = isLastOutgoingMessage, isHighlighted = highlightedItem == timelineItem.identifier(), onClick = { onClick(timelineItem) }, @@ -70,6 +72,7 @@ internal fun TimelineItemRow( TimelineItemEventRow( event = timelineItem, timelineRoomInfo = timelineRoomInfo, + renderReadReceipts = renderReadReceipts, isLastOutgoingMessage = isLastOutgoingMessage, isHighlighted = highlightedItem == timelineItem.identifier(), onClick = { onClick(timelineItem) }, @@ -91,6 +94,7 @@ internal fun TimelineItemRow( TimelineItemGroupedEventsRow( timelineItem = timelineItem, timelineRoomInfo = timelineRoomInfo, + renderReadReceipts = renderReadReceipts, isLastOutgoingMessage = isLastOutgoingMessage, highlightedItem = highlightedItem, sessionState = sessionState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt index 85eba29bc1..af8ed484bc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt @@ -47,6 +47,7 @@ import kotlinx.collections.immutable.toPersistentList @Composable fun TimelineItemStateEventRow( event: TimelineItem.Event, + renderReadReceipts: Boolean, isLastOutgoingMessage: Boolean, isHighlighted: Boolean, onClick: () -> Unit, @@ -90,6 +91,7 @@ fun TimelineItemStateEventRow( isLastOutgoingMessage = isLastOutgoingMessage, receipts = event.readReceiptState.receipts, ), + renderReadReceipts = renderReadReceipts, onReadReceiptsClicked = { onReadReceiptsClick(event) }, ) } @@ -107,6 +109,7 @@ internal fun TimelineItemStateEventRowPreview() = ElementPreview { receipts = listOf(aReadReceiptData(0)).toPersistentList(), ) ), + renderReadReceipts = true, isLastOutgoingMessage = false, isHighlighted = false, onClick = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt index b8f5dc3ca9..8f3d8f3710 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt @@ -58,20 +58,23 @@ import kotlinx.collections.immutable.ImmutableList @Composable fun TimelineItemReadReceiptView( state: ReadReceiptViewState, + renderReadReceipts: Boolean, onReadReceiptsClicked: () -> Unit, modifier: Modifier = Modifier, ) { if (state.receipts.isNotEmpty()) { - ReadReceiptsRow(modifier = modifier) { - ReadReceiptsAvatars( - receipts = state.receipts, - modifier = Modifier - .clip(RoundedCornerShape(4.dp)) - .clickable { - onReadReceiptsClicked() - } - .padding(2.dp) - ) + if (renderReadReceipts) { + ReadReceiptsRow(modifier = modifier) { + ReadReceiptsAvatars( + receipts = state.receipts, + modifier = Modifier + .clip(RoundedCornerShape(4.dp)) + .clickable { + onReadReceiptsClicked() + } + .padding(2.dp) + ) + } } } else { when (state.sendState) { @@ -206,6 +209,7 @@ internal fun TimelineItemReactionsViewPreview( ) = ElementPreview { TimelineItemReadReceiptView( state = state, + renderReadReceipts = true, onReadReceiptsClicked = {}, ) } From 9f6c6ef76fcc7410aad91772625244d77acadd76 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Feb 2024 14:34:54 +0100 Subject: [PATCH 044/119] Change link to Element Android room to Element X Android room. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5cf10c0fdf..24151c882f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ Please read https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.md -Element X Android support can be found in this room: [![Element Android Matrix room #element-android:matrix.org](https://img.shields.io/matrix/element-android:matrix.org.svg?label=%23element-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-android:matrix.org). +Element X Android support can be found in this room: [![Element X_Android Matrix room #element-x-android:matrix.org](https://img.shields.io/matrix/element-x-android:matrix.org.svg?label=%23element-x-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-x-android:matrix.org). The rest of the document contains specific rules for Matrix Android projects From 95fba29669c648c70a275146581da191539d8ea7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Feb 2024 15:16:13 +0100 Subject: [PATCH 045/119] Changelog --- changelog.d/2241.feature | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelog.d/2241.feature diff --git a/changelog.d/2241.feature b/changelog.d/2241.feature new file mode 100644 index 0000000000..4d97eccc21 --- /dev/null +++ b/changelog.d/2241.feature @@ -0,0 +1,2 @@ +Change "Read receipts" advanced setting used to send private Read Receipt to "Share presence" settings. +When disabled, private Read Receipts will be sent, and no typing notification will be sent. Also Read Receipts and typing notifications will not be rendered in the timeline. From cbb11d2550fd1ac98d9adfd3b595eb1df85b0503 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Mon, 5 Feb 2024 14:25:10 +0000 Subject: [PATCH 046/119] Update screenshots --- ...ll_AdvancedSettingsView-Day-1_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ll_AdvancedSettingsView-Day-1_2_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...ll_AdvancedSettingsView-Day-1_2_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...ll_AdvancedSettingsView-Day-1_2_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...ll_AdvancedSettingsView-Day-1_2_null_4,NEXUS_5,1.0,en].png | 4 ++-- ..._AdvancedSettingsView-Night-1_3_null_0,NEXUS_5,1.0,en].png | 4 ++-- ..._AdvancedSettingsView-Night-1_3_null_1,NEXUS_5,1.0,en].png | 4 ++-- ..._AdvancedSettingsView-Night-1_3_null_2,NEXUS_5,1.0,en].png | 4 ++-- ..._AdvancedSettingsView-Night-1_3_null_3,NEXUS_5,1.0,en].png | 4 ++-- ..._AdvancedSettingsView-Night-1_3_null_4,NEXUS_5,1.0,en].png | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_0,NEXUS_5,1.0,en].png index da1d8ddbfd..99f1da2d6f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da3f8614862ddacfd9b54afcd3bf091bec335e1489d7cbffc826059a89ae5478 -size 58453 +oid sha256:41d892f8a97bb9d89d6a6128c7acae4d5c509dadee0e1758c7fec9d76bc4216d +size 56386 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_1,NEXUS_5,1.0,en].png index c99349ac0c..0cee409bf8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1fd599f2e93432d5d5555f086b60059e84452a0da3c2a69d932042b7ee4d3bac -size 57922 +oid sha256:478e8c2d3dd29baf88f2641813760c623631d53446f31bca2f1372c05d4ca914 +size 55856 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_2,NEXUS_5,1.0,en].png index b228d561bd..7366bd20c2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9e9641228e0482c8e5500bb999b180e79cbfcb6a13e121f3be68aae5c58a6839 -size 57953 +oid sha256:6e174f5f0b15343f5f02cc716cb371aa89d87e298104ac8e5df83bdab2db573d +size 55887 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_3,NEXUS_5,1.0,en].png index 3df61a1ef4..7ba5c4b13b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c1c5b00098e5860d4c69ef9a652483ea1192d9fa10f5449fb817769ab054183 -size 36128 +oid sha256:d044628791909fdb8f621b3693cb63726e6a1dbc1fd6e12e1b3a4e3c9be1e868 +size 36288 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_4,NEXUS_5,1.0,en].png index 50da3b5d6c..bfc79dca12 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fe6625641cf06c45a817e9d10bc0d7296aa59160fc5a3e9dc28304e5b794125 -size 57943 +oid sha256:62762b55b56ee307e15a2aa488e8426714771d6e9d76d68ee82185b3420a348f +size 55831 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_0,NEXUS_5,1.0,en].png index 47fbdb6c0f..7324db05b6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e85b252d0d6c166f987974ebcf9a6b58b924316733b868ad0ed5a61413bda381 -size 54577 +oid sha256:923619c9af298ce1a250c5094429b6b820b560c62a55ec8baa40144478e42358 +size 53102 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_1,NEXUS_5,1.0,en].png index 10d7a6c5e1..b1a1b5e134 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f246ce9f60b25d9739d73e2aed98132dfb3a0a694acbf320ff796f35f805fdc -size 54273 +oid sha256:1125869fdf539f15103bef1a380e46af9b56c44262a1986009099e2700206038 +size 52799 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_2,NEXUS_5,1.0,en].png index 4b03ace56b..6b82d8a9ff 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d0403a4bb8da3d36922cb5362977af9b3052624525def05e5236f4f922384c31 -size 54297 +oid sha256:ecee6cd60257a931f717c8ae23f256ded8dd6ac8fa7af7430082c6a3f837b274 +size 52821 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_3,NEXUS_5,1.0,en].png index 01e0e907a6..5f4b3fa5fc 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:824774a1e19974090de4cb32e06a7f984b5db238c3dc8d04a35d3457e647ac84 -size 31999 +oid sha256:8449c20314175d6e829df65b2b1ba3cf36944a679b2e360ee5d2ed4d8333ad07 +size 32149 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_4,NEXUS_5,1.0,en].png index 449fb69524..fbe1510225 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_3_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0820b462a0dc103b66aef78528ae5ce13a55e2614a7a197e06713366c422893 -size 54313 +oid sha256:5c38a6f66d48174c6e2b3ac8525906cd371030f15ac3ca8727a8ca83f528916e +size 52823 From 320d5192c9aca1b73701d4a6eaa2e6ba9adf384e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Feb 2024 15:25:39 +0100 Subject: [PATCH 047/119] Remove `_`, added by mistake. --- CONTRIBUTING.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 24151c882f..c65cd25105 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ Please read https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.md -Element X Android support can be found in this room: [![Element X_Android Matrix room #element-x-android:matrix.org](https://img.shields.io/matrix/element-x-android:matrix.org.svg?label=%23element-x-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-x-android:matrix.org). +Element X Android support can be found in this room: [![Element X Android Matrix room #element-x-android:matrix.org](https://img.shields.io/matrix/element-x-android:matrix.org.svg?label=%23element-x-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-x-android:matrix.org). The rest of the document contains specific rules for Matrix Android projects diff --git a/README.md b/README.md index 91da420f1a..77d76adc3e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=vector-im_element-x-android&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=vector-im_element-x-android&metric=bugs)](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android) [![codecov](https://codecov.io/github/element-hq/element-x-android/branch/develop/graph/badge.svg?token=ecwvia7amV)](https://codecov.io/github/vector-im/element-x-android) -[![Element X_Android Matrix room #element-x-android:matrix.org](https://img.shields.io/matrix/element-x-android:matrix.org.svg?label=%23element-x-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-x-android:matrix.org) +[![Element X Android Matrix room #element-x-android:matrix.org](https://img.shields.io/matrix/element-x-android:matrix.org.svg?label=%23element-x-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-x-android:matrix.org) [![Localazy](https://img.shields.io/endpoint?url=https%3A%2F%2Fconnect.localazy.com%2Fstatus%2Felement%2Fdata%3Fcontent%3Dall%26title%3Dlocalazy%26logo%3Dtrue)](https://localazy.com/p/element) # Element X Android From 87f14d8babb36b6ca9c99140968c88992808dc3a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Feb 2024 16:00:29 +0100 Subject: [PATCH 048/119] Make the button to send problem enable, but show an error if the decription is too short to prevent users from being blocked. --- .../impl/bugreport/BugReportFormError.kt | 21 +++++++++++++++++++ .../impl/bugreport/BugReportPresenter.kt | 8 ++++++- .../impl/bugreport/BugReportState.kt | 5 +++-- .../impl/bugreport/BugReportStateProvider.kt | 1 + .../rageshake/impl/bugreport/BugReportView.kt | 8 ++++++- .../impl/src/main/res/values/localazy.xml | 1 + 6 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFormError.kt diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFormError.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFormError.kt new file mode 100644 index 0000000000..fdaebc376e --- /dev/null +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFormError.kt @@ -0,0 +1,21 @@ +/* + * 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 + * + * http://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.rageshake.impl.bugreport + +sealed class BugReportFormError: Exception() { + data object DescriptionTooShort : BugReportFormError() +} diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt index fea6bc95b1..5e3b3cfeb1 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt @@ -91,7 +91,13 @@ class BugReportPresenter @Inject constructor( fun handleEvents(event: BugReportEvents) { when (event) { - BugReportEvents.SendBugReport -> appCoroutineScope.sendBugReport(formState.value, crashInfo.isNotEmpty(), uploadListener) + BugReportEvents.SendBugReport -> { + if (formState.value.description.length < 10) { + sendingAction.value = AsyncAction.Failure(BugReportFormError.DescriptionTooShort) + } else { + appCoroutineScope.sendBugReport(formState.value, crashInfo.isNotEmpty(), uploadListener) + } + } BugReportEvents.ResetAll -> appCoroutineScope.resetAll() is BugReportEvents.SetDescription -> updateFormState(formState) { copy(description = event.description) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportState.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportState.kt index c3bfbec888..16edae46c4 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportState.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportState.kt @@ -28,8 +28,9 @@ data class BugReportState( val sending: AsyncAction, val eventSink: (BugReportEvents) -> Unit ) { - val submitEnabled = - formState.description.length > 10 && sending !is AsyncAction.Loading + val submitEnabled = sending !is AsyncAction.Loading + val isDescriptionInError = sending is AsyncAction.Failure && + sending.error is BugReportFormError.DescriptionTooShort } @Parcelize diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportStateProvider.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportStateProvider.kt index bb0d273de4..8bda648c4c 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportStateProvider.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportStateProvider.kt @@ -33,6 +33,7 @@ open class BugReportStateProvider : PreviewParameterProvider { ), aBugReportState().copy(sending = AsyncAction.Loading), aBugReportState().copy(sending = AsyncAction.Success(Unit)), + aBugReportState().copy(sending = AsyncAction.Failure(BugReportFormError.DescriptionTooShort)), ) } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt index e10e9250e3..86f351ea00 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt @@ -96,7 +96,7 @@ fun BugReportView( imeAction = ImeAction.Next ), minLines = 3, - // TODO Error text too short + isError = state.isDescriptionInError, ) } Spacer(modifier = Modifier.height(16.dp)) @@ -167,6 +167,12 @@ fun BugReportView( eventSink(BugReportEvents.ResetAll) onDone() }, + errorMessage = { error -> + when (error) { + BugReportFormError.DescriptionTooShort -> stringResource(id = R.string.screen_bug_report_error_description_too_short) + else -> error.message ?: error.toString() + } + }, onErrorDismiss = { eventSink(BugReportEvents.ClearError) }, ) } diff --git a/features/rageshake/impl/src/main/res/values/localazy.xml b/features/rageshake/impl/src/main/res/values/localazy.xml index 83413c3919..1d1805a386 100644 --- a/features/rageshake/impl/src/main/res/values/localazy.xml +++ b/features/rageshake/impl/src/main/res/values/localazy.xml @@ -7,6 +7,7 @@ "Please describe the problem. What did you do? What did you expect to happen? What actually happened. Please go into as much detail as you can." "Describe the problem…" "If possible, please write the description in English." + "The description is too short, please provide more details about what happened. Thanks!" "Send crash logs" "Allow logs" "Send screenshot" From 3814ea00467aced60f061fb7a8aeb596e6c2e1e0 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Mon, 5 Feb 2024 15:25:54 +0000 Subject: [PATCH 049/119] Update screenshots --- ...View_null_BugReportView-Day-0_1_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...View_null_BugReportView-Day-0_1_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...View_null_BugReportView-Day-0_1_null_4,NEXUS_5,1.0,en].png | 3 +++ ...ew_null_BugReportView-Night-0_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ew_null_BugReportView-Night-0_2_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...ew_null_BugReportView-Night-0_2_null_4,NEXUS_5,1.0,en].png | 3 +++ 6 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_0,NEXUS_5,1.0,en].png index 1c8b28b5ad..ad82505d3e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b80e48cadbb6e76fc6675eb0a2c49d6495be6a8145d649ad90c2bce10e8695a6 -size 72701 +oid sha256:96172e8f6a0283ae1abde6eac27d2ce5fde9e74f20ad89bfb2bb188a2f474fe3 +size 73080 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_3,NEXUS_5,1.0,en].png index 1c8b28b5ad..ad82505d3e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b80e48cadbb6e76fc6675eb0a2c49d6495be6a8145d649ad90c2bce10e8695a6 -size 72701 +oid sha256:96172e8f6a0283ae1abde6eac27d2ce5fde9e74f20ad89bfb2bb188a2f474fe3 +size 73080 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ee76b9944b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53089b8e5b88e494ef1565e6b9e759f46cd26b1b3f786221dcc047af31835daf +size 55195 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_0,NEXUS_5,1.0,en].png index 6ae1a1f2ed..ce0d1a50fb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7696307abd3ca96ae3c036cbf3088c2be08b4e4aabb861b0c7472460ab795303 -size 69558 +oid sha256:2eb33de5998edbdb79ff57b39dddcc7aecdd588613c9f5706fecd7b3f668a3a2 +size 69971 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_3,NEXUS_5,1.0,en].png index 6ae1a1f2ed..ce0d1a50fb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7696307abd3ca96ae3c036cbf3088c2be08b4e4aabb861b0c7472460ab795303 -size 69558 +oid sha256:2eb33de5998edbdb79ff57b39dddcc7aecdd588613c9f5706fecd7b3f668a3a2 +size 69971 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8084317ee1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8fbef7e43158f49adcaf30447443deb39f5980b0970eab79874d2a7a8dbca563 +size 49958 From 9cef656d209d01ef5fd449e01ff7c05357026ebf Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Feb 2024 18:33:35 +0100 Subject: [PATCH 050/119] Fiw formatting error. --- .../features/rageshake/impl/bugreport/BugReportFormError.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFormError.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFormError.kt index fdaebc376e..39d1235ee5 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFormError.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFormError.kt @@ -16,6 +16,6 @@ package io.element.android.features.rageshake.impl.bugreport -sealed class BugReportFormError: Exception() { +sealed class BugReportFormError : Exception() { data object DescriptionTooShort : BugReportFormError() } From 3fa17be222ee7aacea505b77f54f592f58df9145 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 19:57:51 +0000 Subject: [PATCH 051/119] Update dependency org.matrix.rustcomponents:sdk-android to v0.1.97 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c5f504934d..78f5e72ad1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -152,7 +152,7 @@ jsoup = "org.jsoup:jsoup:1.17.2" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = "app.cash.molecule:molecule-runtime:1.3.2" timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.96" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.97" matrix_richtexteditor = { module = "io.element.android:wysiwyg", 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" } From d7b6c39eb06fa584349e492c3d4990e578d7af17 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 08:54:49 +0100 Subject: [PATCH 052/119] Small rework: create classical createPresenter() method to reduce code duplication. --- .../impl/bugreport/BugReportPresenterTest.kt | 67 +++++++------------ 1 file changed, 26 insertions(+), 41 deletions(-) diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt index e36cc5b841..8b9b5dbe89 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt @@ -20,6 +20,9 @@ import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.rageshake.api.crash.CrashDataStore +import io.element.android.features.rageshake.api.reporter.BugReporter +import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder import io.element.android.features.rageshake.test.crash.A_CRASH_DATA import io.element.android.features.rageshake.test.crash.FakeCrashDataStore import io.element.android.features.rageshake.test.screenshot.A_SCREENSHOT_URI @@ -27,6 +30,7 @@ import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolde import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.test.A_FAILURE_REASON import io.element.android.tests.testutils.WarmUpRule +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -40,12 +44,7 @@ class BugReportPresenterTest { @Test fun `present - initial state`() = runTest { - val presenter = BugReportPresenter( - FakeBugReporter(), - FakeCrashDataStore(), - FakeScreenshotHolder(), - this, - ) + val presenter = createPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -61,12 +60,7 @@ class BugReportPresenterTest { @Test fun `present - set description`() = runTest { - val presenter = BugReportPresenter( - FakeBugReporter(), - FakeCrashDataStore(), - FakeScreenshotHolder(), - this, - ) + val presenter = createPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -80,12 +74,7 @@ class BugReportPresenterTest { @Test fun `present - can contact`() = runTest { - val presenter = BugReportPresenter( - FakeBugReporter(), - FakeCrashDataStore(), - FakeScreenshotHolder(), - this, - ) + val presenter = createPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -99,12 +88,7 @@ class BugReportPresenterTest { @Test fun `present - send logs`() = runTest { - val presenter = BugReportPresenter( - FakeBugReporter(), - FakeCrashDataStore(), - FakeScreenshotHolder(), - this, - ) + val presenter = createPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -119,12 +103,7 @@ class BugReportPresenterTest { @Test fun `present - send screenshot`() = runTest { - val presenter = BugReportPresenter( - FakeBugReporter(), - FakeCrashDataStore(), - FakeScreenshotHolder(), - this, - ) + val presenter = createPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -138,11 +117,9 @@ class BugReportPresenterTest { @Test fun `present - reset all`() = runTest { - val presenter = BugReportPresenter( - FakeBugReporter(), - FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true), - FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI), - this, + val presenter = createPresenter( + crashDataStore = FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true), + screenshotHolder = FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -160,11 +137,10 @@ class BugReportPresenterTest { @Test fun `present - send success`() = runTest { - val presenter = BugReportPresenter( + val presenter = createPresenter( FakeBugReporter(mode = FakeBugReporterMode.Success), FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true), FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI), - this, ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -185,11 +161,10 @@ class BugReportPresenterTest { @Test fun `present - send failure`() = runTest { - val presenter = BugReportPresenter( + val presenter = createPresenter( FakeBugReporter(mode = FakeBugReporterMode.Failure), FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true), FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI), - this, ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -214,11 +189,10 @@ class BugReportPresenterTest { @Test fun `present - send cancel`() = runTest { - val presenter = BugReportPresenter( + val presenter = createPresenter( FakeBugReporter(mode = FakeBugReporterMode.Cancel), FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true), FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI), - this, ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -235,4 +209,15 @@ class BugReportPresenterTest { assertThat(awaitItem().sending).isEqualTo(AsyncAction.Uninitialized) } } + + private fun TestScope.createPresenter( + bugReporter: BugReporter = FakeBugReporter(), + crashDataStore: CrashDataStore = FakeCrashDataStore(), + screenshotHolder: ScreenshotHolder = FakeScreenshotHolder(), + ) = BugReportPresenter( + bugReporter, + crashDataStore, + screenshotHolder, + this, + ) } From 07d13fc98123637052b10bc7013e1b687a9feb11 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 09:01:59 +0100 Subject: [PATCH 053/119] Fix test now that the send button is always enabled but can fail if description is too short. --- .../impl/bugreport/BugReportPresenterTest.kt | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt index 8b9b5dbe89..7d6f16d412 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt @@ -54,7 +54,7 @@ class BugReportPresenterTest { assertThat(initialState.sending).isEqualTo(AsyncAction.Uninitialized) assertThat(initialState.screenshotUri).isNull() assertThat(initialState.sendingProgress).isEqualTo(0f) - assertThat(initialState.submitEnabled).isFalse() + assertThat(initialState.submitEnabled).isTrue() } } @@ -66,7 +66,7 @@ class BugReportPresenterTest { }.test { val initialState = awaitItem() initialState.eventSink.invoke(BugReportEvents.SetDescription(A_SHORT_DESCRIPTION)) - assertThat(awaitItem().submitEnabled).isFalse() + assertThat(awaitItem().submitEnabled).isTrue() initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION)) assertThat(awaitItem().submitEnabled).isTrue() } @@ -146,6 +146,8 @@ class BugReportPresenterTest { presenter.present() }.test { val initialState = awaitItem() + initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION)) + skipItems(1) initialState.eventSink.invoke(BugReportEvents.SendBugReport) skipItems(1) val progressState = awaitItem() @@ -170,6 +172,8 @@ class BugReportPresenterTest { presenter.present() }.test { val initialState = awaitItem() + initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION)) + skipItems(1) initialState.eventSink.invoke(BugReportEvents.SendBugReport) skipItems(1) val progressState = awaitItem() @@ -187,6 +191,25 @@ class BugReportPresenterTest { } } + @Test + fun `present - send failure description too short`() = runTest { + val presenter = createPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink.invoke(BugReportEvents.SetDescription(A_SHORT_DESCRIPTION)) + skipItems(1) + initialState.eventSink.invoke(BugReportEvents.SendBugReport) + val errorState = awaitItem() + assertThat(errorState.sending).isEqualTo(AsyncAction.Failure(BugReportFormError.DescriptionTooShort)) + // Reset failure + initialState.eventSink.invoke(BugReportEvents.ClearError) + val lastItem = awaitItem() + assertThat(lastItem.sending).isInstanceOf(AsyncAction.Uninitialized::class.java) + } + } + @Test fun `present - send cancel`() = runTest { val presenter = createPresenter( @@ -198,6 +221,8 @@ class BugReportPresenterTest { presenter.present() }.test { val initialState = awaitItem() + initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION)) + skipItems(1) initialState.eventSink.invoke(BugReportEvents.SendBugReport) skipItems(1) val progressState = awaitItem() From 8c56f0bd92bf6b8cc627e1f02959ddda286f6d2d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 09:18:35 +0100 Subject: [PATCH 054/119] Fix API break and change from SDK 0.1.97 --- .../messages/impl/MessagesPresenter.kt | 2 +- .../impl/timeline/TimelinePresenter.kt | 2 +- .../matrix/api/room/MessageEventType.kt | 10 +++++-- .../libraries/matrix/impl/RustMatrixClient.kt | 30 +++++++++---------- .../matrix/impl/room/MessageEventType.kt | 16 ++++++++-- 5 files changed, 39 insertions(+), 21 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index d378014e2f..5f21e5b348 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -139,7 +139,7 @@ class MessagesPresenter @AssistedInject constructor( val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value) val userHasPermissionToRedactOwn by room.canRedactOwnAsState(updateKey = syncUpdateFlow.value) val userHasPermissionToRedactOther by room.canRedactOtherAsState(updateKey = syncUpdateFlow.value) - val userHasPermissionToSendReaction by room.canSendMessageAsState(type = MessageEventType.REACTION_SENT, updateKey = syncUpdateFlow.value) + val userHasPermissionToSendReaction by room.canSendMessageAsState(type = MessageEventType.REACTION, updateKey = syncUpdateFlow.value) val roomName: AsyncData by remember { derivedStateOf { roomInfo?.name?.let { AsyncData.Success(it) } ?: AsyncData.Uninitialized } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index a0d7ebf771..1ce24dff5d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -97,7 +97,7 @@ class TimelinePresenter @AssistedInject constructor( val paginationState by timeline.paginationState.collectAsState() val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value) - val userHasPermissionToSendReaction by room.canSendMessageAsState(type = MessageEventType.REACTION_SENT, updateKey = syncUpdateFlow.value) + val userHasPermissionToSendReaction by room.canSendMessageAsState(type = MessageEventType.REACTION, updateKey = syncUpdateFlow.value) val prevMostRecentItemId = rememberSaveable { mutableStateOf(null) } val newItemState = remember { mutableStateOf(NewEventState.None) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MessageEventType.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MessageEventType.kt index 109e50e602..ea9e37cfd6 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MessageEventType.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MessageEventType.kt @@ -28,9 +28,15 @@ enum class MessageEventType { KEY_VERIFICATION_KEY, KEY_VERIFICATION_MAC, KEY_VERIFICATION_DONE, - REACTION_SENT, + REACTION, ROOM_ENCRYPTED, ROOM_MESSAGE, ROOM_REDACTION, - STICKER + STICKER, + POLL_END, + POLL_RESPONSE, + POLL_START, + UNSTABLE_POLL_END, + UNSTABLE_POLL_RESPONSE, + UNSTABLE_POLL_START, } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 26bc775e55..2ea2699e42 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -76,12 +76,12 @@ import kotlinx.coroutines.withTimeout import org.matrix.rustcomponents.sdk.BackupState import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientDelegate -import org.matrix.rustcomponents.sdk.FilterStateEventType import org.matrix.rustcomponents.sdk.FilterTimelineEventType import org.matrix.rustcomponents.sdk.NotificationProcessSetup import org.matrix.rustcomponents.sdk.PowerLevels import org.matrix.rustcomponents.sdk.Room import org.matrix.rustcomponents.sdk.RoomListItem +import org.matrix.rustcomponents.sdk.StateEventType import org.matrix.rustcomponents.sdk.TaskHandle import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter import org.matrix.rustcomponents.sdk.use @@ -203,20 +203,20 @@ class RustMatrixClient( private val eventFilters = TimelineEventTypeFilter.exclude( listOf( - FilterStateEventType.ROOM_ALIASES, - FilterStateEventType.ROOM_CANONICAL_ALIAS, - FilterStateEventType.ROOM_GUEST_ACCESS, - FilterStateEventType.ROOM_HISTORY_VISIBILITY, - FilterStateEventType.ROOM_JOIN_RULES, - FilterStateEventType.ROOM_PINNED_EVENTS, - FilterStateEventType.ROOM_POWER_LEVELS, - FilterStateEventType.ROOM_SERVER_ACL, - FilterStateEventType.ROOM_TOMBSTONE, - FilterStateEventType.SPACE_CHILD, - FilterStateEventType.SPACE_PARENT, - FilterStateEventType.POLICY_RULE_ROOM, - FilterStateEventType.POLICY_RULE_SERVER, - FilterStateEventType.POLICY_RULE_USER, + StateEventType.ROOM_ALIASES, + StateEventType.ROOM_CANONICAL_ALIAS, + StateEventType.ROOM_GUEST_ACCESS, + StateEventType.ROOM_HISTORY_VISIBILITY, + StateEventType.ROOM_JOIN_RULES, + StateEventType.ROOM_PINNED_EVENTS, + StateEventType.ROOM_POWER_LEVELS, + StateEventType.ROOM_SERVER_ACL, + StateEventType.ROOM_TOMBSTONE, + StateEventType.SPACE_CHILD, + StateEventType.SPACE_PARENT, + StateEventType.POLICY_RULE_ROOM, + StateEventType.POLICY_RULE_SERVER, + StateEventType.POLICY_RULE_USER, ).map(FilterTimelineEventType::State) ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MessageEventType.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MessageEventType.kt index a117c1d313..bce74bc2db 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MessageEventType.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MessageEventType.kt @@ -31,11 +31,17 @@ fun MessageEventType.map(): MessageLikeEventType = when (this) { MessageEventType.KEY_VERIFICATION_KEY -> MessageLikeEventType.KEY_VERIFICATION_KEY MessageEventType.KEY_VERIFICATION_MAC -> MessageLikeEventType.KEY_VERIFICATION_MAC MessageEventType.KEY_VERIFICATION_DONE -> MessageLikeEventType.KEY_VERIFICATION_DONE - MessageEventType.REACTION_SENT -> MessageLikeEventType.REACTION_SENT + MessageEventType.REACTION -> MessageLikeEventType.REACTION MessageEventType.ROOM_ENCRYPTED -> MessageLikeEventType.ROOM_ENCRYPTED MessageEventType.ROOM_MESSAGE -> MessageLikeEventType.ROOM_MESSAGE MessageEventType.ROOM_REDACTION -> MessageLikeEventType.ROOM_REDACTION MessageEventType.STICKER -> MessageLikeEventType.STICKER + MessageEventType.POLL_END -> MessageLikeEventType.POLL_END + MessageEventType.POLL_RESPONSE -> MessageLikeEventType.POLL_RESPONSE + MessageEventType.POLL_START -> MessageLikeEventType.POLL_START + MessageEventType.UNSTABLE_POLL_END -> MessageLikeEventType.UNSTABLE_POLL_END + MessageEventType.UNSTABLE_POLL_RESPONSE -> MessageLikeEventType.UNSTABLE_POLL_RESPONSE + MessageEventType.UNSTABLE_POLL_START -> MessageLikeEventType.UNSTABLE_POLL_START } fun MessageLikeEventType.map(): MessageEventType = when (this) { @@ -50,9 +56,15 @@ fun MessageLikeEventType.map(): MessageEventType = when (this) { MessageLikeEventType.KEY_VERIFICATION_KEY -> MessageEventType.KEY_VERIFICATION_KEY MessageLikeEventType.KEY_VERIFICATION_MAC -> MessageEventType.KEY_VERIFICATION_MAC MessageLikeEventType.KEY_VERIFICATION_DONE -> MessageEventType.KEY_VERIFICATION_DONE - MessageLikeEventType.REACTION_SENT -> MessageEventType.REACTION_SENT + MessageLikeEventType.REACTION -> MessageEventType.REACTION MessageLikeEventType.ROOM_ENCRYPTED -> MessageEventType.ROOM_ENCRYPTED MessageLikeEventType.ROOM_MESSAGE -> MessageEventType.ROOM_MESSAGE MessageLikeEventType.ROOM_REDACTION -> MessageEventType.ROOM_REDACTION MessageLikeEventType.STICKER -> MessageEventType.STICKER + MessageLikeEventType.POLL_END -> MessageEventType.POLL_END + MessageLikeEventType.POLL_RESPONSE -> MessageEventType.POLL_RESPONSE + MessageLikeEventType.POLL_START -> MessageEventType.POLL_START + MessageLikeEventType.UNSTABLE_POLL_END -> MessageEventType.UNSTABLE_POLL_END + MessageLikeEventType.UNSTABLE_POLL_RESPONSE -> MessageEventType.UNSTABLE_POLL_RESPONSE + MessageLikeEventType.UNSTABLE_POLL_START -> MessageEventType.UNSTABLE_POLL_START } From 706ce1d0cdd6fa099a384946c189ce690fe34379 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 15:33:03 +0100 Subject: [PATCH 055/119] Do not use a key for the LazyColumn, or the scroll will not behave as expected if a room is moved to the top of the list. --- .../io/element/android/features/roomlist/impl/RoomListView.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt index 929d2550a4..ace8b9ce60 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt @@ -259,10 +259,11 @@ private fun RoomListContent( } val roomList = state.roomList.dataOrNull().orEmpty() + // Note: do not use a key for the LazyColumn, or the scroll will not behave as expected if a room + // is moved to the top of the list. itemsIndexed( items = roomList, contentType = { _, room -> room.contentType() }, - key = { _, room -> room.roomId.value } ) { index, room -> RoomSummaryRow( room = room, From e66f53f64ce9dddc85d90048182f6936df4210ba Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 17:12:14 +0100 Subject: [PATCH 056/119] Add test to ensure that TimelineView loads more Events. --- .../impl/timeline/TimelineStateProvider.kt | 26 ++++-- .../impl/timeline/TimelineViewTest.kt | 83 +++++++++++++++++++ .../tests/testutils/EnsureNeverCalled.kt | 6 ++ 3 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index 3012a095e8..d5893c2c49 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -45,23 +45,35 @@ import kotlinx.collections.immutable.toPersistentList import java.util.UUID import kotlin.random.Random -fun aTimelineState(timelineItems: ImmutableList = persistentListOf()) = TimelineState( +fun aTimelineState( + timelineItems: ImmutableList = persistentListOf(), + paginationState: MatrixTimeline.PaginationState = aPaginationState(), + eventSink: (TimelineEvents) -> Unit = {}, +) = TimelineState( timelineItems = timelineItems, timelineRoomInfo = aTimelineRoomInfo(), - paginationState = MatrixTimeline.PaginationState( - isBackPaginating = false, - hasMoreToLoadBackwards = true, - beginningOfRoomReached = false, - ), + paginationState = paginationState, highlightedEventId = null, newEventState = NewEventState.None, sessionState = aSessionState( isSessionVerified = true, isKeyBackupEnabled = true, ), - eventSink = {}, + eventSink = eventSink, ) +fun aPaginationState( + isBackPaginating: Boolean = false, + hasMoreToLoadBackwards: Boolean = true, + beginningOfRoomReached: Boolean = false, +): MatrixTimeline.PaginationState { + return MatrixTimeline.PaginationState( + isBackPaginating = isBackPaginating, + hasMoreToLoadBackwards = hasMoreToLoadBackwards, + beginningOfRoomReached = beginningOfRoomReached, + ) +} + internal fun aTimelineItemList(content: TimelineItemEventContent): ImmutableList { return persistentListOf( // 3 items (First Middle Last) with isMine = false diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt new file mode 100644 index 0000000000..f80f4ce4c2 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt @@ -0,0 +1,83 @@ +/* + * 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 + * + * http://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.timeline + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.tests.testutils.EnsureNeverCalledWithParam +import io.element.android.tests.testutils.EnsureNeverCalledWithTwoParams +import io.element.android.tests.testutils.EventsRecorder +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class TimelineViewTest { + @get:Rule val rule = createAndroidComposeRule() + + @Test + fun `reaching the end of the timeline with more events to load emits a LoadMore event`() { + val eventsRecorder = EventsRecorder() + rule.setContent { + TimelineView( + aTimelineState( + eventSink = eventsRecorder, + paginationState = aPaginationState( + hasMoreToLoadBackwards = true, + ) + ), + roomName = null, + onUserDataClicked = EnsureNeverCalledWithParam(), + onMessageClicked = EnsureNeverCalledWithParam(), + onMessageLongClicked = EnsureNeverCalledWithParam(), + onTimestampClicked = EnsureNeverCalledWithParam(), + onSwipeToReply = EnsureNeverCalledWithParam(), + onReactionClicked = EnsureNeverCalledWithTwoParams(), + onReactionLongClicked = EnsureNeverCalledWithTwoParams(), + onMoreReactionsClicked = EnsureNeverCalledWithParam(), + onReadReceiptClick = EnsureNeverCalledWithParam(), + ) + } + eventsRecorder.assertSingle(TimelineEvents.LoadMore) + } + + @Test + fun `reaching the end of the timeline does not send a LoadMore event`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + rule.setContent { + TimelineView( + aTimelineState( + eventSink = eventsRecorder, + paginationState = aPaginationState( + hasMoreToLoadBackwards = false, + ) + ), + roomName = null, + onUserDataClicked = EnsureNeverCalledWithParam(), + onMessageClicked = EnsureNeverCalledWithParam(), + onMessageLongClicked = EnsureNeverCalledWithParam(), + onTimestampClicked = EnsureNeverCalledWithParam(), + onSwipeToReply = EnsureNeverCalledWithParam(), + onReactionClicked = EnsureNeverCalledWithTwoParams(), + onReactionLongClicked = EnsureNeverCalledWithTwoParams(), + onMoreReactionsClicked = EnsureNeverCalledWithParam(), + onReadReceiptClick = EnsureNeverCalledWithParam(), + ) + } + } +} diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt index e98c025238..870806d424 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt @@ -27,3 +27,9 @@ class EnsureNeverCalledWithParam : (T) -> Unit { throw AssertionError("Should not be called and is called with $p1") } } + +class EnsureNeverCalledWithTwoParams : (T, U) -> Unit { + override fun invoke(p1: T, p2: U) { + throw AssertionError("Should not be called and is called with $p1 and $p2") + } +} From 69f0c9935fd3fe00ce268a89a758cfda42cce3dd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 17:57:11 +0100 Subject: [PATCH 057/119] Add type hierarchy on `TimelineEvents`. It is not really OK that a Event was able to invoke `LoadMore` for instance. --- .../messages/impl/timeline/TimelineEvents.kt | 13 ++++++++++--- .../timeline/components/TimelineItemEventRow.kt | 6 +++--- .../components/TimelineItemGroupedEventsRow.kt | 4 ++-- .../impl/timeline/components/TimelineItemRow.kt | 2 +- .../components/TimelineItemStateEventRow.kt | 2 +- .../event/TimelineItemEventContentView.kt | 2 +- .../components/event/TimelineItemPollView.kt | 2 +- 7 files changed, 19 insertions(+), 12 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt index 2fdcb947b4..08cd9d2483 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt @@ -22,16 +22,23 @@ sealed interface TimelineEvents { data object LoadMore : TimelineEvents data class SetHighlightedEvent(val eventId: EventId?) : TimelineEvents data class OnScrollFinished(val firstIndex: Int) : TimelineEvents + + // Events coming from a timeline item + sealed interface EventFromTimelineItem : TimelineEvents + + // Events coming from a poll item + sealed interface TimelineItemPollEvents : EventFromTimelineItem + data class PollAnswerSelected( val pollStartId: EventId, val answerId: String - ) : TimelineEvents + ) : TimelineItemPollEvents data class PollEndClicked( val pollStartId: EventId, - ) : TimelineEvents + ) : TimelineItemPollEvents data class PollEditClicked( val pollStartId: EventId, - ) : TimelineEvents + ) : TimelineItemPollEvents } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index 3e23114729..fb131ed9ff 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -126,7 +126,7 @@ fun TimelineItemEventRow( onMoreReactionsClick: (eventId: TimelineItem.Event) -> Unit, onReadReceiptClick: (event: TimelineItem.Event) -> Unit, onSwipeToReply: () -> Unit, - eventSink: (TimelineEvents) -> Unit, + eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit, modifier: Modifier = Modifier ) { val coroutineScope = rememberCoroutineScope() @@ -267,7 +267,7 @@ private fun TimelineItemEventRowContent( onReactionLongClicked: (emoji: String) -> Unit, onMoreReactionsClicked: (event: TimelineItem.Event) -> Unit, onMentionClicked: (Mention) -> Unit, - eventSink: (TimelineEvents) -> Unit, + eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit, modifier: Modifier = Modifier, ) { fun ConstrainScope.linkStartOrEnd(event: TimelineItem.Event) = if (event.isMine) { @@ -412,7 +412,7 @@ private fun MessageEventBubbleContent( inReplyToClick: () -> Unit, onTimestampClicked: () -> Unit, onMentionClicked: (Mention) -> Unit, - eventSink: (TimelineEvents) -> Unit, + eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit, @SuppressLint("ModifierParameter") // need to rename this modifier to prevent linter false positives @Suppress("ModifierNaming") diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt index 5ca78adee6..4c79592a93 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt @@ -55,7 +55,7 @@ fun TimelineItemGroupedEventsRow( onReactionLongClick: (key: String, TimelineItem.Event) -> Unit, onMoreReactionsClick: (TimelineItem.Event) -> Unit, onReadReceiptClick: (TimelineItem.Event) -> Unit, - eventSink: (TimelineEvents) -> Unit, + eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit, modifier: Modifier = Modifier ) { val isExpanded = rememberSaveable(key = timelineItem.identifier()) { mutableStateOf(false) } @@ -104,7 +104,7 @@ private fun TimelineItemGroupedEventsRowContent( onReactionLongClick: (key: String, TimelineItem.Event) -> Unit, onMoreReactionsClick: (TimelineItem.Event) -> Unit, onReadReceiptClick: (TimelineItem.Event) -> Unit, - eventSink: (TimelineEvents) -> Unit, + eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit, modifier: Modifier = Modifier, ) { Column(modifier = modifier.animateContentSize()) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt index 2fe1080743..0fd5ca3f5c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt @@ -43,7 +43,7 @@ internal fun TimelineItemRow( onReadReceiptClick: (TimelineItem.Event) -> Unit, onTimestampClicked: (TimelineItem.Event) -> Unit, onSwipeToReply: (TimelineItem.Event) -> Unit, - eventSink: (TimelineEvents) -> Unit, + eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit, modifier: Modifier = Modifier ) { when (timelineItem) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt index 85eba29bc1..4a753639b1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt @@ -52,7 +52,7 @@ fun TimelineItemStateEventRow( onClick: () -> Unit, onLongClick: () -> Unit, onReadReceiptsClick: (event: TimelineItem.Event) -> Unit, - eventSink: (TimelineEvents) -> Unit, + eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit, modifier: Modifier = Modifier ) { val interactionSource = remember { MutableInteractionSource() } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEventContentView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEventContentView.kt index e7dbf678af..d0aae12beb 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEventContentView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEventContentView.kt @@ -43,7 +43,7 @@ import io.element.android.libraries.architecture.Presenter fun TimelineItemEventContentView( content: TimelineItemEventContent, onLinkClicked: (url: String) -> Unit, - eventSink: (TimelineEvents) -> Unit, + eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit, modifier: Modifier = Modifier, onContentLayoutChanged: (ContentAvoidingLayoutData) -> Unit = {}, ) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt index 91dd5b99d3..47f4aa7da6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt @@ -31,7 +31,7 @@ import kotlinx.collections.immutable.toImmutableList @Composable fun TimelineItemPollView( content: TimelineItemPollContent, - eventSink: (TimelineEvents) -> Unit, + eventSink: (TimelineEvents.TimelineItemPollEvents) -> Unit, modifier: Modifier = Modifier, ) { fun onAnswerSelected(pollStartId: EventId, answerId: String) { From 80c474e1a69272d7b1e573befb3525039bf43268 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 17:57:18 +0100 Subject: [PATCH 058/119] Format file --- .../messages/impl/timeline/TimelinePresenter.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 1ce24dff5d..3302c7e342 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -154,12 +154,12 @@ class TimelinePresenter @AssistedInject constructor( LaunchedEffect(Unit) { combine(timeline.timelineItems, room.membersStateFlow) { items, membersState -> - timelineItemsFactory.replaceWith( - timelineItems = items, - roomMembers = membersState.roomMembers().orEmpty() - ) - items - } + timelineItemsFactory.replaceWith( + timelineItems = items, + roomMembers = membersState.roomMembers().orEmpty() + ) + items + } .onEach { timelineItems -> if (timelineItems.isEmpty()) { paginateBackwards() From b25509a61191b8f004adc5b7b6dc1e56fae6442d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 18:27:47 +0100 Subject: [PATCH 059/119] Add test for TimelineItemPollView --- features/messages/impl/build.gradle.kts | 1 + .../event/TimelineItemPollViewTest.kt | 98 +++++++++++++++++++ .../android/tests/testutils/EventsRecorder.kt | 4 + 3 files changed, 103 insertions(+) create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollViewTest.kt diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index 897dc36faa..a2960399e9 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -94,6 +94,7 @@ dependencies { testImplementation(projects.libraries.voicerecorder.test) testImplementation(projects.libraries.mediaplayer.test) testImplementation(projects.libraries.mediaviewer.test) + testImplementation(projects.libraries.testtags) testImplementation(libs.test.mockk) testImplementation(libs.test.junitext) testImplementation(libs.test.robolectric) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollViewTest.kt new file mode 100644 index 0000000000..4ea16a26b0 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollViewTest.kt @@ -0,0 +1,98 @@ +/* + * 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 + * + * http://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.timeline.components.event + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.hasText +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.performClick +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.features.messages.impl.timeline.TimelineEvents +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent +import io.element.android.libraries.testtags.TestTags +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.clickOn +import io.element.android.tests.testutils.pressTag +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class TimelineItemPollViewTest { + @get:Rule val rule = createAndroidComposeRule() + + @Test + fun `answering a poll with first answer should emit a PollAnswerSelected event`() { + testAnswer(answerIndex = 0) + } + + @Test + fun `answering a poll with second answer should emit a PollAnswerSelected event`() { + testAnswer(answerIndex = 1) + } + + private fun testAnswer(answerIndex: Int) { + val eventsRecorder = EventsRecorder() + val content = aTimelineItemPollContent() + rule.setContent { + TimelineItemPollView( + content = content, + eventSink = eventsRecorder + ) + } + val answer = content.answerItems[answerIndex].answer + rule.onNode(hasText(answer.text)).performClick() + eventsRecorder.assertSingle(TimelineEvents.PollAnswerSelected(content.eventId!!, answer.id)) + } + + @Test + fun `editing a poll should emit a PollEditClicked event`() { + val eventsRecorder = EventsRecorder() + val content = aTimelineItemPollContent( + isMine = true, + isEditable = true, + ) + rule.setContent { + TimelineItemPollView( + content = content, + eventSink = eventsRecorder + ) + } + rule.clickOn(CommonStrings.action_edit_poll) + eventsRecorder.assertSingle(TimelineEvents.PollEditClicked(content.eventId!!)) + } + + @Test + fun `closing a poll should emit a PollEndClicked event`() { + val eventsRecorder = EventsRecorder() + val content = aTimelineItemPollContent( + isMine = true, + ) + rule.setContent { + TimelineItemPollView( + content = content, + eventSink = eventsRecorder + ) + } + rule.clickOn(CommonStrings.action_end_poll) + // A confirmation dialog should be shown + eventsRecorder.assertEmpty() + rule.pressTag(TestTags.dialogPositive.value) + eventsRecorder.assertSingle(TimelineEvents.PollEndClicked(content.eventId!!)) + } +} diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EventsRecorder.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EventsRecorder.kt index 4cc9bd078c..3a1c4babff 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EventsRecorder.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EventsRecorder.kt @@ -31,6 +31,10 @@ class EventsRecorder( } } + fun assertEmpty() { + assertThat(events).isEmpty() + } + fun assertSingle(event: T) { assertList(listOf(event)) } From 6bae19b6b03f69eade5c2732d0df7186706c45c4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 20:15:34 +0100 Subject: [PATCH 060/119] Try to get more log from Codecov action. --- .github/workflows/tests.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b7b69bb26f..c3545452e7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -82,7 +82,9 @@ jobs: - name: ☂️ Upload coverage reports to codecov if: always() uses: codecov/codecov-action@v4 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + with: + fail_ci_if_error: true + verbose: true + token: ${{ secrets.CODECOV_TOKEN }} # with: # files: build/reports/kover/xml/report.xml From 3f570601ed1ed7a73852a7535bf94d6b1a92f515 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Feb 2024 10:29:18 +0100 Subject: [PATCH 061/119] Remove verbose flag, was added to get more details about an error. --- .github/workflows/tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c3545452e7..bcbfac1d7f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -84,7 +84,6 @@ jobs: uses: codecov/codecov-action@v4 with: fail_ci_if_error: true - verbose: true token: ${{ secrets.CODECOV_TOKEN }} # with: # files: build/reports/kover/xml/report.xml From ff608c9581a8f122b8b09766f89b96f5cf83d608 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Feb 2024 10:30:47 +0100 Subject: [PATCH 062/119] Use Kdoc. --- .../features/messages/impl/timeline/TimelineEvents.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt index 08cd9d2483..cf02664a98 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt @@ -23,10 +23,14 @@ sealed interface TimelineEvents { data class SetHighlightedEvent(val eventId: EventId?) : TimelineEvents data class OnScrollFinished(val firstIndex: Int) : TimelineEvents - // Events coming from a timeline item + /** + * Events coming from a timeline item. + */ sealed interface EventFromTimelineItem : TimelineEvents - // Events coming from a poll item + /** + * Events coming from a poll item. + */ sealed interface TimelineItemPollEvents : EventFromTimelineItem data class PollAnswerSelected( From 1fd407590065a534b933f7fceff1a4af131bce6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 7 Feb 2024 10:46:34 +0100 Subject: [PATCH 063/119] Enable trace logging for the Olm Account This should help us to track down UTD sources, it ensures that we log the state of a Olm session when we try to decrypt an `m.olm.*` to-device message. --- .../libraries/matrix/api/tracing/TracingFilterConfiguration.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingFilterConfiguration.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingFilterConfiguration.kt index 114c7df0cb..aa4297969a 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingFilterConfiguration.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingFilterConfiguration.kt @@ -25,6 +25,7 @@ data class TracingFilterConfiguration( private val targetsToLogLevel: Map = mapOf( Target.HYPER to LogLevel.WARN, Target.MATRIX_SDK_CRYPTO to LogLevel.DEBUG, + Target.MATRIX_SDK_CRYPTO_ACCOUNT to LogLevel.TRACE, Target.MATRIX_SDK_HTTP_CLIENT to LogLevel.DEBUG, Target.MATRIX_SDK_SLIDING_SYNC to LogLevel.TRACE, Target.MATRIX_SDK_BASE_SLIDING_SYNC to LogLevel.TRACE, @@ -58,6 +59,7 @@ enum class Target(open val filter: String) { MATRIX_SDK_FFI("matrix_sdk_ffi"), MATRIX_SDK_UNIFFI_API("matrix_sdk_ffi::uniffi_api"), MATRIX_SDK_CRYPTO("matrix_sdk_crypto"), + MATRIX_SDK_CRYPTO_ACCOUNT("matrix_sdk_crypto::olm::account"), MATRIX_SDK("matrix_sdk"), MATRIX_SDK_HTTP_CLIENT("matrix_sdk::http_client"), MATRIX_SDK_CLIENT("matrix_sdk::client"), From 0b3a0fd6a3d874be65a59b280aaf100052fb55e7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Feb 2024 16:31:43 +0100 Subject: [PATCH 064/119] Use appName from BuildMeta in the crash detection dialog. --- .../android/appnav/RootPresenterTest.kt | 2 ++ .../api/crash/CrashDetectionState.kt | 1 + .../api/crash/CrashDetectionStateProvider.kt | 1 + .../rageshake/api/crash/CrashDetectionView.kt | 5 +++-- .../crash/DefaultCrashDetectionPresenter.kt | 7 ++++++- .../crash/ui/CrashDetectionPresenterTest.kt | 20 +++++++++++++------ 6 files changed, 27 insertions(+), 9 deletions(-) diff --git a/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt index 5b382d1dc5..0806937c1a 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt @@ -28,6 +28,7 @@ import io.element.android.features.rageshake.test.crash.FakeCrashDataStore import io.element.android.features.rageshake.test.rageshake.FakeRageShake import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder +import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.services.apperror.api.AppErrorState import io.element.android.services.apperror.api.AppErrorStateService import io.element.android.services.apperror.impl.DefaultAppErrorStateService @@ -83,6 +84,7 @@ class RootPresenterTest { val rageshake = FakeRageShake() val screenshotHolder = FakeScreenshotHolder() val crashDetectionPresenter = DefaultCrashDetectionPresenter( + buildMeta = aBuildMeta(), crashDataStore = crashDataStore ) val rageshakeDetectionPresenter = DefaultRageshakeDetectionPresenter( diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionState.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionState.kt index 96fb558de6..39ddfa8213 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionState.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionState.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.Immutable @Immutable data class CrashDetectionState( + val appName: String, val crashDetected: Boolean, val eventSink: (CrashDetectionEvents) -> Unit ) diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionStateProvider.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionStateProvider.kt index 184d0ecbbc..b93637010c 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionStateProvider.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionStateProvider.kt @@ -17,6 +17,7 @@ package io.element.android.features.rageshake.api.crash fun aCrashDetectionState() = CrashDetectionState( + appName = "Element", crashDetected = false, eventSink = {} ) diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt index 48c10a75c4..9fa2bdc2f0 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt @@ -41,6 +41,7 @@ fun CrashDetectionView( if (state.crashDetected) { CrashDetectionContent( + appName = state.appName, onYesClicked = onOpenBugReport, onNoClicked = ::onPopupDismissed, onDismiss = ::onPopupDismissed, @@ -50,14 +51,14 @@ fun CrashDetectionView( @Composable private fun CrashDetectionContent( + appName: String, onNoClicked: () -> Unit = { }, onYesClicked: () -> Unit = { }, onDismiss: () -> Unit = { }, ) { ConfirmationDialog( title = stringResource(id = CommonStrings.action_report_bug), - // TODO Replace with app name - content = stringResource(id = R.string.crash_detection_dialog_content, "Element"), + content = stringResource(id = R.string.crash_detection_dialog_content, appName), submitText = stringResource(id = CommonStrings.action_yes), cancelText = stringResource(id = CommonStrings.action_no), onCancelClicked = onNoClicked, diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/DefaultCrashDetectionPresenter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/DefaultCrashDetectionPresenter.kt index 1097c79939..9f09f5f35a 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/DefaultCrashDetectionPresenter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/DefaultCrashDetectionPresenter.kt @@ -24,13 +24,17 @@ import io.element.android.features.rageshake.api.crash.CrashDataStore import io.element.android.features.rageshake.api.crash.CrashDetectionEvents import io.element.android.features.rageshake.api.crash.CrashDetectionPresenter import io.element.android.features.rageshake.api.crash.CrashDetectionState +import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.AppScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import javax.inject.Inject @ContributesBinding(AppScope::class) -class DefaultCrashDetectionPresenter @Inject constructor(private val crashDataStore: CrashDataStore) : +class DefaultCrashDetectionPresenter @Inject constructor( + private val buildMeta: BuildMeta, + private val crashDataStore: CrashDataStore, +) : CrashDetectionPresenter { @Composable override fun present(): CrashDetectionState { @@ -45,6 +49,7 @@ class DefaultCrashDetectionPresenter @Inject constructor(private val crashDataSt } return CrashDetectionState( + appName = buildMeta.applicationName, crashDetected = crashDetected.value, eventSink = ::handleEvents ) diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt index 917e427d9e..8888cf8763 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt @@ -24,6 +24,8 @@ import io.element.android.features.rageshake.api.crash.CrashDetectionEvents import io.element.android.features.rageshake.impl.crash.DefaultCrashDetectionPresenter import io.element.android.features.rageshake.test.crash.A_CRASH_DATA import io.element.android.features.rageshake.test.crash.FakeCrashDataStore +import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.tests.testutils.WarmUpRule import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -35,9 +37,7 @@ class CrashDetectionPresenterTest { @Test fun `present - initial state no crash`() = runTest { - val presenter = DefaultCrashDetectionPresenter( - FakeCrashDataStore() - ) + val presenter = createPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -48,7 +48,7 @@ class CrashDetectionPresenterTest { @Test fun `present - initial state crash`() = runTest { - val presenter = DefaultCrashDetectionPresenter( + val presenter = createPresenter( FakeCrashDataStore(appHasCrashed = true) ) moleculeFlow(RecompositionMode.Immediate) { @@ -62,7 +62,7 @@ class CrashDetectionPresenterTest { @Test fun `present - reset app has crashed`() = runTest { - val presenter = DefaultCrashDetectionPresenter( + val presenter = createPresenter( FakeCrashDataStore(appHasCrashed = true) ) moleculeFlow(RecompositionMode.Immediate) { @@ -78,7 +78,7 @@ class CrashDetectionPresenterTest { @Test fun `present - reset all crash data`() = runTest { - val presenter = DefaultCrashDetectionPresenter( + val presenter = createPresenter( FakeCrashDataStore(appHasCrashed = true, crashData = A_CRASH_DATA) ) moleculeFlow(RecompositionMode.Immediate) { @@ -91,4 +91,12 @@ class CrashDetectionPresenterTest { assertThat(awaitItem().crashDetected).isFalse() } } + + private fun createPresenter( + crashDataStore: FakeCrashDataStore = FakeCrashDataStore(), + buildMeta: BuildMeta = aBuildMeta(), + ) = DefaultCrashDetectionPresenter( + buildMeta = buildMeta, + crashDataStore = crashDataStore, + ) } From afc2e6c7fee73169fa5e4d2f2445289bb15b3888 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Feb 2024 16:33:25 +0100 Subject: [PATCH 065/119] Remove LogCompositions. It was used at the beginning of the project, but is not useful anymore. --- .../analytics/impl/AnalyticsOptInView.kt | 2 - .../features/messages/impl/MessagesView.kt | 5 --- .../rageshake/api/crash/CrashDetectionView.kt | 6 --- .../api/detection/RageshakeDetectionView.kt | 5 --- .../rageshake/impl/bugreport/BugReportView.kt | 2 - .../features/roomlist/impl/RoomListView.kt | 6 --- .../impl/components/RoomListTopBar.kt | 6 --- .../designsystem/utils/LogCompositions.kt | 39 ------------------- 8 files changed, 71 deletions(-) delete mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt index 9f75d4bd6e..e834f55142 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt @@ -57,7 +57,6 @@ import io.element.android.libraries.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.designsystem.theme.temporaryColorBgSpecial -import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.persistentListOf @@ -67,7 +66,6 @@ fun AnalyticsOptInView( onClickTerms: () -> Unit, modifier: Modifier = Modifier, ) { - LogCompositions(tag = "Analytics", msg = "Root") val eventSink = state.eventSink fun onTermsAccepted() { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index be3afb6660..b5dccb823b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -102,7 +102,6 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.utils.KeepScreenOn -import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState @@ -127,8 +126,6 @@ fun MessagesView( onJoinCallClicked: () -> Unit, modifier: Modifier = Modifier, ) { - LogCompositions(tag = "MessagesScreen", msg = "Root") - OnLifecycleEvent { _, event -> state.voiceMessageComposerState.eventSink(VoiceMessageComposerEvents.LifecycleEvent(event)) } @@ -146,8 +143,6 @@ fun MessagesView( // This is needed because the composer is inside an AndroidView that can't be affected by the FocusManager in Compose val localView = LocalView.current - LogCompositions(tag = "MessagesScreen", msg = "Content") - fun onMessageClicked(event: TimelineItem.Event) { Timber.v("OnMessageClicked= ${event.id}") val hideKeyboard = onEventClicked(event) diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt index 9fa2bdc2f0..5af36b5ad8 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt @@ -22,7 +22,6 @@ import io.element.android.features.rageshake.api.R import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -30,11 +29,6 @@ fun CrashDetectionView( state: CrashDetectionState, onOpenBugReport: () -> Unit = { }, ) { - LogCompositions( - tag = "Crash", - msg = "CrashDetectionScreen" - ) - fun onPopupDismissed() { state.eventSink(CrashDetectionEvents.ResetAllCrashData) } diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt index e076ceb1a6..6121c684cd 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt @@ -31,7 +31,6 @@ import io.element.android.libraries.androidutils.hardware.vibrate import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.ui.strings.CommonStrings @@ -40,10 +39,6 @@ fun RageshakeDetectionView( state: RageshakeDetectionState, onOpenBugReport: () -> Unit = { }, ) { - LogCompositions( - tag = "Rageshake", - msg = "RageshakeDetectionScreen" - ) val eventSink = state.eventSink val context = LocalContext.current OnLifecycleEvent { _, event -> diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt index 86f351ea00..f5ffd62fd6 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt @@ -51,7 +51,6 @@ import io.element.android.libraries.designsystem.preview.debugPlaceholderBackgro import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.OutlinedTextField import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -62,7 +61,6 @@ fun BugReportView( onBackPressed: () -> Unit, modifier: Modifier = Modifier, ) { - LogCompositions(tag = "Rageshake", msg = "Root") val eventSink = state.eventSink Box(modifier = modifier) { diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt index ace8b9ce60..ac55e6a0f4 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt @@ -67,7 +67,6 @@ import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState import io.element.android.libraries.matrix.api.core.RoomId @@ -190,11 +189,6 @@ private fun RoomListContent( } } val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState) - LogCompositions( - tag = "RoomListScreen", - msg = "Content" - ) - val nestedScrollConnection = remember { object : NestedScrollConnection { override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt index e6aae4da6e..91b1dc4384 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt @@ -69,7 +69,6 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.MediumTopAppBar import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.model.getAvatarData @@ -92,11 +91,6 @@ fun RoomListTopBar( scrollBehavior: TopAppBarScrollBehavior, modifier: Modifier = Modifier, ) { - LogCompositions( - tag = "RoomListScreen", - msg = "TopBar" - ) - fun closeFilter() { onFilterChanged("") } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt deleted file mode 100644 index 2618a5de82..0000000000 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2023 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 - * - * http://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.libraries.designsystem.utils - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect -import androidx.compose.runtime.remember -import io.element.android.libraries.designsystem.BuildConfig -import timber.log.Timber - -// Note the inline function below which ensures that this function is essentially -// copied at the call site to ensure that its logging only recompositions from the -// original call site. -@Composable -fun LogCompositions(tag: String, msg: String) { - if (BuildConfig.DEBUG) { - val ref = remember { Ref() } - SideEffect { ref.value++ } - Timber.tag(tag).d("Compositions: $msg ${ref.value}") - } -} - -private class Ref { - var value: Int = 0 -} From 688c4dae224304be3e0c27fa4737d2b16180a8da Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Feb 2024 16:36:01 +0100 Subject: [PATCH 066/119] Rename parameter, since it is also invoked in the confirmation dialog, not only for the error dialog. --- .../io/element/android/features/logout/impl/LogoutView.kt | 2 +- .../features/logout/impl/direct/DefaultDirectLogoutView.kt | 2 +- .../android/features/logout/impl/ui/LogoutActionDialog.kt | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt index ecec90cf06..98827b4700 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt @@ -86,7 +86,7 @@ fun LogoutView( onForceLogoutClicked = { eventSink(LogoutEvents.Logout(ignoreSdkError = true)) }, - onDismissError = { + onDismissDialog = { eventSink(LogoutEvents.CloseDialogs) }, onSuccessLogout = { diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt index f646d86c9e..64935f8cda 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt @@ -41,7 +41,7 @@ class DefaultDirectLogoutView @Inject constructor() : DirectLogoutView { onForceLogoutClicked = { eventSink(DirectLogoutEvents.Logout(ignoreSdkError = true)) }, - onDismissError = { + onDismissDialog = { eventSink(DirectLogoutEvents.CloseDialogs) }, onSuccessLogout = { diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt index bf103745b4..18e2809034 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt @@ -32,8 +32,7 @@ fun LogoutActionDialog( state: AsyncAction, onConfirmClicked: () -> Unit, onForceLogoutClicked: () -> Unit, - // TODO Rename - onDismissError: () -> Unit, + onDismissDialog: () -> Unit, onSuccessLogout: (String?) -> Unit, ) { when (state) { @@ -42,7 +41,7 @@ fun LogoutActionDialog( AsyncAction.Confirming -> LogoutConfirmationDialog( onSubmitClicked = onConfirmClicked, - onDismiss = onDismissError + onDismiss = onDismissDialog ) is AsyncAction.Loading -> ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content)) @@ -52,7 +51,7 @@ fun LogoutActionDialog( content = stringResource(id = CommonStrings.error_unknown), retryText = stringResource(id = CommonStrings.action_signout_anyway), onRetry = onForceLogoutClicked, - onDismiss = onDismissError, + onDismiss = onDismissDialog, ) is AsyncAction.Success -> { val latestOnSuccessLogout by rememberUpdatedState(onSuccessLogout) From 57d9b262d337c9e8cf959292b346c39bce13a7ef Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Feb 2024 17:31:37 +0100 Subject: [PATCH 067/119] Extract session folder name computation from RustMatrixClient --- .../libraries/matrix/impl/RustMatrixClient.kt | 21 +++++++-------- .../impl/util/SessionDirectoryNameProvider.kt | 26 +++++++++++++++++++ 2 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/SessionDirectoryNameProvider.kt diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index a22fcdc30c..f7213becc4 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -60,6 +60,7 @@ import io.element.android.libraries.matrix.impl.roomlist.roomOrNull import io.element.android.libraries.matrix.impl.sync.RustSyncService import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper +import io.element.android.libraries.matrix.impl.util.SessionDirectoryNameProvider import io.element.android.libraries.matrix.impl.util.cancelAndDestroy import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService import io.element.android.libraries.sessionstorage.api.SessionStore @@ -134,6 +135,7 @@ class RustMatrixClient( sessionCoroutineScope = sessionCoroutineScope, dispatchers = dispatchers, ).apply { start() } + private val sessionDirectoryNameProvider = SessionDirectoryNameProvider() private val isLoggingOut = AtomicBoolean(false) @@ -397,13 +399,12 @@ class RustMatrixClient( } override suspend fun getCacheSize(): Long { - // Do not use client.userId since it can throw if client has been closed (during clear cache) - return baseDirectory.getCacheSize(userID = sessionId.value) + return baseDirectory.getCacheSize() } override suspend fun clearCache() { close() - baseDirectory.deleteSessionDirectory(userID = sessionId.value, deleteCryptoDb = false) + baseDirectory.deleteSessionDirectory(deleteCryptoDb = false) } override suspend fun logout(ignoreSdkError: Boolean): String? = doLogout( @@ -432,7 +433,7 @@ class RustMatrixClient( } } close() - baseDirectory.deleteSessionDirectory(userID = sessionId.value, deleteCryptoDb = true) + baseDirectory.deleteSessionDirectory(deleteCryptoDb = true) if (removeSession) { sessionStore.removeSession(sessionId.value) } @@ -478,12 +479,10 @@ class RustMatrixClient( override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver private suspend fun File.getCacheSize( - userID: String, includeCryptoDb: Boolean = false, ): Long = withContext(sessionDispatcher) { - // Rust sanitises the user ID replacing invalid characters with an _ - val sanitisedUserID = userID.replace(":", "_") - val sessionDirectory = File(this@getCacheSize, sanitisedUserID) + val sessionDirectoryName = sessionDirectoryNameProvider.provides(sessionId) + val sessionDirectory = File(this@getCacheSize, sessionDirectoryName) if (includeCryptoDb) { sessionDirectory.getSizeOfFiles() } else { @@ -500,12 +499,10 @@ class RustMatrixClient( } private suspend fun File.deleteSessionDirectory( - userID: String, deleteCryptoDb: Boolean = false, ): Boolean = withContext(sessionDispatcher) { - // Rust sanitises the user ID replacing invalid characters with an _ - val sanitisedUserID = userID.replace(":", "_") - val sessionDirectory = File(this@deleteSessionDirectory, sanitisedUserID) + val sessionDirectoryName = sessionDirectoryNameProvider.provides(sessionId) + val sessionDirectory = File(this@deleteSessionDirectory, sessionDirectoryName) if (deleteCryptoDb) { // Delete the folder and all its content sessionDirectory.deleteRecursively() diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/SessionDirectoryNameProvider.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/SessionDirectoryNameProvider.kt new file mode 100644 index 0000000000..de9d6c1520 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/SessionDirectoryNameProvider.kt @@ -0,0 +1,26 @@ +/* + * 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 + * + * http://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.libraries.matrix.impl.util + +import io.element.android.libraries.matrix.api.core.SessionId + +class SessionDirectoryNameProvider { + // Rust sanitises the user ID replacing invalid characters with an _ + fun provides(sessionId: SessionId): String { + return sessionId.value.replace(":", "_") + } +} From b9eaaf30e50d93a73fdf6142163d05120f13443a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:34:03 +0000 Subject: [PATCH 068/119] Update dependency io.element.android:compound-android to v0.0.4 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 78f5e72ad1..c42bf83c4a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -142,7 +142,7 @@ coil = { module = "io.coil-kt:coil", version.ref = "coil" } coil_compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } coil_gif = { module = "io.coil-kt:coil-gif", version.ref = "coil" } coil_test = { module = "io.coil-kt:coil-test", version.ref = "coil" } -compound = { module = "io.element.android:compound-android", version = "0.0.3" } +compound = { module = "io.element.android:compound-android", version = "0.0.4" } datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" } serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization_json" } kotlinx_collections_immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7" From 8454c373882cf7499d8268cf22d4b18662b5768d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:34:08 +0000 Subject: [PATCH 069/119] Update dependency org.matrix.rustcomponents:sdk-android to v0.1.98 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 78f5e72ad1..076dfde210 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -152,7 +152,7 @@ jsoup = "org.jsoup:jsoup:1.17.2" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = "app.cash.molecule:molecule-runtime:1.3.2" timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.97" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.98" matrix_richtexteditor = { module = "io.element.android:wysiwyg", 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" } From d1c4cd1686c1b83f7175e90e247977343591f52b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 18:05:53 +0000 Subject: [PATCH 070/119] Update dependency androidx.compose.material3:material3 to v1.2.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 076dfde210..12d5b1b689 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -93,7 +93,7 @@ androidx_preference = "androidx.preference:preference:1.2.1" androidx_webkit = "androidx.webkit:webkit:1.10.0" androidx_compose_bom = { module = "androidx.compose:compose-bom", version.ref = "compose_bom" } -androidx_compose_material3 = "androidx.compose.material3:material3:1.2.0-rc01" +androidx_compose_material3 = "androidx.compose.material3:material3:1.2.0" androidx_compose_ui = { module = "androidx.compose.ui:ui" } androidx_compose_ui_tooling = { module = "androidx.compose.ui:ui-tooling" } androidx_compose_ui_tooling_preview = { module = "androidx.compose.ui:ui-tooling-preview" } From b2dd7f116973ad59b819d5c2c0e4a78d627added Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 21:41:10 +0000 Subject: [PATCH 071/119] Update dependency androidx.compose.compiler:compiler to v1.5.9 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 076dfde210..222644eb6e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,7 +19,7 @@ media3 = "1.2.1" # Compose compose_bom = "2024.01.00" -composecompiler = "1.5.8" +composecompiler = "1.5.9" # Coroutines coroutines = "1.7.3" From 7910ae5bede69614ee9077b9ef605d46760e9c6a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Feb 2024 17:01:35 +0100 Subject: [PATCH 072/119] Add parameter to functions which create states, to avoid copy. --- .../messages/impl/MessagesStateProvider.kt | 70 +++++++++++-------- .../MessageComposerStateProvider.kt | 4 +- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index 1bb500e08f..34f076b5b9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -19,6 +19,7 @@ package io.element.android.features.messages.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.messages.impl.actionlist.anActionListState import io.element.android.features.messages.impl.messagecomposer.AttachmentsState +import io.element.android.features.messages.impl.messagecomposer.MessageComposerState import io.element.android.features.messages.impl.messagecomposer.aMessageComposerState import io.element.android.features.messages.impl.timeline.aTimelineItemList import io.element.android.features.messages.impl.timeline.aTimelineState @@ -27,6 +28,7 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent +import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessageComposerState import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessagePreviewState import io.element.android.libraries.architecture.AsyncData @@ -42,60 +44,70 @@ open class MessagesStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMessagesState(), - aMessagesState().copy(hasNetworkConnection = false), - aMessagesState().copy(composerState = aMessageComposerState().copy(showAttachmentSourcePicker = true)), - aMessagesState().copy(userHasPermissionToSendMessage = false), - aMessagesState().copy(showReinvitePrompt = true), - aMessagesState().copy( + aMessagesState(hasNetworkConnection = false), + aMessagesState(composerState = aMessageComposerState(showAttachmentSourcePicker = true)), + aMessagesState(userHasPermissionToSendMessage = false), + aMessagesState(showReinvitePrompt = true), + aMessagesState( roomName = AsyncData.Uninitialized, roomAvatar = AsyncData.Uninitialized, ), - aMessagesState().copy(composerState = aMessageComposerState().copy(showTextFormatting = true)), - aMessagesState().copy( + aMessagesState(composerState = aMessageComposerState(showTextFormatting = true)), + aMessagesState( enableVoiceMessages = true, voiceMessageComposerState = aVoiceMessageComposerState(showPermissionRationaleDialog = true), ), - aMessagesState().copy( - composerState = aMessageComposerState().copy( + aMessagesState( + composerState = aMessageComposerState( attachmentsState = AttachmentsState.Sending.Processing(persistentListOf()) ), ), - aMessagesState().copy( - composerState = aMessageComposerState().copy( + aMessagesState( + composerState = aMessageComposerState( attachmentsState = AttachmentsState.Sending.Uploading(0.33f) ), ), - aMessagesState().copy( + aMessagesState( callState = RoomCallState.ONGOING, ), - aMessagesState().copy( + aMessagesState( enableVoiceMessages = true, voiceMessageComposerState = aVoiceMessageComposerState( voiceMessageState = aVoiceMessagePreviewState(), showSendFailureDialog = true ), ), - aMessagesState().copy( + aMessagesState( callState = RoomCallState.DISABLED, ), ) } -fun aMessagesState() = MessagesState( - roomId = RoomId("!id:domain"), - roomName = AsyncData.Success("Room name"), - roomAvatar = AsyncData.Success(AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom)), - userHasPermissionToSendMessage = true, - userHasPermissionToRedactOwn = false, - userHasPermissionToRedactOther = false, - userHasPermissionToSendReaction = true, - composerState = aMessageComposerState().copy( +fun aMessagesState( + roomName: AsyncData = AsyncData.Success("Room name"), + roomAvatar: AsyncData = AsyncData.Success(AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom)), + userHasPermissionToSendMessage: Boolean = true, + composerState: MessageComposerState = aMessageComposerState( richTextEditorState = RichTextEditorState("Hello", initialFocus = true), isFullScreen = false, mode = MessageComposerMode.Normal, ), - voiceMessageComposerState = aVoiceMessageComposerState(), - timelineState = aTimelineState().copy( + voiceMessageComposerState: VoiceMessageComposerState = aVoiceMessageComposerState(), + hasNetworkConnection: Boolean = true, + showReinvitePrompt: Boolean = false, + enableVoiceMessages: Boolean = true, + callState: RoomCallState = RoomCallState.ENABLED, +) = MessagesState( + roomId = RoomId("!id:domain"), + roomName = roomName, + roomAvatar = roomAvatar, + userHasPermissionToSendMessage = userHasPermissionToSendMessage, + userHasPermissionToRedactOwn = false, + userHasPermissionToRedactOther = false, + userHasPermissionToSendReaction = true, + composerState = composerState, + voiceMessageComposerState = voiceMessageComposerState, + timelineState = aTimelineState( timelineItems = aTimelineItemList(aTimelineItemTextContent()), ), retrySendMenuState = RetrySendMenuState( @@ -116,13 +128,13 @@ fun aMessagesState() = MessagesState( target = null, eventSink = {}, ), - hasNetworkConnection = true, + hasNetworkConnection = hasNetworkConnection, snackbarMessage = null, inviteProgress = AsyncData.Uninitialized, - showReinvitePrompt = false, + showReinvitePrompt = showReinvitePrompt, enableTextFormatting = true, - enableVoiceMessages = true, - callState = RoomCallState.ENABLED, + enableVoiceMessages = enableVoiceMessages, + callState = callState, appName = "Element", eventSink = {} ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt index a542cb772d..da3c0c8af7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt @@ -32,7 +32,7 @@ open class MessageComposerStateProvider : PreviewParameterProvider = persistentListOf(), ) = MessageComposerState( - richTextEditorState = composerState, + richTextEditorState = richTextEditorState, isFullScreen = isFullScreen, mode = mode, showTextFormatting = showTextFormatting, From 4e0f308cac7c17e013463d62bfbfcda3126c8504 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Feb 2024 17:22:12 +0100 Subject: [PATCH 073/119] Add result type to EnsureCalledOnceWithParam --- .../tests/testutils/EnsureCalledOnce.kt | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt index ddc8e22a5c..1d5fed72dc 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt @@ -35,15 +35,17 @@ fun ensureCalledOnce(block: (callback: EnsureCalledOnce) -> Unit) { callback.assertSuccess() } -class EnsureCalledOnceWithParam( - private val expectedParam: T -) : (T) -> Unit { +class EnsureCalledOnceWithParam( + private val expectedParam: T, + private val result: R, +) : (T) -> R { private var counter = 0 - override fun invoke(p1: T) { + override fun invoke(p1: T): R { if (p1 != expectedParam) { throw AssertionError("Expected to be called with $expectedParam, but was called with $p1") } counter++ + return result } fun assertSuccess() { @@ -53,8 +55,15 @@ class EnsureCalledOnceWithParam( } } -fun ensureCalledOnceWithParam(param: T, block: (callback: EnsureCalledOnceWithParam) -> Unit) { - val callback = EnsureCalledOnceWithParam(param) +/** + * Shortcut for [ ensureCalledOnceWithParam] with Unit result + */ +fun ensureCalledOnceWithParam(param: T, block: (callback: EnsureCalledOnceWithParam) -> Unit) { + ensureCalledOnceWithParam(param, block, Unit) +} + +fun ensureCalledOnceWithParam(param: T, block: (callback: EnsureCalledOnceWithParam) -> R, result: R) { + val callback = EnsureCalledOnceWithParam(param, result) block(callback) callback.assertSuccess() } From fa52ff54c84268cc2bc09603b6a5b09fb53dfbc9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Feb 2024 21:14:58 +0100 Subject: [PATCH 074/119] Add tests on `MessagesView` --- features/messages/impl/build.gradle.kts | 1 + .../messages/impl/MessagesStateProvider.kt | 38 ++- .../actionlist/ActionListStateProvider.kt | 9 +- .../impl/actionlist/ActionListView.kt | 51 +++- .../timeline/components/MessageEventBubble.kt | 3 + .../messages/impl/MessagesViewTest.kt | 286 ++++++++++++++++++ .../android/libraries/testtags/TestTags.kt | 5 + .../tests/testutils/EnsureNeverCalled.kt | 6 + 8 files changed, 376 insertions(+), 23 deletions(-) create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index a2960399e9..dbfc80d820 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -62,6 +62,7 @@ dependencies { implementation(projects.libraries.voicerecorder.api) implementation(projects.libraries.mediaplayer.api) implementation(projects.libraries.uiUtils) + implementation(projects.libraries.testtags) implementation(projects.features.networkmonitor.api) implementation(projects.services.analytics.api) implementation(libs.coil.compose) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index 34f076b5b9..9e36086c45 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -17,13 +17,16 @@ package io.element.android.features.messages.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.messages.impl.actionlist.ActionListState import io.element.android.features.messages.impl.actionlist.anActionListState import io.element.android.features.messages.impl.messagecomposer.AttachmentsState import io.element.android.features.messages.impl.messagecomposer.MessageComposerState import io.element.android.features.messages.impl.messagecomposer.aMessageComposerState import io.element.android.features.messages.impl.timeline.aTimelineItemList import io.element.android.features.messages.impl.timeline.aTimelineState +import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState +import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState @@ -93,10 +96,14 @@ fun aMessagesState( mode = MessageComposerMode.Normal, ), voiceMessageComposerState: VoiceMessageComposerState = aVoiceMessageComposerState(), + actionListState: ActionListState = anActionListState(), + customReactionState: CustomReactionState = aCustomReactionState(), + reactionSummaryState: ReactionSummaryState = aReactionSummaryState(), hasNetworkConnection: Boolean = true, showReinvitePrompt: Boolean = false, enableVoiceMessages: Boolean = true, callState: RoomCallState = RoomCallState.ENABLED, + eventSink: (MessagesEvents) -> Unit = {}, ) = MessagesState( roomId = RoomId("!id:domain"), roomName = roomName, @@ -118,16 +125,9 @@ fun aMessagesState( selectedEvent = null, eventSink = {}, ), - actionListState = anActionListState(), - customReactionState = CustomReactionState( - target = CustomReactionState.Target.None, - eventSink = {}, - selectedEmoji = persistentSetOf(), - ), - reactionSummaryState = ReactionSummaryState( - target = null, - eventSink = {}, - ), + actionListState = actionListState, + customReactionState = customReactionState, + reactionSummaryState = reactionSummaryState, hasNetworkConnection = hasNetworkConnection, snackbarMessage = null, inviteProgress = AsyncData.Uninitialized, @@ -136,5 +136,21 @@ fun aMessagesState( enableVoiceMessages = enableVoiceMessages, callState = callState, appName = "Element", - eventSink = {} + eventSink = eventSink, +) + +fun aReactionSummaryState( + target: ReactionSummaryState.Summary? = null, + eventSink: (ReactionSummaryEvents) -> Unit = {} +) = ReactionSummaryState( + target = target, + eventSink = eventSink, +) + +fun aCustomReactionState( + eventSink: (CustomReactionEvents) -> Unit = {}, +) = CustomReactionState( + target = CustomReactionState.Target.None, + selectedEmoji = persistentSetOf(), + eventSink = eventSink, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt index e33f54de3e..10952058b5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt @@ -122,9 +122,12 @@ open class ActionListStateProvider : PreviewParameterProvider { } } -fun anActionListState() = ActionListState( - target = ActionListState.Target.None, - eventSink = {} +fun anActionListState( + target: ActionListState.Target = ActionListState.Target.None, + eventSink: (ActionListEvents) -> Unit = {}, +) = ActionListState( + target = target, + eventSink = eventSink ) fun aTimelineItemActionList(): ImmutableList { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index 3db91fd67c..44d2d6ee4c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -45,6 +45,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription @@ -86,6 +87,7 @@ import io.element.android.libraries.designsystem.theme.components.hide import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList +import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -101,28 +103,52 @@ fun ActionListView( val targetItem = (state.target as? ActionListState.Target.Success)?.event fun onItemActionClicked( - itemAction: TimelineItemAction + itemAction: TimelineItemAction, + immediate: Boolean, ) { if (targetItem == null) return - sheetState.hide(coroutineScope) { + if (immediate) { + coroutineScope.launch { sheetState.hide() } state.eventSink(ActionListEvents.Clear) onActionSelected(itemAction, targetItem) + } else { + sheetState.hide(coroutineScope) { + state.eventSink(ActionListEvents.Clear) + onActionSelected(itemAction, targetItem) + } } } - fun onEmojiReactionClicked(emoji: String) { + fun onEmojiReactionClicked( + emoji: String, + immediate: Boolean, + ) { if (targetItem == null) return - sheetState.hide(coroutineScope) { + if (immediate) { + coroutineScope.launch { sheetState.hide() } state.eventSink(ActionListEvents.Clear) onEmojiReactionClicked(emoji, targetItem) + } else { + sheetState.hide(coroutineScope) { + state.eventSink(ActionListEvents.Clear) + onEmojiReactionClicked(emoji, targetItem) + } } } - fun onCustomReactionClicked() { + fun onCustomReactionClicked( + immediate: Boolean, + ) { if (targetItem == null) return - sheetState.hide(coroutineScope) { + if (immediate) { + coroutineScope.launch { sheetState.hide() } state.eventSink(ActionListEvents.Clear) onCustomReactionClicked(targetItem) + } else { + sheetState.hide(coroutineScope) { + state.eventSink(ActionListEvents.Clear) + onCustomReactionClicked(targetItem) + } } } @@ -136,11 +162,18 @@ fun ActionListView( onDismissRequest = ::onDismiss, modifier = modifier, ) { + val immediate = LocalInspectionMode.current SheetContent( state = state, - onActionClicked = ::onItemActionClicked, - onEmojiReactionClicked = ::onEmojiReactionClicked, - onCustomReactionClicked = ::onCustomReactionClicked, + onActionClicked = { + onItemActionClicked(it, immediate) + }, + onEmojiReactionClicked = { + onEmojiReactionClicked(it, immediate) + }, + onCustomReactionClicked = { + onCustomReactionClicked(immediate) + }, modifier = Modifier .navigationBarsPadding() .imePadding() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt index 14bd831efc..e7ca8116f1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt @@ -46,6 +46,8 @@ import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.messageFromMeBackground import io.element.android.libraries.designsystem.theme.messageFromOtherBackground +import io.element.android.libraries.testtags.TestTags +import io.element.android.libraries.testtags.testTag private val BUBBLE_RADIUS = 12.dp internal val BUBBLE_INCOMING_OFFSET = 16.dp @@ -115,6 +117,7 @@ fun MessageEventBubble( ) { Surface( modifier = Modifier + .testTag(TestTags.messageBubble) .widthIn(min = 80.dp) .clip(bubbleShape) .combinedClickable( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt new file mode 100644 index 0000000000..1f4ba8bf73 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt @@ -0,0 +1,286 @@ +/* + * 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 + * + * http://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 + +import androidx.activity.ComponentActivity +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.longClick +import androidx.compose.ui.test.onAllNodesWithContentDescription +import androidx.compose.ui.test.onAllNodesWithTag +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.onFirst +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTouchInput +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.features.messages.impl.actionlist.ActionListState +import io.element.android.features.messages.impl.actionlist.anActionListState +import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction +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.timeline.components.customreaction.CustomReactionEvents +import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents +import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.testtags.TestTags +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.EnsureCalledOnceWithParam +import io.element.android.tests.testutils.EnsureNeverCalled +import io.element.android.tests.testutils.EnsureNeverCalledWithParam +import io.element.android.tests.testutils.EnsureNeverCalledWithParamAndResult +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.clickOn +import io.element.android.tests.testutils.ensureCalledOnce +import io.element.android.tests.testutils.ensureCalledOnceWithParam +import io.element.android.tests.testutils.pressBack +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class MessagesViewTest { + @get:Rule val rule = createAndroidComposeRule() + + @Test + fun `clicking on back invoke expected callback`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + val state = aMessagesState( + eventSink = eventsRecorder + ) + ensureCalledOnce { callback -> + rule.setMessagesView( + state = state, + onBackPressed = callback, + ) + rule.pressBack() + } + } + + @Test + fun `clicking on room name invoke expected callback`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + val state = aMessagesState( + eventSink = eventsRecorder + ) + ensureCalledOnce { callback -> + rule.setMessagesView( + state = state, + onRoomDetailsClicked = callback, + ) + rule.onNodeWithText(state.roomName.dataOrNull().orEmpty()).performClick() + } + } + + @Test + fun `clicking on join call invoke expected callback`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + val state = aMessagesState( + eventSink = eventsRecorder + ) + ensureCalledOnce { callback -> + rule.setMessagesView( + state = state, + onJoinCallClicked = callback, + ) + val joinCallContentDescription = rule.activity.getString(CommonStrings.a11y_start_call) + rule.onNodeWithContentDescription(joinCallContentDescription).performClick() + } + } + + @Test + fun `clicking on an Event invoke expected callback`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + val state = aMessagesState( + eventSink = eventsRecorder + ) + val timelineItem = state.timelineState.timelineItems.first() + val callback = EnsureCalledOnceWithParam( + expectedParam = timelineItem, + result = true, + ) + rule.setMessagesView( + state = state, + onEventClicked = callback, + ) + // Cannot perform click on "Text", it's not detected. Use tag instead + rule.onAllNodesWithTag(TestTags.messageBubble.value).onFirst().performClick() + callback.assertSuccess() + } + + @Test + fun `clicking on send location invoke expected callback`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + val state = aMessagesState( + composerState = aMessageComposerState( + showAttachmentSourcePicker = true + ), + eventSink = eventsRecorder + ) + ensureCalledOnce { callback -> + rule.setMessagesView( + state = state, + onSendLocationClicked = callback, + ) + rule.clickOn(R.string.screen_room_attachment_source_location) + } + } + + @Test + fun `clicking on create poll invoke expected callback`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + val state = aMessagesState( + composerState = aMessageComposerState( + showAttachmentSourcePicker = true + ), + eventSink = eventsRecorder + ) + ensureCalledOnce { callback -> + rule.setMessagesView( + state = state, + onCreatePollClicked = callback, + ) + // Then click on the poll action + rule.clickOn(R.string.screen_room_attachment_source_poll) + } + } + + @Test + fun `clicking on the sender of an Event invoke expected callback`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + val state = aMessagesState( + eventSink = eventsRecorder + ) + val timelineItem = state.timelineState.timelineItems.first() + ensureCalledOnceWithParam( + param = (timelineItem as TimelineItem.Event).senderId + ) { callback -> + rule.setMessagesView( + state = state, + onUserDataClicked = callback, + ) + val senderName = (timelineItem as? TimelineItem.Event)?.senderDisplayName.orEmpty() + rule.onNodeWithText(senderName).performClick() + } + } + + @Test + fun `selecting a action on a message emits the expected Event`() { + val eventsRecorder = EventsRecorder() + val state = aMessagesState( + eventSink = eventsRecorder + ) + val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event + val stateWithMessageAction = state.copy( + actionListState = anActionListState( + target = ActionListState.Target.Success( + event = timelineItem, + displayEmojiReactions = true, + actions = persistentListOf(TimelineItemAction.Edit), + ) + ), + ) + rule.setMessagesView( + state = stateWithMessageAction, + ) + rule.clickOn(CommonStrings.action_edit) + eventsRecorder.assertSingle(MessagesEvents.HandleAction(TimelineItemAction.Edit, timelineItem)) + } + + @Test + fun `clicking on a reaction emits the expected Event`() { + val eventsRecorder = EventsRecorder() + val state = aMessagesState( + eventSink = eventsRecorder + ) + val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event + rule.setMessagesView( + state = state, + ) + rule.onAllNodesWithText("👍️").onFirst().performClick() + eventsRecorder.assertSingle(MessagesEvents.ToggleReaction("👍️", timelineItem.eventId!!)) + } + + @Test + fun `long clicking on a reaction emits the expected Event`() { + val eventsRecorder = EventsRecorder() + val state = aMessagesState( + reactionSummaryState = aReactionSummaryState( + target = null, + eventSink = eventsRecorder, + ), + ) + val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event + rule.setMessagesView( + state = state, + ) + rule.onAllNodesWithText("👍️").onFirst().performTouchInput { longClick() } + eventsRecorder.assertSingle(ReactionSummaryEvents.ShowReactionSummary(timelineItem.eventId!!, timelineItem.reactionsState.reactions, "👍️")) + } + + @Test + fun `clicking on more reaction emits the expected Event`() { + val eventsRecorder = EventsRecorder() + val state = aMessagesState( + customReactionState = aCustomReactionState( + eventSink = eventsRecorder, + ), + ) + val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event + rule.setMessagesView( + state = state, + ) + val moreReactionContentDescription = rule.activity.getString(R.string.screen_room_timeline_add_reaction) + rule.onAllNodesWithContentDescription(moreReactionContentDescription).onFirst().performClick() + eventsRecorder.assertSingle(CustomReactionEvents.ShowCustomReactionSheet(timelineItem)) + } +} + +private fun AndroidComposeTestRule.setMessagesView( + state: MessagesState, + onBackPressed: () -> Unit = EnsureNeverCalled(), + onRoomDetailsClicked: () -> Unit = EnsureNeverCalled(), + onEventClicked: (event: TimelineItem.Event) -> Boolean = EnsureNeverCalledWithParamAndResult(), + onUserDataClicked: (UserId) -> Unit = EnsureNeverCalledWithParam(), + onPreviewAttachments: (ImmutableList) -> Unit = EnsureNeverCalledWithParam(), + onSendLocationClicked: () -> Unit = EnsureNeverCalled(), + onCreatePollClicked: () -> Unit = EnsureNeverCalled(), + onJoinCallClicked: () -> Unit = EnsureNeverCalled(), +) { + setContent { + // Cannot use the RichTextEditor, so simulate a LocalInspectionMode + CompositionLocalProvider(LocalInspectionMode provides true) { + MessagesView( + state = state, + onBackPressed = onBackPressed, + onRoomDetailsClicked = onRoomDetailsClicked, + onEventClicked = onEventClicked, + onUserDataClicked = onUserDataClicked, + onPreviewAttachments = onPreviewAttachments, + onSendLocationClicked = onSendLocationClicked, + onCreatePollClicked = onCreatePollClicked, + onJoinCallClicked = onJoinCallClicked, + ) + } + } +} diff --git a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt index 7542194bab..a88f036fd5 100644 --- a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt +++ b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt @@ -58,6 +58,11 @@ object TestTags { */ val richTextEditor = TestTag("rich_text_editor") + /** + * Message bubble. + */ + val messageBubble = TestTag("message_bubble") + /** * Dialogs. */ diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt index 870806d424..f2f2c31fed 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt @@ -28,6 +28,12 @@ class EnsureNeverCalledWithParam : (T) -> Unit { } } +class EnsureNeverCalledWithParamAndResult : (T) -> R { + override fun invoke(p1: T): R { + throw AssertionError("Should not be called and is called with $p1") + } +} + class EnsureNeverCalledWithTwoParams : (T, U) -> Unit { override fun invoke(p1: T, p2: U) { throw AssertionError("Should not be called and is called with $p1 and $p2") From 093d2012666a2830ad4278f0cf895c12aa70114c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Feb 2024 23:23:49 +0100 Subject: [PATCH 075/119] Add missing period. --- .../io/element/android/tests/testutils/EnsureCalledOnce.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt index 1d5fed72dc..b2c943e1ca 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt @@ -56,7 +56,7 @@ class EnsureCalledOnceWithParam( } /** - * Shortcut for [ ensureCalledOnceWithParam] with Unit result + * Shortcut for [ ensureCalledOnceWithParam] with Unit result. */ fun ensureCalledOnceWithParam(param: T, block: (callback: EnsureCalledOnceWithParam) -> Unit) { ensureCalledOnceWithParam(param, block, Unit) From 1a48c344e25fe6dc27fe36afe20f2201c794c4c3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 09:19:06 +0000 Subject: [PATCH 076/119] Update dependency androidx.compose:compose-bom to v2024.02.00 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a7e07c95ed..c97450b77b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,7 +18,7 @@ activity = "1.8.2" media3 = "1.2.1" # Compose -compose_bom = "2024.01.00" +compose_bom = "2024.02.00" composecompiler = "1.5.9" # Coroutines From d06e5c23cbe87f23a6c2eb6755dbde856d631925 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 8 Feb 2024 11:01:08 +0100 Subject: [PATCH 077/119] Move migration screen to within the room list (#2361) * Rename migration bg drawable and add night variant * Move `migration` package from `ftue` to `messages:impl` module * Update `SunsetPage` with light and dark modes * Fix bloom colors when nested theme is used * Integrate the migration screen in the room list * Fix `WaitListView` cancel button color * Clear migration store when removing the app's cache --------- Co-authored-by: ElementBot --- changelog.d/2310.misc | 1 + .../features/ftue/impl/FtueFlowNode.kt | 15 -- .../impl/migration/MigrationScreenNode.kt | 52 ------- .../ftue/impl/state/DefaultFtueState.kt | 18 +-- .../src/main/res/values-be/translations.xml | 2 - .../src/main/res/values-cs/translations.xml | 2 - .../src/main/res/values-de/translations.xml | 2 - .../src/main/res/values-es/translations.xml | 2 - .../src/main/res/values-fr/translations.xml | 4 +- .../src/main/res/values-hu/translations.xml | 4 +- .../src/main/res/values-in/translations.xml | 4 +- .../src/main/res/values-it/translations.xml | 4 +- .../src/main/res/values-ro/translations.xml | 4 +- .../src/main/res/values-ru/translations.xml | 4 +- .../src/main/res/values-sk/translations.xml | 4 +- .../main/res/values-zh-rTW/translations.xml | 4 +- .../impl/src/main/res/values/localazy.xml | 2 - .../ftue/impl/DefaultFtueStateTests.kt | 35 +---- .../screens/waitlistscreen/WaitListView.kt | 4 +- features/preferences/impl/build.gradle.kts | 1 + .../impl/tasks/ClearCacheUseCase.kt | 4 + .../api}/migration/MigrationScreenStore.kt | 2 +- .../roomlist/impl/RoomListPresenter.kt | 5 + .../features/roomlist/impl/RoomListState.kt | 1 + .../roomlist/impl/RoomListStateProvider.kt | 2 + .../features/roomlist/impl/RoomListView.kt | 26 ++-- .../impl/components/RoomListTopBar.kt | 136 ++++++++++-------- .../migration/MigrationScreenPresenter.kt | 12 +- .../impl/migration/MigrationScreenState.kt | 2 +- .../impl/migration/MigrationScreenView.kt | 46 +++--- .../SharedPrefsMigrationScreenStore.kt | 5 +- .../impl/src/main/res/values/localazy.xml | 2 + .../roomlist/impl/RoomListPresenterTests.kt | 39 +++++ .../migration/InMemoryMigrationScreenStore.kt | 5 +- .../migration/MigrationScreenPresenterTest.kt | 6 +- .../designsystem/atomic/pages/SunsetPage.kt | 33 ++++- .../designsystem/components/Bloom.kt | 9 +- .../main/res/drawable-night/bg_migration.png | Bin 0 -> 550912 bytes .../{light_dark.png => bg_migration.png} | Bin .../android/samples/minimal/RoomListScreen.kt | 6 + ...ionView-Night-0_2_null,NEXUS_5,1.0,en].png | 3 - ...InView-Day-0_1_null_0,NEXUS_5,1.0,en].png} | 0 ...View-Night-0_2_null_0,NEXUS_5,1.0,en].png} | 0 ...comeView-Day-1_2_null,NEXUS_5,1.0,en].png} | 0 ...meView-Night-1_3_null,NEXUS_5,1.0,en].png} | 0 ...istView-Day-8_9_null_0,NEXUS_5,1.0,en].png | 4 +- ...istView-Day-8_9_null_1,NEXUS_5,1.0,en].png | 4 +- ...istView-Day-8_9_null_2,NEXUS_5,1.0,en].png | 4 +- ...istView-Day-8_9_null_3,NEXUS_5,1.0,en].png | 4 +- ...View-Night-8_10_null_0,NEXUS_5,1.0,en].png | 4 +- ...View-Night-8_10_null_1,NEXUS_5,1.0,en].png | 4 +- ...View-Night-8_10_null_2,NEXUS_5,1.0,en].png | 4 +- ...View-Night-8_10_null_3,NEXUS_5,1.0,en].png | 4 +- ...View-Night-8_10_null_4,NEXUS_5,1.0,en].png | 4 +- ...onView-Day-10_11_null,NEXUS_5,1.0,en].png} | 0 ...nView-Night-10_12_null,NEXUS_5,1.0,en].png | 3 + ...ontent-Day-11_12_null,NEXUS_5,1.0,en].png} | 0 ...tent-Night-11_13_null,NEXUS_5,1.0,en].png} | 0 ...stView-Day-3_4_null_12,NEXUS_5,1.0,en].png | 3 + ...View-Night-3_5_null_12,NEXUS_5,1.0,en].png | 3 + ...unsetPage-Night_1_null,NEXUS_5,1.0,en].png | 4 +- tools/localazy/config.json | 4 +- 62 files changed, 274 insertions(+), 291 deletions(-) create mode 100644 changelog.d/2310.misc delete mode 100644 features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenNode.kt rename features/{ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl => roomlist/api/src/main/kotlin/io/element/android/features/roomlist/api}/migration/MigrationScreenStore.kt (93%) rename features/{ftue/impl/src/main/kotlin/io/element/android/features/ftue => roomlist/impl/src/main/kotlin/io/element/android/features/roomlist}/impl/migration/MigrationScreenPresenter.kt (73%) rename features/{ftue/impl/src/main/kotlin/io/element/android/features/ftue => roomlist/impl/src/main/kotlin/io/element/android/features/roomlist}/impl/migration/MigrationScreenState.kt (91%) rename features/{ftue/impl/src/main/kotlin/io/element/android/features/ftue => roomlist/impl/src/main/kotlin/io/element/android/features/roomlist}/impl/migration/MigrationScreenView.kt (54%) rename features/{ftue/impl/src/main/kotlin/io/element/android/features/ftue => roomlist/impl/src/main/kotlin/io/element/android/features/roomlist}/impl/migration/SharedPrefsMigrationScreenStore.kt (92%) rename features/{ftue/impl/src/test/kotlin/io/element/android/features/ftue => roomlist/impl/src/test/kotlin/io/element/android/features/roomlist}/impl/migration/InMemoryMigrationScreenStore.kt (86%) rename features/{ftue/impl/src/test/kotlin/io/element/android/features/ftue => roomlist/impl/src/test/kotlin/io/element/android/features/roomlist}/impl/migration/MigrationScreenPresenterTest.kt (92%) create mode 100644 libraries/designsystem/src/main/res/drawable-night/bg_migration.png rename libraries/designsystem/src/main/res/drawable/{light_dark.png => bg_migration.png} (100%) delete mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.migration_MigrationView_null_MigrationView-Night-0_2_null,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Day-1_2_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Day-0_1_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Night-1_3_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Night-0_2_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-2_3_null,NEXUS_5,1.0,en].png => ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-1_2_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-2_4_null,NEXUS_5,1.0,en].png => ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-1_3_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.ftue.impl.migration_MigrationView_null_MigrationView-Day-0_1_null,NEXUS_5,1.0,en].png => ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Day-10_11_null,NEXUS_5,1.0,en].png} (100%) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Night-10_12_null,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-10_11_null,NEXUS_5,1.0,en].png => ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-11_12_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-10_12_null,NEXUS_5,1.0,en].png => ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-11_13_null,NEXUS_5,1.0,en].png} (100%) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_12,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_12,NEXUS_5,1.0,en].png diff --git a/changelog.d/2310.misc b/changelog.d/2310.misc new file mode 100644 index 0000000000..b5106ad927 --- /dev/null +++ b/changelog.d/2310.misc @@ -0,0 +1 @@ +Move migration screen to within the room list diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt index 5840671b6f..983e0c8db2 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt @@ -33,7 +33,6 @@ import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.analytics.api.AnalyticsEntryPoint import io.element.android.features.ftue.api.FtueEntryPoint -import io.element.android.features.ftue.impl.migration.MigrationScreenNode import io.element.android.features.ftue.impl.notifications.NotificationsOptInNode import io.element.android.features.ftue.impl.state.DefaultFtueState import io.element.android.features.ftue.impl.state.FtueStep @@ -74,9 +73,6 @@ class FtueFlowNode @AssistedInject constructor( @Parcelize data object Placeholder : NavTarget - @Parcelize - data object MigrationScreen : NavTarget - @Parcelize data object WelcomeScreen : NavTarget @@ -114,14 +110,6 @@ class FtueFlowNode @AssistedInject constructor( NavTarget.Placeholder -> { createNode(buildContext) } - NavTarget.MigrationScreen -> { - val callback = object : MigrationScreenNode.Callback { - override fun onMigrationFinished() { - lifecycleScope.launch { moveToNextStep() } - } - } - createNode(buildContext, listOf(callback)) - } NavTarget.WelcomeScreen -> { val callback = object : WelcomeNode.Callback { override fun onContinueClicked() { @@ -158,9 +146,6 @@ class FtueFlowNode @AssistedInject constructor( private fun moveToNextStep() { when (ftueState.getNextStep()) { - FtueStep.MigrationScreen -> { - backstack.newRoot(NavTarget.MigrationScreen) - } FtueStep.WelcomeScreen -> { backstack.newRoot(NavTarget.WelcomeScreen) } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenNode.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenNode.kt deleted file mode 100644 index 7c4257c422..0000000000 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenNode.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2023 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 - * - * http://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.ftue.impl.migration - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -import io.element.android.anvilannotations.ContributesNode -import io.element.android.libraries.di.SessionScope - -@ContributesNode(SessionScope::class) -class MigrationScreenNode @AssistedInject constructor( - @Assisted buildContext: BuildContext, - @Assisted plugins: List, - private val presenter: MigrationScreenPresenter, -) : Node(buildContext, plugins = plugins) { - interface Callback : Plugin { - fun onMigrationFinished() - } - - private fun onMigrationFinished() { - plugins.filterIsInstance().forEach { it.onMigrationFinished() } - } - - @Composable - override fun View(modifier: Modifier) { - val state = presenter.present() - MigrationScreenView( - state, - onMigrationFinished = ::onMigrationFinished, - modifier = modifier - ) - } -} diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/state/DefaultFtueState.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/state/DefaultFtueState.kt index 212919a691..ce7a769187 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/state/DefaultFtueState.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/state/DefaultFtueState.kt @@ -21,11 +21,9 @@ import android.os.Build import androidx.annotation.VisibleForTesting import com.squareup.anvil.annotations.ContributesBinding import io.element.android.features.ftue.api.state.FtueState -import io.element.android.features.ftue.impl.migration.MigrationScreenStore import io.element.android.features.ftue.impl.welcome.state.WelcomeScreenState import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.permissions.api.PermissionStateProvider import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider @@ -43,17 +41,14 @@ class DefaultFtueState @Inject constructor( coroutineScope: CoroutineScope, private val analyticsService: AnalyticsService, private val welcomeScreenState: WelcomeScreenState, - private val migrationScreenStore: MigrationScreenStore, private val permissionStateProvider: PermissionStateProvider, private val lockScreenService: LockScreenService, - private val matrixClient: MatrixClient, ) : FtueState { override val shouldDisplayFlow = MutableStateFlow(isAnyStepIncomplete()) override suspend fun reset() { welcomeScreenState.reset() analyticsService.reset() - migrationScreenStore.reset() if (sdkVersionProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) { permissionStateProvider.resetPermission(Manifest.permission.POST_NOTIFICATIONS) } @@ -67,12 +62,7 @@ class DefaultFtueState @Inject constructor( fun getNextStep(currentStep: FtueStep? = null): FtueStep? = when (currentStep) { - null -> if (shouldDisplayMigrationScreen()) { - FtueStep.MigrationScreen - } else { - getNextStep(FtueStep.MigrationScreen) - } - FtueStep.MigrationScreen -> if (shouldDisplayWelcomeScreen()) { + null -> if (shouldDisplayWelcomeScreen()) { FtueStep.WelcomeScreen } else { getNextStep(FtueStep.WelcomeScreen) @@ -97,7 +87,6 @@ class DefaultFtueState @Inject constructor( private fun isAnyStepIncomplete(): Boolean { return listOf( - { shouldDisplayMigrationScreen() }, { shouldDisplayWelcomeScreen() }, { shouldAskNotificationPermissions() }, { needsAnalyticsOptIn() }, @@ -105,10 +94,6 @@ class DefaultFtueState @Inject constructor( ).any { it() } } - private fun shouldDisplayMigrationScreen(): Boolean { - return migrationScreenStore.isMigrationScreenNeeded(matrixClient.sessionId) - } - private fun needsAnalyticsOptIn(): Boolean { // We need this function to not be suspend, so we need to load the value through runBlocking return runBlocking { analyticsService.didAskUserConsent().first().not() } @@ -147,7 +132,6 @@ class DefaultFtueState @Inject constructor( } sealed interface FtueStep { - data object MigrationScreen : FtueStep data object WelcomeScreen : FtueStep data object NotificationsOptIn : FtueStep data object AnalyticsOptIn : FtueStep diff --git a/features/ftue/impl/src/main/res/values-be/translations.xml b/features/ftue/impl/src/main/res/values-be/translations.xml index 3d785bbbdc..413bad9132 100644 --- a/features/ftue/impl/src/main/res/values-be/translations.xml +++ b/features/ftue/impl/src/main/res/values-be/translations.xml @@ -1,7 +1,5 @@ - "Гэта аднаразовы працэс, дзякуем за чаканне." - "Налада ўліковага запісу." "Вы можаце змяніць налады пазней." "Дазвольце апавяшчэнні і ніколі не прапускайце іх" "Званкі, апытанні, пошук і многае іншае будзе дададзена пазней у гэтым годзе." diff --git a/features/ftue/impl/src/main/res/values-cs/translations.xml b/features/ftue/impl/src/main/res/values-cs/translations.xml index 9363c704ad..bc262cf2b4 100644 --- a/features/ftue/impl/src/main/res/values-cs/translations.xml +++ b/features/ftue/impl/src/main/res/values-cs/translations.xml @@ -1,7 +1,5 @@ - "Jedná se o jednorázový proces, prosíme o strpení." - "Nastavení vašeho účtu" "Nastavení můžete později změnit." "Povolte oznámení a nezmeškejte žádnou zprávu" "Hovory, hlasování, vyhledávání a další budou přidány koncem tohoto roku." diff --git a/features/ftue/impl/src/main/res/values-de/translations.xml b/features/ftue/impl/src/main/res/values-de/translations.xml index d2a71e98e5..151500adda 100644 --- a/features/ftue/impl/src/main/res/values-de/translations.xml +++ b/features/ftue/impl/src/main/res/values-de/translations.xml @@ -1,7 +1,5 @@ - "Dies ist ein einmaliger Vorgang, danke fürs Warten." - "Dein Konto wird eingerichtet." "Du kannst deine Einstellungen später ändern." "Erlaube Benachrichtigungen und verpasse keine Nachricht" "Anrufe, Umfragen, Suchfunktionen und mehr werden im Laufe des Jahres hinzugefügt." diff --git a/features/ftue/impl/src/main/res/values-es/translations.xml b/features/ftue/impl/src/main/res/values-es/translations.xml index b5bac7f6a1..6a3224c531 100644 --- a/features/ftue/impl/src/main/res/values-es/translations.xml +++ b/features/ftue/impl/src/main/res/values-es/translations.xml @@ -1,7 +1,5 @@ - "Este proceso solo se hace una vez, gracias por esperar." - "Configura tu cuenta" "Puedes cambiar la configuración más tarde." "Activa las notificaciones y nunca te pierdas un mensaje" "Las llamadas, las encuestas, la búsqueda y más se agregarán más adelante este año." diff --git a/features/ftue/impl/src/main/res/values-fr/translations.xml b/features/ftue/impl/src/main/res/values-fr/translations.xml index 864a887f20..15a66c2323 100644 --- a/features/ftue/impl/src/main/res/values-fr/translations.xml +++ b/features/ftue/impl/src/main/res/values-fr/translations.xml @@ -1,8 +1,6 @@ - "Il s’agit d’une opération ponctuelle, merci d’attendre quelques instants." - "Configuration de votre compte." - "Vous pourrez modifier vos paramètres ultérieurement." + "Vous pourrez modifier vos paramètres ultérieurement." "Autorisez les notifications et ne manquez aucun message" "Les appels, les sondages, les recherches et plus encore seront ajoutés plus tard cette année." "L’historique des messages pour les salons chiffrés ne sera pas disponible dans cette mise à jour." diff --git a/features/ftue/impl/src/main/res/values-hu/translations.xml b/features/ftue/impl/src/main/res/values-hu/translations.xml index 6585aaf4a8..30ec7ab478 100644 --- a/features/ftue/impl/src/main/res/values-hu/translations.xml +++ b/features/ftue/impl/src/main/res/values-hu/translations.xml @@ -1,8 +1,6 @@ - "Ez egy egyszeri folyamat, köszönjük a türelmét." - "A fiók beállítása." - "A beállításokat később is módosíthatja." + "A beállításokat később is módosíthatja." "Értesítések engedélyezése, hogy soha ne maradjon le egyetlen üzenetről sem" "A hívások, szavazások, keresések és egyebek az év további részében kerülnek hozzáadásra." "A titkosított szobák üzenetelőzményei nem lesznek elérhetők ebben a frissítésben." diff --git a/features/ftue/impl/src/main/res/values-in/translations.xml b/features/ftue/impl/src/main/res/values-in/translations.xml index 126ce6376b..c4caa3d31c 100644 --- a/features/ftue/impl/src/main/res/values-in/translations.xml +++ b/features/ftue/impl/src/main/res/values-in/translations.xml @@ -1,8 +1,6 @@ - "Ini adalah proses satu kali, terima kasih telah menunggu." - "Menyiapkan akun Anda." - "Anda dapat mengubah pengaturan Anda nanti." + "Anda dapat mengubah pengaturan Anda nanti." "Izinkan pemberitahuan dan jangan pernah melewatkan pesan" "Panggilan, pemungutan suara, pencarian, dan lainnya akan ditambahkan di tahun ini." "Riwayat pesan untuk ruangan terenkripsi tidak akan tersedia dalam pembaruan ini." diff --git a/features/ftue/impl/src/main/res/values-it/translations.xml b/features/ftue/impl/src/main/res/values-it/translations.xml index 69013ebdb8..b4bb889c93 100644 --- a/features/ftue/impl/src/main/res/values-it/translations.xml +++ b/features/ftue/impl/src/main/res/values-it/translations.xml @@ -1,8 +1,6 @@ - "Si tratta di una procedura che si effettua una sola volta, grazie per l\'attesa." - "Configurazione del tuo account." - "Potrai modificare le tue impostazioni in seguito." + "Potrai modificare le tue impostazioni in seguito." "Consenti le notifiche e non perdere mai un messaggio" "Chiamate, sondaggi, ricerche e altro ancora saranno aggiunti nel corso dell\'anno." "La cronologia dei messaggi per le stanze crittografate non è ancora disponibile." diff --git a/features/ftue/impl/src/main/res/values-ro/translations.xml b/features/ftue/impl/src/main/res/values-ro/translations.xml index f932256318..a1b7ca15f1 100644 --- a/features/ftue/impl/src/main/res/values-ro/translations.xml +++ b/features/ftue/impl/src/main/res/values-ro/translations.xml @@ -1,8 +1,6 @@ - "Acesta este un proces care se desfășoară o singură dată, vă mulțumim pentru așteptare." - "Contul dumneavoastră se configurează" - "Apelurile, sondajele, căutare și multe altele vor fi adăugate în cursul acestui an." + "Apelurile, sondajele, căutare și multe altele vor fi adăugate în cursul acestui an." "Istoricul mesajelor pentru camerele criptate nu va fi disponibil în această actualizare." "Ne-ar plăcea să auzim de la dumneavoastră, spuneți-ne ce părere aveți prin intermediul paginii de setări." "Să începem!" diff --git a/features/ftue/impl/src/main/res/values-ru/translations.xml b/features/ftue/impl/src/main/res/values-ru/translations.xml index ab225b5d0f..685b72ded1 100644 --- a/features/ftue/impl/src/main/res/values-ru/translations.xml +++ b/features/ftue/impl/src/main/res/values-ru/translations.xml @@ -1,8 +1,6 @@ - "Это одноразовый процесс, спасибо, что подождали." - "Настройка учетной записи." - "Вы можете изменить настройки позже." + "Вы можете изменить настройки позже." "Разрешите уведомления и никогда не пропустите сообщение" "Звонки, опросы, поиск и многое другое будут добавлены позже в этом году." "История сообщений для зашифрованных комнат в этом обновлении будет недоступна." diff --git a/features/ftue/impl/src/main/res/values-sk/translations.xml b/features/ftue/impl/src/main/res/values-sk/translations.xml index c7eb1f83be..e5e6654164 100644 --- a/features/ftue/impl/src/main/res/values-sk/translations.xml +++ b/features/ftue/impl/src/main/res/values-sk/translations.xml @@ -1,8 +1,6 @@ - "Ide o jednorazový proces, ďakujeme za trpezlivosť." - "Nastavenie vášho účtu." - "Svoje nastavenia môžete neskôr zmeniť." + "Svoje nastavenia môžete neskôr zmeniť." "Povoľte oznámenia a nikdy nezmeškajte žiadnu správu" "Hovory, ankety, vyhľadávanie a ďalšie funkcie pribudnú neskôr v tomto roku." "História správ pre zašifrované miestnosti nebude v tejto aktualizácii k dispozícii." diff --git a/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml b/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml index 1edd145776..0edb4ec4f1 100644 --- a/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml @@ -1,8 +1,6 @@ - "這是一次性的程序,感謝您耐心等候。" - "正在設定您的帳號。" - "通話、投票、搜尋等更多功能將在今年登場。" + "通話、投票、搜尋等更多功能將在今年登場。" "在這次的更新,您無法查看聊天室內被加密的歷史訊息。" "我們很樂意聽取您的意見,請到設定頁面告訴我們您的想法。" "開始吧!" diff --git a/features/ftue/impl/src/main/res/values/localazy.xml b/features/ftue/impl/src/main/res/values/localazy.xml index f89cc622f6..3e8c86b761 100644 --- a/features/ftue/impl/src/main/res/values/localazy.xml +++ b/features/ftue/impl/src/main/res/values/localazy.xml @@ -1,7 +1,5 @@ - "This is a one time process, thanks for waiting." - "Setting up your account." "You can change your settings later." "Allow notifications and never miss a message" "Calls, polls, search and more will be added later this year." diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt index 44df35ca05..834aaa5c1f 100644 --- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt +++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt @@ -18,16 +18,11 @@ package io.element.android.features.ftue.impl import android.os.Build import com.google.common.truth.Truth.assertThat -import io.element.android.features.ftue.impl.migration.InMemoryMigrationScreenStore -import io.element.android.features.ftue.impl.migration.MigrationScreenStore import io.element.android.features.ftue.impl.state.DefaultFtueState import io.element.android.features.ftue.impl.state.FtueStep import io.element.android.features.ftue.impl.welcome.state.FakeWelcomeState import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.lockscreen.test.FakeLockScreenService -import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.test.A_SESSION_ID -import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.permissions.impl.FakePermissionStateProvider import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService @@ -54,7 +49,6 @@ class DefaultFtueStateTests { fun `given all checks being true, should display flow is false`() = runTest { val welcomeState = FakeWelcomeState() val analyticsService = FakeAnalyticsService() - val migrationScreenStore = InMemoryMigrationScreenStore() val permissionStateProvider = FakePermissionStateProvider(permissionGranted = true) val lockScreenService = FakeLockScreenService() val coroutineScope = CoroutineScope(coroutineContext + SupervisorJob()) @@ -63,14 +57,12 @@ class DefaultFtueStateTests { coroutineScope = coroutineScope, welcomeState = welcomeState, analyticsService = analyticsService, - migrationScreenStore = migrationScreenStore, permissionStateProvider = permissionStateProvider, lockScreenService = lockScreenService, ) welcomeState.setWelcomeScreenShown() analyticsService.setDidAskUserConsent() - migrationScreenStore.setMigrationScreenShown(A_SESSION_ID) permissionStateProvider.setPermissionGranted() lockScreenService.setIsPinSetup(true) state.updateState() @@ -85,7 +77,6 @@ class DefaultFtueStateTests { fun `traverse flow`() = runTest { val welcomeState = FakeWelcomeState() val analyticsService = FakeAnalyticsService() - val migrationScreenStore = InMemoryMigrationScreenStore() val permissionStateProvider = FakePermissionStateProvider(permissionGranted = false) val lockScreenService = FakeLockScreenService() val coroutineScope = CoroutineScope(coroutineContext + SupervisorJob()) @@ -94,29 +85,24 @@ class DefaultFtueStateTests { coroutineScope = coroutineScope, welcomeState = welcomeState, analyticsService = analyticsService, - migrationScreenStore = migrationScreenStore, permissionStateProvider = permissionStateProvider, lockScreenService = lockScreenService, ) val steps = mutableListOf() - // First step, migration screen - steps.add(state.getNextStep(steps.lastOrNull())) - migrationScreenStore.setMigrationScreenShown(A_SESSION_ID) - - // Second step, welcome screen + // First step, welcome screen steps.add(state.getNextStep(steps.lastOrNull())) welcomeState.setWelcomeScreenShown() - // Third step, notifications opt in + // Second step, notifications opt in steps.add(state.getNextStep(steps.lastOrNull())) permissionStateProvider.setPermissionGranted() - // Fourth step, entering PIN code + // Third step, entering PIN code steps.add(state.getNextStep(steps.lastOrNull())) lockScreenService.setIsPinSetup(true) - // Fifth step, analytics opt in + // Fourth step, analytics opt in steps.add(state.getNextStep(steps.lastOrNull())) analyticsService.setDidAskUserConsent() @@ -124,7 +110,6 @@ class DefaultFtueStateTests { steps.add(state.getNextStep(steps.lastOrNull())) assertThat(steps).containsExactly( - FtueStep.MigrationScreen, FtueStep.WelcomeScreen, FtueStep.NotificationsOptIn, FtueStep.LockscreenSetup, @@ -141,19 +126,16 @@ class DefaultFtueStateTests { fun `if a check for a step is true, start from the next one`() = runTest { val coroutineScope = CoroutineScope(coroutineContext + SupervisorJob()) val analyticsService = FakeAnalyticsService() - val migrationScreenStore = InMemoryMigrationScreenStore() val permissionStateProvider = FakePermissionStateProvider(permissionGranted = false) val lockScreenService = FakeLockScreenService() val state = createState( coroutineScope = coroutineScope, analyticsService = analyticsService, - migrationScreenStore = migrationScreenStore, permissionStateProvider = permissionStateProvider, lockScreenService = lockScreenService, ) - // Skip first 4 steps - migrationScreenStore.setMigrationScreenShown(A_SESSION_ID) + // Skip first 3 steps state.setWelcomeScreenShown() permissionStateProvider.setPermissionGranted() lockScreenService.setIsPinSetup(true) @@ -171,18 +153,15 @@ class DefaultFtueStateTests { fun `if version is older than 13 we don't display the notification opt in screen`() = runTest { val coroutineScope = CoroutineScope(coroutineContext + SupervisorJob()) val analyticsService = FakeAnalyticsService() - val migrationScreenStore = InMemoryMigrationScreenStore() val lockScreenService = FakeLockScreenService() val state = createState( sdkIntVersion = Build.VERSION_CODES.M, coroutineScope = coroutineScope, analyticsService = analyticsService, - migrationScreenStore = migrationScreenStore, lockScreenService = lockScreenService, ) - migrationScreenStore.setMigrationScreenShown(A_SESSION_ID) assertThat(state.getNextStep()).isEqualTo(FtueStep.WelcomeScreen) state.setWelcomeScreenShown() lockScreenService.setIsPinSetup(true) @@ -200,9 +179,7 @@ class DefaultFtueStateTests { coroutineScope: CoroutineScope, welcomeState: FakeWelcomeState = FakeWelcomeState(), analyticsService: AnalyticsService = FakeAnalyticsService(), - migrationScreenStore: MigrationScreenStore = InMemoryMigrationScreenStore(), permissionStateProvider: FakePermissionStateProvider = FakePermissionStateProvider(permissionGranted = false), - matrixClient: MatrixClient = FakeMatrixClient(), lockScreenService: LockScreenService = FakeLockScreenService(), // First version where notification permission is required sdkIntVersion: Int = Build.VERSION_CODES.TIRAMISU, @@ -211,9 +188,7 @@ class DefaultFtueStateTests { coroutineScope = coroutineScope, analyticsService = analyticsService, welcomeScreenState = welcomeState, - migrationScreenStore = migrationScreenStore, permissionStateProvider = permissionStateProvider, lockScreenService = lockScreenService, - matrixClient = matrixClient, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt index e8a3bddc43..ed6d8b1825 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt @@ -25,11 +25,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.lifecycle.Lifecycle +import io.element.android.compound.theme.ElementTheme import io.element.android.features.login.impl.R import io.element.android.features.login.impl.error.isWaitListError import io.element.android.features.login.impl.error.loginError @@ -123,7 +123,7 @@ private fun OverallContent( ) { Box(modifier = modifier.fillMaxSize()) { if (state.loginAction !is AsyncData.Success) { - CompositionLocalProvider(LocalContentColor provides Color.Black) { + CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.textOnSolidPrimary) { TextButton( text = stringResource(CommonStrings.action_cancel), onClick = onCancelClicked, diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts index e6a605f5e4..ee0658ddc6 100644 --- a/features/preferences/impl/build.gradle.kts +++ b/features/preferences/impl/build.gradle.kts @@ -55,6 +55,7 @@ dependencies { implementation(projects.features.analytics.api) implementation(projects.features.ftue.api) implementation(projects.features.logout.api) + implementation(projects.features.roomlist.api) implementation(projects.services.analytics.api) implementation(projects.services.toolbox.api) implementation(libs.datetime) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt index 07ca0716e9..2d590b90c1 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt @@ -24,6 +24,7 @@ import coil.annotation.ExperimentalCoilApi import com.squareup.anvil.annotations.ContributesBinding import io.element.android.features.ftue.api.state.FtueState import io.element.android.features.preferences.impl.DefaultCacheService +import io.element.android.features.roomlist.api.migration.MigrationScreenStore import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.SessionScope @@ -45,6 +46,7 @@ class DefaultClearCacheUseCase @Inject constructor( private val defaultCacheIndexProvider: DefaultCacheService, private val okHttpClient: Provider, private val ftueState: FtueState, + private val migrationScreenStore: MigrationScreenStore, ) : ClearCacheUseCase { override suspend fun invoke() = withContext(coroutineDispatchers.io) { // Clear Matrix cache @@ -60,6 +62,8 @@ class DefaultClearCacheUseCase @Inject constructor( context.cacheDir.deleteRecursively() // Clear some settings ftueState.reset() + // Clear migration screen store + migrationScreenStore.reset() // Ensure the app is restarted defaultCacheIndexProvider.onClearedCache(matrixClient.sessionId) } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenStore.kt b/features/roomlist/api/src/main/kotlin/io/element/android/features/roomlist/api/migration/MigrationScreenStore.kt similarity index 93% rename from features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenStore.kt rename to features/roomlist/api/src/main/kotlin/io/element/android/features/roomlist/api/migration/MigrationScreenStore.kt index 42eeab673a..6e20b1ad4a 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenStore.kt +++ b/features/roomlist/api/src/main/kotlin/io/element/android/features/roomlist/api/migration/MigrationScreenStore.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.ftue.impl.migration +package io.element.android.features.roomlist.api.migration import io.element.android.libraries.matrix.api.core.SessionId diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt index 421cdb0c62..f25ebcec20 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt @@ -33,6 +33,7 @@ import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource import io.element.android.features.roomlist.impl.datasource.RoomListDataSource +import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher @@ -63,6 +64,7 @@ class RoomListPresenter @Inject constructor( private val encryptionService: EncryptionService, private val featureFlagService: FeatureFlagService, private val indicatorService: IndicatorService, + private val migrationScreenPresenter: MigrationScreenPresenter, ) : Presenter { @Composable override fun present(): RoomListState { @@ -82,6 +84,8 @@ class RoomListPresenter @Inject constructor( initialLoad(matrixUser) } + val isMigrating = migrationScreenPresenter.present().isMigrating + // Session verification status (unknown, not verified, verified) val canVerifySession by sessionVerificationService.canVerifySessionFlow.collectAsState(initial = false) var verificationPromptDismissed by rememberSaveable { mutableStateOf(false) } @@ -148,6 +152,7 @@ class RoomListPresenter @Inject constructor( displaySearchResults = displaySearchResults, contextMenu = contextMenu, leaveRoomState = leaveRoomState, + displayMigrationStatus = isMigrating, eventSink = ::handleEvents ) } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt index b1180aecb3..13ba030720 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt @@ -40,6 +40,7 @@ data class RoomListState( val displaySearchResults: Boolean, val contextMenu: ContextMenu, val leaveRoomState: LeaveRoomState, + val displayMigrationStatus: Boolean, val eventSink: (RoomListEvents) -> Unit, ) { sealed interface ContextMenu { diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt index 55cbcf925b..7fda3a180a 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt @@ -53,6 +53,7 @@ open class RoomListStateProvider : PreviewParameterProvider { aRoomListState().copy(displayRecoveryKeyPrompt = true), aRoomListState().copy(roomList = AsyncData.Success(persistentListOf())), aRoomListState().copy(roomList = AsyncData.Loading(prevData = RoomListRoomSummaryFactory.createFakeList())), + aRoomListState().copy(matrixUser = null, displayMigrationStatus = true), ) } @@ -70,6 +71,7 @@ internal fun aRoomListState() = RoomListState( displaySearchResults = false, contextMenu = RoomListState.ContextMenu.Hidden, leaveRoomState = aLeaveRoomState(), + displayMigrationStatus = false, eventSink = {} ) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt index ac55e6a0f4..3d084e15c4 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt @@ -54,6 +54,7 @@ import io.element.android.features.roomlist.impl.components.RequestVerificationH import io.element.android.features.roomlist.impl.components.RoomListMenuAction import io.element.android.features.roomlist.impl.components.RoomListTopBar import io.element.android.features.roomlist.impl.components.RoomSummaryRow +import io.element.android.features.roomlist.impl.migration.MigrationScreenView import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.features.roomlist.impl.search.RoomListSearchResultView import io.element.android.libraries.architecture.AsyncData @@ -212,6 +213,7 @@ private fun RoomListContent( onMenuActionClicked = onMenuActionClicked, onOpenSettings = onOpenSettings, scrollBehavior = scrollBehavior, + displayMenuItems = !state.displayMigrationStatus, ) }, content = { padding -> @@ -270,18 +272,22 @@ private fun RoomListContent( } } } + + MigrationScreenView(isMigrating = state.displayMigrationStatus) }, floatingActionButton = { - FloatingActionButton( - // FIXME align on Design system theme - containerColor = MaterialTheme.colorScheme.primary, - onClick = onCreateRoomClicked - ) { - Icon( - // Note cannot use Icons.Outlined.EditSquare, it does not exist :/ - resourceId = CommonDrawables.ic_new_message, - contentDescription = stringResource(id = R.string.screen_roomlist_a11y_create_message) - ) + if (!state.displayMigrationStatus) { + FloatingActionButton( + // FIXME align on Design system theme + containerColor = MaterialTheme.colorScheme.primary, + onClick = onCreateRoomClicked + ) { + Icon( + // Note cannot use Icons.Outlined.EditSquare, it does not exist :/ + resourceId = CommonDrawables.ic_new_message, + contentDescription = stringResource(id = R.string.screen_roomlist_a11y_create_message) + ) + } } }, snackbarHost = { SnackbarHost(snackbarHostState) }, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt index 91b1dc4384..731e195744 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt @@ -21,8 +21,10 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.TopAppBarDefaults @@ -68,6 +70,7 @@ import io.element.android.libraries.designsystem.theme.components.HorizontalDivi import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.MediumTopAppBar +import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser @@ -89,6 +92,7 @@ fun RoomListTopBar( onMenuActionClicked: (RoomListMenuAction) -> Unit, onOpenSettings: () -> Unit, scrollBehavior: TopAppBarScrollBehavior, + displayMenuItems: Boolean, modifier: Modifier = Modifier, ) { fun closeFilter() { @@ -108,6 +112,7 @@ fun RoomListTopBar( onSearchClicked = onToggleSearch, onMenuActionClicked = onMenuActionClicked, scrollBehavior = scrollBehavior, + displayMenuItems = displayMenuItems, modifier = modifier, ) } @@ -122,6 +127,7 @@ private fun DefaultRoomListTopBar( onOpenSettings: () -> Unit, onSearchClicked: () -> Unit, onMenuActionClicked: (RoomListMenuAction) -> Unit, + displayMenuItems: Boolean, modifier: Modifier = Modifier, ) { // We need this to manually clip the top app bar in preview mode @@ -195,79 +201,89 @@ private fun DefaultRoomListTopBar( Text(text = stringResource(id = R.string.screen_roomlist_main_space_title)) }, navigationIcon = { - avatarData?.let { - IconButton( - modifier = Modifier.testTag(TestTags.homeScreenSettings), - onClick = onOpenSettings - ) { + IconButton( + modifier = Modifier.testTag(TestTags.homeScreenSettings), + onClick = onOpenSettings + ) { + if (avatarData != null) { Avatar( - avatarData = it, + avatarData = avatarData!!, contentDescription = stringResource(CommonStrings.common_settings), ) - if (showAvatarIndicator) { - RedIndicatorAtom( - modifier = Modifier - .padding(4.5.dp) - .align(Alignment.TopEnd) - ) - } + } else { + // Placeholder avatar until the avatarData is available + Surface( + modifier = Modifier.size(AvatarSize.CurrentUserTopBar.dp), + shape = CircleShape, + color = ElementTheme.colors.iconSecondary, + content = {} + ) + } + if (showAvatarIndicator) { + RedIndicatorAtom( + modifier = Modifier + .padding(4.5.dp) + .align(Alignment.TopEnd) + ) } } }, actions = { - IconButton( - onClick = onSearchClicked, - ) { - Icon( - imageVector = CompoundIcons.Search, - contentDescription = stringResource(CommonStrings.action_search), - ) - } - if (RoomListConfig.HAS_DROP_DOWN_MENU) { - var showMenu by remember { mutableStateOf(false) } + if (displayMenuItems) { IconButton( - onClick = { showMenu = !showMenu } + onClick = onSearchClicked, ) { Icon( - imageVector = CompoundIcons.OverflowVertical, - contentDescription = null, + imageVector = CompoundIcons.Search, + contentDescription = stringResource(CommonStrings.action_search), ) } - DropdownMenu( - expanded = showMenu, - onDismissRequest = { showMenu = false } - ) { - if (RoomListConfig.SHOW_INVITE_MENU_ITEM) { - DropdownMenuItem( - onClick = { - showMenu = false - onMenuActionClicked(RoomListMenuAction.InviteFriends) - }, - text = { Text(stringResource(id = CommonStrings.action_invite)) }, - leadingIcon = { - Icon( - imageVector = CompoundIcons.ShareAndroid, - tint = ElementTheme.materialColors.secondary, - contentDescription = null, - ) - } + if (RoomListConfig.HAS_DROP_DOWN_MENU) { + var showMenu by remember { mutableStateOf(false) } + IconButton( + onClick = { showMenu = !showMenu } + ) { + Icon( + imageVector = CompoundIcons.OverflowVertical, + contentDescription = null, ) } - if (RoomListConfig.SHOW_REPORT_PROBLEM_MENU_ITEM) { - DropdownMenuItem( - onClick = { - showMenu = false - onMenuActionClicked(RoomListMenuAction.ReportBug) - }, - text = { Text(stringResource(id = CommonStrings.common_report_a_problem)) }, - leadingIcon = { - Icon( - imageVector = CompoundIcons.ChatProblem, - tint = ElementTheme.materialColors.secondary, - contentDescription = null, - ) - } - ) + DropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false } + ) { + if (RoomListConfig.SHOW_INVITE_MENU_ITEM) { + DropdownMenuItem( + onClick = { + showMenu = false + onMenuActionClicked(RoomListMenuAction.InviteFriends) + }, + text = { Text(stringResource(id = CommonStrings.action_invite)) }, + leadingIcon = { + Icon( + imageVector = CompoundIcons.ShareAndroid, + tint = ElementTheme.materialColors.secondary, + contentDescription = null, + ) + } + ) + } + if (RoomListConfig.SHOW_REPORT_PROBLEM_MENU_ITEM) { + DropdownMenuItem( + onClick = { + showMenu = false + onMenuActionClicked(RoomListMenuAction.ReportBug) + }, + text = { Text(stringResource(id = CommonStrings.common_report_a_problem)) }, + leadingIcon = { + Icon( + imageVector = CompoundIcons.ChatProblem, + tint = ElementTheme.materialColors.secondary, + contentDescription = null, + ) + } + ) + } } } } @@ -299,6 +315,7 @@ internal fun DefaultRoomListTopBarPreview() = ElementPreview { scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()), onOpenSettings = {}, onSearchClicked = {}, + displayMenuItems = true, onMenuActionClicked = {}, ) } @@ -314,6 +331,7 @@ internal fun DefaultRoomListTopBarWithIndicatorPreview() = ElementPreview { scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()), onOpenSettings = {}, onSearchClicked = {}, + displayMenuItems = true, onMenuActionClicked = {}, ) } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/migration/MigrationScreenPresenter.kt similarity index 73% rename from features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenPresenter.kt rename to features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/migration/MigrationScreenPresenter.kt index 6507130383..4886eca7bb 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/migration/MigrationScreenPresenter.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 New Vector Ltd + * 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. @@ -14,12 +14,16 @@ * limitations under the License. */ -package io.element.android.features.ftue.impl.migration +package io.element.android.features.roomlist.impl.migration import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import io.element.android.features.roomlist.api.migration.MigrationScreenStore import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.roomlist.RoomListService @@ -32,13 +36,15 @@ class MigrationScreenPresenter @Inject constructor( @Composable override fun present(): MigrationScreenState { val roomListState by matrixClient.roomListService.state.collectAsState() + var needsMigration by remember { mutableStateOf(migrationScreenStore.isMigrationScreenNeeded(matrixClient.sessionId)) } if (roomListState == RoomListService.State.Running) { LaunchedEffect(Unit) { + needsMigration = false migrationScreenStore.setMigrationScreenShown(matrixClient.sessionId) } } return MigrationScreenState( - isMigrating = roomListState != RoomListService.State.Running + isMigrating = needsMigration && roomListState != RoomListService.State.Running ) } } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenState.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/migration/MigrationScreenState.kt similarity index 91% rename from features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenState.kt rename to features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/migration/MigrationScreenState.kt index fa718990e4..884c706da5 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenState.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/migration/MigrationScreenState.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.ftue.impl.migration +package io.element.android.features.roomlist.impl.migration data class MigrationScreenState( val isMigrating: Boolean diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/migration/MigrationScreenView.kt similarity index 54% rename from features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenView.kt rename to features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/migration/MigrationScreenView.kt index 54a1b541be..b7627f1ce0 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/migration/MigrationScreenView.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 New Vector Ltd + * 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. @@ -14,45 +14,43 @@ * limitations under the License. */ -package io.element.android.features.ftue.impl.migration +package io.element.android.features.roomlist.impl.migration +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.res.stringResource -import io.element.android.features.ftue.impl.R +import io.element.android.features.roomlist.impl.R import io.element.android.libraries.designsystem.atomic.pages.SunsetPage import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @Composable fun MigrationScreenView( - migrationState: MigrationScreenState, - onMigrationFinished: () -> Unit, + isMigrating: Boolean, modifier: Modifier = Modifier, ) { - if (migrationState.isMigrating.not()) { - val latestOnMigrationFinished by rememberUpdatedState(onMigrationFinished) - LaunchedEffect(Unit) { - latestOnMigrationFinished() - } - } - SunsetPage( - modifier = modifier, - isLoading = true, - title = stringResource(id = R.string.screen_migration_title), - subtitle = stringResource(id = R.string.screen_migration_message), - overallContent = {} + val displayMigrationStatusFadeProgress by animateFloatAsState( + targetValue = if (isMigrating) 1f else 0f, + animationSpec = tween(durationMillis = 200), + label = "Migration view fade" ) + if (displayMigrationStatusFadeProgress > 0f) { + SunsetPage( + modifier = modifier.alpha(displayMigrationStatusFadeProgress), + isLoading = true, + title = stringResource(id = R.string.screen_migration_title), + subtitle = stringResource(id = R.string.screen_migration_message), + overallContent = {} + ) + } } -@PreviewsDayNight @Composable +@PreviewsDayNight internal fun MigrationViewPreview() = ElementPreview { - MigrationScreenView( - migrationState = MigrationScreenState(isMigrating = true), - onMigrationFinished = {} - ) + MigrationScreenView(isMigrating = true) } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/SharedPrefsMigrationScreenStore.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/migration/SharedPrefsMigrationScreenStore.kt similarity index 92% rename from features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/SharedPrefsMigrationScreenStore.kt rename to features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/migration/SharedPrefsMigrationScreenStore.kt index 8ece45c08c..62121fd282 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/SharedPrefsMigrationScreenStore.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/migration/SharedPrefsMigrationScreenStore.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 New Vector Ltd + * 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. @@ -14,11 +14,12 @@ * limitations under the License. */ -package io.element.android.features.ftue.impl.migration +package io.element.android.features.roomlist.impl.migration import android.content.SharedPreferences import androidx.core.content.edit import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.roomlist.api.migration.MigrationScreenStore import io.element.android.libraries.androidutils.hash.hash import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.DefaultPreferences diff --git a/features/roomlist/impl/src/main/res/values/localazy.xml b/features/roomlist/impl/src/main/res/values/localazy.xml index 7f0c4cce9c..d9980835f4 100644 --- a/features/roomlist/impl/src/main/res/values/localazy.xml +++ b/features/roomlist/impl/src/main/res/values/localazy.xml @@ -2,6 +2,8 @@ "Your chat backup is currently out of sync. You need to confirm your recovery key to maintain access to your chat backup." "Confirm your recovery key" + "This is a one time process, thanks for waiting." + "Setting up your account." "Create a new conversation or room" "Get started by messaging someone." "No chats yet." diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index 09d6f12e0c..10730f0c90 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -29,6 +29,8 @@ import io.element.android.features.roomlist.impl.datasource.FakeInviteDataSource import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource import io.element.android.features.roomlist.impl.datasource.RoomListDataSource import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory +import io.element.android.features.roomlist.impl.migration.InMemoryMigrationScreenStore +import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter @@ -44,12 +46,14 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.room.RoomNotificationMode +import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_NAME +import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.FakeMatrixClient @@ -396,6 +400,36 @@ class RoomListPresenterTests { } } + @Test + fun `present - change in migration presenter state modifies isMigrating`() = runTest { + val client = FakeMatrixClient(sessionId = A_SESSION_ID) + val migrationStore = InMemoryMigrationScreenStore() + val migrationScreenPresenter = MigrationScreenPresenter(client, migrationStore) + val scope = CoroutineScope(coroutineContext + SupervisorJob()) + val presenter = createRoomListPresenter( + client = client, + coroutineScope = scope, + migrationScreenPresenter = migrationScreenPresenter, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + // The migration screen is shown if the migration screen has not been shown before + assertThat(initialState.displayMigrationStatus).isTrue() + skipItems(2) + + // Set migration as done and set the room list service as running to trigger a refresh of the presenter value + (client.roomListService as FakeRoomListService).postState(RoomListService.State.Running) + migrationStore.setMigrationScreenShown(A_SESSION_ID) + + // The migration screen is not shown anymore + assertThat(awaitItem().displayMigrationStatus).isFalse() + cancelAndIgnoreRemainingEvents() + scope.cancel() + } + } + private fun TestScope.createRoomListPresenter( client: MatrixClient = FakeMatrixClient(), sessionVerificationService: SessionVerificationService = FakeSessionVerificationService(), @@ -409,6 +443,10 @@ class RoomListPresenterTests { roomLastMessageFormatter: RoomLastMessageFormatter = FakeRoomLastMessageFormatter(), encryptionService: EncryptionService = FakeEncryptionService(), coroutineScope: CoroutineScope, + migrationScreenPresenter: MigrationScreenPresenter = MigrationScreenPresenter( + matrixClient = client, + migrationScreenStore = InMemoryMigrationScreenStore(), + ) ) = RoomListPresenter( client = client, sessionVerificationService = sessionVerificationService, @@ -433,6 +471,7 @@ class RoomListPresenterTests { encryptionService = encryptionService, featureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.SecureStorage.key to true)), ), + migrationScreenPresenter = migrationScreenPresenter, ) } diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/migration/InMemoryMigrationScreenStore.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/migration/InMemoryMigrationScreenStore.kt similarity index 86% rename from features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/migration/InMemoryMigrationScreenStore.kt rename to features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/migration/InMemoryMigrationScreenStore.kt index a77a4d001a..79d0b7c628 100644 --- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/migration/InMemoryMigrationScreenStore.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/migration/InMemoryMigrationScreenStore.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 New Vector Ltd + * 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. @@ -14,8 +14,9 @@ * limitations under the License. */ -package io.element.android.features.ftue.impl.migration +package io.element.android.features.roomlist.impl.migration +import io.element.android.features.roomlist.api.migration.MigrationScreenStore import io.element.android.libraries.matrix.api.core.SessionId class InMemoryMigrationScreenStore : MigrationScreenStore { diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenPresenterTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/migration/MigrationScreenPresenterTest.kt similarity index 92% rename from features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenPresenterTest.kt rename to features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/migration/MigrationScreenPresenterTest.kt index 44dcba6e00..2ddcaed566 100644 --- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenPresenterTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/migration/MigrationScreenPresenterTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 New Vector Ltd + * 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. @@ -14,12 +14,13 @@ * limitations under the License. */ -package io.element.android.features.ftue.impl.migration +package io.element.android.features.roomlist.impl.migration import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.roomlist.api.migration.MigrationScreenStore import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.test.A_SESSION_ID @@ -61,6 +62,7 @@ class MigrationScreenPresenterTest { val nextState = awaitItem() assertThat(nextState.isMigrating).isFalse() assertThat(migrationScreenStore.isMigrationScreenNeeded(A_SESSION_ID)).isFalse() + cancelAndIgnoreRemainingEvents() } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt index 476545befc..2bc4290c67 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt @@ -37,8 +37,10 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import io.element.android.compound.annotations.CoreColorToken import io.element.android.compound.theme.ElementTheme -import io.element.android.compound.theme.ForcedDarkElementTheme +import io.element.android.compound.tokens.generated.internal.DarkColorTokens +import io.element.android.compound.tokens.generated.internal.LightColorTokens import io.element.android.libraries.designsystem.R import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -54,7 +56,11 @@ fun SunsetPage( modifier: Modifier = Modifier, overallContent: @Composable () -> Unit, ) { - ForcedDarkElementTheme(lightStatusBar = true) { + ElementTheme( + // Always use the opposite value of the current theme + darkTheme = ElementTheme.isLightTheme, + applySystemBarsUpdate = false, + ) { Box( modifier = modifier.fillMaxSize() ) { @@ -107,21 +113,34 @@ fun SunsetPage( } } +@OptIn(CoreColorToken::class) @Composable private fun SunsetBackground( modifier: Modifier = Modifier, ) { Column(modifier = modifier.fillMaxSize()) { + // The top background colors are the opposite of the current theme ones + val topBackgroundColor = if (ElementTheme.isLightTheme) { + DarkColorTokens.colorThemeBg + } else { + LightColorTokens.colorThemeBg + } + // The bottom background colors follow the current theme + val bottomBackgroundColor = if (ElementTheme.isLightTheme) { + LightColorTokens.colorThemeBg + } else { + // The dark background color doesn't 100% match the image, so we use a custom color + Color(0xFF121418) + } Box( modifier = Modifier .fillMaxWidth() .weight(0.3f) - .background(Color.White) + .background(topBackgroundColor) ) Image( - modifier = Modifier - .fillMaxWidth(), - painter = painterResource(id = R.drawable.light_dark), + modifier = Modifier.fillMaxWidth(), + painter = painterResource(id = R.drawable.bg_migration), contentScale = ContentScale.Crop, contentDescription = null, ) @@ -129,7 +148,7 @@ private fun SunsetBackground( modifier = Modifier .fillMaxWidth() .weight(0.7f) - .background(Color(0xFF121418)) + .background(bottomBackgroundColor) ) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt index 5742438641..bb3373b397 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt @@ -23,7 +23,6 @@ import android.os.Build import android.text.TextPaint import androidx.annotation.FloatRange import androidx.compose.foundation.Image -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.consumeWindowInsets @@ -136,13 +135,13 @@ object BloomDefaults { @Composable fun defaultLayers() = persistentListOf( // Bottom layer - if (isSystemInDarkTheme()) { - BloomLayer(0.5f, BlendMode.Exclusion) - } else { + if (ElementTheme.isLightTheme) { BloomLayer(0.2f, BlendMode.Hardlight) + } else { + BloomLayer(0.5f, BlendMode.Exclusion) }, // Top layer - BloomLayer(if (isSystemInDarkTheme()) 0.2f else 0.8f, BlendMode.Color), + BloomLayer(if (ElementTheme.isLightTheme) 0.8f else 0.2f, BlendMode.Color), ) } diff --git a/libraries/designsystem/src/main/res/drawable-night/bg_migration.png b/libraries/designsystem/src/main/res/drawable-night/bg_migration.png new file mode 100644 index 0000000000000000000000000000000000000000..41cba4f4ba9e34e54d68eff004cdf4a87dc0b548 GIT binary patch literal 550912 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 MO*QeoyGo@@N^>FsK*cPzTvp7UPj{e$n(`>CnC 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%0D%u1g5Rnv3sIbMCj#%dN+P*mh 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С177TM#=KzESR~T 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 BXR-hQ literal 0 HcmV?d00001 diff --git a/libraries/designsystem/src/main/res/drawable/light_dark.png b/libraries/designsystem/src/main/res/drawable/bg_migration.png similarity index 100% rename from libraries/designsystem/src/main/res/drawable/light_dark.png rename to libraries/designsystem/src/main/res/drawable/bg_migration.png diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt index 286a80d71d..f87119293d 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt @@ -28,6 +28,8 @@ import io.element.android.features.roomlist.impl.RoomListView import io.element.android.features.roomlist.impl.datasource.DefaultInviteStateDataSource import io.element.android.features.roomlist.impl.datasource.RoomListDataSource import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory +import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter +import io.element.android.features.roomlist.impl.migration.SharedPrefsMigrationScreenStore import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.dateformatter.impl.DateFormatters import io.element.android.libraries.dateformatter.impl.DefaultLastMessageTimestampFormatter @@ -103,6 +105,10 @@ class RoomListScreen( featureFlagService = featureFlagService, ), featureFlagService = featureFlagService, + migrationScreenPresenter = MigrationScreenPresenter( + matrixClient = matrixClient, + migrationScreenStore = SharedPrefsMigrationScreenStore(context.getSharedPreferences("migration", Context.MODE_PRIVATE)) + ) ) @Composable diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.migration_MigrationView_null_MigrationView-Night-0_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.migration_MigrationView_null_MigrationView-Night-0_2_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 99a9e03973..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.migration_MigrationView_null_MigrationView-Night-0_2_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4c9d9aa75b2b01e9b0106377fbcd88a92fb8b4d6a34323b18264fed8f9a48cab -size 133347 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Day-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Day-0_1_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Day-1_2_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Day-0_1_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Night-1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Night-0_2_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Night-1_3_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Night-0_2_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-1_2_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-2_3_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-1_2_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-2_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-1_3_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-2_4_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-1_3_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_0,NEXUS_5,1.0,en].png index 412475009c..54f60964f3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce7cb3f4a50912cd7aa96340ff850d438a26e5efc331173c658448fdbecc7b65 -size 145047 +oid sha256:9db077d2e2981e9b3d431312b171741505e22816fa4b2bb3699f6ae4e00b48e9 +size 145160 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_1,NEXUS_5,1.0,en].png index f89a48fbdf..e9fdb0d176 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b06c44fcc0afee1a398dbddeb18253f25002c3c8e77929d049edd878b3927e8a -size 145776 +oid sha256:de09324bd0a41e25eff91ae77d5d88d96289a379ecf24e626fa9ea09a5acd43b +size 145928 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_2,NEXUS_5,1.0,en].png index f24b59907d..c5b8dcbb18 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30a12c0e23d30c5841fef2f705af3fc4ea691ff06425d84a5c33f34809b78d8c -size 65492 +oid sha256:54f47ca8a687adf5528e5ff8ddd87d901d2aa1280e6252ec962c176d6a207237 +size 65584 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_3,NEXUS_5,1.0,en].png index 412475009c..54f60964f3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Day-8_9_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce7cb3f4a50912cd7aa96340ff850d438a26e5efc331173c658448fdbecc7b65 -size 145047 +oid sha256:9db077d2e2981e9b3d431312b171741505e22816fa4b2bb3699f6ae4e00b48e9 +size 145160 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_0,NEXUS_5,1.0,en].png index 412475009c..5bdbb9dbff 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce7cb3f4a50912cd7aa96340ff850d438a26e5efc331173c658448fdbecc7b65 -size 145047 +oid sha256:5d5c0f2cf39d75b09a8cc1f4ffbd65912cab79ce5881ed5da5fdadeb9f57cb00 +size 170070 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_1,NEXUS_5,1.0,en].png index f89a48fbdf..91a55dee0f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b06c44fcc0afee1a398dbddeb18253f25002c3c8e77929d049edd878b3927e8a -size 145776 +oid sha256:c23b11ebcc1e73255714b07cee27c4583a9923112cdf0eda60983925d8074bb7 +size 170640 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_2,NEXUS_5,1.0,en].png index f6aaba2f2d..17bd0f8204 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6687f1be6e8abab9ae846152cc4edeb5fe1becda4ec9bcf64c255ded9dc937a -size 63857 +oid sha256:026200b29fb2599e1f04ae19ab75cfed41a8c4aff5b0ef0086a6948244733830 +size 67437 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_3,NEXUS_5,1.0,en].png index 412475009c..5bdbb9dbff 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce7cb3f4a50912cd7aa96340ff850d438a26e5efc331173c658448fdbecc7b65 -size 145047 +oid sha256:5d5c0f2cf39d75b09a8cc1f4ffbd65912cab79ce5881ed5da5fdadeb9f57cb00 +size 170070 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_4,NEXUS_5,1.0,en].png index 220fb6de61..9b7088595f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_WaitListView_null_WaitListView-Night-8_10_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ddf3e62bd00f179fbeed4e432bd9a7a21321e83dd1d5192e3dc213fcfbc8c372 -size 128199 +oid sha256:97aa5dbc8ddcd05a44ae56771a13085096f11b2469c1a7edb3281e242d86ad21 +size 150264 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.migration_MigrationView_null_MigrationView-Day-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Day-10_11_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.migration_MigrationView_null_MigrationView-Day-0_1_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Day-10_11_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Night-10_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Night-10_12_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..46a349cbce --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Night-10_12_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fea824bf7f4c74c827fa3c1fa892b9beeb9d11e0863fc9e5c104c725236c7d10 +size 156506 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-10_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-11_12_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-10_11_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-11_12_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-10_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-11_13_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-10_12_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-11_13_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_12,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f901e915e5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_12,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64c4eb481f40871925405ae317cb80927caf31cb552a8aa9549bfb5658ca91e4 +size 137589 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_12,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..56f7f133f3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_12,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a7bf47b0a25c455b108d7b3585a42849f03c6652af73a175fe2147cb1ad62a66 +size 161125 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_SunsetPage_null_SunsetPage-Night_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_SunsetPage_null_SunsetPage-Night_1_null,NEXUS_5,1.0,en].png index 9a5371804e..a6b5cfab9b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_SunsetPage_null_SunsetPage-Night_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_SunsetPage_null_SunsetPage-Night_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50ca2afda159bdd7f57e78b04fbe3c022a9aeee1b68f72cf4d4269b3e503f0e5 -size 127335 +oid sha256:d56b02025c9de6cca3aaa3e4cc5cd82b222790c4efa0013f957307c13e45ee40 +size 151229 diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 1bf1e9071b..2416876fa4 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -106,7 +106,8 @@ "includeRegex" : [ "screen_roomlist_.*", "session_verification_banner_.*", - "confirm_recovery_key_banner_.*" + "confirm_recovery_key_banner_.*", + "screen_migration_.*" ] }, { @@ -154,7 +155,6 @@ "name" : ":features:ftue:impl", "includeRegex" : [ "screen_welcome_.*", - "screen_migration_.*", "screen_notification_optin_.*" ] }, From b104dba84512afaa1ec47b993782f438f98babbf Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 8 Feb 2024 10:03:24 +0100 Subject: [PATCH 078/119] Remove Modifier parameter in private function when only default value is used. This will improve the code coverage metrics, and this also fixes a few potential bug, where the Modifier was used several times. --- .../io/element/android/x/icon/IconPreview.kt | 18 +++---- .../appnav/room/LoadingRoomNodeView.kt | 2 - .../analytics/impl/AnalyticsOptInView.kt | 17 ++----- .../impl/addpeople/AddPeopleView.kt | 2 - .../impl/configureroom/ConfigureRoomView.kt | 2 - .../impl/root/CreateRoomRootView.kt | 8 +-- .../notifications/NotificationsOptInView.kt | 10 ++-- .../impl/components/PinEntryTextField.kt | 5 +- .../setup/biometric/SetupBiometricView.kt | 8 +-- .../lockscreen/impl/setup/pin/SetupPinView.kt | 6 +-- .../lockscreen/impl/unlock/PinUnlockView.kt | 20 ++++---- .../impl/unlock/keypad/PinKeypad.kt | 3 +- .../loginpassword/LoginPasswordView.kt | 3 +- .../screens/waitlistscreen/WaitListView.kt | 3 +- .../features/messages/impl/MessagesView.kt | 16 ++---- .../impl/actionlist/ActionListView.kt | 3 +- .../preview/AttachmentsPreviewView.kt | 3 +- .../messagecomposer/AttachmentsBottomSheet.kt | 3 +- .../components/MessagesReactionButton.kt | 10 ++-- .../reactionsummary/ReactionSummaryView.kt | 13 ++--- .../impl/timeline/debug/EventDebugInfoView.kt | 3 +- .../api/ui/ConnectivityIndicatorView.kt | 5 +- .../onboarding/impl/OnBoardingView.kt | 6 +-- .../poll/api/pollcontent/PollContentView.kt | 14 ++---- .../impl/developer/DeveloperSettingsView.kt | 5 +- .../developer/tracing/ConfigureTracingView.kt | 12 +---- .../notifications/NotificationSettingsView.kt | 4 -- .../roomdetails/impl/RoomDetailsView.kt | 50 +++++++++---------- .../impl/blockuser/BlockUserSection.kt | 7 +-- .../impl/edit/RoomDetailsEditView.kt | 2 - .../impl/invite/RoomInviteMembersView.kt | 2 - .../impl/members/RoomMemberListView.kt | 2 - .../members/details/RoomMemberDetailsView.kt | 2 - .../RoomNotificationSettingsView.kt | 2 - ...UserDefinedRoomNotificationSettingsView.kt | 2 - .../impl/search/RoomListSearchResultView.kt | 2 - .../features/signedout/impl/SignedOutView.kt | 11 ++-- .../impl/VerifySelfSessionView.kt | 18 +++---- .../designsystem/atomic/pages/SunsetPage.kt | 6 +-- .../components/BlurHashAsyncImage.kt | 3 +- .../components/dialogs/ConfirmationDialog.kt | 2 - .../components/dialogs/ErrorDialog.kt | 2 - .../components/dialogs/ListDialog.kt | 2 - .../dialogs/MultipleSelectionDialog.kt | 2 - .../components/dialogs/RetryDialog.kt | 2 - .../dialogs/SingleSelectionDialog.kt | 2 - .../preferences/PreferenceCategory.kt | 4 +- .../components/preferences/PreferencePage.kt | 2 - .../preferences/PreferenceTextField.kt | 2 - .../designsystem/icons/IconsPreview.kt | 2 - .../theme/components/AlertDialogContent.kt | 6 --- .../featureflag/ui/FeatureListView.kt | 2 - .../mediaviewer/api/local/pdf/PdfViewer.kt | 5 +- .../mediaviewer/api/viewer/MediaViewerView.kt | 2 - .../roomselect/impl/RoomSelectView.kt | 3 +- .../libraries/textcomposer/TextComposer.kt | 16 ++---- .../textcomposer/components/TextFormatting.kt | 1 + .../components/VoiceMessagePreview.kt | 3 +- .../components/VoiceMessageRecording.kt | 6 +-- 59 files changed, 117 insertions(+), 262 deletions(-) diff --git a/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt b/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt index 2effcd9f4c..ce8eb0eba7 100644 --- a/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt +++ b/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt @@ -35,10 +35,8 @@ import io.element.android.x.R @Preview @Composable -internal fun IconPreview( - modifier: Modifier = Modifier, -) { - Box(modifier = modifier) { +internal fun IconPreview() { + Box { Image(painter = painterResource(id = R.mipmap.ic_launcher_background), contentDescription = null) Image(painter = painterResource(id = R.mipmap.ic_launcher_foreground), contentDescription = null) } @@ -46,10 +44,8 @@ internal fun IconPreview( @Preview @Composable -internal fun RoundIconPreview( - modifier: Modifier = Modifier, -) { - Box(modifier = modifier.clip(shape = CircleShape)) { +internal fun RoundIconPreview() { + Box(modifier = Modifier.clip(shape = CircleShape)) { Image(painter = painterResource(id = R.mipmap.ic_launcher_background), contentDescription = null) Image(painter = painterResource(id = R.mipmap.ic_launcher_foreground), contentDescription = null) } @@ -57,11 +53,9 @@ internal fun RoundIconPreview( @Preview @Composable -internal fun MonochromeIconPreview( - modifier: Modifier = Modifier, -) { +internal fun MonochromeIconPreview() { Box( - modifier = modifier + modifier = Modifier .size(108.dp) .background(Color(0xFF2F3133)) .clip(shape = RoundedCornerShape(32.dp)), diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt index 26d69cc213..8db8d608b2 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt @@ -84,10 +84,8 @@ fun LoadingRoomNodeView( @Composable private fun LoadingRoomTopBar( onBackClicked: () -> Unit, - modifier: Modifier = Modifier ) { TopAppBar( - modifier = modifier, navigationIcon = { BackButton(onClick = onBackClicked) }, diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt index e834f55142..cf8700b684 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt @@ -99,10 +99,8 @@ private const val LINK_TAG = "link" private fun AnalyticsOptInHeader( state: AnalyticsOptInState, onClickTerms: () -> Unit, - modifier: Modifier = Modifier, ) { Column( - modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally, ) { IconTitleSubtitleMolecule( @@ -139,9 +137,9 @@ private fun AnalyticsOptInHeader( } @Composable -private fun CheckIcon(modifier: Modifier = Modifier) { +private fun CheckIcon() { Icon( - modifier = modifier + modifier = Modifier .size(20.dp) .background(color = MaterialTheme.colorScheme.background, shape = CircleShape) .padding(2.dp), @@ -152,11 +150,9 @@ private fun CheckIcon(modifier: Modifier = Modifier) { } @Composable -private fun AnalyticsOptInContent( - modifier: Modifier = Modifier, -) { +private fun AnalyticsOptInContent() { Box( - modifier = modifier.fillMaxSize(), + modifier = Modifier.fillMaxSize(), contentAlignment = BiasAlignment( horizontalBias = 0f, verticalBias = -0.4f @@ -188,11 +184,8 @@ private fun AnalyticsOptInContent( private fun AnalyticsOptInFooter( onTermsAccepted: () -> Unit, onTermsDeclined: () -> Unit, - modifier: Modifier = Modifier, ) { - ButtonColumnMolecule( - modifier = modifier, - ) { + ButtonColumnMolecule { Button( text = stringResource(id = CommonStrings.action_ok), onClick = onTermsAccepted, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt index 147020fddb..788e6b64ee 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt @@ -88,10 +88,8 @@ private fun AddPeopleViewTopBar( hasSelectedUsers: Boolean, onBackPressed: () -> Unit, onNextPressed: () -> Unit, - modifier: Modifier = Modifier, ) { TopAppBar( - modifier = modifier, title = { Text( text = stringResource(id = R.string.screen_create_room_add_people_title), diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index c3887ecba6..04ebc05c3a 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -173,10 +173,8 @@ private fun ConfigureRoomToolbar( isNextActionEnabled: Boolean, onBackPressed: () -> Unit, onNextPressed: () -> Unit, - modifier: Modifier = Modifier, ) { TopAppBar( - modifier = modifier, title = { Text( text = stringResource(R.string.screen_create_room_title), diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt index 24b1145e2e..77b54644dd 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt @@ -117,10 +117,8 @@ fun CreateRoomRootView( @Composable private fun CreateRoomRootViewTopBar( onClosePressed: () -> Unit, - modifier: Modifier = Modifier, ) { TopAppBar( - modifier = modifier, title = { Text( text = stringResource(id = CommonStrings.action_start_chat), @@ -141,9 +139,8 @@ private fun CreateRoomActionButtonsList( state: CreateRoomRootState, onNewRoomClicked: () -> Unit, onInvitePeopleClicked: () -> Unit, - modifier: Modifier = Modifier, ) { - Column(modifier = modifier) { + Column { CreateRoomActionButton( iconRes = CompoundDrawables.ic_plus, text = stringResource(id = R.string.screen_create_room_action_create_room), @@ -162,10 +159,9 @@ private fun CreateRoomActionButton( @DrawableRes iconRes: Int, text: String, onClick: () -> Unit, - modifier: Modifier = Modifier, ) { Row( - modifier = modifier + modifier = Modifier .fillMaxWidth() .height(56.dp) .clickable { onClick() } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt index 616e6aa809..c617d684e4 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt @@ -67,7 +67,7 @@ fun NotificationsOptInView( header = { NotificationsOptInHeader(modifier = Modifier.padding(top = 60.dp, bottom = 12.dp)) }, footer = { NotificationsOptInFooter(state) }, ) { - NotificationsOptInContent(modifier = Modifier.fillMaxWidth()) + NotificationsOptInContent() } } @@ -104,10 +104,8 @@ private fun NotificationsOptInFooter(state: NotificationsOptInState) { } @Composable -private fun NotificationsOptInContent( - modifier: Modifier = Modifier, -) { - Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) { +private fun NotificationsOptInContent() { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Column( verticalArrangement = Arrangement.spacedBy( 16.dp, @@ -144,10 +142,8 @@ private fun NotificationRow( avatarColorsId: String, firstRowPercent: Float, secondRowPercent: Float, - modifier: Modifier = Modifier ) { Surface( - modifier = modifier, color = ElementTheme.colors.bgCanvasDisabled, shape = RoundedCornerShape(14.dp), shadowElevation = 2.dp, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/components/PinEntryTextField.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/components/PinEntryTextField.kt index 3c7a451bfc..674bc0d27e 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/components/PinEntryTextField.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/components/PinEntryTextField.kt @@ -66,10 +66,8 @@ fun PinEntryTextField( private fun PinEntryRow( pinEntry: PinEntry, isSecured: Boolean, - modifier: Modifier = Modifier, ) { FlowRow( - modifier = modifier, horizontalArrangement = Arrangement.spacedBy(8.dp, alignment = Alignment.CenterHorizontally), verticalArrangement = Arrangement.spacedBy(8.dp), ) { @@ -83,7 +81,6 @@ private fun PinEntryRow( private fun PinDigitView( digit: PinDigit, isSecured: Boolean, - modifier: Modifier = Modifier, ) { val shape = RoundedCornerShape(8.dp) val appearanceModifier = when (digit) { @@ -95,7 +92,7 @@ private fun PinDigitView( } } Box( - modifier = modifier + modifier = Modifier .size(48.dp) .then(appearanceModifier), contentAlignment = Alignment.Center, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt index 8e7a326c90..99001e5334 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt @@ -57,13 +57,12 @@ fun SetupBiometricView( } @Composable -private fun SetupBiometricHeader(modifier: Modifier = Modifier) { +private fun SetupBiometricHeader() { val biometricAuth = stringResource(id = R.string.screen_app_lock_biometric_authentication) IconTitleSubtitleMolecule( iconImageVector = Icons.Default.Fingerprint, title = stringResource(id = R.string.screen_app_lock_settings_enable_biometric_unlock), subTitle = stringResource(id = R.string.screen_app_lock_setup_biometric_unlock_subtitle, biometricAuth), - modifier = modifier ) } @@ -71,11 +70,8 @@ private fun SetupBiometricHeader(modifier: Modifier = Modifier) { private fun SetupBiometricFooter( onAllowClicked: () -> Unit, onSkipClicked: () -> Unit, - modifier: Modifier = Modifier ) { - ButtonColumnMolecule( - modifier = modifier, - ) { + ButtonColumnMolecule { val biometricAuth = stringResource(id = R.string.screen_app_lock_biometric_authentication) Button( text = stringResource(id = R.string.screen_app_lock_setup_biometric_unlock_allow_title, biometricAuth), diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt index 095848c9fb..a3dcab5a43 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt @@ -86,10 +86,8 @@ fun SetupPinView( private fun SetupPinHeader( isValidationStep: Boolean, appName: String, - modifier: Modifier = Modifier, ) { Column( - modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally, ) { IconTitleSubtitleMolecule( @@ -107,7 +105,6 @@ private fun SetupPinHeader( @Composable private fun SetupPinContent( state: SetupPinState, - modifier: Modifier = Modifier, ) { val focusRequester = remember { FocusRequester() } LaunchedEffect(Unit) { @@ -119,14 +116,13 @@ private fun SetupPinContent( onValueChange = { entry -> state.eventSink(SetupPinEvents.OnPinEntryChanged(entry, state.isConfirmationStep)) }, - modifier = modifier + modifier = Modifier .focusRequester(focusRequester) .padding(top = 36.dp) .fillMaxWidth() ) if (state.setupPinFailure != null) { ErrorDialog( - modifier = modifier, title = state.setupPinFailure.title(), content = state.setupPinFailure.content(), onDismiss = { diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt index ad4dc5da99..db12006e04 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt @@ -107,10 +107,9 @@ fun PinUnlockView( private fun PinUnlockPage( state: PinUnlockState, isInAppUnlock: Boolean, - modifier: Modifier = Modifier ) { BoxWithConstraints { - val commonModifier = modifier + val commonModifier = Modifier .fillMaxSize() .systemBarsPadding() .imePadding() @@ -188,7 +187,6 @@ private fun SignOutPrompt( isCancellable: Boolean, onSignOut: () -> Unit, onDismiss: () -> Unit, - modifier: Modifier = Modifier ) { if (isCancellable) { ConfirmationDialog( @@ -196,14 +194,12 @@ private fun SignOutPrompt( content = stringResource(id = R.string.screen_app_lock_signout_alert_message), onSubmitClicked = onSignOut, onDismiss = onDismiss, - modifier = modifier, ) } else { ErrorDialog( title = stringResource(id = R.string.screen_app_lock_signout_alert_title), content = stringResource(id = R.string.screen_app_lock_signout_alert_message), onDismiss = onSignOut, - modifier = modifier, ) } } @@ -258,9 +254,11 @@ private fun PinUnlockExpandedView( @Composable private fun PinDotsRow( pinEntry: PinEntry, - modifier: Modifier = Modifier, ) { - Row(modifier, horizontalArrangement = spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) { + Row( + horizontalArrangement = spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { for (digit in pinEntry.digits) { PinDot(isFilled = digit is PinDigit.Filled) } @@ -270,7 +268,6 @@ private fun PinDotsRow( @Composable private fun PinDot( isFilled: Boolean, - modifier: Modifier = Modifier, ) { val backgroundColor = if (isFilled) { ElementTheme.colors.iconPrimary @@ -278,7 +275,7 @@ private fun PinDot( ElementTheme.colors.bgSubtlePrimary } Box( - modifier = modifier + modifier = Modifier .size(14.dp) .background(backgroundColor, CircleShape) ) @@ -290,7 +287,10 @@ private fun PinUnlockHeader( isInAppUnlock: Boolean, modifier: Modifier = Modifier, ) { - Column(modifier, horizontalAlignment = Alignment.CenterHorizontally) { + Column( + modifier = modifier, + horizontalAlignment = Alignment.CenterHorizontally, + ) { if (isInAppUnlock) { RoundedIconAtom(imageVector = Icons.Filled.Lock) } else { diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypad.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypad.kt index 6c51b10979..4033a1b6f7 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypad.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypad.kt @@ -108,14 +108,13 @@ private fun PinKeypadRow( models: ImmutableList, onClick: (PinKeypadModel) -> Unit, pinKeySize: Dp, - modifier: Modifier = Modifier, horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, verticalAlignment: Alignment.Vertical = Alignment.Top, ) { Row( horizontalArrangement = horizontalArrangement, verticalAlignment = verticalAlignment, - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) { val commonModifier = Modifier.size(pinKeySize) for (model in models) { diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt index 6dc1efbbf1..2bf76c2189 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt @@ -179,7 +179,6 @@ private fun LoginForm( state: LoginPasswordState, isLoading: Boolean, onSubmit: () -> Unit, - modifier: Modifier = Modifier ) { var loginFieldState by textFieldState(stateValue = state.formState.login) var passwordFieldState by textFieldState(stateValue = state.formState.password) @@ -187,7 +186,7 @@ private fun LoginForm( val focusManager = LocalFocusManager.current val eventSink = state.eventSink - Column(modifier) { + Column { Text( text = stringResource(R.string.screen_login_form_header), modifier = Modifier.padding(start = 16.dp), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt index ed6d8b1825..7a47255b6c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt @@ -119,9 +119,8 @@ private fun WaitListContent( private fun OverallContent( state: WaitListState, onCancelClicked: () -> Unit, - modifier: Modifier = Modifier, ) { - Box(modifier = modifier.fillMaxSize()) { + Box(modifier = Modifier.fillMaxSize()) { if (state.loginAction !is AsyncData.Success) { CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.textOnSolidPrimary) { TextButton( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index b5dccb823b..1c1bca9179 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -412,10 +412,9 @@ private fun MessagesViewContent( private fun MessagesViewComposerBottomSheetContents( subcomposing: Boolean, state: MessagesState, - modifier: Modifier = Modifier, ) { if (state.userHasPermissionToSendMessage) { - Column(modifier = modifier.fillMaxWidth()) { + Column(modifier = Modifier.fillMaxWidth()) { MentionSuggestionsPickerView( modifier = Modifier .heightIn(max = 230.dp) @@ -443,7 +442,7 @@ private fun MessagesViewComposerBottomSheetContents( ) } } else { - CantSendMessageBanner(modifier = modifier) + CantSendMessageBanner() } } @@ -456,10 +455,8 @@ private fun MessagesViewTopBar( onRoomDetailsClicked: () -> Unit, onJoinCallClicked: () -> Unit, onBackPressed: () -> Unit, - modifier: Modifier = Modifier, ) { TopAppBar( - modifier = modifier, navigationIcon = { BackButton(onClick = onBackPressed) }, @@ -497,7 +494,6 @@ private fun MessagesViewTopBar( @Composable private fun JoinCallMenuItem( - modifier: Modifier = Modifier, onJoinCallClicked: () -> Unit, ) { Material3Button( @@ -507,7 +503,7 @@ private fun JoinCallMenuItem( containerColor = ElementTheme.colors.iconAccentTertiary ), contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp), - modifier = modifier.heightIn(min = 36.dp), + modifier = Modifier.heightIn(min = 36.dp), ) { Icon( modifier = Modifier.size(20.dp), @@ -545,11 +541,9 @@ private fun RoomAvatarAndNameRow( } @Composable -private fun CantSendMessageBanner( - modifier: Modifier = Modifier, -) { +private fun CantSendMessageBanner() { Row( - modifier = modifier + modifier = Modifier .fillMaxWidth() .background(MaterialTheme.colorScheme.secondary) .padding(16.dp), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index 3db91fd67c..11b1922fcd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -337,7 +337,6 @@ private fun EmojiButton( emoji: String, isHighlighted: Boolean, onClicked: (String) -> Unit, - modifier: Modifier = Modifier, ) { val backgroundColor = if (isHighlighted) { ElementTheme.colors.bgActionPrimaryRest @@ -350,7 +349,7 @@ private fun EmojiButton( stringResource(id = CommonStrings.a11y_react_with, emoji) } Box( - modifier = modifier + modifier = Modifier .size(48.dp) .background(backgroundColor, CircleShape) .clearAndSetSemantics { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt index b18d36d61d..45f83568a3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt @@ -118,10 +118,9 @@ private fun AttachmentPreviewContent( attachment: Attachment, onSendClicked: () -> Unit, onDismiss: () -> Unit, - modifier: Modifier = Modifier, ) { Column( - modifier = modifier + modifier = Modifier .fillMaxSize() .padding(top = 24.dp) ) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt index af268856e9..7108363ffd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt @@ -101,10 +101,9 @@ private fun AttachmentSourcePickerMenu( onSendLocationClicked: () -> Unit, onCreatePollClicked: () -> Unit, enableTextFormatting: Boolean, - modifier: Modifier = Modifier, ) { Column( - modifier = modifier + modifier = Modifier .navigationBarsPadding() .imePadding() ) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt index 9b340eee67..fb10d03c39 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt @@ -126,9 +126,8 @@ private val ADD_EMOJI_SIZE = 16.dp @Composable private fun TextContent( text: String, - modifier: Modifier = Modifier, ) = Text( - modifier = modifier + modifier = Modifier .height(REACTION_EMOJI_LINE_HEIGHT.toDp()), text = text, style = ElementTheme.typography.fontBodyMdRegular, @@ -138,27 +137,24 @@ private fun TextContent( @Composable private fun IconContent( @DrawableRes resourceId: Int, - modifier: Modifier = Modifier ) = Icon( resourceId = resourceId, contentDescription = stringResource(id = R.string.screen_room_timeline_add_reaction), tint = ElementTheme.materialColors.secondary, - modifier = modifier + modifier = Modifier .size(ADD_EMOJI_SIZE) ) @Composable private fun ReactionContent( reaction: AggregatedReaction, - modifier: Modifier = Modifier, ) = Row( verticalAlignment = Alignment.CenterVertically, - modifier = modifier, ) { // Check if this is a custom reaction (MSC4027) if (reaction.key.startsWith("mxc://")) { AsyncImage( - modifier = modifier + modifier = Modifier .heightIn(min = REACTION_EMOJI_LINE_HEIGHT.toDp(), max = REACTION_EMOJI_LINE_HEIGHT.toDp()) .aspectRatio(REACTION_IMAGE_ASPECT_RATIO, false), model = MediaRequestData(MediaSource(reaction.key), MediaRequestData.Kind.Content), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt index e58f614234..c21d0deaa5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt @@ -26,6 +26,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding @@ -106,7 +107,6 @@ fun ReactionSummaryView( @Composable private fun SheetContent( summary: ReactionSummaryState.Summary, - modifier: Modifier = Modifier, ) { val animationScope = rememberCoroutineScope() var selectedReactionKey: String by rememberSaveable { mutableStateOf(summary.selectedKey) } @@ -127,9 +127,8 @@ private fun SheetContent( } Column( - modifier = modifier - .fillMaxWidth() - .fillMaxHeight() + modifier = Modifier + .fillMaxSize() ) { LazyRow( state = reactionListState, @@ -172,7 +171,6 @@ private fun AggregatedReactionButton( reaction: AggregatedReaction, isHighlighted: Boolean, onClick: () -> Unit, - modifier: Modifier = Modifier, ) { val buttonColor = if (isHighlighted) { ElementTheme.colors.bgActionPrimaryRest @@ -188,7 +186,7 @@ private fun AggregatedReactionButton( val roundedCornerShape = RoundedCornerShape(corner = CornerSize(percent = 50)) Surface( - modifier = modifier + modifier = Modifier .background(buttonColor, roundedCornerShape) .clip(roundedCornerShape) .clickable(onClick = onClick) @@ -238,10 +236,9 @@ private fun SenderRow( name: String, userId: String, sentTime: String, - modifier: Modifier = Modifier, ) { Row( - modifier = modifier + modifier = Modifier .fillMaxWidth() .heightIn(min = 56.dp) .padding(start = 16.dp, top = 4.dp, end = 16.dp, bottom = 4.dp), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt index 443451f565..72974b067c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt @@ -125,11 +125,10 @@ fun EventDebugInfoView( private fun CollapsibleSection( title: String, text: String, - modifier: Modifier = Modifier, initiallyExpanded: Boolean = false, ) { var isExpanded by remember { mutableStateOf(initiallyExpanded) } - Column(modifier = modifier.fillMaxWidth()) { + Column(modifier = Modifier.fillMaxWidth()) { Row( modifier = Modifier .clickable { isExpanded = !isExpanded } diff --git a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt index 67721cab56..844f23f6c8 100644 --- a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt +++ b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt @@ -37,7 +37,6 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight @Composable fun ConnectivityIndicatorView( isOnline: Boolean, - modifier: Modifier = Modifier, ) { val isIndicatorVisible = remember { MutableTransitionState(!isOnline) }.apply { targetState = !isOnline } val isStatusBarPaddingVisible = remember { MutableTransitionState(isOnline) }.apply { targetState = isOnline } @@ -48,7 +47,7 @@ fun ConnectivityIndicatorView( enter = fadeIn() + expandVertically(), exit = fadeOut() + shrinkVertically(), ) { - Indicator(modifier) + Indicator() } // Show missing status bar padding when the indicator is not visible @@ -57,7 +56,7 @@ fun ConnectivityIndicatorView( enter = fadeIn() + expandVertically(), exit = fadeOut() + shrinkVertically(), ) { - StatusBarPaddingSpacer(modifier) + StatusBarPaddingSpacer() } } diff --git a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt index 50dce445ac..a7073f8388 100644 --- a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt +++ b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt @@ -93,10 +93,9 @@ fun OnBoardingView( private fun OnBoardingContent( state: OnBoardingState, onOpenDeveloperSettings: () -> Unit, - modifier: Modifier = Modifier ) { Box( - modifier = modifier.fillMaxSize(), + modifier = Modifier.fillMaxSize(), ) { Box( modifier = Modifier.fillMaxSize(), @@ -158,9 +157,8 @@ private fun OnBoardingButtons( onSignIn: () -> Unit, onCreateAccount: () -> Unit, onReportProblem: () -> Unit, - modifier: Modifier = Modifier, ) { - ButtonColumnMolecule(modifier = modifier) { + ButtonColumnMolecule { val signInButtonStringRes = if (state.canLoginWithQrCode || state.canCreateAccount) { R.string.screen_onboarding_sign_in_manually } else { diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentView.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentView.kt index 7b19c5da7e..63a5db3e0d 100644 --- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentView.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentView.kt @@ -143,10 +143,8 @@ fun PollContentView( private fun PollTitle( title: String, isPollEnded: Boolean, - modifier: Modifier = Modifier ) { Row( - modifier = modifier, horizontalArrangement = Arrangement.spacedBy(12.dp), ) { if (isPollEnded) { @@ -173,10 +171,9 @@ private fun PollTitle( private fun PollAnswers( answerItems: ImmutableList, onAnswerSelected: (PollAnswer) -> Unit, - modifier: Modifier = Modifier, ) { Column( - modifier = modifier.selectableGroup(), + modifier = Modifier.selectableGroup(), verticalArrangement = Arrangement.spacedBy(16.dp), ) { answerItems.forEach { @@ -197,10 +194,9 @@ private fun PollAnswers( @Composable private fun ColumnScope.DisclosedPollBottomNotice( votesCount: Int, - modifier: Modifier = Modifier ) { Text( - modifier = modifier.align(Alignment.End), + modifier = Modifier.align(Alignment.End), style = ElementTheme.typography.fontBodyXsRegular, color = ElementTheme.colors.textSecondary, text = stringResource(CommonStrings.common_poll_total_votes, votesCount), @@ -208,11 +204,9 @@ private fun ColumnScope.DisclosedPollBottomNotice( } @Composable -private fun ColumnScope.UndisclosedPollBottomNotice( - modifier: Modifier = Modifier -) { +private fun ColumnScope.UndisclosedPollBottomNotice() { Text( - modifier = modifier + modifier = Modifier .align(Alignment.Start) .padding(start = 34.dp), style = ElementTheme.typography.fontBodyXsRegular, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt index ae20933871..5a40534b87 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt @@ -92,9 +92,8 @@ fun DeveloperSettingsView( @Composable private fun ElementCallCategory( state: DeveloperSettingsState, - modifier: Modifier = Modifier, ) { - PreferenceCategory(modifier = modifier, title = "Element Call", showDivider = true) { + PreferenceCategory(title = "Element Call", showDivider = true) { val callUrlState = state.customElementCallBaseUrlState fun isUsingDefaultUrl(value: String?): Boolean { return value.isNullOrEmpty() || value == callUrlState.defaultUrl @@ -120,14 +119,12 @@ private fun ElementCallCategory( @Composable private fun FeatureListContent( state: DeveloperSettingsState, - modifier: Modifier = Modifier ) { fun onFeatureEnabled(feature: FeatureUiModel, isEnabled: Boolean) { state.eventSink(DeveloperSettingsEvents.UpdateEnabledFeature(feature, isEnabled)) } FeatureListView( - modifier = modifier, features = state.features, onCheckedChange = ::onFeatureEnabled, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt index 71ff1c3d58..628b5b0d2d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt @@ -141,14 +141,12 @@ fun ConfigureTracingView( @Composable private fun CrateListContent( state: ConfigureTracingState, - modifier: Modifier = Modifier ) { fun onLogLevelChange(target: Target, logLevel: LogLevel) { state.eventSink(ConfigureTracingEvents.UpdateFilter(target, logLevel)) } TargetAndLogLevelListView( - modifier = modifier, data = state.targetsToLogLevel, onLogLevelChange = ::onLogLevelChange, ) @@ -158,11 +156,8 @@ private fun CrateListContent( private fun TargetAndLogLevelListView( data: ImmutableMap, onLogLevelChange: (Target, LogLevel) -> Unit, - modifier: Modifier = Modifier, ) { - Column( - modifier = modifier, - ) { + Column { data.forEach { item -> fun onLogLevelChange(logLevel: LogLevel) { onLogLevelChange(item.key, logLevel) @@ -182,10 +177,8 @@ private fun TargetAndLogLevelView( target: Target, logLevel: LogLevel, onLogLevelChange: (LogLevel) -> Unit, - modifier: Modifier = Modifier ) { ListItem( - modifier = modifier, headlineContent = { Text(text = target.filter.takeIf { it.isNotEmpty() } ?: "(common)") }, trailingContent = ListItemContent.Custom { LogLevelDropdownMenu( @@ -200,10 +193,9 @@ private fun TargetAndLogLevelView( private fun LogLevelDropdownMenu( logLevel: LogLevel, onLogLevelChange: (LogLevel) -> Unit, - modifier: Modifier = Modifier, ) { var expanded by remember { mutableStateOf(false) } - Box(modifier = modifier) { + Box { DropdownMenuItem( modifier = Modifier.widthIn(max = 120.dp), text = { Text(text = logLevel.filter) }, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt index 2226049efe..6dacfd49f7 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt @@ -99,7 +99,6 @@ private fun NotificationSettingsContentView( // TODO We are removing the call notification toggle until support for call notifications has been added // onCallsNotificationsChanged: (Boolean) -> Unit, onInviteForMeNotificationsChanged: (Boolean) -> Unit, - modifier: Modifier = Modifier, ) { val context = LocalContext.current if (systemSettings.appNotificationsEnabled && !systemSettings.systemNotificationsEnabled) { @@ -117,7 +116,6 @@ private fun NotificationSettingsContentView( } PreferenceSwitch( - modifier = modifier, title = stringResource(id = R.string.screen_notification_settings_enable_notifications), isChecked = systemSettings.appNotificationsEnabled, switchAlignment = Alignment.Top, @@ -182,10 +180,8 @@ private fun InvalidNotificationSettingsView( showError: Boolean, onContinueClicked: () -> Unit, onDismissError: () -> Unit, - modifier: Modifier = Modifier ) { DialogLikeBannerMolecule( - modifier = modifier, title = stringResource(R.string.screen_notification_settings_configuration_mismatch), content = stringResource(R.string.screen_notification_settings_configuration_mismatch_description), onSubmitClicked = onContinueClicked, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index bbdccbc302..a1ec13bb20 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -204,12 +204,10 @@ private fun RoomDetailsTopBar( goBack: () -> Unit, onActionClicked: (RoomDetailsAction) -> Unit, showEdit: Boolean, - modifier: Modifier = Modifier, ) { var showMenu by remember { mutableStateOf(false) } TopAppBar( - modifier = modifier, title = { }, navigationIcon = { BackButton(onClick = goBack) }, actions = { @@ -237,8 +235,14 @@ private fun RoomDetailsTopBar( } @Composable -private fun MainActionsSection(state: RoomDetailsState, onShareRoom: () -> Unit, modifier: Modifier = Modifier) { - Row(modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { +private fun MainActionsSection( + state: RoomDetailsState, + onShareRoom: () -> Unit, +) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + ) { val roomNotificationSettings = state.roomNotificationSettings if (state.canShowNotificationSettings && roomNotificationSettings != null) { if (roomNotificationSettings.mode == RoomNotificationMode.MUTE) { @@ -275,10 +279,9 @@ private fun RoomHeaderSection( roomName: String, roomAlias: String?, openAvatarPreview: (url: String) -> Unit, - modifier: Modifier = Modifier ) { Column( - modifier = modifier + modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp), horizontalAlignment = Alignment.CenterHorizontally, @@ -312,9 +315,8 @@ private fun RoomHeaderSection( private fun TopicSection( roomTopic: RoomTopicState, onActionClicked: (RoomDetailsAction) -> Unit, - modifier: Modifier = Modifier ) { - PreferenceCategory(title = stringResource(CommonStrings.common_topic), modifier = modifier) { + PreferenceCategory(title = stringResource(CommonStrings.common_topic)) { if (roomTopic is RoomTopicState.CanAddTopic) { PreferenceText( title = stringResource(R.string.screen_room_details_add_topic_title), @@ -338,14 +340,13 @@ private fun TopicSection( private fun NotificationSection( isDefaultMode: Boolean, openRoomNotificationSettings: () -> Unit, - modifier: Modifier = Modifier ) { val subtitle = if (isDefaultMode) { stringResource(R.string.screen_room_details_notification_mode_default) } else { stringResource(R.string.screen_room_details_notification_mode_custom) } - PreferenceCategory(modifier = modifier) { + PreferenceCategory { ListItem( headlineContent = { Text(text = stringResource(R.string.screen_room_details_notification_title)) }, supportingContent = { Text(text = subtitle) }, @@ -359,9 +360,8 @@ private fun NotificationSection( private fun MembersSection( memberCount: Long, openRoomMemberList: () -> Unit, - modifier: Modifier = Modifier, ) { - PreferenceCategory(modifier = modifier) { + PreferenceCategory { ListItem( headlineContent = { Text(stringResource(CommonStrings.common_people)) }, leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_user)), @@ -374,9 +374,8 @@ private fun MembersSection( @Composable private fun InviteSection( invitePeople: () -> Unit, - modifier: Modifier = Modifier, ) { - PreferenceCategory(modifier = modifier) { + PreferenceCategory { ListItem( headlineContent = { Text(stringResource(R.string.screen_room_details_invite_people_title)) }, leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_user_add)), @@ -388,9 +387,8 @@ private fun InviteSection( @Composable private fun PollsSection( openPollHistory: () -> Unit, - modifier: Modifier = Modifier, ) { - PreferenceCategory(modifier = modifier) { + PreferenceCategory { ListItem( headlineContent = { Text(stringResource(R.string.screen_polls_history_title)) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Polls)), @@ -400,8 +398,8 @@ private fun PollsSection( } @Composable -private fun SecuritySection(modifier: Modifier = Modifier) { - PreferenceCategory(title = stringResource(R.string.screen_room_details_security_title), modifier = modifier) { +private fun SecuritySection() { + PreferenceCategory(title = stringResource(R.string.screen_room_details_security_title)) { ListItem( headlineContent = { Text(stringResource(R.string.screen_room_details_encryption_enabled_title)) }, supportingContent = { Text(stringResource(R.string.screen_room_details_encryption_enabled_subtitle)) }, @@ -411,15 +409,17 @@ private fun SecuritySection(modifier: Modifier = Modifier) { } @Composable -private fun OtherActionsSection(isDm: Boolean, onLeaveRoom: () -> Unit, modifier: Modifier = Modifier) { - PreferenceCategory(showDivider = false, modifier = modifier) { +private fun OtherActionsSection(isDm: Boolean, onLeaveRoom: () -> Unit) { + PreferenceCategory(showDivider = false) { ListItem( headlineContent = { - val leaveText = stringResource(id = if (isDm) { - R.string.screen_room_details_leave_conversation_title - } else { - R.string.screen_room_details_leave_room_title - }) + val leaveText = stringResource( + id = if (isDm) { + R.string.screen_room_details_leave_conversation_title + } else { + R.string.screen_room_details_leave_room_title + } + ) Text(leaveText) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Leave)), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt index 7ecf4ef1f9..6c397b9bc2 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt @@ -39,8 +39,8 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.ui.strings.CommonStrings @Composable -internal fun BlockUserSection(state: RoomMemberDetailsState, modifier: Modifier = Modifier) { - PreferenceCategory(showDivider = false, modifier = modifier) { +internal fun BlockUserSection(state: RoomMemberDetailsState) { + PreferenceCategory(showDivider = false) { when (state.isBlocked) { is AsyncData.Failure -> PreferenceBlockUser(isBlocked = state.isBlocked.prevData, isLoading = false, eventSink = state.eventSink) is AsyncData.Loading -> PreferenceBlockUser(isBlocked = state.isBlocked.prevData, isLoading = true, eventSink = state.eventSink) @@ -70,7 +70,6 @@ private fun PreferenceBlockUser( isBlocked: Boolean?, isLoading: Boolean, eventSink: (RoomMemberDetailsEvents) -> Unit, - modifier: Modifier = Modifier, ) { val loadingCurrentValue = @Composable { CircularProgressIndicator( @@ -87,7 +86,6 @@ private fun PreferenceBlockUser( onClick = { if (!isLoading) eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = true)) }, trailingContent = if (isLoading) ListItemContent.Custom(loadingCurrentValue) else null, style = ListItemStyle.Primary, - modifier = modifier, ) } else { ListItem( @@ -96,7 +94,6 @@ private fun PreferenceBlockUser( style = ListItemStyle.Destructive, onClick = { if (!isLoading) eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = true)) }, trailingContent = if (isLoading) ListItemContent.Custom(loadingCurrentValue) else null, - modifier = modifier, ) } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt index cac6e23878..bdaa2763bf 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt @@ -193,10 +193,8 @@ fun RoomDetailsEditView( private fun LabelledReadOnlyField( title: String, value: String, - modifier: Modifier = Modifier ) { Column( - modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp), ) { Text( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt index d51e7c0a47..bc94519b3e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt @@ -115,10 +115,8 @@ private fun RoomInviteMembersTopBar( canSend: Boolean, onBackPressed: () -> Unit, onSubmitPressed: () -> Unit, - modifier: Modifier = Modifier, ) { TopAppBar( - modifier = modifier, title = { Text( text = stringResource(R.string.screen_room_details_invite_people_title), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt index 0fe08ef02c..37bfcf870e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt @@ -194,10 +194,8 @@ private fun RoomMemberListTopBar( canInvite: Boolean, onBackPressed: () -> Unit, onInvitePressed: () -> Unit, - modifier: Modifier = Modifier, ) { TopAppBar( - modifier = modifier, title = { Text( text = stringResource(CommonStrings.common_people), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt index 8463c98ee0..56475f92c2 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt @@ -108,14 +108,12 @@ fun RoomMemberDetailsView( @Composable private fun StartDMSection( onStartDMClicked: () -> Unit, - modifier: Modifier = Modifier ) { ListItem( headlineContent = { Text(stringResource(CommonStrings.common_direct_chat)) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Chat)), style = ListItemStyle.Primary, onClick = onStartDMClicked, - modifier = modifier, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt index f534fb17f4..75dabb9772 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt @@ -178,10 +178,8 @@ private fun RoomSpecificNotificationSettingsView( @Composable private fun RoomNotificationSettingsTopBar( onBackPressed: () -> Unit, - modifier: Modifier = Modifier, ) { TopAppBar( - modifier = modifier, title = { Text( text = stringResource(R.string.screen_room_details_notification_title), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt index df1a41ea87..99903b7466 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt @@ -103,10 +103,8 @@ fun UserDefinedRoomNotificationSettingsView( private fun UserDefinedRoomNotificationSettingsTopBar( roomName: String, onBackPressed: () -> Unit, - modifier: Modifier = Modifier, ) { TopAppBar( - modifier = modifier, title = { Text( text = roomName, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchResultView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchResultView.kt index e05f0a3ac5..9dd70fb0ef 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchResultView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchResultView.kt @@ -103,7 +103,6 @@ private fun RoomListSearchResultContent( state: RoomListState, onRoomClicked: (RoomId) -> Unit, onRoomLongClicked: (RoomListRoomSummary) -> Unit, - modifier: Modifier = Modifier, ) { val borderColor = MaterialTheme.colorScheme.tertiary val strokeWidth = 1.dp @@ -115,7 +114,6 @@ private fun RoomListSearchResultContent( onRoomClicked(room.roomId) } Scaffold( - modifier = modifier, topBar = { TopAppBar( modifier = Modifier.drawBehind { diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt index 9f62546c1a..a703baa180 100644 --- a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt +++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt @@ -78,11 +78,9 @@ private fun SignedOutHeader(state: SignedOutState) { } @Composable -private fun SignedOutContent( - modifier: Modifier = Modifier, -) { +private fun SignedOutContent() { Box( - modifier = modifier.fillMaxSize(), + modifier = Modifier.fillMaxSize(), contentAlignment = BiasAlignment( horizontalBias = 0f, verticalBias = -0.4f @@ -112,12 +110,9 @@ private fun SignedOutContent( @Composable private fun SignedOutFooter( - modifier: Modifier = Modifier, onSignInAgain: () -> Unit, ) { - ButtonColumnMolecule( - modifier = modifier, - ) { + ButtonColumnMolecule { Button( text = stringResource(id = CommonStrings.action_sign_in_again), onClick = onSignInAgain, diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt index 02f5514e9f..e71df4c6d1 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt @@ -94,7 +94,7 @@ fun VerifySelfSessionView( } @Composable -private fun HeaderContent(verificationFlowStep: FlowStep, modifier: Modifier = Modifier) { +private fun HeaderContent(verificationFlowStep: FlowStep) { val iconResourceId = when (verificationFlowStep) { FlowStep.Initial -> R.drawable.ic_verification_devices FlowStep.Canceled -> R.drawable.ic_verification_warning @@ -125,7 +125,7 @@ private fun HeaderContent(verificationFlowStep: FlowStep, modifier: Modifier = M } IconTitleSubtitleMolecule( - modifier = modifier.padding(top = 60.dp), + modifier = Modifier.padding(top = 60.dp), iconResourceId = iconResourceId, title = stringResource(id = titleTextId), subTitle = stringResource(id = subtitleTextId) @@ -133,8 +133,8 @@ private fun HeaderContent(verificationFlowStep: FlowStep, modifier: Modifier = M } @Composable -private fun Content(flowState: FlowStep, modifier: Modifier = Modifier) { - Column(modifier.fillMaxHeight(), verticalArrangement = Arrangement.Center) { +private fun Content(flowState: FlowStep) { + Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.Center) { when (flowState) { FlowStep.Initial, FlowStep.Ready, FlowStep.Canceled, FlowStep.Completed -> Unit FlowStep.AwaitingOtherDeviceResponse -> ContentWaiting() @@ -144,19 +144,19 @@ private fun Content(flowState: FlowStep, modifier: Modifier = Modifier) { } @Composable -private fun ContentWaiting(modifier: Modifier = Modifier) { - Row(modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { +private fun ContentWaiting() { + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { CircularProgressIndicator() } } @Composable -private fun ContentVerifying(verificationFlowStep: FlowStep.Verifying, modifier: Modifier = Modifier) { +private fun ContentVerifying(verificationFlowStep: FlowStep.Verifying) { when (verificationFlowStep.data) { is SessionVerificationData.Decimals -> { val text = verificationFlowStep.data.decimals.joinToString(separator = " - ") { it.toString() } Text( - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), text = text, style = ElementTheme.typography.fontHeadingLgBold, color = MaterialTheme.colorScheme.primary, @@ -167,7 +167,7 @@ private fun ContentVerifying(verificationFlowStep: FlowStep.Verifying, modifier: // We want each row to have up to 4 emojis val rows = verificationFlowStep.data.emojis.chunked(4) Column( - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(40.dp), ) { rows.forEach { emojis -> diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt index 2bc4290c67..c4a56404cb 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt @@ -115,10 +115,8 @@ fun SunsetPage( @OptIn(CoreColorToken::class) @Composable -private fun SunsetBackground( - modifier: Modifier = Modifier, -) { - Column(modifier = modifier.fillMaxSize()) { +private fun SunsetBackground() { + Column(modifier = Modifier.fillMaxSize()) { // The top background colors are the opposite of the current theme ones val topBackgroundColor = if (ElementTheme.isLightTheme) { DarkColorTokens.colorThemeBg diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BlurHashAsyncImage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BlurHashAsyncImage.kt index dcfce743c1..23e1882382 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BlurHashAsyncImage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BlurHashAsyncImage.kt @@ -73,7 +73,6 @@ fun BlurHashAsyncImage( @Composable private fun BlurHashImage( blurHash: String?, - modifier: Modifier = Modifier, contentDescription: String? = null, contentScale: ContentScale = ContentScale.Fit, ) { @@ -91,7 +90,7 @@ private fun BlurHashImage( } bitmapState.value?.let { bitmap -> Image( - modifier = modifier.fillMaxSize(), + modifier = Modifier.fillMaxSize(), bitmap = bitmap.asImageBitmap(), contentScale = contentScale, contentDescription = contentDescription diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt index aed618cd3b..9fccecaa6e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt @@ -65,7 +65,6 @@ private fun ConfirmationDialogContent( cancelText: String, onSubmitClicked: () -> Unit, onCancelClicked: () -> Unit, - modifier: Modifier = Modifier, title: String? = null, thirdButtonText: String? = null, onThirdButtonClicked: () -> Unit = {}, @@ -73,7 +72,6 @@ private fun ConfirmationDialogContent( icon: @Composable (() -> Unit)? = null, ) { SimpleAlertDialogContent( - modifier = modifier, title = title, content = content, submitText = submitText, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt index 52779052a6..a4a4a6e8b9 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt @@ -51,12 +51,10 @@ fun ErrorDialog( private fun ErrorDialogContent( content: String, onSubmitClicked: () -> Unit, - modifier: Modifier = Modifier, title: String = ErrorDialogDefaults.title, submitText: String = ErrorDialogDefaults.submitText, ) { SimpleAlertDialogContent( - modifier = modifier, title = title, content = content, submitText = submitText, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ListDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ListDialog.kt index 83079584c3..71cf3e78ee 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ListDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ListDialog.kt @@ -80,7 +80,6 @@ private fun ListDialogContent( onSubmitClicked: () -> Unit, cancelText: String, submitText: String, - modifier: Modifier = Modifier, title: String? = null, enabled: Boolean = true, subtitle: @Composable (() -> Unit)? = null, @@ -88,7 +87,6 @@ private fun ListDialogContent( SimpleAlertDialogContent( title = title, subtitle = subtitle, - modifier = modifier, cancelText = cancelText, submitText = submitText, onCancelClicked = onDismissRequest, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/MultipleSelectionDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/MultipleSelectionDialog.kt index e790cb1921..7bea51952a 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/MultipleSelectionDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/MultipleSelectionDialog.kt @@ -84,7 +84,6 @@ private fun MultipleSelectionDialogContent( onConfirmClicked: (List) -> Unit, dismissButtonTitle: String, onDismissRequest: () -> Unit, - modifier: Modifier = Modifier, title: String? = null, initialSelected: ImmutableList = persistentListOf(), subtitle: @Composable (() -> Unit)? = null, @@ -96,7 +95,6 @@ private fun MultipleSelectionDialogContent( SimpleAlertDialogContent( title = title, subtitle = subtitle, - modifier = modifier, submitText = confirmButtonTitle, onSubmitClicked = { onConfirmClicked(selectedOptionIndexes.toList()) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt index 78897efb50..161b552ce4 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt @@ -56,13 +56,11 @@ private fun RetryDialogContent( content: String, onRetry: () -> Unit, onDismiss: () -> Unit, - modifier: Modifier = Modifier, title: String = RetryDialogDefaults.title, retryText: String = RetryDialogDefaults.retryText, dismissText: String = RetryDialogDefaults.dismissText, ) { SimpleAlertDialogContent( - modifier = modifier, title = title, content = content, submitText = retryText, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SingleSelectionDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SingleSelectionDialog.kt index 2462fd779f..99ed32b674 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SingleSelectionDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SingleSelectionDialog.kt @@ -79,7 +79,6 @@ private fun SingleSelectionDialogContent( onOptionSelected: (Int) -> Unit, dismissButtonTitle: String, onDismissRequest: () -> Unit, - modifier: Modifier = Modifier, title: String? = null, initialSelection: Int? = null, subtitle: @Composable (() -> Unit)? = null, @@ -87,7 +86,6 @@ private fun SingleSelectionDialogContent( SimpleAlertDialogContent( title = title, subtitle = subtitle, - modifier = modifier, submitText = dismissButtonTitle, onSubmitClicked = onDismissRequest, applyPaddingToContents = false, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCategory.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCategory.kt index 42dc343782..45764c588b 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCategory.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCategory.kt @@ -52,9 +52,9 @@ fun PreferenceCategory( } @Composable -private fun PreferenceCategoryTitle(title: String, modifier: Modifier = Modifier) { +private fun PreferenceCategoryTitle(title: String) { Text( - modifier = modifier.padding( + modifier = Modifier.padding( top = 20.dp, bottom = 8.dp, start = preferencePaddingHorizontal, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferencePage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferencePage.kt index 063c17aac1..1d6a5669b9 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferencePage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferencePage.kt @@ -80,10 +80,8 @@ fun PreferencePage( private fun PreferenceTopAppBar( title: String, onBackPressed: () -> Unit, - modifier: Modifier = Modifier, ) { TopAppBar( - modifier = modifier, navigationIcon = { BackButton(onClick = onBackPressed) }, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceTextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceTextField.kt index 0c3735547b..f616c20a72 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceTextField.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceTextField.kt @@ -91,7 +91,6 @@ private fun TextFieldDialog( onDismissRequest: () -> Unit, value: String?, placeholder: String?, - modifier: Modifier = Modifier, validation: (String?) -> Boolean = { true }, onValidationErrorMessage: String? = null, autoSelectOnDisplay: Boolean = true, @@ -110,7 +109,6 @@ private fun TextFieldDialog( onSubmit = { onSubmit(textFieldContents.text) }, onDismissRequest = onDismissRequest, enabled = canSubmit, - modifier = modifier, ) { item { TextFieldListItem( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt index 8bd7d2b16e..e9952ee1e0 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt @@ -98,11 +98,9 @@ private fun IconsPreview( title: String, iconsList: ImmutableList, iconNameTransform: (String) -> String, - modifier: Modifier = Modifier, ) = ElementPreview { val context = LocalContext.current Column( - modifier = modifier, verticalArrangement = Arrangement.spacedBy(2.dp), ) { Text( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt index 8ea42eef88..a52c023fa6 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt @@ -55,7 +55,6 @@ internal fun SimpleAlertDialogContent( content: String, submitText: String, onSubmitClicked: () -> Unit, - modifier: Modifier = Modifier, title: String? = null, subtitle: @Composable (() -> Unit)? = null, destructiveSubmit: Boolean = false, @@ -67,7 +66,6 @@ internal fun SimpleAlertDialogContent( icon: @Composable (() -> Unit)? = null, ) { SimpleAlertDialogContent( - modifier = modifier, icon = icon, title = title, subtitle = subtitle, @@ -92,7 +90,6 @@ internal fun SimpleAlertDialogContent( internal fun SimpleAlertDialogContent( submitText: String, onSubmitClicked: () -> Unit, - modifier: Modifier = Modifier, title: String? = null, subtitle: @Composable (() -> Unit)? = null, destructiveSubmit: Boolean = false, @@ -148,7 +145,6 @@ internal fun SimpleAlertDialogContent( } } }, - modifier = modifier, title = title?.let { titleText -> @Composable { Text( @@ -192,11 +188,9 @@ internal fun AlertDialogContent( iconContentColor: Color, titleContentColor: Color, textContentColor: Color, - modifier: Modifier = Modifier, applyPaddingToContents: Boolean = true, ) { Surface( - modifier = modifier, shape = shape, color = containerColor, tonalElevation = tonalElevation, diff --git a/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/FeatureListView.kt b/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/FeatureListView.kt index a0e298db26..20648e6142 100644 --- a/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/FeatureListView.kt +++ b/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/FeatureListView.kt @@ -49,13 +49,11 @@ fun FeatureListView( private fun FeaturePreferenceView( feature: FeatureUiModel, onCheckedChange: (Boolean) -> Unit, - modifier: Modifier = Modifier ) { PreferenceCheckbox( title = feature.title, supportingText = feature.description, isChecked = feature.isEnabled, - modifier = modifier, onCheckedChange = onCheckedChange ) } diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewer.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewer.kt index 0f00e3faa6..a4b523d45d 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewer.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewer.kt @@ -86,7 +86,6 @@ private fun PdfPagesView( @Composable private fun PdfPageView( pdfPage: PdfPage, - modifier: Modifier = Modifier, ) { val pdfPageState by pdfPage.stateFlow.collectAsState() DisposableEffect(pdfPage) { @@ -101,12 +100,12 @@ private fun PdfPageView( bitmap = state.bitmap.asImageBitmap(), contentDescription = stringResource(id = CommonStrings.a11y_page_n, pdfPage.pageIndex), contentScale = ContentScale.FillWidth, - modifier = modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth() ) } is PdfPage.State.Loading -> { Box( - modifier = modifier + modifier = Modifier .fillMaxWidth() .height(state.height.toDp()) .background(color = Color.White) diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt index 2d5412cc25..2e4497e5bf 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt @@ -259,10 +259,8 @@ private fun ErrorView( errorMessage: String, onRetry: () -> Unit, onDismiss: () -> Unit, - modifier: Modifier = Modifier, ) { RetryDialog( - modifier = modifier, content = errorMessage, onRetry = onRetry, onDismiss = onDismiss diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt index d7453ec264..9851a8c7c2 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt @@ -209,10 +209,9 @@ private fun RoomSummaryView( summary: RoomSummaryDetails, isSelected: Boolean, onSelection: (RoomSummaryDetails) -> Unit, - modifier: Modifier = Modifier ) { Row( - modifier = modifier + modifier = Modifier .clickable { onSelection(summary) } .fillMaxWidth() .padding(start = 16.dp, end = 4.dp) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 13359c1c0c..bd8cdd5215 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -405,14 +405,13 @@ private fun TextInput( onError: (Throwable) -> Unit, onTyping: (Boolean) -> Unit, onRichContentSelected: ((Uri) -> Unit)?, - modifier: Modifier = Modifier, ) { val bgColor = ElementTheme.colors.bgSubtleSecondary val borderColor = ElementTheme.colors.borderDisabled val roundedCorners = textInputRoundedCornerShape(composerMode = composerMode) Column( - modifier = modifier + modifier = Modifier .clip(roundedCorners) .border(0.5.dp, borderColor, roundedCorners) .background(color = bgColor) @@ -464,15 +463,14 @@ private fun TextInput( private fun ComposerModeView( composerMode: MessageComposerMode, onResetComposerMode: () -> Unit, - modifier: Modifier = Modifier, ) { when (composerMode) { is MessageComposerMode.Edit -> { - EditingModeView(onResetComposerMode = onResetComposerMode, modifier = modifier) + EditingModeView(onResetComposerMode = onResetComposerMode) } is MessageComposerMode.Reply -> { ReplyToModeView( - modifier = modifier.padding(8.dp), + modifier = Modifier.padding(8.dp), senderName = composerMode.senderName, text = composerMode.defaultContent, attachmentThumbnailInfo = composerMode.attachmentThumbnailInfo, @@ -486,12 +484,11 @@ private fun ComposerModeView( @Composable private fun EditingModeView( onResetComposerMode: () -> Unit, - modifier: Modifier = Modifier, ) { Row( horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.CenterVertically, - modifier = modifier + modifier = Modifier .fillMaxWidth() .padding(start = 12.dp) ) { @@ -881,11 +878,8 @@ internal fun TextComposerVoicePreview() = ElementPreview { @Composable private fun PreviewColumn( items: ImmutableList<@Composable () -> Unit>, - modifier: Modifier = Modifier, ) { - Column( - modifier = modifier - ) { + Column { items.forEach { item -> Box( modifier = Modifier.height(IntrinsicSize.Min) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/TextFormatting.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/TextFormatting.kt index 81fd1ca81c..4ed978d7f0 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/TextFormatting.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/TextFormatting.kt @@ -43,6 +43,7 @@ import io.element.android.wysiwyg.view.models.LinkAction import kotlinx.coroutines.launch import uniffi.wysiwyg_composer.ActionState import uniffi.wysiwyg_composer.ComposerAction + @Composable internal fun TextFormatting( state: RichTextEditorState, diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt index db6a489321..e1ca066402 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt @@ -124,11 +124,10 @@ private fun PlayerButton( type: PlayerButtonType, enabled: Boolean, onClick: () -> Unit, - modifier: Modifier = Modifier, ) { IconButton( onClick = onClick, - modifier = modifier + modifier = Modifier .background(color = ElementTheme.colors.bgCanvasDefault, shape = CircleShape) .size(30.dp), enabled = enabled, diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt index f98fbd5b9e..353b7bf70f 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt @@ -88,9 +88,7 @@ internal fun VoiceMessageRecording( } @Composable -private fun RedRecordingDot( - modifier: Modifier = Modifier, -) { +private fun RedRecordingDot() { val infiniteTransition = rememberInfiniteTransition("RedRecordingDot") val alpha by infiniteTransition.animateFloat( initialValue = 1f, @@ -102,7 +100,7 @@ private fun RedRecordingDot( label = "RedRecordingDotAlpha", ) Box( - modifier = modifier + modifier = Modifier .size(8.dp) .alpha(alpha) .background(color = ElementTheme.colors.textCriticalPrimary, shape = CircleShape) From a55d3d0acec54c94ed0708c95bc77c4eac67552e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 8 Feb 2024 10:10:21 +0100 Subject: [PATCH 079/119] Move `clearFocusOnTap` to the designsystem library to reduce code duplication. --- .../impl/configureroom/ConfigureRoomView.kt | 11 +------ .../user/editprofile/EditUserProfileView.kt | 11 +------ .../impl/edit/RoomDetailsEditView.kt | 11 +------ .../designsystem/modifiers/ClearFocusOnTap.kt | 29 +++++++++++++++++++ 4 files changed, 32 insertions(+), 30 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/ClearFocusOnTap.kt diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index 04ebc05c3a..2f1c55adc7 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -18,7 +18,6 @@ package io.element.android.features.createroom.impl.configureroom import android.net.Uri import androidx.compose.foundation.clickable -import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -38,8 +37,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusManager -import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardCapitalization @@ -52,6 +49,7 @@ import io.element.android.libraries.designsystem.components.LabelledTextField import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.modifiers.clearFocusOnTap import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.aliasScreenTitle @@ -257,13 +255,6 @@ private fun RoomPrivacyOptions( } } -private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier = - pointerInput(Unit) { - detectTapGestures(onTap = { - focusManager.clearFocus() - }) - } - @PreviewsDayNight @Composable internal fun ConfigureRoomViewPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = ElementPreview { diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt index 92294f27de..a0d0fcfad6 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt @@ -16,7 +16,6 @@ package io.element.android.features.preferences.impl.user.editprofile -import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -34,8 +33,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusManager -import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -48,6 +45,7 @@ import io.element.android.libraries.designsystem.components.async.AsyncActionVie import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.modifiers.clearFocusOnTap import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.aliasScreenTitle @@ -166,13 +164,6 @@ fun EditUserProfileView( ) } -private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier = - pointerInput(Unit) { - detectTapGestures(onTap = { - focusManager.clearFocus() - }) - } - @PreviewsDayNight @Composable internal fun EditUserProfileViewPreview(@PreviewParameter(EditUserProfileStateProvider::class) state: EditUserProfileState) = diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt index bdaa2763bf..b93bbd1f47 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt @@ -18,7 +18,6 @@ package io.element.android.features.roomdetails.impl.edit -import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -38,8 +37,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusManager -import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardCapitalization @@ -52,6 +49,7 @@ import io.element.android.libraries.designsystem.components.async.AsyncActionVie import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.modifiers.clearFocusOnTap import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.aliasScreenTitle @@ -213,13 +211,6 @@ private fun LabelledReadOnlyField( } } -private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier = - pointerInput(Unit) { - detectTapGestures(onTap = { - focusManager.clearFocus() - }) - } - @PreviewsDayNight @Composable internal fun RoomDetailsEditViewPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) = ElementPreview { diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/ClearFocusOnTap.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/ClearFocusOnTap.kt new file mode 100644 index 0000000000..18f3e413ba --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/ClearFocusOnTap.kt @@ -0,0 +1,29 @@ +/* + * 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 + * + * http://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.libraries.designsystem.modifiers + +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusManager +import androidx.compose.ui.input.pointer.pointerInput + +fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier = + pointerInput(Unit) { + detectTapGestures(onTap = { + focusManager.clearFocus() + }) + } From cebc06aa5e94438d48d773a1719f78f057284263 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 8 Feb 2024 10:11:37 +0100 Subject: [PATCH 080/119] Fix potential error: Modifier factory functions must use the receiver Modifier instance --- .../libraries/designsystem/modifiers/ClearFocusOnTap.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/ClearFocusOnTap.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/ClearFocusOnTap.kt index 18f3e413ba..15f925b993 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/ClearFocusOnTap.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/ClearFocusOnTap.kt @@ -21,9 +21,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.input.pointer.pointerInput -fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier = +fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier = then( pointerInput(Unit) { detectTapGestures(onTap = { focusManager.clearFocus() }) } +) From 9d461a49170d8bb2c1f674c1ea9652290855fe92 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 12:21:45 +0100 Subject: [PATCH 081/119] Add ability mark as unread / mark as read a room. --- .../messages/impl/MessagesPresenter.kt | 8 +++ .../messages/impl/MessagesPresenterTest.kt | 17 +++++ features/roomlist/impl/build.gradle.kts | 2 + .../roomlist/impl/RoomListContextMenu.kt | 58 +++++++++++++++-- .../features/roomlist/impl/RoomListEvents.kt | 2 + .../roomlist/impl/RoomListPresenter.kt | 18 ++++++ .../features/roomlist/impl/RoomListState.kt | 1 + .../roomlist/impl/RoomListStateProvider.kt | 1 + .../datasource/RoomListRoomSummaryFactory.kt | 2 + .../impl/model/RoomListRoomSummary.kt | 9 ++- .../impl/model/RoomListRoomSummaryProvider.kt | 2 + .../roomlist/impl/RoomListPresenterTests.kt | 62 +++++++++++++++++-- .../libraries/matrix/api/room/MatrixRoom.kt | 12 ++++ .../matrix/api/roomlist/RoomSummary.kt | 1 + .../matrix/impl/room/RustMatrixRoom.kt | 18 ++++++ .../roomlist/RoomSummaryDetailsFactory.kt | 1 + .../matrix/test/room/FakeMatrixRoom.kt | 15 +++++ .../matrix/test/room/RoomSummaryFixture.kt | 2 + .../matrix/ui/components/SelectedRoom.kt | 2 + samples/minimal/build.gradle.kts | 1 + .../android/samples/minimal/RoomListScreen.kt | 8 ++- 21 files changed, 229 insertions(+), 13 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index 5f21e5b348..1b264ac1f7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -155,6 +155,14 @@ class MessagesPresenter @AssistedInject constructor( mutableStateOf(false) } + LaunchedEffect(Unit) { + // Mark the room as read on entering but don't send read receipts + // as those will be handled by the timeline. + withContext(dispatchers.io) { + room.markAsRead(null) + } + } + LaunchedEffect(syncUpdateFlow.value) { withContext(dispatchers.io) { canJoinCall = room.canUserJoinCall(room.sessionId).getOrDefault(false) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 9dd9ff5d88..fe7e07e6f9 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -96,7 +96,9 @@ import io.element.android.tests.testutils.consumeItemsUntilTimeout import io.element.android.tests.testutils.testCoroutineDispatchers import io.mockk.mockk import kotlinx.collections.immutable.persistentListOf +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -129,6 +131,21 @@ class MessagesPresenterTest { } } + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `present - check that the room is marked as read`() = runTest { + val room = FakeMatrixRoom() + assertThat(room.markAsReadCalls).isEmpty() + val presenter = createMessagesPresenter(matrixRoom = room) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + runCurrent() + assertThat(room.markAsReadCalls).isEqualTo(listOf(null)) + cancelAndIgnoreRemainingEvents() + } + } + @Test fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest { val room = FakeMatrixRoom().apply { diff --git a/features/roomlist/impl/build.gradle.kts b/features/roomlist/impl/build.gradle.kts index 4e415fd2a2..02fbcc058a 100644 --- a/features/roomlist/impl/build.gradle.kts +++ b/features/roomlist/impl/build.gradle.kts @@ -52,6 +52,7 @@ dependencies { implementation(projects.libraries.eventformatter.api) implementation(projects.libraries.indicator.api) implementation(projects.libraries.deeplink) + implementation(projects.libraries.preferences.api) implementation(projects.features.invitelist.api) implementation(projects.features.networkmonitor.api) implementation(projects.features.leaveroom.api) @@ -71,6 +72,7 @@ dependencies { testImplementation(projects.libraries.eventformatter.test) testImplementation(projects.libraries.indicator.impl) testImplementation(projects.libraries.permissions.noop) + testImplementation(projects.libraries.preferences.test) testImplementation(projects.features.invitelist.test) testImplementation(projects.features.networkmonitor.test) testImplementation(projects.tests.testutils) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt index 1d2cf2c910..f605b71a10 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt @@ -49,6 +49,14 @@ fun RoomListContextMenu( ) { RoomListModalBottomSheetContent( contextMenu = contextMenu, + onRoomMarkReadClicked = { + eventSink(RoomListEvents.HideContextMenu) + eventSink(RoomListEvents.MarkAsRead(it)) + }, + onRoomMarkUnreadClicked = { + eventSink(RoomListEvents.HideContextMenu) + eventSink(RoomListEvents.MarkAsUnread(it)) + }, onRoomSettingsClicked = { eventSink(RoomListEvents.HideContextMenu) onRoomSettingsClicked(it) @@ -64,6 +72,8 @@ fun RoomListContextMenu( @Composable private fun RoomListModalBottomSheetContent( contextMenu: RoomListState.ContextMenu.Shown, + onRoomMarkReadClicked: (roomId: RoomId) -> Unit, + onRoomMarkUnreadClicked: (roomId: RoomId) -> Unit, onRoomSettingsClicked: (roomId: RoomId) -> Unit, onLeaveRoomClicked: (roomId: RoomId) -> Unit, ) { @@ -78,6 +88,36 @@ private fun RoomListModalBottomSheetContent( ) } ) + ListItem( + headlineContent = { + Text( + text = stringResource( + id = if (contextMenu.hasNewContent) { + R.string.screen_roomlist_mark_as_read + } else { + R.string.screen_roomlist_mark_as_unread + } + ), + style = MaterialTheme.typography.bodyLarge, + ) + }, + modifier = Modifier.clickable { + if (contextMenu.hasNewContent) { + onRoomMarkReadClicked(contextMenu.roomId) + } else { + onRoomMarkUnreadClicked(contextMenu.roomId) + } + }, + /* TODO Design + leadingContent = ListItemContent.Icon( + iconSource = IconSource.Vector( + CompoundIcons.Settings, + contentDescription = stringResource(id = CommonStrings.common_settings) + ) + ), + */ + style = ListItemStyle.Primary, + ) ListItem( headlineContent = { Text( @@ -96,11 +136,13 @@ private fun RoomListModalBottomSheetContent( ) ListItem( headlineContent = { - val leaveText = stringResource(id = if (contextMenu.isDm) { - CommonStrings.action_leave_conversation - } else { - CommonStrings.action_leave_room - }) + val leaveText = stringResource( + id = if (contextMenu.isDm) { + CommonStrings.action_leave_conversation + } else { + CommonStrings.action_leave_room + } + ) Text(text = leaveText) }, modifier = Modifier.clickable { onLeaveRoomClicked(contextMenu.roomId) }, @@ -126,7 +168,10 @@ internal fun RoomListModalBottomSheetContentPreview() = ElementPreview { roomId = RoomId(value = "!aRoom:aDomain"), roomName = "aRoom", isDm = false, + hasNewContent = true, ), + onRoomMarkReadClicked = {}, + onRoomMarkUnreadClicked = {}, onRoomSettingsClicked = {}, onLeaveRoomClicked = {} ) @@ -140,7 +185,10 @@ internal fun RoomListModalBottomSheetContentForDmPreview() = ElementPreview { roomId = RoomId(value = "!aRoom:aDomain"), roomName = "aRoom", isDm = true, + hasNewContent = false, ), + onRoomMarkReadClicked = {}, + onRoomMarkUnreadClicked = {}, onRoomSettingsClicked = {}, onLeaveRoomClicked = {} ) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt index d54bc604cf..6831f63451 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt @@ -28,4 +28,6 @@ sealed interface RoomListEvents { data class ShowContextMenu(val roomListRoomSummary: RoomListRoomSummary) : RoomListEvents data object HideContextMenu : RoomListEvents data class LeaveRoom(val roomId: RoomId) : RoomListEvents + data class MarkAsRead(val roomId: RoomId) : RoomListEvents + data class MarkAsUnread(val roomId: RoomId) : RoomListEvents } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt index f25ebcec20..2a06914aa2 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt @@ -25,12 +25,14 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.produceState import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomPresenter import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus +import io.element.android.features.preferences.api.store.SessionPreferencesStore import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource import io.element.android.features.roomlist.impl.datasource.RoomListDataSource import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter @@ -44,10 +46,12 @@ import io.element.android.libraries.indicator.api.IndicatorService import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.RecoveryState +import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.getCurrentUser import io.element.android.libraries.matrix.api.verification.SessionVerificationService import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import javax.inject.Inject @@ -65,9 +69,11 @@ class RoomListPresenter @Inject constructor( private val featureFlagService: FeatureFlagService, private val indicatorService: IndicatorService, private val migrationScreenPresenter: MigrationScreenPresenter, + private val sessionPreferencesStore: SessionPreferencesStore, ) : Presenter { @Composable override fun present(): RoomListState { + val coroutineScope = rememberCoroutineScope() val leaveRoomState = leaveRoomPresenter.present() val matrixUser: MutableState = rememberSaveable { mutableStateOf(null) @@ -129,10 +135,22 @@ class RoomListPresenter @Inject constructor( roomId = event.roomListRoomSummary.roomId, roomName = event.roomListRoomSummary.name, isDm = event.roomListRoomSummary.isDm, + hasNewContent = event.roomListRoomSummary.hasNewContent ) } is RoomListEvents.HideContextMenu -> contextMenu = RoomListState.ContextMenu.Hidden is RoomListEvents.LeaveRoom -> leaveRoomState.eventSink(LeaveRoomEvent.ShowConfirmation(event.roomId)) + is RoomListEvents.MarkAsRead -> coroutineScope.launch { + val receiptType = if (sessionPreferencesStore.isSendPublicReadReceiptsEnabled().first()) { + ReceiptType.READ + } else { + ReceiptType.READ_PRIVATE + } + client.getRoom(event.roomId)?.markAsRead(receiptType) + } + is RoomListEvents.MarkAsUnread -> coroutineScope.launch { + client.getRoom(event.roomId)?.markAsUnread() + } } } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt index 13ba030720..10dd17037a 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt @@ -49,6 +49,7 @@ data class RoomListState( val roomId: RoomId, val roomName: String, val isDm: Boolean, + val hasNewContent: Boolean, ) : ContextMenu } } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt index 7fda3a180a..f56359c5d1 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt @@ -48,6 +48,7 @@ open class RoomListStateProvider : PreviewParameterProvider { roomId = RoomId("!aRoom:aDomain"), roomName = "A nice room name", isDm = false, + hasNewContent = false, ) ), aRoomListState().copy(displayRecoveryKeyPrompt = true), diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt index 2d47be35a3..d3c7c43148 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt @@ -45,6 +45,7 @@ class RoomListRoomSummaryFactory @Inject constructor( numberOfUnreadMessages = 0, numberOfUnreadMentions = 0, numberOfUnreadNotifications = 0, + isMarkedUnread = false, userDefinedNotificationMode = null, hasRoomCall = false, isDm = false, @@ -73,6 +74,7 @@ class RoomListRoomSummaryFactory @Inject constructor( numberOfUnreadMessages = roomSummary.details.numUnreadMessages, numberOfUnreadMentions = roomSummary.details.numUnreadMentions, numberOfUnreadNotifications = roomSummary.details.numUnreadNotifications, + isMarkedUnread = roomSummary.details.isMarkedUnread, timestamp = lastMessageTimestampFormatter.format(roomSummary.details.lastMessageTimestamp), lastMessage = roomSummary.details.lastMessage?.let { message -> roomLastMessageFormatter.format(message.event, roomSummary.details.isDirect) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt index 26cd9e2d32..960a7c23bb 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt @@ -29,6 +29,7 @@ data class RoomListRoomSummary( val numberOfUnreadMessages: Int, val numberOfUnreadMentions: Int, val numberOfUnreadNotifications: Int, + val isMarkedUnread: Boolean, val timestamp: String?, val lastMessage: CharSequence?, val avatarData: AvatarData, @@ -37,10 +38,12 @@ data class RoomListRoomSummary( val hasRoomCall: Boolean, val isDm: Boolean, ) { - val isHighlighted = userDefinedNotificationMode != RoomNotificationMode.MUTE && - (numberOfUnreadNotifications > 0 || numberOfUnreadMentions > 0) + val isHighlighted = (userDefinedNotificationMode != RoomNotificationMode.MUTE && + (numberOfUnreadNotifications > 0 || numberOfUnreadMentions > 0)) || + isMarkedUnread val hasNewContent = numberOfUnreadMessages > 0 || numberOfUnreadMentions > 0 || - numberOfUnreadNotifications > 0 + numberOfUnreadNotifications > 0 || + isMarkedUnread } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt index e53e1f9072..9b80edecb0 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt @@ -89,6 +89,7 @@ internal fun aRoomListRoomSummary( numberOfUnreadMessages: Int = 0, numberOfUnreadMentions: Int = 0, numberOfUnreadNotifications: Int = 0, + isMarkedUnread: Boolean = false, lastMessage: String? = "Last message", timestamp: String? = lastMessage?.let { "88:88" }, isPlaceholder: Boolean = false, @@ -103,6 +104,7 @@ internal fun aRoomListRoomSummary( numberOfUnreadMessages = numberOfUnreadMessages, numberOfUnreadMentions = numberOfUnreadMentions, numberOfUnreadNotifications = numberOfUnreadNotifications, + isMarkedUnread = isMarkedUnread, timestamp = timestamp, lastMessage = lastMessage, avatarData = avatarData, diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index 10730f0c90..d2543828fb 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -25,6 +25,7 @@ import io.element.android.features.leaveroom.api.LeaveRoomPresenter import io.element.android.features.leaveroom.fake.FakeLeaveRoomPresenter import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.test.FakeNetworkMonitor +import io.element.android.features.preferences.api.store.SessionPreferencesStore import io.element.android.features.roomlist.impl.datasource.FakeInviteDataSource import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource import io.element.android.features.roomlist.impl.datasource.RoomListDataSource @@ -41,12 +42,14 @@ import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService +import io.element.android.libraries.featureflag.test.InMemorySessionPreferencesStore import io.element.android.libraries.indicator.impl.DefaultIndicatorService import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.roomlist.RoomListService +import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.test.AN_AVATAR_URL @@ -59,6 +62,7 @@ import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService @@ -326,8 +330,14 @@ class RoomListPresenterTests { initialState.eventSink(RoomListEvents.ShowContextMenu(summary)) val shownState = awaitItem() - assertThat(shownState.contextMenu) - .isEqualTo(RoomListState.ContextMenu.Shown(summary.roomId, summary.name, false)) + assertThat(shownState.contextMenu).isEqualTo( + RoomListState.ContextMenu.Shown( + roomId = summary.roomId, + roomName = summary.name, + isDm = false, + hasNewContent = true, + ) + ) scope.cancel() } } @@ -346,8 +356,14 @@ class RoomListPresenterTests { initialState.eventSink(RoomListEvents.ShowContextMenu(summary)) val shownState = awaitItem() - assertThat(shownState.contextMenu) - .isEqualTo(RoomListState.ContextMenu.Shown(summary.roomId, summary.name, false)) + assertThat(shownState.contextMenu).isEqualTo( + RoomListState.ContextMenu.Shown( + roomId = summary.roomId, + roomName = summary.name, + isDm = false, + hasNewContent = true, + ) + ) shownState.eventSink(RoomListEvents.HideContextMenu) val hiddenState = awaitItem() @@ -430,6 +446,41 @@ class RoomListPresenterTests { } } + @Test + fun `present - check that the room is marked as read with correct RR and as unread`() = runTest { + val room = FakeMatrixRoom() + val sessionPreferencesStore = InMemorySessionPreferencesStore() + val matrixClient = FakeMatrixClient().apply { + givenGetRoomResult(A_ROOM_ID, room) + } + val scope = CoroutineScope(coroutineContext + SupervisorJob()) + val presenter = createRoomListPresenter( + client = matrixClient, + coroutineScope = scope, + sessionPreferencesStore = sessionPreferencesStore, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(room.markAsReadCalls).isEmpty() + assertThat(room.markAsUnreadReadCallCount).isEqualTo(0) + initialState.eventSink.invoke(RoomListEvents.MarkAsRead(A_ROOM_ID)) + assertThat(room.markAsReadCalls).isEqualTo(listOf(ReceiptType.READ)) + assertThat(room.markAsUnreadReadCallCount).isEqualTo(0) + initialState.eventSink.invoke(RoomListEvents.MarkAsUnread(A_ROOM_ID)) + assertThat(room.markAsReadCalls).isEqualTo(listOf(ReceiptType.READ)) + assertThat(room.markAsUnreadReadCallCount).isEqualTo(1) + // Test again with private read receipts + sessionPreferencesStore.setSendPublicReadReceipts(false) + initialState.eventSink.invoke(RoomListEvents.MarkAsRead(A_ROOM_ID)) + assertThat(room.markAsReadCalls).isEqualTo(listOf(ReceiptType.READ, ReceiptType.READ_PRIVATE)) + assertThat(room.markAsUnreadReadCallCount).isEqualTo(1) + cancelAndIgnoreRemainingEvents() + scope.cancel() + } + } + private fun TestScope.createRoomListPresenter( client: MatrixClient = FakeMatrixClient(), sessionVerificationService: SessionVerificationService = FakeSessionVerificationService(), @@ -442,6 +493,7 @@ class RoomListPresenterTests { }, roomLastMessageFormatter: RoomLastMessageFormatter = FakeRoomLastMessageFormatter(), encryptionService: EncryptionService = FakeEncryptionService(), + sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(), coroutineScope: CoroutineScope, migrationScreenPresenter: MigrationScreenPresenter = MigrationScreenPresenter( matrixClient = client, @@ -472,6 +524,7 @@ class RoomListPresenterTests { featureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.SecureStorage.key to true)), ), migrationScreenPresenter = migrationScreenPresenter, + sessionPreferencesStore = sessionPreferencesStore, ) } @@ -484,6 +537,7 @@ private val aRoomListRoomSummary = RoomListRoomSummary( numberOfUnreadMentions = 1, numberOfUnreadMessages = 2, numberOfUnreadNotifications = 0, + isMarkedUnread = false, timestamp = A_FORMATTED_DATE, lastMessage = "", avatarData = AvatarData(id = A_ROOM_ID.value, name = A_ROOM_NAME, size = AvatarSize.RoomListItem), diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index 550bb7012a..051e0e419e 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -30,6 +30,7 @@ import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.matrix.api.poll.PollKind import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.timeline.MatrixTimeline +import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings import kotlinx.coroutines.flow.Flow @@ -150,6 +151,17 @@ interface MatrixRoom : Closeable { suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result + /** + * Reverts a previously set unread flag, and eventually send a Read Receipt. + * @param receiptType The type of receipt to send. If null, no Read Receipt will be sent. + */ + suspend fun markAsRead(receiptType: ReceiptType?): Result + + /** + * Sets a flag on the room to indicate that the user has explicitly marked it as unread. + */ + suspend fun markAsUnread(): Result + /** * Share a location message in the room. * diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt index cc584f08bb..b8e6dbd84f 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt @@ -43,6 +43,7 @@ data class RoomSummaryDetails( val numUnreadMessages: Int, val numUnreadMentions: Int, val numUnreadNotifications: Int, + val isMarkedUnread: Boolean, val inviter: RoomMember?, val userDefinedNotificationMode: RoomNotificationMode?, val hasRoomCall: Boolean, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 3fc7d65468..36b6cde936 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -40,6 +40,7 @@ import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.room.roomNotificationSettings import io.element.android.libraries.matrix.api.timeline.MatrixTimeline +import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings import io.element.android.libraries.matrix.impl.core.toProgressWatcher @@ -51,6 +52,7 @@ import io.element.android.libraries.matrix.impl.poll.toInner import io.element.android.libraries.matrix.impl.room.location.toInner import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline +import io.element.android.libraries.matrix.impl.timeline.toRustReceiptType import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import io.element.android.libraries.matrix.impl.widget.RustWidgetDriver import io.element.android.libraries.matrix.impl.widget.generateWidgetWebViewUrl @@ -423,6 +425,22 @@ class RustMatrixRoom( } } + override suspend fun markAsRead(receiptType: ReceiptType?): Result = withContext(roomDispatcher) { + runCatching { + if (receiptType != null) { + innerRoom.markAsReadAndSendReadReceipt(receiptType.toRustReceiptType()) + } else { + innerRoom.markAsRead() + } + } + } + + override suspend fun markAsUnread(): Result = withContext(roomDispatcher) { + runCatching { + innerRoom.markAsUnread() + } + } + override suspend fun sendLocation( body: String, geoUri: String, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt index 1b3e900f17..cd3c86871b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt @@ -38,6 +38,7 @@ class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFacto numUnreadMentions = roomInfo.numUnreadMentions.toInt(), numUnreadMessages = roomInfo.numUnreadMessages.toInt(), numUnreadNotifications = roomInfo.numUnreadNotifications.toInt(), + isMarkedUnread = roomInfo.isMarkedUnread, lastMessage = latestRoomMessage, inviter = roomInfo.inviter?.let(RoomMemberMapper::map), userDefinedNotificationMode = roomInfo.userDefinedNotificationMode?.let(RoomNotificationSettingsMapper::mapMode), diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index c33a6d43b2..fdd0dbb6cb 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -41,6 +41,7 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.timeline.MatrixTimeline +import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings @@ -374,6 +375,20 @@ class FakeMatrixRoom( return reportContentResult } + val markAsReadCalls = mutableListOf() + override suspend fun markAsRead(receiptType: ReceiptType?): Result { + markAsReadCalls.add(receiptType) + return Result.success(Unit) + } + + var markAsUnreadReadCallCount = 0 + private set + + override suspend fun markAsUnread(): Result { + markAsUnreadReadCallCount++ + return Result.success(Unit) + } + override suspend fun sendLocation( body: String, geoUri: String, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index 2bd04894c0..bc3ce1d807 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -62,6 +62,7 @@ fun aRoomSummaryDetails( numUnreadMentions: Int = 0, numUnreadMessages: Int = 0, numUnreadNotifications: Int = 0, + isMarkedUnread: Boolean = false, notificationMode: RoomNotificationMode? = null, inviter: RoomMember? = null, canonicalAlias: String? = null, @@ -76,6 +77,7 @@ fun aRoomSummaryDetails( numUnreadMentions = numUnreadMentions, numUnreadMessages = numUnreadMessages, numUnreadNotifications = numUnreadNotifications, + isMarkedUnread = isMarkedUnread, userDefinedNotificationMode = notificationMode, inviter = inviter, canonicalAlias = canonicalAlias, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt index a4d9a479c2..bebe82dca2 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt @@ -116,6 +116,7 @@ fun aRoomSummaryDetails( numUnreadMentions: Int = 0, numUnreadMessages: Int = 0, numUnreadNotifications: Int = 0, + isMarkedUnread: Boolean = false, ) = RoomSummaryDetails( roomId = roomId, name = name, @@ -130,4 +131,5 @@ fun aRoomSummaryDetails( numUnreadMentions = numUnreadMentions, numUnreadMessages = numUnreadMessages, numUnreadNotifications = numUnreadNotifications, + isMarkedUnread = isMarkedUnread, ) diff --git a/samples/minimal/build.gradle.kts b/samples/minimal/build.gradle.kts index edf2527572..f9bfc63932 100644 --- a/samples/minimal/build.gradle.kts +++ b/samples/minimal/build.gradle.kts @@ -54,6 +54,7 @@ dependencies { implementation(projects.libraries.network) implementation(projects.libraries.dateformatter.impl) implementation(projects.libraries.eventformatter.impl) + implementation(projects.libraries.preferences.impl) implementation(projects.libraries.indicator.impl) implementation(projects.features.invitelist.impl) implementation(projects.features.roomlist.impl) diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt index f87119293d..218dfdd697 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt @@ -45,6 +45,7 @@ import io.element.android.libraries.indicator.impl.DefaultIndicatorService import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.RoomMembershipObserver +import io.element.android.libraries.preferences.impl.store.DefaultSessionPreferencesStore import io.element.android.services.toolbox.impl.strings.AndroidStringProvider import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking @@ -108,7 +109,12 @@ class RoomListScreen( migrationScreenPresenter = MigrationScreenPresenter( matrixClient = matrixClient, migrationScreenStore = SharedPrefsMigrationScreenStore(context.getSharedPreferences("migration", Context.MODE_PRIVATE)) - ) + ), + sessionPreferencesStore = DefaultSessionPreferencesStore( + context = context, + sessionId = matrixClient.sessionId, + sessionCoroutineScope = Singleton.appScope + ), ) @Composable From 25768c7e87bd22a42ccfff66ecf56804b8812aaf Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 14:54:55 +0100 Subject: [PATCH 082/119] Move `RoomListRoomSummary` factory to a dedicated file, more tests will be added. --- .../roomlist/impl/RoomListPresenterTests.kt | 32 +++--------- .../impl/model/RoomListRoomSummaryTest.kt | 49 +++++++++++++++++++ .../test/FakeLastMessageTimestampFormatter.kt | 2 + 3 files changed, 57 insertions(+), 26 deletions(-) create mode 100644 features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index d2543828fb..b8b7190cf6 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -32,11 +32,10 @@ import io.element.android.features.roomlist.impl.datasource.RoomListDataSource import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory import io.element.android.features.roomlist.impl.migration.InMemoryMigrationScreenStore import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter -import io.element.android.features.roomlist.impl.model.RoomListRoomSummary +import io.element.android.features.roomlist.impl.model.createRoomListRoomSummary import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter +import io.element.android.libraries.dateformatter.test.A_FORMATTED_DATE import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter -import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter @@ -184,7 +183,7 @@ class RoomListPresenterTests { val withRoomStateItems = withRoomState.roomList.dataOrNull().orEmpty() assertThat(withRoomStateItems.size).isEqualTo(1) assertThat(withRoomStateItems.first()) - .isEqualTo(aRoomListRoomSummary) + .isEqualTo(createRoomListRoomSummary()) scope.cancel() } } @@ -212,7 +211,7 @@ class RoomListPresenterTests { assertThat(withFilteredRoomState.filter).isEqualTo(A_ROOM_NAME.substring(0, 3)) assertThat(withFilteredRoomState.filteredRoomList.size).isEqualTo(1) assertThat(withFilteredRoomState.filteredRoomList.first()) - .isEqualTo(aRoomListRoomSummary) + .isEqualTo(createRoomListRoomSummary()) // Test filtering without result withFilteredRoomState.eventSink.invoke(RoomListEvents.UpdateFilter("tada")) skipItems(1) @@ -326,7 +325,7 @@ class RoomListPresenterTests { skipItems(1) val initialState = awaitItem() - val summary = aRoomListRoomSummary + val summary = createRoomListRoomSummary() initialState.eventSink(RoomListEvents.ShowContextMenu(summary)) val shownState = awaitItem() @@ -352,7 +351,7 @@ class RoomListPresenterTests { skipItems(1) val initialState = awaitItem() - val summary = aRoomListRoomSummary + val summary = createRoomListRoomSummary() initialState.eventSink(RoomListEvents.ShowContextMenu(summary)) val shownState = awaitItem() @@ -527,22 +526,3 @@ class RoomListPresenterTests { sessionPreferencesStore = sessionPreferencesStore, ) } - -private const val A_FORMATTED_DATE = "formatted_date" - -private val aRoomListRoomSummary = RoomListRoomSummary( - id = A_ROOM_ID.value, - roomId = A_ROOM_ID, - name = A_ROOM_NAME, - numberOfUnreadMentions = 1, - numberOfUnreadMessages = 2, - numberOfUnreadNotifications = 0, - isMarkedUnread = false, - timestamp = A_FORMATTED_DATE, - lastMessage = "", - avatarData = AvatarData(id = A_ROOM_ID.value, name = A_ROOM_NAME, size = AvatarSize.RoomListItem), - isPlaceholder = false, - userDefinedNotificationMode = null, - hasRoomCall = false, - isDm = false, -) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt new file mode 100644 index 0000000000..dee72702ff --- /dev/null +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt @@ -0,0 +1,49 @@ +/* + * 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 + * + * http://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.roomlist.impl.model + +import io.element.android.libraries.dateformatter.test.A_FORMATTED_DATE +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_ROOM_NAME +import org.junit.Test + +class RoomListRoomSummaryTest { + @Test + fun `todo`() { + + } +} + +internal fun createRoomListRoomSummary( +) = RoomListRoomSummary( + id = A_ROOM_ID.value, + roomId = A_ROOM_ID, + name = A_ROOM_NAME, + numberOfUnreadMentions = 1, + numberOfUnreadMessages = 2, + numberOfUnreadNotifications = 0, + isMarkedUnread = false, + timestamp = A_FORMATTED_DATE, + lastMessage = "", + avatarData = AvatarData(id = A_ROOM_ID.value, name = A_ROOM_NAME, size = AvatarSize.RoomListItem), + isPlaceholder = false, + userDefinedNotificationMode = null, + hasRoomCall = false, + isDm = false, +) diff --git a/libraries/dateformatter/test/src/main/kotlin/io/element/android/libraries/dateformatter/test/FakeLastMessageTimestampFormatter.kt b/libraries/dateformatter/test/src/main/kotlin/io/element/android/libraries/dateformatter/test/FakeLastMessageTimestampFormatter.kt index 47226c34d9..ca248f6bf8 100644 --- a/libraries/dateformatter/test/src/main/kotlin/io/element/android/libraries/dateformatter/test/FakeLastMessageTimestampFormatter.kt +++ b/libraries/dateformatter/test/src/main/kotlin/io/element/android/libraries/dateformatter/test/FakeLastMessageTimestampFormatter.kt @@ -18,6 +18,8 @@ package io.element.android.libraries.dateformatter.test import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter +const val A_FORMATTED_DATE = "formatted_date" + class FakeLastMessageTimestampFormatter : LastMessageTimestampFormatter { private var format = "" fun givenFormat(format: String) { From c4f72ecfbc684955e865d8815cfb04f53e9db223 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 15:00:44 +0100 Subject: [PATCH 083/119] Add test to check behavior of `isMarkedUnread` parameter --- .../impl/model/RoomListRoomSummaryTest.kt | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt index dee72702ff..2b413abe99 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt @@ -16,34 +16,62 @@ package io.element.android.features.roomlist.impl.model +import com.google.common.truth.Truth.assertThat import io.element.android.libraries.dateformatter.test.A_FORMATTED_DATE import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_NAME import org.junit.Test class RoomListRoomSummaryTest { @Test - fun `todo`() { + fun `test default value`() { + val sut = createRoomListRoomSummary( + numberOfUnreadMentions = 0, + numberOfUnreadMessages = 0, + numberOfUnreadNotifications = 0, + isMarkedUnread = false, + userDefinedNotificationMode = null, + ) + assertThat(sut.isHighlighted).isFalse() + assertThat(sut.hasNewContent).isFalse() + } + @Test + fun `test isMarkedUnread set to true`() { + val sut = createRoomListRoomSummary( + numberOfUnreadMentions = 0, + numberOfUnreadMessages = 0, + numberOfUnreadNotifications = 0, + isMarkedUnread = true, + userDefinedNotificationMode = null, + ) + assertThat(sut.isHighlighted).isTrue() + assertThat(sut.hasNewContent).isTrue() } } internal fun createRoomListRoomSummary( + numberOfUnreadMentions: Int = 1, + numberOfUnreadMessages: Int = 2, + numberOfUnreadNotifications: Int = 0, + isMarkedUnread: Boolean = false, + userDefinedNotificationMode: RoomNotificationMode? = null, ) = RoomListRoomSummary( id = A_ROOM_ID.value, roomId = A_ROOM_ID, name = A_ROOM_NAME, - numberOfUnreadMentions = 1, - numberOfUnreadMessages = 2, - numberOfUnreadNotifications = 0, - isMarkedUnread = false, + numberOfUnreadMentions = numberOfUnreadMentions, + numberOfUnreadMessages = numberOfUnreadMessages, + numberOfUnreadNotifications = numberOfUnreadNotifications, + isMarkedUnread = isMarkedUnread, timestamp = A_FORMATTED_DATE, lastMessage = "", avatarData = AvatarData(id = A_ROOM_ID.value, name = A_ROOM_NAME, size = AvatarSize.RoomListItem), isPlaceholder = false, - userDefinedNotificationMode = null, + userDefinedNotificationMode = userDefinedNotificationMode, hasRoomCall = false, isDm = false, ) From 17f23304d01d41d6079f4878e62f81229a82d3f5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 15:06:00 +0100 Subject: [PATCH 084/119] Give `0` has default value for fixtures creators. Also increases the readability of tests. --- .../roomlist/impl/RoomListPresenterTests.kt | 34 +++++++++++++++---- .../impl/model/RoomListRoomSummaryTest.kt | 12 ++----- .../matrix/test/room/RoomSummaryFixture.kt | 4 +-- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index b8b7190cf6..1b8c75f153 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -178,12 +178,23 @@ class RoomListPresenterTests { val initialItems = initialState.roomList.dataOrNull().orEmpty() assertThat(initialItems.size).isEqualTo(16) assertThat(initialItems.all { it.isPlaceholder }).isTrue() - roomListService.postAllRooms(listOf(aRoomSummaryFilled())) + roomListService.postAllRooms( + listOf( + aRoomSummaryFilled( + numUnreadMentions = 1, + numUnreadMessages = 2, + ) + ) + ) val withRoomState = consumeItemsUntilPredicate { state -> state.roomList.dataOrNull()?.size == 1 }.last() val withRoomStateItems = withRoomState.roomList.dataOrNull().orEmpty() assertThat(withRoomStateItems.size).isEqualTo(1) - assertThat(withRoomStateItems.first()) - .isEqualTo(createRoomListRoomSummary()) + assertThat(withRoomStateItems.first()).isEqualTo( + createRoomListRoomSummary( + numberOfUnreadMentions = 1, + numberOfUnreadMessages = 2, + ) + ) scope.cancel() } } @@ -199,7 +210,14 @@ class RoomListPresenterTests { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - roomListService.postAllRooms(listOf(aRoomSummaryFilled())) + roomListService.postAllRooms( + listOf( + aRoomSummaryFilled( + numUnreadMentions = 1, + numUnreadMessages = 2, + ) + ) + ) skipItems(3) val loadedState = awaitItem() // Test filtering with result @@ -210,8 +228,12 @@ class RoomListPresenterTests { assertThat(withFilteredRoomState.filteredRoomList.size).isEqualTo(1) assertThat(withFilteredRoomState.filter).isEqualTo(A_ROOM_NAME.substring(0, 3)) assertThat(withFilteredRoomState.filteredRoomList.size).isEqualTo(1) - assertThat(withFilteredRoomState.filteredRoomList.first()) - .isEqualTo(createRoomListRoomSummary()) + assertThat(withFilteredRoomState.filteredRoomList.first()).isEqualTo( + createRoomListRoomSummary( + numberOfUnreadMentions = 1, + numberOfUnreadMessages = 2, + ) + ) // Test filtering without result withFilteredRoomState.eventSink.invoke(RoomListEvents.UpdateFilter("tada")) skipItems(1) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt index 2b413abe99..c991d7bf36 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt @@ -29,11 +29,7 @@ class RoomListRoomSummaryTest { @Test fun `test default value`() { val sut = createRoomListRoomSummary( - numberOfUnreadMentions = 0, - numberOfUnreadMessages = 0, - numberOfUnreadNotifications = 0, isMarkedUnread = false, - userDefinedNotificationMode = null, ) assertThat(sut.isHighlighted).isFalse() assertThat(sut.hasNewContent).isFalse() @@ -42,11 +38,7 @@ class RoomListRoomSummaryTest { @Test fun `test isMarkedUnread set to true`() { val sut = createRoomListRoomSummary( - numberOfUnreadMentions = 0, - numberOfUnreadMessages = 0, - numberOfUnreadNotifications = 0, isMarkedUnread = true, - userDefinedNotificationMode = null, ) assertThat(sut.isHighlighted).isTrue() assertThat(sut.hasNewContent).isTrue() @@ -54,8 +46,8 @@ class RoomListRoomSummaryTest { } internal fun createRoomListRoomSummary( - numberOfUnreadMentions: Int = 1, - numberOfUnreadMessages: Int = 2, + numberOfUnreadMentions: Int = 0, + numberOfUnreadMessages: Int = 0, numberOfUnreadNotifications: Int = 0, isMarkedUnread: Boolean = false, userDefinedNotificationMode: RoomNotificationMode? = null, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index bc3ce1d807..4871dcb402 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -37,8 +37,8 @@ fun aRoomSummaryFilled( isDirect: Boolean = false, avatarUrl: String? = null, lastMessage: RoomMessage? = aRoomMessage(), - numUnreadMentions: Int = 1, - numUnreadMessages: Int = 2, + numUnreadMentions: Int = 0, + numUnreadMessages: Int = 0, notificationMode: RoomNotificationMode? = null, ) = RoomSummary.Filled( aRoomSummaryDetails( From 50859b62de5efa1769d532831e9b40dafd2cb1ca Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 15:09:26 +0100 Subject: [PATCH 085/119] Add more tests --- .../impl/model/RoomListRoomSummaryTest.kt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt index c991d7bf36..f36299b1b7 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt @@ -35,6 +35,35 @@ class RoomListRoomSummaryTest { assertThat(sut.hasNewContent).isFalse() } + @Test + fun `test muted room`() { + val sut = createRoomListRoomSummary( + userDefinedNotificationMode = RoomNotificationMode.MUTE, + ) + assertThat(sut.isHighlighted).isFalse() + assertThat(sut.hasNewContent).isFalse() + } + + @Test + fun `test muted room isMarkedUnread set to true`() { + val sut = createRoomListRoomSummary( + isMarkedUnread = true, + userDefinedNotificationMode = RoomNotificationMode.MUTE, + ) + assertThat(sut.isHighlighted).isTrue() + assertThat(sut.hasNewContent).isTrue() + } + + @Test + fun `test muted room with unread message`() { + val sut = createRoomListRoomSummary( + numberOfUnreadNotifications = 1, + userDefinedNotificationMode = RoomNotificationMode.MUTE, + ) + assertThat(sut.isHighlighted).isFalse() + assertThat(sut.hasNewContent).isTrue() + } + @Test fun `test isMarkedUnread set to true`() { val sut = createRoomListRoomSummary( From ff73173f2c288da6449a8dea71e80f538a04432d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 16:20:00 +0100 Subject: [PATCH 086/119] Changelog --- changelog.d/2261.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2261.feature diff --git a/changelog.d/2261.feature b/changelog.d/2261.feature new file mode 100644 index 0000000000..c643cdf43a --- /dev/null +++ b/changelog.d/2261.feature @@ -0,0 +1 @@ +Manually mark a room as unread From 27b475286ff4e6417f2f8d3cf5c27c4e11183192 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 6 Feb 2024 15:31:47 +0000 Subject: [PATCH 087/119] Update screenshots --- ...alBottomSheetContentForDm-Day-2_3_null,NEXUS_5,1.0,en].png | 4 ++-- ...BottomSheetContentForDm-Night-2_4_null,NEXUS_5,1.0,en].png | 4 ++-- ...stModalBottomSheetContent-Day-1_2_null,NEXUS_5,1.0,en].png | 4 ++-- ...ModalBottomSheetContent-Night-1_3_null,NEXUS_5,1.0,en].png | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Day-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Day-2_3_null,NEXUS_5,1.0,en].png index d5a64e473f..111f5427d6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Day-2_3_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Day-2_3_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bd5f590fd30facfb007050c26aa567d568ab0d797a27986b6f88a23af76f9dc -size 14167 +oid sha256:547fe148039b4f0c4e84897af1b950d372ddb6aa5d0d79b53ca05fbc7fbdc11f +size 17388 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Night-2_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Night-2_4_null,NEXUS_5,1.0,en].png index 1d1319d036..5585832f27 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Night-2_4_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Night-2_4_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e609d908d159cffdc2a17ac8944413dbb52e5cd1ebdd2bd30057dc9865749bd6 -size 13369 +oid sha256:2c603ba7cecfbb554b2c1c64bfba76f5c97d1c58c3adbbe69fb944592abca54a +size 16287 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_2_null,NEXUS_5,1.0,en].png index 6e7ba7e325..1f28bb6439 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_2_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_2_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c7e78d4f8fd595922f4bdce5263bf9c5cf7094df5edb9a0e9d35fe270b21d23 -size 12541 +oid sha256:b142adcfc92ed9ddca5c8ec788c610b11296c128a577e5766ef1b10aa6a216cb +size 15550 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_3_null,NEXUS_5,1.0,en].png index a6395dcbd7..f8de05de07 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_3_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_3_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:715154a104ef257a1865be35775cc7223e7adf3494863e0d24f1f1a8c7129920 -size 11888 +oid sha256:f898bbf9dd2cc11c594261f08682d8cebaefe1b9d8e526793c6f23a51d0f696b +size 14560 From 4dc4e5bef8971392b6bcf99042457323d6592bdf Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Feb 2024 20:19:57 +0100 Subject: [PATCH 088/119] Remove unnecessary parenthesis --- .../features/roomlist/impl/model/RoomListRoomSummary.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt index 960a7c23bb..2a5289c9db 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt @@ -38,8 +38,8 @@ data class RoomListRoomSummary( val hasRoomCall: Boolean, val isDm: Boolean, ) { - val isHighlighted = (userDefinedNotificationMode != RoomNotificationMode.MUTE && - (numberOfUnreadNotifications > 0 || numberOfUnreadMentions > 0)) || + val isHighlighted = userDefinedNotificationMode != RoomNotificationMode.MUTE && + (numberOfUnreadNotifications > 0 || numberOfUnreadMentions > 0) || isMarkedUnread val hasNewContent = numberOfUnreadMessages > 0 || From 525de38ee2b6aef7f66a7701470628d0f9fb4394 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Feb 2024 10:00:08 +0100 Subject: [PATCH 089/119] Fix test after default value change on parameter of `createRoomListRoomSummary`. --- .../android/features/roomlist/impl/RoomListPresenterTests.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index 1b8c75f153..1ede6b44d4 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -356,7 +356,7 @@ class RoomListPresenterTests { roomId = summary.roomId, roomName = summary.name, isDm = false, - hasNewContent = true, + hasNewContent = false, ) ) scope.cancel() @@ -382,7 +382,7 @@ class RoomListPresenterTests { roomId = summary.roomId, roomName = summary.name, isDm = false, - hasNewContent = true, + hasNewContent = false, ) ) shownState.eventSink(RoomListEvents.HideContextMenu) From e304913030a61f2ab2b14ba547a57bd3b960e61e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Feb 2024 10:58:31 +0100 Subject: [PATCH 090/119] Introduce function `aContextMenuShown` to create `RoomListState.ContextMenu.Shown` --- .../roomlist/impl/RoomListContextMenu.kt | 14 ++----------- .../roomlist/impl/RoomListStateProvider.kt | 20 +++++++++++-------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt index f605b71a10..15213ad29e 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt @@ -164,12 +164,7 @@ private fun RoomListModalBottomSheetContent( @Composable internal fun RoomListModalBottomSheetContentPreview() = ElementPreview { RoomListModalBottomSheetContent( - contextMenu = RoomListState.ContextMenu.Shown( - roomId = RoomId(value = "!aRoom:aDomain"), - roomName = "aRoom", - isDm = false, - hasNewContent = true, - ), + contextMenu = aContextMenuShown(hasNewContent = true), onRoomMarkReadClicked = {}, onRoomMarkUnreadClicked = {}, onRoomSettingsClicked = {}, @@ -181,12 +176,7 @@ internal fun RoomListModalBottomSheetContentPreview() = ElementPreview { @Composable internal fun RoomListModalBottomSheetContentForDmPreview() = ElementPreview { RoomListModalBottomSheetContent( - contextMenu = RoomListState.ContextMenu.Shown( - roomId = RoomId(value = "!aRoom:aDomain"), - roomName = "aRoom", - isDm = true, - hasNewContent = false, - ), + contextMenu = aContextMenuShown(isDm = true), onRoomMarkReadClicked = {}, onRoomMarkUnreadClicked = {}, onRoomSettingsClicked = {}, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt index f56359c5d1..64f75c85dc 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt @@ -43,14 +43,7 @@ open class RoomListStateProvider : PreviewParameterProvider { aRoomListState().copy(invitesState = InvitesState.NewInvites), aRoomListState().copy(displaySearchResults = true, filter = "", filteredRoomList = persistentListOf()), aRoomListState().copy(displaySearchResults = true), - aRoomListState().copy( - contextMenu = RoomListState.ContextMenu.Shown( - roomId = RoomId("!aRoom:aDomain"), - roomName = "A nice room name", - isDm = false, - hasNewContent = false, - ) - ), + aRoomListState().copy(contextMenu = aContextMenuShown(roomName = "A nice room name")), aRoomListState().copy(displayRecoveryKeyPrompt = true), aRoomListState().copy(roomList = AsyncData.Success(persistentListOf())), aRoomListState().copy(roomList = AsyncData.Loading(prevData = RoomListRoomSummaryFactory.createFakeList())), @@ -104,3 +97,14 @@ internal fun aRoomListRoomSummaryList(): ImmutableList { ), ) } + +internal fun aContextMenuShown( + roomName: String = "aRoom", + isDm: Boolean = false, + hasNewContent: Boolean = false, +) = RoomListState.ContextMenu.Shown( + roomId = RoomId("!aRoom:aDomain"), + roomName = roomName, + isDm = isDm, + hasNewContent = hasNewContent, +) From 3c38201b2cf4da70db77ba204f820c7a079814f8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Feb 2024 11:10:05 +0100 Subject: [PATCH 091/119] Add test for `RoomListContextMenu` --- features/roomlist/impl/build.gradle.kts | 1 + .../roomlist/impl/RoomListContextMenuTest.kt | 133 ++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt diff --git a/features/roomlist/impl/build.gradle.kts b/features/roomlist/impl/build.gradle.kts index 02fbcc058a..7d0a071277 100644 --- a/features/roomlist/impl/build.gradle.kts +++ b/features/roomlist/impl/build.gradle.kts @@ -60,6 +60,7 @@ dependencies { api(projects.features.roomlist.api) ksp(libs.showkase.processor) + testImplementation(libs.androidx.compose.ui.test.junit) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) testImplementation(libs.molecule.runtime) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt new file mode 100644 index 0000000000..7f51583db8 --- /dev/null +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt @@ -0,0 +1,133 @@ +/* + * 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 + * + * http://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.roomlist.impl + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.EnsureCalledOnceWithParam +import io.element.android.tests.testutils.EnsureNeverCalledWithParam +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.clickOn +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class RoomListContextMenuTest { + @get:Rule val rule = createAndroidComposeRule() + + @Test + fun `clicking on Mark as read generates expected Events`() { + val eventsRecorder = EventsRecorder() + val contextMenu = aContextMenuShown(hasNewContent = true) + rule.setContent { + RoomListContextMenu( + contextMenu = contextMenu, + eventSink = eventsRecorder, + onRoomSettingsClicked = EnsureNeverCalledWithParam(), + ) + } + rule.clickOn(R.string.screen_roomlist_mark_as_read) + eventsRecorder.assertList( + listOf( + RoomListEvents.HideContextMenu, + RoomListEvents.MarkAsRead(contextMenu.roomId), + ) + ) + } + + @Test + fun `clicking on Mark as unread generates expected Events`() { + val eventsRecorder = EventsRecorder() + val contextMenu = aContextMenuShown(hasNewContent = false) + rule.setContent { + RoomListContextMenu( + contextMenu = contextMenu, + eventSink = eventsRecorder, + onRoomSettingsClicked = EnsureNeverCalledWithParam(), + ) + } + rule.clickOn(R.string.screen_roomlist_mark_as_unread) + eventsRecorder.assertList( + listOf( + RoomListEvents.HideContextMenu, + RoomListEvents.MarkAsUnread(contextMenu.roomId), + ) + ) + } + + @Test + fun `clicking on Leave dm generates expected Events`() { + val eventsRecorder = EventsRecorder() + val contextMenu = aContextMenuShown(isDm = true) + rule.setContent { + RoomListContextMenu( + contextMenu = contextMenu, + eventSink = eventsRecorder, + onRoomSettingsClicked = EnsureNeverCalledWithParam(), + ) + } + rule.clickOn(CommonStrings.action_leave_conversation) + eventsRecorder.assertList( + listOf( + RoomListEvents.HideContextMenu, + RoomListEvents.LeaveRoom(contextMenu.roomId), + ) + ) + } + + @Test + fun `clicking on Leave room generates expected Events`() { + val eventsRecorder = EventsRecorder() + val contextMenu = aContextMenuShown(isDm = false) + rule.setContent { + RoomListContextMenu( + contextMenu = contextMenu, + eventSink = eventsRecorder, + onRoomSettingsClicked = EnsureNeverCalledWithParam(), + ) + } + rule.clickOn(CommonStrings.action_leave_room) + eventsRecorder.assertList( + listOf( + RoomListEvents.HideContextMenu, + RoomListEvents.LeaveRoom(contextMenu.roomId), + ) + ) + } + + @Test + fun `clicking on Settings invokes the expected callback and generates expected Event`() { + val eventsRecorder = EventsRecorder() + val contextMenu = aContextMenuShown() + val callback = EnsureCalledOnceWithParam(contextMenu.roomId) + rule.setContent { + RoomListContextMenu( + contextMenu = contextMenu, + eventSink = eventsRecorder, + onRoomSettingsClicked = callback, + ) + } + rule.clickOn(CommonStrings.common_settings) + eventsRecorder.assertSingle(RoomListEvents.HideContextMenu) + callback.assertSuccess() + } +} + + From b6dcadd98095e53dd3ed8a7e4782e5f76e358198 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Feb 2024 11:17:15 +0100 Subject: [PATCH 092/119] Simplify a bit the API. --- .../roomlist/impl/RoomListContextMenu.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt index 15213ad29e..81a746426d 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt @@ -51,15 +51,15 @@ fun RoomListContextMenu( contextMenu = contextMenu, onRoomMarkReadClicked = { eventSink(RoomListEvents.HideContextMenu) - eventSink(RoomListEvents.MarkAsRead(it)) + eventSink(RoomListEvents.MarkAsRead(contextMenu.roomId)) }, onRoomMarkUnreadClicked = { eventSink(RoomListEvents.HideContextMenu) - eventSink(RoomListEvents.MarkAsUnread(it)) + eventSink(RoomListEvents.MarkAsUnread(contextMenu.roomId)) }, onRoomSettingsClicked = { eventSink(RoomListEvents.HideContextMenu) - onRoomSettingsClicked(it) + onRoomSettingsClicked(contextMenu.roomId) }, onLeaveRoomClicked = { eventSink(RoomListEvents.HideContextMenu) @@ -72,10 +72,10 @@ fun RoomListContextMenu( @Composable private fun RoomListModalBottomSheetContent( contextMenu: RoomListState.ContextMenu.Shown, - onRoomMarkReadClicked: (roomId: RoomId) -> Unit, - onRoomMarkUnreadClicked: (roomId: RoomId) -> Unit, - onRoomSettingsClicked: (roomId: RoomId) -> Unit, - onLeaveRoomClicked: (roomId: RoomId) -> Unit, + onRoomMarkReadClicked: () -> Unit, + onRoomMarkUnreadClicked: () -> Unit, + onRoomSettingsClicked: () -> Unit, + onLeaveRoomClicked: () -> Unit, ) { Column( modifier = Modifier.fillMaxWidth() @@ -103,9 +103,9 @@ private fun RoomListModalBottomSheetContent( }, modifier = Modifier.clickable { if (contextMenu.hasNewContent) { - onRoomMarkReadClicked(contextMenu.roomId) + onRoomMarkReadClicked() } else { - onRoomMarkUnreadClicked(contextMenu.roomId) + onRoomMarkUnreadClicked() } }, /* TODO Design @@ -125,7 +125,7 @@ private fun RoomListModalBottomSheetContent( style = MaterialTheme.typography.bodyLarge, ) }, - modifier = Modifier.clickable { onRoomSettingsClicked(contextMenu.roomId) }, + modifier = Modifier.clickable { onRoomSettingsClicked() }, leadingContent = ListItemContent.Icon( iconSource = IconSource.Vector( CompoundIcons.Settings, @@ -145,7 +145,7 @@ private fun RoomListModalBottomSheetContent( ) Text(text = leaveText) }, - modifier = Modifier.clickable { onLeaveRoomClicked(contextMenu.roomId) }, + modifier = Modifier.clickable { onLeaveRoomClicked() }, leadingContent = ListItemContent.Icon( iconSource = IconSource.Vector( CompoundIcons.Leave, From 69acc684b54029639443639f2ce504ffd5639c36 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Feb 2024 11:21:08 +0100 Subject: [PATCH 093/119] Introduce `RoomListBottomSheetEvents` "scope" for Events from the Room List Bottom Sheet. --- .../features/roomlist/impl/RoomListContextMenu.kt | 2 +- .../android/features/roomlist/impl/RoomListEvents.kt | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt index 81a746426d..fc42ee7af6 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt @@ -41,7 +41,7 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun RoomListContextMenu( contextMenu: RoomListState.ContextMenu.Shown, - eventSink: (RoomListEvents) -> Unit, + eventSink: (RoomListEvents.RoomListBottomSheetEvents) -> Unit, onRoomSettingsClicked: (roomId: RoomId) -> Unit, ) { ModalBottomSheet( diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt index 6831f63451..affd3946f2 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt @@ -26,8 +26,10 @@ sealed interface RoomListEvents { data object DismissRecoveryKeyPrompt : RoomListEvents data object ToggleSearchResults : RoomListEvents data class ShowContextMenu(val roomListRoomSummary: RoomListRoomSummary) : RoomListEvents - data object HideContextMenu : RoomListEvents - data class LeaveRoom(val roomId: RoomId) : RoomListEvents - data class MarkAsRead(val roomId: RoomId) : RoomListEvents - data class MarkAsUnread(val roomId: RoomId) : RoomListEvents + + sealed interface RoomListBottomSheetEvents : RoomListEvents + data object HideContextMenu : RoomListBottomSheetEvents + data class LeaveRoom(val roomId: RoomId) : RoomListBottomSheetEvents + data class MarkAsRead(val roomId: RoomId) : RoomListBottomSheetEvents + data class MarkAsUnread(val roomId: RoomId) : RoomListBottomSheetEvents } From 75b0a12a8198b3135b945149ac08213d873ed59b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Feb 2024 11:51:09 +0100 Subject: [PATCH 094/119] Remove extra new lines --- .../android/features/roomlist/impl/RoomListContextMenuTest.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt index 7f51583db8..6907fac598 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt @@ -129,5 +129,3 @@ class RoomListContextMenuTest { callback.assertSuccess() } } - - From f79e0cd56a413bac7e878ce9044d492b651dd7bb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Feb 2024 11:51:57 +0100 Subject: [PATCH 095/119] Fix test in release. --- features/roomlist/impl/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/features/roomlist/impl/build.gradle.kts b/features/roomlist/impl/build.gradle.kts index 7d0a071277..61f40b53ec 100644 --- a/features/roomlist/impl/build.gradle.kts +++ b/features/roomlist/impl/build.gradle.kts @@ -61,6 +61,7 @@ dependencies { ksp(libs.showkase.processor) testImplementation(libs.androidx.compose.ui.test.junit) + testReleaseImplementation(libs.androidx.compose.ui.test.manifest) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) testImplementation(libs.molecule.runtime) From 3e344610078aac2c7a863826d438189aa26630c3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 8 Feb 2024 11:49:14 +0100 Subject: [PATCH 096/119] Mark as unread: add a feature flag, disabled on release build. --- .../roomlist/impl/RoomListContextMenu.kt | 62 ++++++++++--------- .../roomlist/impl/RoomListPresenter.kt | 4 ++ .../features/roomlist/impl/RoomListState.kt | 1 + .../roomlist/impl/RoomListStateProvider.kt | 1 + .../roomlist/impl/RoomListPresenterTests.kt | 2 + .../libraries/featureflag/api/FeatureFlags.kt | 7 +++ .../impl/StaticFeatureFlagProvider.kt | 1 + 7 files changed, 48 insertions(+), 30 deletions(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt index fc42ee7af6..cdd8c4527d 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt @@ -88,36 +88,38 @@ private fun RoomListModalBottomSheetContent( ) } ) - ListItem( - headlineContent = { - Text( - text = stringResource( - id = if (contextMenu.hasNewContent) { - R.string.screen_roomlist_mark_as_read - } else { - R.string.screen_roomlist_mark_as_unread - } - ), - style = MaterialTheme.typography.bodyLarge, - ) - }, - modifier = Modifier.clickable { - if (contextMenu.hasNewContent) { - onRoomMarkReadClicked() - } else { - onRoomMarkUnreadClicked() - } - }, - /* TODO Design - leadingContent = ListItemContent.Icon( - iconSource = IconSource.Vector( - CompoundIcons.Settings, - contentDescription = stringResource(id = CommonStrings.common_settings) - ) - ), - */ - style = ListItemStyle.Primary, - ) + if (contextMenu.markAsUnreadFeatureFlagEnabled) { + ListItem( + headlineContent = { + Text( + text = stringResource( + id = if (contextMenu.hasNewContent) { + R.string.screen_roomlist_mark_as_read + } else { + R.string.screen_roomlist_mark_as_unread + } + ), + style = MaterialTheme.typography.bodyLarge, + ) + }, + modifier = Modifier.clickable { + if (contextMenu.hasNewContent) { + onRoomMarkReadClicked() + } else { + onRoomMarkUnreadClicked() + } + }, + /* TODO Design + leadingContent = ListItemContent.Icon( + iconSource = IconSource.Vector( + CompoundIcons.Settings, + contentDescription = stringResource(id = CommonStrings.common_settings) + ) + ), + */ + style = ListItemStyle.Primary, + ) + } ListItem( headlineContent = { Text( diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt index 2a06914aa2..fa841ff88f 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt @@ -111,6 +111,9 @@ class RoomListPresenter @Inject constructor( } } + val markAsUnreadFeatureFlagEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.MarkAsUnread) + .collectAsState(initial = null) + // Avatar indicator val showAvatarIndicator by indicatorService.showRoomListTopBarIndicator() @@ -135,6 +138,7 @@ class RoomListPresenter @Inject constructor( roomId = event.roomListRoomSummary.roomId, roomName = event.roomListRoomSummary.name, isDm = event.roomListRoomSummary.isDm, + markAsUnreadFeatureFlagEnabled = markAsUnreadFeatureFlagEnabled == true, hasNewContent = event.roomListRoomSummary.hasNewContent ) } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt index 10dd17037a..d2f1a55671 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt @@ -49,6 +49,7 @@ data class RoomListState( val roomId: RoomId, val roomName: String, val isDm: Boolean, + val markAsUnreadFeatureFlagEnabled: Boolean, val hasNewContent: Boolean, ) : ContextMenu } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt index 64f75c85dc..c975ee069a 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt @@ -106,5 +106,6 @@ internal fun aContextMenuShown( roomId = RoomId("!aRoom:aDomain"), roomName = roomName, isDm = isDm, + markAsUnreadFeatureFlagEnabled = true, hasNewContent = hasNewContent, ) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index 1ede6b44d4..49d7eb2974 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -356,6 +356,7 @@ class RoomListPresenterTests { roomId = summary.roomId, roomName = summary.name, isDm = false, + markAsUnreadFeatureFlagEnabled = true, hasNewContent = false, ) ) @@ -382,6 +383,7 @@ class RoomListPresenterTests { roomId = summary.roomId, roomName = summary.name, isDm = false, + markAsUnreadFeatureFlagEnabled = true, hasNewContent = false, ) ) diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt index 61c155caac..3c56c81fae 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt @@ -75,4 +75,11 @@ enum class FeatureFlags( defaultValue = true, isFinished = false, ), + MarkAsUnread( + key = "feature.markAsUnread", + title = "Mark as unread", + description = "Allow user to mark a room as unread", + defaultValue = true, + isFinished = false, + ), } diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt index 75b16d4e84..8462a33ba5 100644 --- a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt +++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt @@ -40,6 +40,7 @@ class StaticFeatureFlagProvider @Inject constructor() : FeatureFlags.PinUnlock -> true FeatureFlags.Mentions -> true FeatureFlags.SecureStorage -> true + FeatureFlags.MarkAsUnread -> false } } else { false From 65ace63e24e575a0110e944b207bbdcc7bebd941 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 12:27:28 +0000 Subject: [PATCH 097/119] Update wysiwyg to v2.28.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a7e07c95ed..af1186d2dd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -38,7 +38,7 @@ serialization_json = "1.6.2" showkase = "1.0.2" appyx = "1.4.0" sqldelight = "2.0.1" -wysiwyg = "2.27.0" +wysiwyg = "2.28.0" # DI dagger = "2.50" From e77740f1d25f470d49a7cd55d7e57afe531f5ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 8 Feb 2024 13:38:05 +0100 Subject: [PATCH 098/119] Remove `FilterHiddenStateEventsProcessor` The same process is already done by the Rust SDK, so it's now redundant. --- .../+remove-hidden-event-type-filter.misc | 2 + .../api/timeline/item/event/EventContent.kt | 6 +- .../api/timeline/item/event/OtherState.kt | 26 ------- .../impl/timeline/RustMatrixTimeline.kt | 4 - .../FilterHiddenStateEventsProcessor.kt | 42 ---------- .../FilterHiddenStateEventsProcessorTest.kt | 77 ------------------- 6 files changed, 3 insertions(+), 154 deletions(-) create mode 100644 changelog.d/+remove-hidden-event-type-filter.misc delete mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterHiddenStateEventsProcessor.kt delete mode 100644 libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterHiddenStateEventsProcessorTest.kt diff --git a/changelog.d/+remove-hidden-event-type-filter.misc b/changelog.d/+remove-hidden-event-type-filter.misc new file mode 100644 index 0000000000..27c1d83bfa --- /dev/null +++ b/changelog.d/+remove-hidden-event-type-filter.misc @@ -0,0 +1,2 @@ +Remove `FilterHiddenStateEventsProcessor`, as this is already handled by the Rust SDK. + diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt index 87e4f4ab73..fc4840d610 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt @@ -85,11 +85,7 @@ data class ProfileChangeContent( data class StateContent( val stateKey: String, val content: OtherState -) : EventContent { - fun isVisibleInTimeline(): Boolean { - return content.isVisibleInTimeline() - } -} +) : EventContent data class FailedToParseMessageLikeContent( val eventType: String, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/OtherState.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/OtherState.kt index e5119c388e..6960b3565d 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/OtherState.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/OtherState.kt @@ -41,30 +41,4 @@ sealed interface OtherState { data object SpaceChild : OtherState data object SpaceParent : OtherState data class Custom(val eventType: String) : OtherState - - fun isVisibleInTimeline() = when (this) { - // Visible - is RoomAvatar, - is RoomName, - is RoomTopic, - is RoomThirdPartyInvite, - is RoomCreate, - is RoomEncryption, - is Custom -> true - // Hidden - is RoomAliases, - is RoomCanonicalAlias, - is RoomGuestAccess, - is RoomHistoryVisibility, - is RoomJoinRules, - is RoomPinnedEvents, - is RoomPowerLevels, - is RoomServerAcl, - is RoomTombstone, - is SpaceChild, - is SpaceParent, - is PolicyRuleRoom, - is PolicyRuleServer, - is PolicyRuleUser -> false - } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt index 7b1e7d0500..0f8da55a75 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt @@ -28,7 +28,6 @@ import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelin import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper import io.element.android.libraries.matrix.impl.timeline.postprocessor.DmBeginningTimelineProcessor -import io.element.android.libraries.matrix.impl.timeline.postprocessor.FilterHiddenStateEventsProcessor import io.element.android.libraries.matrix.impl.timeline.postprocessor.TimelineEncryptedHistoryPostProcessor import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineDispatcher @@ -84,8 +83,6 @@ class RustMatrixTimeline( dispatcher = dispatcher, ) - private val filterHiddenStateEventsProcessor = FilterHiddenStateEventsProcessor() - private val dmBeginningTimelineProcessor = DmBeginningTimelineProcessor() private val timelineItemFactory = MatrixTimelineItemMapper( @@ -109,7 +106,6 @@ class RustMatrixTimeline( @OptIn(ExperimentalCoroutinesApi::class) override val timelineItems: Flow> = _timelineItems .mapLatest { items -> encryptedHistoryPostProcessor.process(items) } - .mapLatest { items -> filterHiddenStateEventsProcessor.process(items) } .mapLatest { items -> dmBeginningTimelineProcessor.process( items = items, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterHiddenStateEventsProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterHiddenStateEventsProcessor.kt deleted file mode 100644 index c54221d84e..0000000000 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterHiddenStateEventsProcessor.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 - * - * http://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.libraries.matrix.impl.timeline.postprocessor - -import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem -import io.element.android.libraries.matrix.api.timeline.item.event.StateContent - -/** - * This class is used to filter out 'hidden' state events from the timeline. - */ -class FilterHiddenStateEventsProcessor { - fun process(items: List): List { - return items.filter { item -> - when (item) { - is MatrixTimelineItem.Event -> { - when (val content = item.event.content) { - // If it's a state event, make sure it's visible - is StateContent -> content.isVisibleInTimeline() - // We can display any other event - else -> true - } - } - is MatrixTimelineItem.Virtual -> true - is MatrixTimelineItem.Other -> true - } - } - } -} diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterHiddenStateEventsProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterHiddenStateEventsProcessorTest.kt deleted file mode 100644 index 0532d1b762..0000000000 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterHiddenStateEventsProcessorTest.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 - * - * http://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.libraries.matrix.impl.timeline.postprocessor - -import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem -import io.element.android.libraries.matrix.api.timeline.item.event.OtherState -import io.element.android.libraries.matrix.api.timeline.item.event.StateContent -import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem -import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem -import org.junit.Test - -class FilterHiddenStateEventsProcessorTest { - @Test - fun test() { - val items = listOf( - // These are visible because they're not state events - MatrixTimelineItem.Other, - MatrixTimelineItem.Virtual("virtual", VirtualTimelineItem.ReadMarker), - MatrixTimelineItem.Event("event", anEventTimelineItem()), - // These are visible state events - MatrixTimelineItem.Event("m.room.avatar", anEventTimelineItem(content = StateContent("", OtherState.RoomAvatar("")))), - MatrixTimelineItem.Event("m.room.create", anEventTimelineItem(content = StateContent("", OtherState.RoomCreate))), - MatrixTimelineItem.Event("m.room.encrypted", anEventTimelineItem(content = StateContent("", OtherState.RoomEncryption))), - MatrixTimelineItem.Event("m.room.name", anEventTimelineItem(content = StateContent("", OtherState.RoomName("")))), - MatrixTimelineItem.Event("m.room.third_party_invite", anEventTimelineItem(content = StateContent("", OtherState.RoomThirdPartyInvite("")))), - MatrixTimelineItem.Event("m.room.topic", anEventTimelineItem(content = StateContent("", OtherState.RoomTopic("")))), - MatrixTimelineItem.Event("m.room.custom", anEventTimelineItem(content = StateContent("", OtherState.Custom("")))), - // These ones are hidden - MatrixTimelineItem.Event("m.room.aliases", anEventTimelineItem(content = StateContent("", OtherState.RoomAliases))), - MatrixTimelineItem.Event("m.room.canonical_alias", anEventTimelineItem(content = StateContent("", OtherState.RoomCanonicalAlias))), - MatrixTimelineItem.Event("m.room.guest_access", anEventTimelineItem(content = StateContent("", OtherState.RoomGuestAccess))), - MatrixTimelineItem.Event("m.room.history_visibility", anEventTimelineItem(content = StateContent("", OtherState.RoomHistoryVisibility))), - MatrixTimelineItem.Event("m.room.join_rules", anEventTimelineItem(content = StateContent("", OtherState.RoomJoinRules))), - MatrixTimelineItem.Event("m.room.pinned_events", anEventTimelineItem(content = StateContent("", OtherState.RoomPinnedEvents))), - MatrixTimelineItem.Event("m.room.power_levels", anEventTimelineItem(content = StateContent("", OtherState.RoomPowerLevels))), - MatrixTimelineItem.Event("m.room.server_acl", anEventTimelineItem(content = StateContent("", OtherState.RoomServerAcl))), - MatrixTimelineItem.Event("m.room.tombstone", anEventTimelineItem(content = StateContent("", OtherState.RoomTombstone))), - MatrixTimelineItem.Event("m.space.child", anEventTimelineItem(content = StateContent("", OtherState.SpaceChild))), - MatrixTimelineItem.Event("m.space.parent", anEventTimelineItem(content = StateContent("", OtherState.SpaceParent))), - MatrixTimelineItem.Event("m.room.policy.rule.room", anEventTimelineItem(content = StateContent("", OtherState.PolicyRuleRoom))), - MatrixTimelineItem.Event("m.room.policy.rule.server", anEventTimelineItem(content = StateContent("", OtherState.PolicyRuleServer))), - MatrixTimelineItem.Event("m.room.policy.rule.user", anEventTimelineItem(content = StateContent("", OtherState.PolicyRuleUser))), - ) - - val expected = listOf( - MatrixTimelineItem.Other, - MatrixTimelineItem.Virtual("virtual", VirtualTimelineItem.ReadMarker), - MatrixTimelineItem.Event("event", anEventTimelineItem()), - MatrixTimelineItem.Event("m.room.avatar", anEventTimelineItem(content = StateContent("", OtherState.RoomAvatar("")))), - MatrixTimelineItem.Event("m.room.create", anEventTimelineItem(content = StateContent("", OtherState.RoomCreate))), - MatrixTimelineItem.Event("m.room.encrypted", anEventTimelineItem(content = StateContent("", OtherState.RoomEncryption))), - MatrixTimelineItem.Event("m.room.name", anEventTimelineItem(content = StateContent("", OtherState.RoomName("")))), - MatrixTimelineItem.Event("m.room.third_party_invite", anEventTimelineItem(content = StateContent("", OtherState.RoomThirdPartyInvite("")))), - MatrixTimelineItem.Event("m.room.topic", anEventTimelineItem(content = StateContent("", OtherState.RoomTopic("")))), - MatrixTimelineItem.Event("m.room.custom", anEventTimelineItem(content = StateContent("", OtherState.Custom("")))), - ) - - val processor = FilterHiddenStateEventsProcessor() - - assertThat(processor.process(items)).isEqualTo(expected) - } -} From e63e5bd739277837116a05722663d69536a74032 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 8 Feb 2024 14:38:44 +0100 Subject: [PATCH 099/119] Remove patch on ActionListView and ensure all the test are passing. --- .../impl/actionlist/ActionListView.kt | 51 ++++--------------- .../messages/impl/MessagesViewTest.kt | 2 + 2 files changed, 11 insertions(+), 42 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index 44d2d6ee4c..3db91fd67c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -45,7 +45,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription @@ -87,7 +86,6 @@ import io.element.android.libraries.designsystem.theme.components.hide import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList -import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -103,52 +101,28 @@ fun ActionListView( val targetItem = (state.target as? ActionListState.Target.Success)?.event fun onItemActionClicked( - itemAction: TimelineItemAction, - immediate: Boolean, + itemAction: TimelineItemAction ) { if (targetItem == null) return - if (immediate) { - coroutineScope.launch { sheetState.hide() } + sheetState.hide(coroutineScope) { state.eventSink(ActionListEvents.Clear) onActionSelected(itemAction, targetItem) - } else { - sheetState.hide(coroutineScope) { - state.eventSink(ActionListEvents.Clear) - onActionSelected(itemAction, targetItem) - } } } - fun onEmojiReactionClicked( - emoji: String, - immediate: Boolean, - ) { + fun onEmojiReactionClicked(emoji: String) { if (targetItem == null) return - if (immediate) { - coroutineScope.launch { sheetState.hide() } + sheetState.hide(coroutineScope) { state.eventSink(ActionListEvents.Clear) onEmojiReactionClicked(emoji, targetItem) - } else { - sheetState.hide(coroutineScope) { - state.eventSink(ActionListEvents.Clear) - onEmojiReactionClicked(emoji, targetItem) - } } } - fun onCustomReactionClicked( - immediate: Boolean, - ) { + fun onCustomReactionClicked() { if (targetItem == null) return - if (immediate) { - coroutineScope.launch { sheetState.hide() } + sheetState.hide(coroutineScope) { state.eventSink(ActionListEvents.Clear) onCustomReactionClicked(targetItem) - } else { - sheetState.hide(coroutineScope) { - state.eventSink(ActionListEvents.Clear) - onCustomReactionClicked(targetItem) - } } } @@ -162,18 +136,11 @@ fun ActionListView( onDismissRequest = ::onDismiss, modifier = modifier, ) { - val immediate = LocalInspectionMode.current SheetContent( state = state, - onActionClicked = { - onItemActionClicked(it, immediate) - }, - onEmojiReactionClicked = { - onEmojiReactionClicked(it, immediate) - }, - onCustomReactionClicked = { - onCustomReactionClicked(immediate) - }, + onActionClicked = ::onItemActionClicked, + onEmojiReactionClicked = ::onEmojiReactionClicked, + onCustomReactionClicked = ::onCustomReactionClicked, modifier = Modifier .navigationBarsPadding() .imePadding() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt index 1f4ba8bf73..449abc3f46 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt @@ -204,6 +204,8 @@ class MessagesViewTest { state = stateWithMessageAction, ) rule.clickOn(CommonStrings.action_edit) + // Give time for the close animation to complete + rule.mainClock.advanceTimeBy(milliseconds = 1_000) eventsRecorder.assertSingle(MessagesEvents.HandleAction(TimelineItemAction.Edit, timelineItem)) } From 5d6716da6786040109e7c3790ad5cefb8ef1b208 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 29 Jan 2024 14:33:07 +0100 Subject: [PATCH 100/119] Rendering typing notification #2242 --- changelog.d/2242.feature | 1 + .../messages/impl/MessagesPresenter.kt | 4 + .../features/messages/impl/MessagesState.kt | 2 + .../messages/impl/MessagesStateProvider.kt | 2 + .../features/messages/impl/MessagesView.kt | 1 + .../messages/impl/timeline/TimelineView.kt | 8 + .../typing/TypingNotificationPresenter.kt | 82 +++++++++ .../impl/typing/TypingNotificationState.kt | 24 +++ .../typing/TypingNotificationStateProvider.kt | 95 ++++++++++ .../impl/typing/TypingNotificationView.kt | 103 +++++++++++ .../messages/impl/MessagesPresenterTest.kt | 3 + .../typing/TypingNotificationPresenterTest.kt | 170 ++++++++++++++++++ .../libraries/matrix/api/room/MatrixRoom.kt | 1 + .../libraries/matrix/api/room/RoomMember.kt | 14 +- .../matrix/impl/room/RustMatrixRoom.kt | 16 ++ .../matrix/test/room/FakeMatrixRoom.kt | 7 + 16 files changed, 532 insertions(+), 1 deletion(-) create mode 100644 changelog.d/2242.feature create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenter.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationState.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt diff --git a/changelog.d/2242.feature b/changelog.d/2242.feature new file mode 100644 index 0000000000..947661653f --- /dev/null +++ b/changelog.d/2242.feature @@ -0,0 +1 @@ +Rendering typing notification diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index 1b264ac1f7..986dba4709 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -59,6 +59,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent +import io.element.android.features.messages.impl.typing.TypingNotificationPresenter import io.element.android.features.messages.impl.utils.messagesummary.MessageSummaryFormatter import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPresenter import io.element.android.features.networkmonitor.api.NetworkMonitor @@ -97,6 +98,7 @@ class MessagesPresenter @AssistedInject constructor( private val composerPresenter: MessageComposerPresenter, private val voiceMessageComposerPresenter: VoiceMessageComposerPresenter, timelinePresenterFactory: TimelinePresenter.Factory, + private val typingNotificationPresenter: TypingNotificationPresenter, private val actionListPresenter: ActionListPresenter, private val customReactionPresenter: CustomReactionPresenter, private val reactionSummaryPresenter: ReactionSummaryPresenter, @@ -129,6 +131,7 @@ class MessagesPresenter @AssistedInject constructor( val composerState = composerPresenter.present() val voiceMessageComposerState = voiceMessageComposerPresenter.present() val timelineState = timelinePresenter.present() + val typingNotificationState = typingNotificationPresenter.present() val actionListState = actionListPresenter.present() val customReactionState = customReactionPresenter.present() val reactionSummaryState = reactionSummaryPresenter.present() @@ -233,6 +236,7 @@ class MessagesPresenter @AssistedInject constructor( composerState = composerState, voiceMessageComposerState = voiceMessageComposerState, timelineState = timelineState, + typingNotificationState = typingNotificationState, actionListState = actionListState, customReactionState = customReactionState, reactionSummaryState = reactionSummaryState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt index 8e4d1c484b..5c6e1c7ffa 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt @@ -24,6 +24,7 @@ import io.element.android.features.messages.impl.timeline.components.customreact import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState +import io.element.android.features.messages.impl.typing.TypingNotificationState import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -42,6 +43,7 @@ data class MessagesState( val composerState: MessageComposerState, val voiceMessageComposerState: VoiceMessageComposerState, val timelineState: TimelineState, + val typingNotificationState: TypingNotificationState, val actionListState: ActionListState, val customReactionState: CustomReactionState, val reactionSummaryState: ReactionSummaryState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index 9e36086c45..da278904a3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -31,6 +31,7 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent +import io.element.android.features.messages.impl.typing.aTypingNotificationState import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessageComposerState import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessagePreviewState @@ -117,6 +118,7 @@ fun aMessagesState( timelineState = aTimelineState( timelineItems = aTimelineItemList(aTimelineItemTextContent()), ), + typingNotificationState = aTypingNotificationState(), retrySendMenuState = RetrySendMenuState( selectedEvent = null, eventSink = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 1c1bca9179..e629571c3c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -384,6 +384,7 @@ private fun MessagesViewContent( modifier = Modifier.padding(paddingValues), state = state.timelineState, roomName = state.roomName.dataOrNull(), + typingNotificationState = state.typingNotificationState, onMessageClicked = onMessageClicked, onMessageLongClicked = onMessageLongClicked, onUserDataClicked = onUserDataClicked, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index 72cf2be504..410e74faa4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -62,6 +62,9 @@ import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentProvider +import io.element.android.features.messages.impl.typing.TypingNotificationState +import io.element.android.features.messages.impl.typing.TypingNotificationView +import io.element.android.features.messages.impl.typing.aTypingNotificationState import io.element.android.libraries.designsystem.animation.alphaAnimation import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -75,6 +78,7 @@ import kotlinx.coroutines.launch @Composable fun TimelineView( state: TimelineState, + typingNotificationState: TypingNotificationState, roomName: String?, onUserDataClicked: (UserId) -> Unit, onMessageClicked: (TimelineItem.Event) -> Unit, @@ -112,6 +116,9 @@ fun TimelineView( reverseLayout = true, contentPadding = PaddingValues(vertical = 8.dp), ) { + item { + TypingNotificationView(state = typingNotificationState) + } items( items = state.timelineItems, contentType = { timelineItem -> timelineItem.contentType() }, @@ -256,6 +263,7 @@ internal fun TimelineViewPreview( TimelineView( state = aTimelineState(timelineItems), roomName = null, + typingNotificationState = aTypingNotificationState(), onMessageClicked = {}, onTimestampClicked = {}, onUserDataClicked = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenter.kt new file mode 100644 index 0000000000..fdbb698a02 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenter.kt @@ -0,0 +1,82 @@ +/* + * 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 + * + * http://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.typing + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.RoomMember +import io.element.android.libraries.matrix.api.room.RoomMembershipState +import io.element.android.libraries.matrix.api.room.roomMembers +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import javax.inject.Inject + +class TypingNotificationPresenter @Inject constructor( + private val room: MatrixRoom, +) : Presenter { + @Composable + override fun present(): TypingNotificationState { + var typingMembers by remember { mutableStateOf(emptyList()) } + LaunchedEffect(Unit) { + combine(room.roomTypingMembersFlow, room.membersStateFlow) { typingMembers, membersState -> + typingMembers + .map { userId -> + membersState.roomMembers() + ?.firstOrNull { roomMember -> roomMember.userId == userId } + ?: createDefaultRoomMemberForTyping(userId) + } + } + .distinctUntilChanged() + .onEach { members -> + typingMembers = members + } + .launchIn(this) + } + + return TypingNotificationState( + typingMembers = typingMembers.toImmutableList(), + ) + } +} + +/** + * Create a default [RoomMember] for typing events. + * In this case, only the userId will be used for rendering, other fields are not used, but keep them + * as close as possible to the actual data. + */ +private fun createDefaultRoomMemberForTyping(userId: UserId): RoomMember { + return RoomMember( + userId = userId, + displayName = null, + avatarUrl = null, + membership = RoomMembershipState.JOIN, + isNameAmbiguous = false, + powerLevel = 0, + normalizedPowerLevel = 0, + isIgnored = false, + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationState.kt new file mode 100644 index 0000000000..d4398a6351 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationState.kt @@ -0,0 +1,24 @@ +/* + * 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 + * + * http://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.typing + +import io.element.android.libraries.matrix.api.room.RoomMember +import kotlinx.collections.immutable.ImmutableList + +data class TypingNotificationState( + val typingMembers: ImmutableList, +) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt new file mode 100644 index 0000000000..88a3c8677f --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023 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 + * + * http://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.typing + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.room.RoomMember +import io.element.android.libraries.matrix.api.room.RoomMembershipState +import kotlinx.collections.immutable.toImmutableList + +class TypingNotificationStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aTypingNotificationState(), + aTypingNotificationState( + typingMembers = listOf( + aTypingRoomMember(), + ), + ), + aTypingNotificationState( + typingMembers = listOf( + aTypingRoomMember(displayName = "Alice"), + ), + ), + aTypingNotificationState( + typingMembers = listOf( + aTypingRoomMember(displayName = "Alice", isNameAmbiguous = true), + ), + ), + aTypingNotificationState( + typingMembers = listOf( + aTypingRoomMember(displayName = "Alice"), + aTypingRoomMember(displayName = "Bob"), + ), + ), + aTypingNotificationState( + typingMembers = listOf( + aTypingRoomMember(displayName = "Alice"), + aTypingRoomMember(displayName = "Bob"), + aTypingRoomMember(displayName = "Charlie"), + ), + ), + aTypingNotificationState( + typingMembers = listOf( + aTypingRoomMember(displayName = "Alice"), + aTypingRoomMember(displayName = "Bob"), + aTypingRoomMember(displayName = "Charlie"), + aTypingRoomMember(displayName = "Dan"), + aTypingRoomMember(displayName = "Eve"), + ), + ), + aTypingNotificationState( + typingMembers = listOf( + aTypingRoomMember(displayName = "Alice with a very long display name"), + ), + ), + ) +} + +internal fun aTypingNotificationState( + typingMembers: List = emptyList(), +) = TypingNotificationState( + typingMembers = typingMembers.toImmutableList(), +) + +internal fun aTypingRoomMember( + userId: UserId = UserId("@alice:example.com"), + displayName: String? = null, + isNameAmbiguous: Boolean = false, +): RoomMember { + return RoomMember( + userId = userId, + displayName = displayName, + avatarUrl = null, + membership = RoomMembershipState.JOIN, + isNameAmbiguous = isNameAmbiguous, + powerLevel = 0, + normalizedPowerLevel = 0, + isIgnored = false, + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt new file mode 100644 index 0000000000..7ad31abd9d --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt @@ -0,0 +1,103 @@ +/* + * 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 + * + * http://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.typing + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.features.messages.impl.R +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.matrix.api.room.RoomMember + +@Composable +fun TypingNotificationView( + state: TypingNotificationState, + modifier: Modifier = Modifier, +) { + if (state.typingMembers.isEmpty()) return + val typingNotificationText = computeTypingNotificationText(state.typingMembers) + Text( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 68.dp, vertical = 2.dp), + text = typingNotificationText, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + style = ElementTheme.typography.fontBodySmRegular, + color = ElementTheme.colors.textSecondary, + ) +} + +@Composable +private fun computeTypingNotificationText(typingMembers: List): AnnotatedString { + val names = when (typingMembers.size) { + 0 -> "" // Cannot happen + 1 -> typingMembers[0].disambiguatedDisplayName + 2 -> stringResource( + id = R.string.screen_room_typing_two_members, + typingMembers[0].disambiguatedDisplayName, + typingMembers[1].disambiguatedDisplayName, + ) + else -> pluralStringResource( + id = R.plurals.screen_room_typing_many_members, + count = typingMembers.size - 2, + typingMembers[0].disambiguatedDisplayName, + typingMembers[1].disambiguatedDisplayName, + typingMembers.size - 2, + ) + } + // Get the translated string with a fake pattern + val tmpString = pluralStringResource( + id = R.plurals.screen_room_typing_notification, + count = typingMembers.size, + "<>", + ) + // Split the string in 3 parts + val parts = tmpString.split("<>") + // And rebuild the string with the names + return buildAnnotatedString { + append(parts[0]) + withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { + append(names) + } + append(parts[1]) + } +} + +@PreviewsDayNight +@Composable +internal fun TypingNotificationViewPreview( + @PreviewParameter(TypingNotificationStateProvider::class) state: TypingNotificationState, +) = ElementPreview { + TypingNotificationView( + state = state, + ) +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index fe7e07e6f9..9e729e32e1 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -42,6 +42,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.TimelineItemVideoContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent +import io.element.android.features.messages.impl.typing.TypingNotificationPresenter import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPlayer import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPresenter import io.element.android.features.messages.impl.voicemessages.timeline.FakeRedactedVoiceMessageManager @@ -730,6 +731,7 @@ class MessagesPresenterTest { } } val actionListPresenter = ActionListPresenter(appPreferencesStore = appPreferencesStore) + val typingNotificationPresenter = TypingNotificationPresenter(matrixRoom) val readReceiptBottomSheetPresenter = ReadReceiptBottomSheetPresenter() val customReactionPresenter = CustomReactionPresenter(emojibaseProvider = FakeEmojibaseProvider()) val reactionSummaryPresenter = ReactionSummaryPresenter(room = matrixRoom) @@ -739,6 +741,7 @@ class MessagesPresenterTest { composerPresenter = messageComposerPresenter, voiceMessageComposerPresenter = voiceMessageComposerPresenter, timelinePresenterFactory = timelinePresenterFactory, + typingNotificationPresenter = typingNotificationPresenter, actionListPresenter = actionListPresenter, customReactionPresenter = customReactionPresenter, reactionSummaryPresenter = reactionSummaryPresenter, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt new file mode 100644 index 0000000000..4bedd6f6a3 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt @@ -0,0 +1,170 @@ +/* + * 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 + * + * http://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.typing + +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState +import io.element.android.libraries.matrix.api.room.RoomMember +import io.element.android.libraries.matrix.api.room.RoomMembershipState +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.A_USER_ID_2 +import io.element.android.libraries.matrix.test.A_USER_ID_3 +import io.element.android.libraries.matrix.test.A_USER_ID_4 +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomInfo +import io.element.android.tests.testutils.WarmUpRule +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test + +@Suppress("LargeClass") +class TypingNotificationPresenterTest { + @get:Rule + val warmUpRule = WarmUpRule() + + @Test + fun `present - initial state`() = runTest { + val presenter = createPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.typingMembers).isEmpty() + } + } + + @Test + fun `present - state is updated when a member is typing, member is not known`() = runTest { + val aDefaultRoomMember = createDefaultRoomMember(A_USER_ID_2) + val room = FakeMatrixRoom() + val presenter = createPresenter(matrixRoom = room) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.typingMembers).isEmpty() + room.givenRoomTypingMembers(listOf(A_USER_ID_2)) + val oneMemberTypingState = awaitItem() + assertThat(oneMemberTypingState.typingMembers.size).isEqualTo(1) + assertThat(oneMemberTypingState.typingMembers.first()).isEqualTo(aDefaultRoomMember) + // User stops typing + room.givenRoomTypingMembers(emptyList()) + val finalState = awaitItem() + assertThat(finalState.typingMembers).isEmpty() + } + } + + @Test + fun `present - state is updated when a member is typing, member is known`() = runTest { + val aKnownRoomMember = createKnownRoomMember(userId = A_USER_ID_2) + val room = FakeMatrixRoom().apply { + givenRoomMembersState( + MatrixRoomMembersState.Ready( + listOf( + createKnownRoomMember(A_USER_ID), + aKnownRoomMember, + createKnownRoomMember(A_USER_ID_3), + createKnownRoomMember(A_USER_ID_4), + ).toImmutableList() + ) + ) + } + val presenter = createPresenter(matrixRoom = room) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.typingMembers).isEmpty() + room.givenRoomTypingMembers(listOf(A_USER_ID_2)) + val oneMemberTypingState = awaitItem() + assertThat(oneMemberTypingState.typingMembers.size).isEqualTo(1) + assertThat(oneMemberTypingState.typingMembers.first()).isEqualTo(aKnownRoomMember) + // User stops typing + room.givenRoomTypingMembers(emptyList()) + val finalState = awaitItem() + assertThat(finalState.typingMembers).isEmpty() + } + } + + @Test + fun `present - state is updated when a member is typing, member is not known, then known`() = runTest { + val aDefaultRoomMember = createDefaultRoomMember(A_USER_ID_2) + val aKnownRoomMember = createKnownRoomMember(A_USER_ID_2) + val room = FakeMatrixRoom() + val presenter = createPresenter(matrixRoom = room) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.typingMembers).isEmpty() + room.givenRoomTypingMembers(listOf(A_USER_ID_2)) + val oneMemberTypingState = awaitItem() + assertThat(oneMemberTypingState.typingMembers.size).isEqualTo(1) + assertThat(oneMemberTypingState.typingMembers.first()).isEqualTo(aDefaultRoomMember) + // User is getting known + room.givenRoomMembersState( + MatrixRoomMembersState.Ready( + listOf(aKnownRoomMember).toImmutableList() + ) + ) + val finalState = awaitItem() + assertThat(finalState.typingMembers.first()).isEqualTo(aKnownRoomMember) + } + } + + private fun createPresenter( + matrixRoom: MatrixRoom = FakeMatrixRoom().apply { + givenRoomInfo(aRoomInfo(id = roomId.value, name = "")) + }, + ): TypingNotificationPresenter { + return TypingNotificationPresenter( + room = matrixRoom, + ) + } + + private fun createDefaultRoomMember( + userId: UserId, + ) = RoomMember( + userId = userId, + displayName = null, + avatarUrl = null, + membership = RoomMembershipState.JOIN, + isNameAmbiguous = false, + powerLevel = 0, + normalizedPowerLevel = 0, + isIgnored = false, + ) + + private fun createKnownRoomMember( + userId: UserId, + ) = RoomMember( + userId = userId, + displayName = "Alice Doe", + avatarUrl = "an_avatar_url", + membership = RoomMembershipState.JOIN, + isNameAmbiguous = true, + powerLevel = 0, + normalizedPowerLevel = 0, + isIgnored = false, + ) +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index 051e0e419e..db5cd6c260 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -57,6 +57,7 @@ interface MatrixRoom : Closeable { val isDm: Boolean get() = isDirect && isOneToOne val roomInfoFlow: Flow + val roomTypingMembersFlow: Flow> /** * A one-to-one is a room with exactly 2 members. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt index 86cff93688..908390484b 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt @@ -27,7 +27,19 @@ data class RoomMember( val powerLevel: Long, val normalizedPowerLevel: Long, val isIgnored: Boolean, -) +) { + /** + * Disambiguated display name for the RoomMember. + * If the display name is null, the user ID is returned. + * If the display name is ambiguous, the user ID is appended in parentheses. + * Otherwise, the display name is returned. + */ + val disambiguatedDisplayName: String = when { + displayName == null -> userId.value + isNameAmbiguous -> "$displayName ($userId)" + else -> displayName + } +} enum class RoomMembershipState { BAN, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 36b6cde936..65c648e5e2 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -73,6 +73,7 @@ import org.matrix.rustcomponents.sdk.RoomInfoListener import org.matrix.rustcomponents.sdk.RoomListItem import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle +import org.matrix.rustcomponents.sdk.TypingNotificationsListener import org.matrix.rustcomponents.sdk.WidgetCapabilities import org.matrix.rustcomponents.sdk.WidgetCapabilitiesProvider import org.matrix.rustcomponents.sdk.messageEventContentFromHtml @@ -113,6 +114,21 @@ class RustMatrixRoom( }) } + override val roomTypingMembersFlow: Flow> = mxCallbackFlow { + launch { + val initial = emptyList() + channel.trySend(initial) + } + innerRoom.subscribeToTypingNotifications(object : TypingNotificationsListener { + override fun call(typingUsers: List) { + channel.trySend( + typingUsers + .filter { it != sessionData.userId } + .map(::UserId)) + } + }) + } + // Create a dispatcher for all room methods... private val roomDispatcher = coroutineDispatchers.io.limitedParallelism(32) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index fdd0dbb6cb..60969cfd0b 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -170,6 +170,9 @@ class FakeMatrixRoom( private val _roomInfoFlow: MutableSharedFlow = MutableSharedFlow(replay = 1) override val roomInfoFlow: Flow = _roomInfoFlow + private val _roomTypingMembersFlow: MutableSharedFlow> = MutableSharedFlow(replay = 1) + override val roomTypingMembersFlow: Flow> = _roomTypingMembersFlow + override val membersStateFlow: MutableStateFlow = MutableStateFlow(MatrixRoomMembersState.Unknown) override val roomNotificationSettingsStateFlow: MutableStateFlow = @@ -589,6 +592,10 @@ class FakeMatrixRoom( fun givenRoomInfo(roomInfo: MatrixRoomInfo) { _roomInfoFlow.tryEmit(roomInfo) } + + fun givenRoomTypingMembers(typingMembers: List) { + _roomTypingMembersFlow.tryEmit(typingMembers) + } } data class SendLocationInvocation( From dfaf4ab8c86a6a2edba438e2d9b1ab458528d8a9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 29 Jan 2024 16:20:18 +0100 Subject: [PATCH 101/119] Typing: add some specific MessagesView preview. --- .../typing/MessagesViewWithTypingPreview.kt | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/MessagesViewWithTypingPreview.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/MessagesViewWithTypingPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/MessagesViewWithTypingPreview.kt new file mode 100644 index 0000000000..3dbc21cb15 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/MessagesViewWithTypingPreview.kt @@ -0,0 +1,46 @@ +/* + * 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 + * + * http://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.typing + +import androidx.compose.runtime.Composable +import io.element.android.features.messages.impl.MessagesView +import io.element.android.features.messages.impl.aMessagesState +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight + +@PreviewsDayNight +@Composable +internal fun MessagesViewWithTypingPreview() = ElementPreview { + MessagesView( + state = aMessagesState().copy( + typingNotificationState = aTypingNotificationState( + typingMembers = listOf( + aTypingRoomMember(displayName = "Alice"), + aTypingRoomMember(displayName = "Bob"), + ), + ), + ), + onBackPressed = {}, + onRoomDetailsClicked = {}, + onEventClicked = { false }, + onPreviewAttachments = {}, + onUserDataClicked = {}, + onSendLocationClicked = {}, + onCreatePollClicked = {}, + onJoinCallClicked = {}, + ) +} From 7b809ac50762259c59eac9aeba62225d09a8f58b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 29 Jan 2024 18:20:27 +0100 Subject: [PATCH 102/119] Add a way to hide the Jump to Bottom button for coherent preview --- .../android/features/messages/impl/MessagesView.kt | 5 +++++ .../features/messages/impl/timeline/TimelineView.kt | 9 ++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index e629571c3c..fd1c6bf19b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -125,6 +125,7 @@ fun MessagesView( onCreatePollClicked: () -> Unit, onJoinCallClicked: () -> Unit, modifier: Modifier = Modifier, + forceJumpToBottomVisibility: Boolean = false ) { OnLifecycleEvent { _, event -> state.voiceMessageComposerState.eventSink(VoiceMessageComposerEvents.LifecycleEvent(event)) @@ -224,6 +225,7 @@ fun MessagesView( onSwipeToReply = { targetEvent -> state.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Reply, targetEvent)) }, + forceJumpToBottomVisibility = forceJumpToBottomVisibility, ) }, snackbarHost = { @@ -324,6 +326,7 @@ private fun MessagesViewContent( onTimestampClicked: (TimelineItem.Event) -> Unit, onSendLocationClicked: () -> Unit, onCreatePollClicked: () -> Unit, + forceJumpToBottomVisibility: Boolean, modifier: Modifier = Modifier, onSwipeToReply: (TimelineItem.Event) -> Unit, ) { @@ -394,6 +397,7 @@ private fun MessagesViewContent( onMoreReactionsClicked = onMoreReactionsClicked, onReadReceiptClick = onReadReceiptClick, onSwipeToReply = onSwipeToReply, + forceJumpToBottomVisibility = forceJumpToBottomVisibility, ) }, sheetContent = { subcomposing: Boolean -> @@ -574,5 +578,6 @@ internal fun MessagesViewPreview(@PreviewParameter(MessagesStateProvider::class) onSendLocationClicked = {}, onCreatePollClicked = {}, onJoinCallClicked = {}, + forceJumpToBottomVisibility = true, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index 410e74faa4..3c0d7fc67b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -47,7 +47,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.rotate -import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp @@ -90,6 +89,7 @@ fun TimelineView( onMoreReactionsClicked: (TimelineItem.Event) -> Unit, onReadReceiptClick: (TimelineItem.Event) -> Unit, modifier: Modifier = Modifier, + forceJumpToBottomVisibility: Boolean = false ) { fun onReachedLoadMore() { state.eventSink(TimelineEvents.LoadMore) @@ -164,6 +164,7 @@ fun TimelineView( TimelineScrollHelper( isTimelineEmpty = state.timelineItems.isEmpty(), lazyListState = lazyListState, + forceJumpToBottomVisibility = forceJumpToBottomVisibility, newEventState = state.newEventState, onScrollFinishedAt = ::onScrollFinishedAt ) @@ -175,6 +176,7 @@ private fun BoxScope.TimelineScrollHelper( isTimelineEmpty: Boolean, lazyListState: LazyListState, newEventState: NewEventState, + forceJumpToBottomVisibility: Boolean, onScrollFinishedAt: (Int) -> Unit, ) { val coroutineScope = rememberCoroutineScope() @@ -212,7 +214,7 @@ private fun BoxScope.TimelineScrollHelper( JumpToBottomButton( // Use inverse of canAutoScroll otherwise we might briefly see the before the scroll animation is triggered - isVisible = !canAutoScroll, + isVisible = !canAutoScroll || forceJumpToBottomVisibility, modifier = Modifier .align(Alignment.BottomEnd) .padding(end = 24.dp, bottom = 12.dp), @@ -228,7 +230,7 @@ private fun JumpToBottomButton( ) { AnimatedVisibility( modifier = modifier, - visible = isVisible || LocalInspectionMode.current, + visible = isVisible, enter = scaleIn(animationSpec = tween(100)), exit = scaleOut(animationSpec = tween(100)), ) { @@ -273,6 +275,7 @@ internal fun TimelineViewPreview( onMoreReactionsClicked = {}, onSwipeToReply = {}, onReadReceiptClick = {}, + forceJumpToBottomVisibility = true, ) } } From 656ef9d6d0dccb7c3e01d9389f80179cfa58705a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 8 Feb 2024 18:17:57 +0100 Subject: [PATCH 103/119] Typing notification: reduce horizontal padding. --- .../messages/impl/typing/TypingNotificationStateProvider.kt | 2 +- .../features/messages/impl/typing/TypingNotificationView.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt index 88a3c8677f..1de315c8c5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt @@ -65,7 +65,7 @@ class TypingNotificationStateProvider : PreviewParameterProvider Date: Thu, 8 Feb 2024 18:31:37 +0100 Subject: [PATCH 104/119] Typing notification: fix test compilation issue after rebase. --- .../features/messages/impl/timeline/TimelineViewTest.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt index f80f4ce4c2..78cd671e3f 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt @@ -19,6 +19,7 @@ package io.element.android.features.messages.impl.timeline import androidx.activity.ComponentActivity import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.features.messages.impl.typing.aTypingNotificationState import io.element.android.tests.testutils.EnsureNeverCalledWithParam import io.element.android.tests.testutils.EnsureNeverCalledWithTwoParams import io.element.android.tests.testutils.EventsRecorder @@ -41,6 +42,7 @@ class TimelineViewTest { hasMoreToLoadBackwards = true, ) ), + typingNotificationState = aTypingNotificationState(), roomName = null, onUserDataClicked = EnsureNeverCalledWithParam(), onMessageClicked = EnsureNeverCalledWithParam(), @@ -67,6 +69,7 @@ class TimelineViewTest { hasMoreToLoadBackwards = false, ) ), + typingNotificationState = aTypingNotificationState(), roomName = null, onUserDataClicked = EnsureNeverCalledWithParam(), onMessageClicked = EnsureNeverCalledWithParam(), From 5d055a5fd22c7a83636a184b11d9af7bbc90bcf1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:07:01 +0000 Subject: [PATCH 105/119] Update dependency org.matrix.rustcomponents:sdk-android to v0.1.99 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 469c43a47b..e28c900f7b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -152,7 +152,7 @@ jsoup = "org.jsoup:jsoup:1.17.2" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = "app.cash.molecule:molecule-runtime:1.3.2" timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.98" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.99" matrix_richtexteditor = { module = "io.element.android:wysiwyg", 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" } From 2c24a48ed70b95e0116aca9b7a83aa592eb0922a Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 8 Feb 2024 20:46:27 +0100 Subject: [PATCH 106/119] Rust : map the new filter api, will need more rework when branching the new ui later. --- .../matrix/api/roomlist/DynamicRoomList.kt | 26 +-------- .../matrix/api/roomlist/RoomListFilter.kt | 56 +++++++++++++++++++ .../matrix/impl/roomlist/RoomListFactory.kt | 17 ++---- .../matrix/impl/roomlist/RoomListFilter.kt | 35 ++++++++++++ .../impl/roomlist/RustRoomListService.kt | 3 +- 5 files changed, 99 insertions(+), 38 deletions(-) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListFilter.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilter.kt diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/DynamicRoomList.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/DynamicRoomList.kt index 02833762e2..8cb43af24c 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/DynamicRoomList.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/DynamicRoomList.kt @@ -28,29 +28,7 @@ import kotlinx.coroutines.flow.onEach * It lets load rooms on demand and filter them. */ interface DynamicRoomList : RoomList { - sealed interface Filter { - /** - * No filter applied. - */ - data object All : Filter - - /** - * Filter only the left rooms. - */ - data object AllNonLeft : Filter - - /** - * Filter all rooms. - */ - data object None : Filter - - /** - * Filter rooms by normalized room name. - */ - data class NormalizedMatchRoomName(val pattern: String) : Filter - } - - val currentFilter: StateFlow + val currentFilter: StateFlow val loadedPages: StateFlow val pageSize: Int @@ -68,7 +46,7 @@ interface DynamicRoomList : RoomList { * Update the filter to apply to the list. * @param filter the filter to apply. */ - suspend fun updateFilter(filter: Filter) + suspend fun updateFilter(filter: RoomListFilter) } /** diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListFilter.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListFilter.kt new file mode 100644 index 0000000000..99ba4531e2 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListFilter.kt @@ -0,0 +1,56 @@ +/* + * 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 + * + * http://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.libraries.matrix.api.roomlist + +sealed interface RoomListFilter { + companion object { + fun all(vararg filters: RoomListFilter): RoomListFilter { + return All(filters.toList()) + } + + fun any(vararg filters: RoomListFilter): RoomListFilter { + return Any(filters.toList()) + } + } + + data class All( + val filters: List + ) : RoomListFilter + + data class Any( + val filters: List + ) : RoomListFilter + + data object NonLeft : RoomListFilter + + data object Unread : RoomListFilter + + sealed interface Category : RoomListFilter { + data object Group : Category + data object People : Category + } + + data object None : RoomListFilter + + data class NormalizedMatchRoomName( + val pattern: String + ) : RoomListFilter + + data class FuzzyMatchRoomName( + val pattern: String + ) : RoomListFilter +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt index c8d4dfd507..f0d9c7e2a4 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.roomlist import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList import io.element.android.libraries.matrix.api.roomlist.RoomList +import io.element.android.libraries.matrix.api.roomlist.RoomListFilter import io.element.android.libraries.matrix.api.roomlist.RoomSummary import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope @@ -28,7 +29,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind import org.matrix.rustcomponents.sdk.RoomListLoadingState import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService @@ -44,7 +44,7 @@ internal class RoomListFactory( */ fun createRoomList( pageSize: Int, - initialFilter: DynamicRoomList.Filter = DynamicRoomList.Filter.All, + initialFilter: RoomListFilter = RoomListFilter.all(), innerProvider: suspend () -> InnerRoomList ): DynamicRoomList { val loadingStateFlow: MutableStateFlow = MutableStateFlow(RoomList.LoadingState.NotLoaded) @@ -91,7 +91,7 @@ internal class RoomListFactory( private class RustDynamicRoomList( override val summaries: MutableStateFlow>, override val loadingState: MutableStateFlow, - override val currentFilter: MutableStateFlow, + override val currentFilter: MutableStateFlow, override val loadedPages: MutableStateFlow, private val dynamicEvents: MutableSharedFlow, private val processor: RoomSummaryListProcessor, @@ -101,7 +101,7 @@ private class RustDynamicRoomList( processor.rebuildRoomSummaries() } - override suspend fun updateFilter(filter: DynamicRoomList.Filter) { + override suspend fun updateFilter(filter: RoomListFilter) { currentFilter.emit(filter) val filterEvent = RoomListDynamicEvents.SetFilter(filter.toRustFilter()) dynamicEvents.emit(filterEvent) @@ -124,12 +124,3 @@ private fun RoomListLoadingState.toLoadingState(): RoomList.LoadingState { RoomListLoadingState.NotLoaded -> RoomList.LoadingState.NotLoaded } } - -private fun DynamicRoomList.Filter.toRustFilter(): RoomListEntriesDynamicFilterKind { - return when (this) { - DynamicRoomList.Filter.All -> RoomListEntriesDynamicFilterKind.All - is DynamicRoomList.Filter.NormalizedMatchRoomName -> RoomListEntriesDynamicFilterKind.NormalizedMatchRoomName(this.pattern) - DynamicRoomList.Filter.None -> RoomListEntriesDynamicFilterKind.None - DynamicRoomList.Filter.AllNonLeft -> RoomListEntriesDynamicFilterKind.AllNonLeft - } -} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilter.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilter.kt new file mode 100644 index 0000000000..c28da59ea4 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilter.kt @@ -0,0 +1,35 @@ +/* + * 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 + * + * http://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.libraries.matrix.impl.roomlist + +import io.element.android.libraries.matrix.api.roomlist.RoomListFilter +import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind +import org.matrix.rustcomponents.sdk.RoomListFilterCategory + +fun RoomListFilter.toRustFilter(): RoomListEntriesDynamicFilterKind { + return when (this) { + is RoomListFilter.All -> RoomListEntriesDynamicFilterKind.All(filters.map { it.toRustFilter() }) + is RoomListFilter.Any -> RoomListEntriesDynamicFilterKind.Any(filters.map { it.toRustFilter() }) + RoomListFilter.Category.Group -> RoomListEntriesDynamicFilterKind.Category(RoomListFilterCategory.GROUP) + RoomListFilter.Category.People -> RoomListEntriesDynamicFilterKind.Category(RoomListFilterCategory.PEOPLE) + is RoomListFilter.FuzzyMatchRoomName -> RoomListEntriesDynamicFilterKind.FuzzyMatchRoomName(pattern) + RoomListFilter.NonLeft -> RoomListEntriesDynamicFilterKind.NonLeft + RoomListFilter.None -> RoomListEntriesDynamicFilterKind.None + is RoomListFilter.NormalizedMatchRoomName -> RoomListEntriesDynamicFilterKind.NormalizedMatchRoomName(pattern) + RoomListFilter.Unread -> RoomListEntriesDynamicFilterKind.Unread + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt index 77d3a4e1fe..4fef34b571 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.roomlist import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList import io.element.android.libraries.matrix.api.roomlist.RoomList +import io.element.android.libraries.matrix.api.roomlist.RoomListFilter import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.loadAllIncrementally import kotlinx.coroutines.CoroutineScope @@ -45,7 +46,7 @@ internal class RustRoomListService( ) : RoomListService { override val allRooms: DynamicRoomList = roomListFactory.createRoomList( pageSize = DEFAULT_PAGE_SIZE, - initialFilter = DynamicRoomList.Filter.AllNonLeft, + initialFilter = RoomListFilter.all(RoomListFilter.NonLeft), ) { innerRoomListService.allRooms() } From 76cd6ecaef88ca6a3df09eba55ffdd1f82a4b012 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 8 Feb 2024 21:03:18 +0100 Subject: [PATCH 107/119] Rust : fix tests for the new filter api --- .../libraries/matrix/test/roomlist/FakeRoomListService.kt | 5 +++-- .../libraries/matrix/test/roomlist/SimplePagedRoomList.kt | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt index e998a35ecf..7540d6cee8 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.test.roomlist import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList import io.element.android.libraries.matrix.api.roomlist.RoomList +import io.element.android.libraries.matrix.api.roomlist.RoomListFilter import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.RoomSummary import kotlinx.coroutines.flow.MutableStateFlow @@ -61,13 +62,13 @@ class FakeRoomListService : RoomListService { override val allRooms: DynamicRoomList = SimplePagedRoomList( allRoomSummariesFlow, allRoomsLoadingStateFlow, - MutableStateFlow(DynamicRoomList.Filter.None) + MutableStateFlow(RoomListFilter.all()) ) override val invites: RoomList = SimplePagedRoomList( inviteRoomSummariesFlow, inviteRoomsLoadingStateFlow, - MutableStateFlow(DynamicRoomList.Filter.None) + MutableStateFlow(RoomListFilter.all()) ) override fun updateAllRoomsVisibleRange(range: IntRange) { diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimplePagedRoomList.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimplePagedRoomList.kt index e5f70b1b30..4f1b07ce69 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimplePagedRoomList.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimplePagedRoomList.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.test.roomlist import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList import io.element.android.libraries.matrix.api.roomlist.RoomList +import io.element.android.libraries.matrix.api.roomlist.RoomListFilter import io.element.android.libraries.matrix.api.roomlist.RoomSummary import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -26,7 +27,7 @@ import kotlinx.coroutines.flow.getAndUpdate data class SimplePagedRoomList( override val summaries: StateFlow>, override val loadingState: StateFlow, - override val currentFilter: MutableStateFlow + override val currentFilter: MutableStateFlow ) : DynamicRoomList { override val pageSize: Int = Int.MAX_VALUE override val loadedPages = MutableStateFlow(1) @@ -40,7 +41,7 @@ data class SimplePagedRoomList( loadedPages.emit(1) } - override suspend fun updateFilter(filter: DynamicRoomList.Filter) { + override suspend fun updateFilter(filter: RoomListFilter) { currentFilter.emit(filter) } From 5213849f8bff26074219faf94eff99a23c63e83e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 9 Feb 2024 09:35:52 +0100 Subject: [PATCH 108/119] Typing notification: disable rendering when the user preference `isRenderTypingNotificationsEnabled` is false. --- .../typing/TypingNotificationPresenter.kt | 46 ++++++++++++------- .../impl/typing/TypingNotificationState.kt | 1 + .../typing/TypingNotificationStateProvider.kt | 1 + .../impl/typing/TypingNotificationView.kt | 2 +- .../messages/impl/MessagesPresenterTest.kt | 5 +- .../typing/TypingNotificationPresenterTest.kt | 43 +++++++++++++++++ 6 files changed, 80 insertions(+), 18 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenter.kt index fdbb698a02..92cae4c4d9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenter.kt @@ -18,10 +18,12 @@ package io.element.android.features.messages.impl.typing import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import io.element.android.features.preferences.api.store.SessionPreferencesStore import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom @@ -29,6 +31,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipState import io.element.android.libraries.matrix.api.room.roomMembers import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn @@ -37,30 +40,41 @@ import javax.inject.Inject class TypingNotificationPresenter @Inject constructor( private val room: MatrixRoom, + private val sessionPreferencesStore: SessionPreferencesStore, ) : Presenter { @Composable override fun present(): TypingNotificationState { - var typingMembers by remember { mutableStateOf(emptyList()) } - LaunchedEffect(Unit) { - combine(room.roomTypingMembersFlow, room.membersStateFlow) { typingMembers, membersState -> - typingMembers - .map { userId -> - membersState.roomMembers() - ?.firstOrNull { roomMember -> roomMember.userId == userId } - ?: createDefaultRoomMemberForTyping(userId) - } + val typingMembersState = remember { mutableStateOf(emptyList()) } + val renderTypingNotifications by sessionPreferencesStore.isRenderTypingNotificationsEnabled().collectAsState(initial = true) + LaunchedEffect(renderTypingNotifications) { + if (renderTypingNotifications) { + observeRoomTypingMembers(typingMembersState) + } else { + typingMembersState.value = emptyList() } - .distinctUntilChanged() - .onEach { members -> - typingMembers = members - } - .launchIn(this) } return TypingNotificationState( - typingMembers = typingMembers.toImmutableList(), + renderTypingNotifications = renderTypingNotifications, + typingMembers = typingMembersState.value.toImmutableList(), ) } + + private fun CoroutineScope.observeRoomTypingMembers(typingMembersState: MutableState>) { + combine(room.roomTypingMembersFlow, room.membersStateFlow) { typingMembers, membersState -> + typingMembers + .map { userId -> + membersState.roomMembers() + ?.firstOrNull { roomMember -> roomMember.userId == userId } + ?: createDefaultRoomMemberForTyping(userId) + } + } + .distinctUntilChanged() + .onEach { members -> + typingMembersState.value = members + } + .launchIn(this) + } } /** diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationState.kt index d4398a6351..380586f9c7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationState.kt @@ -20,5 +20,6 @@ import io.element.android.libraries.matrix.api.room.RoomMember import kotlinx.collections.immutable.ImmutableList data class TypingNotificationState( + val renderTypingNotifications: Boolean, val typingMembers: ImmutableList, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt index 1de315c8c5..92d7e357d2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt @@ -74,6 +74,7 @@ class TypingNotificationStateProvider : PreviewParameterProvider = emptyList(), ) = TypingNotificationState( + renderTypingNotifications = true, typingMembers = typingMembers.toImmutableList(), ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt index 97ed4f85dd..633758c541 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt @@ -42,7 +42,7 @@ fun TypingNotificationView( state: TypingNotificationState, modifier: Modifier = Modifier, ) { - if (state.typingMembers.isEmpty()) return + if (state.typingMembers.isEmpty() || !state.renderTypingNotifications) return val typingNotificationText = computeTypingNotificationText(state.typingMembers) Text( modifier = modifier diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 9e729e32e1..44dc96d804 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -731,7 +731,10 @@ class MessagesPresenterTest { } } val actionListPresenter = ActionListPresenter(appPreferencesStore = appPreferencesStore) - val typingNotificationPresenter = TypingNotificationPresenter(matrixRoom) + val typingNotificationPresenter = TypingNotificationPresenter( + room = matrixRoom, + sessionPreferencesStore = sessionPreferencesStore, + ) val readReceiptBottomSheetPresenter = ReadReceiptBottomSheetPresenter() val customReactionPresenter = CustomReactionPresenter(emojibaseProvider = FakeEmojibaseProvider()) val reactionSummaryPresenter = ReactionSummaryPresenter(room = matrixRoom) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt index 4bedd6f6a3..a879c5a4bc 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt @@ -20,6 +20,8 @@ import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.preferences.api.store.SessionPreferencesStore +import io.element.android.libraries.featureflag.test.InMemorySessionPreferencesStore import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState @@ -49,10 +51,47 @@ class TypingNotificationPresenterTest { presenter.present() }.test { val initialState = awaitItem() + assertThat(initialState.renderTypingNotifications).isTrue() assertThat(initialState.typingMembers).isEmpty() } } + @Test + fun `present - typing notification disabled`() = runTest { + val aDefaultRoomMember = createDefaultRoomMember(A_USER_ID_2) + val room = FakeMatrixRoom() + val sessionPreferencesStore = InMemorySessionPreferencesStore( + isRenderTypingNotificationsEnabled = false + ) + val presenter = createPresenter( + matrixRoom = room, + sessionPreferencesStore = sessionPreferencesStore, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(1) + val initialState = awaitItem() + assertThat(initialState.renderTypingNotifications).isFalse() + assertThat(initialState.typingMembers).isEmpty() + room.givenRoomTypingMembers(listOf(A_USER_ID_2)) + expectNoEvents() + // Preferences changes + sessionPreferencesStore.setRenderTypingNotifications(true) + skipItems(1) + val oneMemberTypingState = awaitItem() + assertThat(oneMemberTypingState.renderTypingNotifications).isTrue() + assertThat(oneMemberTypingState.typingMembers.size).isEqualTo(1) + assertThat(oneMemberTypingState.typingMembers.first()).isEqualTo(aDefaultRoomMember) + // Preferences changes again + sessionPreferencesStore.setRenderTypingNotifications(false) + skipItems(1) + val finalState = awaitItem() + assertThat(finalState.renderTypingNotifications).isFalse() + assertThat(finalState.typingMembers).isEmpty() + } + } + @Test fun `present - state is updated when a member is typing, member is not known`() = runTest { val aDefaultRoomMember = createDefaultRoomMember(A_USER_ID_2) @@ -136,9 +175,13 @@ class TypingNotificationPresenterTest { matrixRoom: MatrixRoom = FakeMatrixRoom().apply { givenRoomInfo(aRoomInfo(id = roomId.value, name = "")) }, + sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore( + isRenderTypingNotificationsEnabled = true + ), ): TypingNotificationPresenter { return TypingNotificationPresenter( room = matrixRoom, + sessionPreferencesStore = sessionPreferencesStore, ) } From b229bb19ce824438d24ffb89cc710fdc837e09c3 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 9 Feb 2024 08:45:45 +0000 Subject: [PATCH 109/119] Update screenshots --- ...l_MessagesViewWithTyping-Day-59_59_null,NEXUS_5,1.0,en].png | 3 +++ ...MessagesViewWithTyping-Night-59_60_null,NEXUS_5,1.0,en].png | 3 +++ ...TypingNotificationView-Day-60_60_null_0,NEXUS_5,1.0,en].png | 3 +++ ...TypingNotificationView-Day-60_60_null_1,NEXUS_5,1.0,en].png | 3 +++ ...TypingNotificationView-Day-60_60_null_2,NEXUS_5,1.0,en].png | 3 +++ ...TypingNotificationView-Day-60_60_null_3,NEXUS_5,1.0,en].png | 3 +++ ...TypingNotificationView-Day-60_60_null_4,NEXUS_5,1.0,en].png | 3 +++ ...TypingNotificationView-Day-60_60_null_5,NEXUS_5,1.0,en].png | 3 +++ ...TypingNotificationView-Day-60_60_null_6,NEXUS_5,1.0,en].png | 3 +++ ...TypingNotificationView-Day-60_60_null_7,NEXUS_5,1.0,en].png | 3 +++ ...pingNotificationView-Night-60_61_null_0,NEXUS_5,1.0,en].png | 3 +++ ...pingNotificationView-Night-60_61_null_1,NEXUS_5,1.0,en].png | 3 +++ ...pingNotificationView-Night-60_61_null_2,NEXUS_5,1.0,en].png | 3 +++ ...pingNotificationView-Night-60_61_null_3,NEXUS_5,1.0,en].png | 3 +++ ...pingNotificationView-Night-60_61_null_4,NEXUS_5,1.0,en].png | 3 +++ ...pingNotificationView-Night-60_61_null_5,NEXUS_5,1.0,en].png | 3 +++ ...pingNotificationView-Night-60_61_null_6,NEXUS_5,1.0,en].png | 3 +++ ...pingNotificationView-Night-60_61_null_7,NEXUS_5,1.0,en].png | 3 +++ 18 files changed, 54 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_MessagesViewWithTyping_null_MessagesViewWithTyping-Day-59_59_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_MessagesViewWithTyping_null_MessagesViewWithTyping-Night-59_60_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_5,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_6,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_7,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_5,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_6,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_7,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_MessagesViewWithTyping_null_MessagesViewWithTyping-Day-59_59_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_MessagesViewWithTyping_null_MessagesViewWithTyping-Day-59_59_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9e5eb38198 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_MessagesViewWithTyping_null_MessagesViewWithTyping-Day-59_59_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0dda5041c16d690fe8e0c13aaca813a06acbcee8e9ab6268256ad8925defe5d0 +size 55428 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_MessagesViewWithTyping_null_MessagesViewWithTyping-Night-59_60_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_MessagesViewWithTyping_null_MessagesViewWithTyping-Night-59_60_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..aeb62038c2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_MessagesViewWithTyping_null_MessagesViewWithTyping-Night-59_60_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6946a2d952a72e29aaa54543ce0e8a73b7ab7c6db2c6fd23ac833fa7f61f8425 +size 54141 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..665c8811ac --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb0d3bfcfd75cbd75fd9270ff1dc27090e5dbac79ca8db8a46d91a4c12bc966b +size 4457 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ae320c8dbc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff84eecce0c3e489e0596c524a361e277679fc0f6b1c3f3011e50bb666074574 +size 8953 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5b271ff6fb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c46396fd61ca8188bf3ea7745dee0456f3f944c2a1fcbf04402cfb1127d8bbef +size 6856 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..45e0208327 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:272038adab5288f287dcf81c815de34945092ca20c67dad15b7e0a3715663be4 +size 9932 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fc071c13d3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3200b2363f50db05157314f5c5d9d491ffb51860e66ffe28bd4c024b6857dda0 +size 8161 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..39402506e6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3409d9e8e10bae9e90f2f9088897261619ecd8d0e6e32441671396d50fed8ac0 +size 8940 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f4281d3fe3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ca0ccfd706c8dca696481f7d1f60de83ed15435b1096c46d9dc79e21573817b +size 9265 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..919be03513 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Day-60_60_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18f8d2c5c872598293ac759a074e88ba38062e54ca9348a12aff62f3d91c34f4 +size 11149 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fae8a6fca3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c89ac73df77c2bccb0c2aa80cee1420f78e7d07f0eda89a90bffef55e8cf753 +size 4464 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3e8ac12a44 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5c002a4929aa725cf22ca46840ff3374efe4c4e1757e3b76c3a064911525ce6 +size 8875 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a4bb07b2ab --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a227b3aca2f20d895c483ca72e3c3b49a3b6263e01f10cd59dda7147f750ecc8 +size 6840 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1e529c35db --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b2d151892e95c633ef8bd6960f32eca0e00b2ddbcaba01cbca830fea356471f9 +size 9877 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..83ead64bd3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f3e0ec1f6946fa12c56d17f7ff72e7fca50ad52e41864b0351a36842a220df02 +size 8093 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..dc9eb04e55 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59ed8a9b93f09875de225096a7c7c044840c603c31e0f525522f5be13fa116a8 +size 8786 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4819f10183 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32087f3ad5e63e8075d2b981881ba36e7fb36464a10b3d7506ed1cf6d00c2e3d +size 9106 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4540dee44e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.typing_TypingNotificationView_null_TypingNotificationView-Night-60_61_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:833aa5ba2891ca8a0f81534ca2b36b6971dc851a2842507b1ae3f2f5b701961f +size 11004 From 4a170bd57e29a707c1fe5b16233ebaece4de71e5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 9 Feb 2024 09:48:20 +0100 Subject: [PATCH 110/119] Fix test compilation after change on other PR getting merged. --- .../android/features/roomlist/impl/RoomListContextMenuTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt index 6907fac598..6030ea1c6f 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt @@ -116,7 +116,7 @@ class RoomListContextMenuTest { fun `clicking on Settings invokes the expected callback and generates expected Event`() { val eventsRecorder = EventsRecorder() val contextMenu = aContextMenuShown() - val callback = EnsureCalledOnceWithParam(contextMenu.roomId) + val callback = EnsureCalledOnceWithParam(contextMenu.roomId, Unit) rule.setContent { RoomListContextMenu( contextMenu = contextMenu, From d4e6f7b3dfad22cc4bbb6c88a2b5106c3952baa3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 Feb 2024 09:09:43 +0000 Subject: [PATCH 111/119] Update dependency com.google.firebase:firebase-bom to v32.7.2 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 469c43a47b..0a0f465fbe 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -62,7 +62,7 @@ kotlin_gradle_plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", v kover_gradle_plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" } gms_google_services = "com.google.gms:google-services:4.4.0" # https://firebase.google.com/docs/android/setup#available-libraries -google_firebase_bom = "com.google.firebase:firebase-bom:32.7.1" +google_firebase_bom = "com.google.firebase:firebase-bom:32.7.2" firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppDistribution" } autonomousapps_dependencyanalysis_plugin = { module = "com.autonomousapps:dependency-analysis-gradle-plugin", version.ref = "dependencyAnalysis" } From f5089f94aa0d0dc46cb71bc84fa64812e1061acd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 Feb 2024 09:09:47 +0000 Subject: [PATCH 112/119] Update dependency com.google.gms:google-services to v4.4.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 469c43a47b..54be65c695 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -60,7 +60,7 @@ android_gradle_plugin = { module = "com.android.tools.build:gradle", version.ref android_desugar = "com.android.tools:desugar_jdk_libs:2.0.4" kotlin_gradle_plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } kover_gradle_plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" } -gms_google_services = "com.google.gms:google-services:4.4.0" +gms_google_services = "com.google.gms:google-services:4.4.1" # https://firebase.google.com/docs/android/setup#available-libraries google_firebase_bom = "com.google.firebase:firebase-bom:32.7.1" firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppDistribution" } From c8aeeec460c98e8fffa5f2ac0af08de24a5fab67 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 Feb 2024 09:09:52 +0000 Subject: [PATCH 113/119] Update dependency com.google.firebase:firebase-appdistribution-gradle to v4.1.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 469c43a47b..a1e3fb09f0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ android_gradle_plugin = "8.2.2" kotlin = "1.9.22" ksp = "1.9.22-1.0.17" -firebaseAppDistribution = "4.0.1" +firebaseAppDistribution = "4.1.0" # AndroidX core = "1.12.0" From b3715ca9c48320f1841183b5251ff00e84adbde2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 9 Feb 2024 10:11:33 +0100 Subject: [PATCH 114/119] Use ImmutableList type. --- .../features/messages/impl/typing/TypingNotificationView.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt index 633758c541..47200132d8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt @@ -36,6 +36,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.room.RoomMember +import kotlinx.collections.immutable.ImmutableList @Composable fun TypingNotificationView( @@ -57,7 +58,7 @@ fun TypingNotificationView( } @Composable -private fun computeTypingNotificationText(typingMembers: List): AnnotatedString { +private fun computeTypingNotificationText(typingMembers: ImmutableList): AnnotatedString { val names = when (typingMembers.size) { 0 -> "" // Cannot happen 1 -> typingMembers[0].disambiguatedDisplayName From 7831311d1463c540b1e0e7ecc781f24a71fdf0ce Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 9 Feb 2024 10:12:50 +0100 Subject: [PATCH 115/119] Fix compilation warning. --- .../android/libraries/matrix/impl/room/RustMatrixRoom.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 65c648e5e2..a22811f893 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -120,9 +120,9 @@ class RustMatrixRoom( channel.trySend(initial) } innerRoom.subscribeToTypingNotifications(object : TypingNotificationsListener { - override fun call(typingUsers: List) { + override fun call(typingUserIds: List) { channel.trySend( - typingUsers + typingUserIds .filter { it != sessionData.userId } .map(::UserId)) } From 3ea448e6bb5d32ab0a991bc21a592f7a67ec30eb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 9 Feb 2024 10:43:08 +0100 Subject: [PATCH 116/119] Fix formatting issue. --- .../android/libraries/matrix/impl/room/RustMatrixRoom.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index a22811f893..6ab29c1840 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -124,7 +124,8 @@ class RustMatrixRoom( channel.trySend( typingUserIds .filter { it != sessionData.userId } - .map(::UserId)) + .map(::UserId) + ) } }) } From 1df1e83efc8f78fc9665f90e6cccf24969d01299 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 9 Feb 2024 12:47:23 +0100 Subject: [PATCH 117/119] Fix Konsist test by removing the explicit type. --- .../impl/typing/TypingNotificationPresenterTest.kt | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt index a879c5a4bc..485c318e93 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt @@ -178,12 +178,10 @@ class TypingNotificationPresenterTest { sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore( isRenderTypingNotificationsEnabled = true ), - ): TypingNotificationPresenter { - return TypingNotificationPresenter( - room = matrixRoom, - sessionPreferencesStore = sessionPreferencesStore, - ) - } + ) = TypingNotificationPresenter( + room = matrixRoom, + sessionPreferencesStore = sessionPreferencesStore, + ) private fun createDefaultRoomMember( userId: UserId, From 5912f6418e30bb72f743bc24f0ac8e35d1632531 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 Feb 2024 16:53:49 +0000 Subject: [PATCH 118/119] Update wysiwyg to v2.29.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3e0a98ced6..232a845e70 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -38,7 +38,7 @@ serialization_json = "1.6.2" showkase = "1.0.2" appyx = "1.4.0" sqldelight = "2.0.1" -wysiwyg = "2.28.0" +wysiwyg = "2.29.0" # DI dagger = "2.50" From a614b0d91e1df3b28f64229ae458c92734de47a1 Mon Sep 17 00:00:00 2001 From: bmarty <3940906+bmarty@users.noreply.github.com> Date: Mon, 12 Feb 2024 00:10:26 +0000 Subject: [PATCH 119/119] Sync Strings from Localazy --- .../ftue/impl/src/main/res/values-fr/translations.xml | 2 +- .../ftue/impl/src/main/res/values-hu/translations.xml | 2 +- .../ftue/impl/src/main/res/values-in/translations.xml | 2 +- .../ftue/impl/src/main/res/values-it/translations.xml | 2 +- .../ftue/impl/src/main/res/values-ro/translations.xml | 2 +- .../ftue/impl/src/main/res/values-ru/translations.xml | 2 +- .../ftue/impl/src/main/res/values-sk/translations.xml | 2 +- .../impl/src/main/res/values-zh-rTW/translations.xml | 2 +- .../api/src/main/res/values-de/translations.xml | 1 + .../api/src/main/res/values-hu/translations.xml | 1 + .../impl/src/main/res/values-de/translations.xml | 9 +++++++++ .../impl/src/main/res/values-hu/translations.xml | 9 +++++++++ .../impl/src/main/res/values-sk/translations.xml | 11 +++++++++++ .../impl/src/main/res/values-cs/translations.xml | 2 ++ .../impl/src/main/res/values-de/translations.xml | 2 ++ .../impl/src/main/res/values-hu/translations.xml | 4 ++++ .../impl/src/main/res/values-ru/translations.xml | 2 ++ .../impl/src/main/res/values-sk/translations.xml | 2 ++ .../impl/src/main/res/values-cs/translations.xml | 1 + .../impl/src/main/res/values-de/translations.xml | 1 + .../impl/src/main/res/values-hu/translations.xml | 2 ++ .../impl/src/main/res/values-ru/translations.xml | 1 + .../impl/src/main/res/values-sk/translations.xml | 1 + .../impl/src/main/res/values-be/translations.xml | 1 + .../impl/src/main/res/values-de/translations.xml | 1 + .../impl/src/main/res/values-hu/translations.xml | 1 + .../impl/src/main/res/values-ru/translations.xml | 2 ++ .../impl/src/main/res/values-sk/translations.xml | 2 ++ .../roomdetails/impl/src/main/res/values/localazy.xml | 2 ++ .../impl/src/main/res/values-be/translations.xml | 3 +++ .../impl/src/main/res/values-cs/translations.xml | 2 ++ .../impl/src/main/res/values-de/translations.xml | 8 ++++++++ .../impl/src/main/res/values-es/translations.xml | 2 ++ .../impl/src/main/res/values-fr/translations.xml | 2 ++ .../impl/src/main/res/values-hu/translations.xml | 8 ++++++++ .../impl/src/main/res/values-in/translations.xml | 2 ++ .../impl/src/main/res/values-it/translations.xml | 2 ++ .../impl/src/main/res/values-ro/translations.xml | 2 ++ .../impl/src/main/res/values-ru/translations.xml | 2 ++ .../impl/src/main/res/values-sk/translations.xml | 4 ++++ .../impl/src/main/res/values-zh-rTW/translations.xml | 2 ++ .../impl/src/main/res/values-de/translations.xml | 2 ++ .../push/impl/src/main/res/values-de/translations.xml | 1 + .../push/impl/src/main/res/values-hu/translations.xml | 1 + .../push/impl/src/main/res/values-sk/translations.xml | 1 + .../src/main/res/values-be/translations.xml | 3 +++ .../src/main/res/values-de/translations.xml | 2 ++ .../src/main/res/values-hu/translations.xml | 2 ++ .../src/main/res/values-sk/translations.xml | 1 + 49 files changed, 118 insertions(+), 8 deletions(-) diff --git a/features/ftue/impl/src/main/res/values-fr/translations.xml b/features/ftue/impl/src/main/res/values-fr/translations.xml index 15a66c2323..9fc11413cb 100644 --- a/features/ftue/impl/src/main/res/values-fr/translations.xml +++ b/features/ftue/impl/src/main/res/values-fr/translations.xml @@ -1,6 +1,6 @@ - "Vous pourrez modifier vos paramètres ultérieurement." + "Vous pourrez modifier vos paramètres ultérieurement." "Autorisez les notifications et ne manquez aucun message" "Les appels, les sondages, les recherches et plus encore seront ajoutés plus tard cette année." "L’historique des messages pour les salons chiffrés ne sera pas disponible dans cette mise à jour." diff --git a/features/ftue/impl/src/main/res/values-hu/translations.xml b/features/ftue/impl/src/main/res/values-hu/translations.xml index 30ec7ab478..6a7acab0a4 100644 --- a/features/ftue/impl/src/main/res/values-hu/translations.xml +++ b/features/ftue/impl/src/main/res/values-hu/translations.xml @@ -1,6 +1,6 @@ - "A beállításokat később is módosíthatja." + "A beállításokat később is módosíthatja." "Értesítések engedélyezése, hogy soha ne maradjon le egyetlen üzenetről sem" "A hívások, szavazások, keresések és egyebek az év további részében kerülnek hozzáadásra." "A titkosított szobák üzenetelőzményei nem lesznek elérhetők ebben a frissítésben." diff --git a/features/ftue/impl/src/main/res/values-in/translations.xml b/features/ftue/impl/src/main/res/values-in/translations.xml index c4caa3d31c..1004f8a643 100644 --- a/features/ftue/impl/src/main/res/values-in/translations.xml +++ b/features/ftue/impl/src/main/res/values-in/translations.xml @@ -1,6 +1,6 @@ - "Anda dapat mengubah pengaturan Anda nanti." + "Anda dapat mengubah pengaturan Anda nanti." "Izinkan pemberitahuan dan jangan pernah melewatkan pesan" "Panggilan, pemungutan suara, pencarian, dan lainnya akan ditambahkan di tahun ini." "Riwayat pesan untuk ruangan terenkripsi tidak akan tersedia dalam pembaruan ini." diff --git a/features/ftue/impl/src/main/res/values-it/translations.xml b/features/ftue/impl/src/main/res/values-it/translations.xml index b4bb889c93..eaff5216dc 100644 --- a/features/ftue/impl/src/main/res/values-it/translations.xml +++ b/features/ftue/impl/src/main/res/values-it/translations.xml @@ -1,6 +1,6 @@ - "Potrai modificare le tue impostazioni in seguito." + "Potrai modificare le tue impostazioni in seguito." "Consenti le notifiche e non perdere mai un messaggio" "Chiamate, sondaggi, ricerche e altro ancora saranno aggiunti nel corso dell\'anno." "La cronologia dei messaggi per le stanze crittografate non è ancora disponibile." diff --git a/features/ftue/impl/src/main/res/values-ro/translations.xml b/features/ftue/impl/src/main/res/values-ro/translations.xml index a1b7ca15f1..d48049b8b8 100644 --- a/features/ftue/impl/src/main/res/values-ro/translations.xml +++ b/features/ftue/impl/src/main/res/values-ro/translations.xml @@ -1,6 +1,6 @@ - "Apelurile, sondajele, căutare și multe altele vor fi adăugate în cursul acestui an." + "Apelurile, sondajele, căutare și multe altele vor fi adăugate în cursul acestui an." "Istoricul mesajelor pentru camerele criptate nu va fi disponibil în această actualizare." "Ne-ar plăcea să auzim de la dumneavoastră, spuneți-ne ce părere aveți prin intermediul paginii de setări." "Să începem!" diff --git a/features/ftue/impl/src/main/res/values-ru/translations.xml b/features/ftue/impl/src/main/res/values-ru/translations.xml index 685b72ded1..6ddceef57f 100644 --- a/features/ftue/impl/src/main/res/values-ru/translations.xml +++ b/features/ftue/impl/src/main/res/values-ru/translations.xml @@ -1,6 +1,6 @@ - "Вы можете изменить настройки позже." + "Вы можете изменить настройки позже." "Разрешите уведомления и никогда не пропустите сообщение" "Звонки, опросы, поиск и многое другое будут добавлены позже в этом году." "История сообщений для зашифрованных комнат в этом обновлении будет недоступна." diff --git a/features/ftue/impl/src/main/res/values-sk/translations.xml b/features/ftue/impl/src/main/res/values-sk/translations.xml index e5e6654164..d963dbd734 100644 --- a/features/ftue/impl/src/main/res/values-sk/translations.xml +++ b/features/ftue/impl/src/main/res/values-sk/translations.xml @@ -1,6 +1,6 @@ - "Svoje nastavenia môžete neskôr zmeniť." + "Svoje nastavenia môžete neskôr zmeniť." "Povoľte oznámenia a nikdy nezmeškajte žiadnu správu" "Hovory, ankety, vyhľadávanie a ďalšie funkcie pribudnú neskôr v tomto roku." "História správ pre zašifrované miestnosti nebude v tejto aktualizácii k dispozícii." diff --git a/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml b/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml index 0edb4ec4f1..8e5d5e3109 100644 --- a/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml @@ -1,6 +1,6 @@ - "通話、投票、搜尋等更多功能將在今年登場。" + "通話、投票、搜尋等更多功能將在今年登場。" "在這次的更新,您無法查看聊天室內被加密的歷史訊息。" "我們很樂意聽取您的意見,請到設定頁面告訴我們您的想法。" "開始吧!" diff --git a/features/leaveroom/api/src/main/res/values-de/translations.xml b/features/leaveroom/api/src/main/res/values-de/translations.xml index c691f57471..e684dd1e42 100644 --- a/features/leaveroom/api/src/main/res/values-de/translations.xml +++ b/features/leaveroom/api/src/main/res/values-de/translations.xml @@ -1,5 +1,6 @@ + "Bist du sicher, dass du diese Unterhaltung verlassen willst? Diese Unterhaltung ist nicht öffentlich und du kannst ihr ohne Einladung nicht wieder beitreten." "Bist du sicher, dass du diesen Raum verlassen möchtest? Du bist die einzige Person hier. Wenn du austritst, kann in Zukunft niemand mehr eintreten, auch du nicht." "Bist du sicher, dass du diesen Raum verlassen möchtest? Dieser Raum ist nicht öffentlich und du kannst ihm ohne Einladung nicht erneut beitreten." "Bist du sicher, dass du den Raum verlassen willst?" diff --git a/features/leaveroom/api/src/main/res/values-hu/translations.xml b/features/leaveroom/api/src/main/res/values-hu/translations.xml index 0fb52a61aa..c75cddfd05 100644 --- a/features/leaveroom/api/src/main/res/values-hu/translations.xml +++ b/features/leaveroom/api/src/main/res/values-hu/translations.xml @@ -1,5 +1,6 @@ + "Biztos, hogy elhagyja ezt a beszélgetést? Ez a beszélgetés nem nyilvános, és meghívás nélkül nem fog tudni visszacsatlakozni." "Biztos, hogy elhagyja ezt a szobát? Ön az egyedüli ember itt. Ha kilép, akkor senki sem fog tudni csatlakozni a jövőben, Önt is beleértve." "Biztos, hogy elhagyod ezt a szobát? Ez a szoba nem nyilvános, és meghívó nélkül nem fogsz tudni újra belépni." "Biztos, hogy elhagyod a szobát?" diff --git a/features/messages/impl/src/main/res/values-de/translations.xml b/features/messages/impl/src/main/res/values-de/translations.xml index 8d72d7ffb2..1bf50dae29 100644 --- a/features/messages/impl/src/main/res/values-de/translations.xml +++ b/features/messages/impl/src/main/res/values-de/translations.xml @@ -12,6 +12,14 @@ "%1$d Raumänderung" "%1$d Raumänderungen" + + "%1$s, %2$s und %3$d weitere Person" + "%1$s, %2$s und %3$d weitere Person" + + + "%1$s schreibt…" + "%1$s schreiben…" + "Diese Meldung wird an den Administrator deines Homeservers weitergeleitet. Dieser kann keine verschlüsselten Nachrichten lesen." "Grund für die Meldung dieses Inhalts" "Dies ist der Anfang von %1$s." @@ -52,6 +60,7 @@ "Deine Nachricht konnte nicht gesendet werden" "Emoji hinzufügen" "Weniger anzeigen" + "%1$s und %2$s" "Zum Aufnehmen gedrückt halten" "Alle" "Benutzer blockieren" diff --git a/features/messages/impl/src/main/res/values-hu/translations.xml b/features/messages/impl/src/main/res/values-hu/translations.xml index 78e078c3c8..4f260a2fc4 100644 --- a/features/messages/impl/src/main/res/values-hu/translations.xml +++ b/features/messages/impl/src/main/res/values-hu/translations.xml @@ -12,6 +12,14 @@ "%1$d szobaváltozás" "%1$d szobaváltozás" + + "%1$s, %2$s és %3$d további felhasználó" + "%1$s, %2$s és %3$d további felhasználó" + + + "%1$s éppen ír…" + "%1$s éppen ír…" + "Ez az üzenet jelentve lesz a Matrix-kiszolgáló rendszergazdájának. Nem fogja tudni elolvasni a titkosított üzeneteket." "A tartalom jelentésének oka" "Ez a(z) %1$s kezdete." @@ -52,6 +60,7 @@ "Az üzenet elküldése sikertelen" "Emodzsi hozzáadása" "Kevesebb megjelenítése" + "%1$s és %2$s" "Tartsa a rögzítéshez" "Mindenki" "Felhasználó letiltása" diff --git a/features/messages/impl/src/main/res/values-sk/translations.xml b/features/messages/impl/src/main/res/values-sk/translations.xml index 7d2b0b903c..b4dc86aa86 100644 --- a/features/messages/impl/src/main/res/values-sk/translations.xml +++ b/features/messages/impl/src/main/res/values-sk/translations.xml @@ -13,6 +13,16 @@ "%1$d zmeny miestnosti" "%1$d zmien miestnosti" + + "%1$s, %2$s a %3$d ďalší" + "%1$s, %2$s a %3$d ďalší" + "%1$s, %2$s a %3$d ďalší" + + + "%1$s píše" + "%1$s píšu" + "%1$s píšu" + "Táto správa bude nahlásená správcovi vášho domovského servera. Nebude môcť prečítať žiadne šifrované správy." "Dôvod nahlásenia tohto obsahu" "Toto je začiatok %1$s." @@ -53,6 +63,7 @@ "Vašu správu sa nepodarilo odoslať" "Pridať emoji" "Zobraziť menej" + "%1$s a %2$s" "Podržaním nahrajte" "Všetci" "Zablokovať používateľa" diff --git a/features/preferences/impl/src/main/res/values-cs/translations.xml b/features/preferences/impl/src/main/res/values-cs/translations.xml index 96aa762e00..015a3382e4 100644 --- a/features/preferences/impl/src/main/res/values-cs/translations.xml +++ b/features/preferences/impl/src/main/res/values-cs/translations.xml @@ -8,6 +8,8 @@ "Vypněte editor formátovaného textu pro ruční zadání Markdown." "Potvrzení o přečtení" "Pokud je vypnuto, potvrzení o přečtení se nikomu neodesílají. Stále budete dostávat potvrzení o přečtení od ostatních uživatelů." + "Sdílejte přítomnost" + "Pokud je tato funkce vypnutá, nebudete moci odesílat ani přijímat potvrzení o přečtení ani upozornění na psaní" "Povolit možnost zobrazení zdroje zprávy na časové ose." "Zobrazované jméno" "Vaše zobrazované jméno" diff --git a/features/preferences/impl/src/main/res/values-de/translations.xml b/features/preferences/impl/src/main/res/values-de/translations.xml index fd5d203471..4962ad31e0 100644 --- a/features/preferences/impl/src/main/res/values-de/translations.xml +++ b/features/preferences/impl/src/main/res/values-de/translations.xml @@ -6,6 +6,8 @@ "Entwickler-Modus" "Aktivieren, um Zugriff auf Features und Funktionen für Entwickler zu aktivieren." "Deaktiviere den Rich-Text-Editor, um Markdown manuell einzugeben." + "Lesebestätigungen" + "Wenn diese Option deaktiviert ist, werden Ihre Lesebestätigungen an niemanden gesendet. Du erhältst weiterhin Lesebestätigungen von anderen Benutzern." "Option aktiveren, um Nachrichtenquelle in der Zeitleiste anzuzeigen." "Anzeigename" "Dein Anzeigename" diff --git a/features/preferences/impl/src/main/res/values-hu/translations.xml b/features/preferences/impl/src/main/res/values-hu/translations.xml index 598c61da3d..32b7c4e63d 100644 --- a/features/preferences/impl/src/main/res/values-hu/translations.xml +++ b/features/preferences/impl/src/main/res/values-hu/translations.xml @@ -6,6 +6,10 @@ "Fejlesztői mód" "Engedélyezd, hogy elérd a fejlesztőknek szánt funkciókat." "A formázott szöveges szerkesztő letiltása, hogy kézzel írhass Markdownt." + "Olvasási visszaigazolások" + "Ha ki van kapcsolva, az olvasási visszaigazolások nem lesznek elküldve senkinek. A többi felhasználó olvasási visszaigazolását továbbra is meg fogja kapni." + "Jelenlét megosztása" + "Ha ki van kapcsolva, nem tud olvasási visszaigazolást vagy írási értesítést küldeni és fogadni" "Engedélyezd a beállítást az üzenet forrásának megjelenítéséhez az idővonalon." "Megjelenítendő név" "Saját megjelenítendő név" diff --git a/features/preferences/impl/src/main/res/values-ru/translations.xml b/features/preferences/impl/src/main/res/values-ru/translations.xml index 8c79b71df5..9d9a9a636e 100644 --- a/features/preferences/impl/src/main/res/values-ru/translations.xml +++ b/features/preferences/impl/src/main/res/values-ru/translations.xml @@ -8,6 +8,8 @@ "Отключить редактор форматированного текста и включить Markdown." "Уведомления о прочтении" "Если этот параметр выключен, ваш статус о прочтении не будет отображаться. Вы по-прежнему будете видеть статус о прочтении от других пользователей." + "Поделиться присутствием" + "Если выключено, вы не сможете отправлять, получать уведомления о прочтении и наборе текста" "Включить опцию просмотра источника сообщения в ленте." "Отображаемое имя" "Ваше отображаемое имя" diff --git a/features/preferences/impl/src/main/res/values-sk/translations.xml b/features/preferences/impl/src/main/res/values-sk/translations.xml index ad0a77b61e..7115a87501 100644 --- a/features/preferences/impl/src/main/res/values-sk/translations.xml +++ b/features/preferences/impl/src/main/res/values-sk/translations.xml @@ -8,6 +8,8 @@ "Vypnite rozšírený textový editor na ručné písanie Markdown." "Potvrdenia o prečítaní" "Ak je táto funkcia vypnutá, vaše potvrdenia o prečítaní sa nebudú nikomu odosielať. Stále budete dostávať potvrdenia o prečítaní od ostatných používateľov." + "Zdieľať prítomnosť" + "Ak je vypnuté, nebudete môcť odosielať ani prijímať potvrdenia o prečítaní alebo písať upozornenia" "Povoliť možnosť zobrazenia zdroja správy na časovej osi." "Zobrazované meno" "Vaše zobrazované meno" diff --git a/features/rageshake/impl/src/main/res/values-cs/translations.xml b/features/rageshake/impl/src/main/res/values-cs/translations.xml index 824819e5ee..dbd320b974 100644 --- a/features/rageshake/impl/src/main/res/values-cs/translations.xml +++ b/features/rageshake/impl/src/main/res/values-cs/translations.xml @@ -7,6 +7,7 @@ "Popište prosím chybu. Co jste udělali? Co jste očekávali, že se stane? Co se ve skutečnosti stalo? Uveďte co nejvíce podrobností." "Popište chybu…" "Pokud je to možné, prosím, napište popis anglicky." + "Popis je příliš krátký, uveďte prosím více podrobností o tom, co se stalo. Děkujeme!" "Odeslat záznamy o selhání" "Povolit protokoly" "Odeslat snímek obrazovky" diff --git a/features/rageshake/impl/src/main/res/values-de/translations.xml b/features/rageshake/impl/src/main/res/values-de/translations.xml index 95e8c0228b..406c7e0efb 100644 --- a/features/rageshake/impl/src/main/res/values-de/translations.xml +++ b/features/rageshake/impl/src/main/res/values-de/translations.xml @@ -11,5 +11,6 @@ "Protokolle zulassen" "Bildschirmfoto senden" "Die Protokolle werden deiner Nachricht beigefügt, um sicherzustellen, dass alles ordnungsgemäß funktioniert. Um deine Nachricht ohne Protokolle zu senden, deaktiviere diese Einstellung." + "Logs ansehen" "%1$s ist bei der letzten Nutzung abgestürzt. Möchtest du einen Absturzbericht mit uns teilen?" diff --git a/features/rageshake/impl/src/main/res/values-hu/translations.xml b/features/rageshake/impl/src/main/res/values-hu/translations.xml index 85f22f8449..e0ed00018f 100644 --- a/features/rageshake/impl/src/main/res/values-hu/translations.xml +++ b/features/rageshake/impl/src/main/res/values-hu/translations.xml @@ -7,9 +7,11 @@ "Írja le a hibát. Mit csinált? Mire számított, hogy mi fog történni? Mi történt valójában? Fogalmazzon a lehető legrészletesebben." "Írja le a problémát…" "Ha lehetséges, a leírást angolul írja meg." + "A leírás túl rövid, adjon meg további részleteket a történtekről. Köszönjük!" "Összeomlásnaplók küldése" "Naplók engedélyezése" "Képernyőkép küldése" "A naplók szerepelni fognak az üzenetben, hogy megbizonyosodhassunk arról, hogy minden megfelelően működik-e. Ha naplók nélkül szeretné elküldeni az üzenetet, akkor kapcsolja ki ezt a beállítást." + "Naplók megtekintése" "Az %1$s összeomlott a legutóbbi használata óta. Megosztod velünk az összeomlás-jelentést?" diff --git a/features/rageshake/impl/src/main/res/values-ru/translations.xml b/features/rageshake/impl/src/main/res/values-ru/translations.xml index 7c0ea079a3..2de3e3ce67 100644 --- a/features/rageshake/impl/src/main/res/values-ru/translations.xml +++ b/features/rageshake/impl/src/main/res/values-ru/translations.xml @@ -7,6 +7,7 @@ "Пожалуйста, опишите ошибку. Что вы сделали? Какое поведение вы ожидали? Что произошло на самом деле. Пожалуйста, опишите все как можно подробнее." "Опишите проблему…" "Если возможно, пожалуйста, напишите описание на английском языке." + "Описание слишком короткое, пожалуйста, расскажите подробнее о том, что произошло. Спасибо!" "Отправка журналов сбоев" "Разрешить ведение журналов" "Отправить снимок экрана" diff --git a/features/rageshake/impl/src/main/res/values-sk/translations.xml b/features/rageshake/impl/src/main/res/values-sk/translations.xml index 8e94848650..2fbd637033 100644 --- a/features/rageshake/impl/src/main/res/values-sk/translations.xml +++ b/features/rageshake/impl/src/main/res/values-sk/translations.xml @@ -7,6 +7,7 @@ "Popíšte prosím chybu. Čo ste urobili? Čo ste očakávali, že sa stane? Čo sa skutočne stalo. Prosím, uveďte čo najviac podrobností." "Popíšte chybu…" "Ak je to možné, napíšte popis v angličtine." + "Popis je príliš krátky, uveďte viac podrobností o tom, čo sa stalo. Ďakujeme!" "Odoslať záznamy o zlyhaní" "Povoliť záznamy" "Odoslať snímku obrazovky" diff --git a/features/roomdetails/impl/src/main/res/values-be/translations.xml b/features/roomdetails/impl/src/main/res/values-be/translations.xml index 6119feaff9..3debaf9166 100644 --- a/features/roomdetails/impl/src/main/res/values-be/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-be/translations.xml @@ -47,6 +47,7 @@ "Разблакіраваць" "Вы зноў зможаце ўбачыць усе паведамленні." "Разблакіраваць карыстальніка" + "Пакінуць размову" "Пакінуць пакой" "Назва пакоя" "Бяспека" diff --git a/features/roomdetails/impl/src/main/res/values-de/translations.xml b/features/roomdetails/impl/src/main/res/values-de/translations.xml index 1f7501eb25..c070b7fe3d 100644 --- a/features/roomdetails/impl/src/main/res/values-de/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-de/translations.xml @@ -46,6 +46,7 @@ "Blockierung aufheben" "Du kannst dann wieder alle Nachrichten von ihnen sehen." "Blockierung aufheben" + "Unterhaltung verlassen" "Raum verlassen" "Raumname" "Sicherheit" diff --git a/features/roomdetails/impl/src/main/res/values-hu/translations.xml b/features/roomdetails/impl/src/main/res/values-hu/translations.xml index 586719a6cc..2c16665001 100644 --- a/features/roomdetails/impl/src/main/res/values-hu/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-hu/translations.xml @@ -46,6 +46,7 @@ "Letiltás feloldása" "Újra láthatja az összes üzenetét." "Felhasználó kitiltásának feloldása" + "Beszélgetés elhagyása" "Szoba elhagyása" "Szoba neve" "Biztonság" diff --git a/features/roomdetails/impl/src/main/res/values-ru/translations.xml b/features/roomdetails/impl/src/main/res/values-ru/translations.xml index 3562a9ff70..541592a77f 100644 --- a/features/roomdetails/impl/src/main/res/values-ru/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ru/translations.xml @@ -26,6 +26,8 @@ "Поделиться комнатой" "Обновление комнаты…" "В ожидании" + "Администратор" + "Модератор" "Участники комнаты" "Разрешить пользовательские настройки" "Включение этого параметра отменяет настройки по умолчанию" diff --git a/features/roomdetails/impl/src/main/res/values-sk/translations.xml b/features/roomdetails/impl/src/main/res/values-sk/translations.xml index 4108c25083..9821d5ea50 100644 --- a/features/roomdetails/impl/src/main/res/values-sk/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-sk/translations.xml @@ -26,6 +26,8 @@ "Zdieľať miestnosť" "Aktualizácia miestnosti…" "Čaká sa" + "Administrátor" + "Moderátor" "Členovia miestnosti" "Povoliť vlastné nastavenie" "Zapnutím tohto nastavenia sa prepíše vaše predvolené nastavenie" diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index b79ad0e8b2..f2d3c16aad 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -25,6 +25,8 @@ "Share room" "Updating room…" "Pending" + "Admin" + "Moderator" "Room members" "Allow custom setting" "Turning this on will override your default setting" diff --git a/features/roomlist/impl/src/main/res/values-be/translations.xml b/features/roomlist/impl/src/main/res/values-be/translations.xml index b7449b98bc..aeb01f846d 100644 --- a/features/roomlist/impl/src/main/res/values-be/translations.xml +++ b/features/roomlist/impl/src/main/res/values-be/translations.xml @@ -2,10 +2,13 @@ "Ваша рэзервовая копія чата зараз не сінхранізавана. Вам трэба пацвердзіць ключ аднаўлення, каб захаваць доступ да рэзервовай копіі чата." "Пацвердзіце ключ аднаўлення" + "Гэта аднаразовы працэс, дзякуем за чаканне." + "Налада ўліковага запісу." "Стварыце новую размову або пакой" "Пачніце з паведамлення каму-небудзь." "Пакуль няма чатаў." "Усе чаты" "Здаецца, вы карыстаецеся новай прыладай. Праверце з дапамогай іншай прылады, каб атрымаць доступ да зашыфраваных паведамленняў." "Пацвердзіце, што гэта вы" + "Людзі" diff --git a/features/roomlist/impl/src/main/res/values-cs/translations.xml b/features/roomlist/impl/src/main/res/values-cs/translations.xml index b5858e5767..74b1b7223b 100644 --- a/features/roomlist/impl/src/main/res/values-cs/translations.xml +++ b/features/roomlist/impl/src/main/res/values-cs/translations.xml @@ -2,6 +2,8 @@ "Vaše záloha chatu není aktuálně synchronizována. Abyste si zachovali přístup k záloze chatu, musíte potvrdit klíč pro obnovení." "Potvrďte klíč pro obnovení" + "Jedná se o jednorázový proces, prosíme o strpení." + "Nastavení vašeho účtu" "Vytvořte novou konverzaci nebo místnost" "Začněte tím, že někomu pošnete zprávu." "Zatím žádné konverzace." diff --git a/features/roomlist/impl/src/main/res/values-de/translations.xml b/features/roomlist/impl/src/main/res/values-de/translations.xml index edd5b61c0d..c64dbf332e 100644 --- a/features/roomlist/impl/src/main/res/values-de/translations.xml +++ b/features/roomlist/impl/src/main/res/values-de/translations.xml @@ -2,10 +2,18 @@ "Dein Chat-Backup ist derzeit nicht synchronisiert. Du musst deinen Wiederherstellungsschlüssel bestätigen, um Zugriff auf dein Chat-Backup zu erhalten." "Wiederherstellungsschlüssel bestätigen." + "Dies ist ein einmaliger Vorgang, danke fürs Warten." + "Dein Konto wird eingerichtet." "Eine neue Unterhaltung oder einen neuen Raum erstellen" "Beginne, indem du jemandem eine Nachricht sendest." "Noch keine Chats." + "Favoriten" + "Niedrige Priorität" + "Räume" + "Ungelesen" "Alle Chats" + "Als gelesen markieren" + "Als ungelesen markieren" "Es sieht aus, als würdest du ein neues Gerät verwenden. Verifiziere es mit einem anderen Gerät, damit du auf deine verschlüsselten Nachrichten zugreifen kannst." "Bestätige deine Identität" "Personen" diff --git a/features/roomlist/impl/src/main/res/values-es/translations.xml b/features/roomlist/impl/src/main/res/values-es/translations.xml index dceb9e3b2b..530da8271f 100644 --- a/features/roomlist/impl/src/main/res/values-es/translations.xml +++ b/features/roomlist/impl/src/main/res/values-es/translations.xml @@ -2,6 +2,8 @@ "La copia de seguridad del chat no está sincronizada en este momento. Debes confirmar tu clave de recuperación para mantener el acceso a la copia de seguridad del chat." "Confirma tu clave de recuperación" + "Este proceso solo se hace una vez, gracias por esperar." + "Configura tu cuenta" "Crear una nueva conversación o sala" "Empieza enviando un mensaje a alguien." "Aún no hay chats." diff --git a/features/roomlist/impl/src/main/res/values-fr/translations.xml b/features/roomlist/impl/src/main/res/values-fr/translations.xml index 85593ed0f5..77f7b1e3e6 100644 --- a/features/roomlist/impl/src/main/res/values-fr/translations.xml +++ b/features/roomlist/impl/src/main/res/values-fr/translations.xml @@ -2,6 +2,8 @@ "La sauvegarde des conversations est désynchronisée. Vous devez confirmer la clé de récupération pour accéder à votre historique." "Confirmer votre clé de récupération" + "Il s’agit d’une opération ponctuelle, merci d’attendre quelques instants." + "Configuration de votre compte." "Créer une nouvelle discussion ou un nouveau salon" "Commencez par envoyer un message à quelqu’un." "Aucune discussion pour le moment." diff --git a/features/roomlist/impl/src/main/res/values-hu/translations.xml b/features/roomlist/impl/src/main/res/values-hu/translations.xml index d6d8b82210..5410e5364f 100644 --- a/features/roomlist/impl/src/main/res/values-hu/translations.xml +++ b/features/roomlist/impl/src/main/res/values-hu/translations.xml @@ -2,10 +2,18 @@ "A csevegés biztonsági mentése nincs szinkronban. Meg kell erősítened a helyreállítási kulcsát, hogy továbbra is hozzáférj a csevegés biztonsági mentéséhez." "Helyreállítási kulcs megerősítése" + "Ez egy egyszeri folyamat, köszönjük a türelmét." + "A fiók beállítása." "Új beszélgetés vagy szoba létrehozása" "Kezdje azzal, hogy üzenetet küld valakinek." "Még nincsenek csevegések." + "Kedvencek" + "Alacsony prioritás" + "Szobák" + "Olvasatlan" "Összes csevegés" + "Megjelölés olvasottként" + "Megjelölés olvasatlanként" "Úgy tűnik, hogy új eszközt használ. Ellenőrizze egy másik eszközzel, hogy a továbbiakban elérje a titkosított üzeneteket." "Ellenőrizze, hogy Ön az" "Emberek" diff --git a/features/roomlist/impl/src/main/res/values-in/translations.xml b/features/roomlist/impl/src/main/res/values-in/translations.xml index 09a83b3370..c3560b5b58 100644 --- a/features/roomlist/impl/src/main/res/values-in/translations.xml +++ b/features/roomlist/impl/src/main/res/values-in/translations.xml @@ -2,6 +2,8 @@ "Cadangan percakapan Anda saat ini tidak tersinkron. Anda perlu mengonfirmasi kunci pemulihan Anda untuk tetap memiliki akses ke cadangan percakapan Anda." "Konfirmasi kunci pemulihan Anda" + "Ini adalah proses satu kali, terima kasih telah menunggu." + "Menyiapkan akun Anda." "Buat percakapan atau ruangan baru" "Mulailah dengan mengirim pesan kepada seseorang." "Belum ada obrolan." diff --git a/features/roomlist/impl/src/main/res/values-it/translations.xml b/features/roomlist/impl/src/main/res/values-it/translations.xml index 60ec4f6dbd..6f7648254f 100644 --- a/features/roomlist/impl/src/main/res/values-it/translations.xml +++ b/features/roomlist/impl/src/main/res/values-it/translations.xml @@ -2,6 +2,8 @@ "Il backup della chat non è attualmente sincronizzato. Devi confermare la chiave di recupero per mantenere l\'accesso al backup della chat." "Conferma la chiave di recupero" + "Si tratta di una procedura che si effettua una sola volta, grazie per l\'attesa." + "Configurazione del tuo account." "Crea una nuova conversazione o stanza" "Inizia inviando un messaggio a qualcuno." "Ancora nessuna chat." diff --git a/features/roomlist/impl/src/main/res/values-ro/translations.xml b/features/roomlist/impl/src/main/res/values-ro/translations.xml index 9bb61c8442..53ff7c87f7 100644 --- a/features/roomlist/impl/src/main/res/values-ro/translations.xml +++ b/features/roomlist/impl/src/main/res/values-ro/translations.xml @@ -1,5 +1,7 @@ + "Acesta este un proces care se desfășoară o singură dată, vă mulțumim pentru așteptare." + "Contul dumneavoastră se configurează" "Creați o conversație sau o cameră nouă" "Începeți prin a trimite mesaje cuiva." "Nu există încă discuții." diff --git a/features/roomlist/impl/src/main/res/values-ru/translations.xml b/features/roomlist/impl/src/main/res/values-ru/translations.xml index 6b915e7325..f15eed6fb2 100644 --- a/features/roomlist/impl/src/main/res/values-ru/translations.xml +++ b/features/roomlist/impl/src/main/res/values-ru/translations.xml @@ -2,6 +2,8 @@ "В настоящее время резервная копия вашего чата не синхронизирована. Требуется подтвердить вашим ключом восстановления, чтобы сохранить доступ к резервной копии чата." "Подтвердите ключ восстановления" + "Это одноразовый процесс, спасибо, что подождали." + "Настройка учетной записи." "Создайте новую беседу или комнату" "Начните переписку с отправки сообщения." "Пока нет доступных чатов." diff --git a/features/roomlist/impl/src/main/res/values-sk/translations.xml b/features/roomlist/impl/src/main/res/values-sk/translations.xml index 3ba59ce857..412828ce85 100644 --- a/features/roomlist/impl/src/main/res/values-sk/translations.xml +++ b/features/roomlist/impl/src/main/res/values-sk/translations.xml @@ -2,6 +2,8 @@ "Vaša záloha konverzácie nie je momentálne synchronizovaná. Na zachovanie prístupu k zálohe konverzácie musíte potvrdiť svoj kľúč na obnovu." "Potvrďte svoj kľúč na obnovenie" + "Ide o jednorazový proces, ďakujeme za trpezlivosť." + "Nastavenie vášho účtu." "Vytvorte novú konverzáciu alebo miestnosť" "Začnite tým, že niekomu pošlete správu." "Zatiaľ žiadne konverzácie." @@ -10,6 +12,8 @@ "Miestnosti" "Neprečítané" "Všetky konverzácie" + "Označiť ako prečítané" + "Označiť ako neprečítané" "Vyzerá to tak, že používate nové zariadenie. Overte svoj prístup k zašifrovaným správam pomocou vášho druhého zariadenia." "Overte, že ste to vy" "Ľudia" diff --git a/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml index 3a4a5f6585..db51ea8a48 100644 --- a/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml @@ -1,5 +1,7 @@ + "這是一次性的程序,感謝您耐心等候。" + "正在設定您的帳號。" "建立新的對話或聊天室" "所有聊天室" "您似乎正在使用新的裝置。請使用另一個裝置進行驗證,以存取您的加密訊息。" diff --git a/libraries/eventformatter/impl/src/main/res/values-de/translations.xml b/libraries/eventformatter/impl/src/main/res/values-de/translations.xml index 51f73581c1..3444cdaef0 100644 --- a/libraries/eventformatter/impl/src/main/res/values-de/translations.xml +++ b/libraries/eventformatter/impl/src/main/res/values-de/translations.xml @@ -39,6 +39,8 @@ "Du hast den Raumnamen geändert in: %1$s" "%1$s hat den Raumnamen entfernt" "Du hast den Raumnamen entfernt" + "%1$shat keine Änderungen vorgenommen" + "Du hast keine Änderungen vorgenommen" "%1$s hat die Einladung abgelehnt" "Du hast die Einladung abgelehnt" "%1$s hat %2$s entfernt" diff --git a/libraries/push/impl/src/main/res/values-de/translations.xml b/libraries/push/impl/src/main/res/values-de/translations.xml index c641eda4c3..ad90ed6e39 100644 --- a/libraries/push/impl/src/main/res/values-de/translations.xml +++ b/libraries/push/impl/src/main/res/values-de/translations.xml @@ -48,5 +48,6 @@ "Keine gültigen Google Play-Dienste gefunden. Benachrichtigungen funktionieren möglicherweise nicht richtig." "Mitteilung" "Beitreten" + "Als gelesen markieren" "Schnelle Antwort" diff --git a/libraries/push/impl/src/main/res/values-hu/translations.xml b/libraries/push/impl/src/main/res/values-hu/translations.xml index c7c1f209c2..1ef970fb28 100644 --- a/libraries/push/impl/src/main/res/values-hu/translations.xml +++ b/libraries/push/impl/src/main/res/values-hu/translations.xml @@ -48,5 +48,6 @@ "A Google Play szolgáltatások nem találhatók. Előfordulhat, hogy az értesítések nem működnek megfelelően." "Értesítés" "Csatlakozás" + "Megjelölés olvasottként" "Gyors válasz" diff --git a/libraries/push/impl/src/main/res/values-sk/translations.xml b/libraries/push/impl/src/main/res/values-sk/translations.xml index 8917d57e6a..2f77c4cf39 100644 --- a/libraries/push/impl/src/main/res/values-sk/translations.xml +++ b/libraries/push/impl/src/main/res/values-sk/translations.xml @@ -54,5 +54,6 @@ "Nenašli sa žiadne platné služby Google Play. Oznámenia nemusia fungovať správne." "Oznámenie" "Pripojiť sa" + "Označiť ako prečítané" "Rýchla odpoveď" diff --git a/libraries/ui-strings/src/main/res/values-be/translations.xml b/libraries/ui-strings/src/main/res/values-be/translations.xml index 9dd4e336ca..358d1c7fd9 100644 --- a/libraries/ui-strings/src/main/res/values-be/translations.xml +++ b/libraries/ui-strings/src/main/res/values-be/translations.xml @@ -57,6 +57,7 @@ "Далучыцца" "Падрабязней" "Пакінуць" + "Пакінуць размову" "Пакінуць пакой" "Кіраванне ўліковым запісам" "Кіраванне прыладамі" @@ -100,6 +101,7 @@ "Палітыка дапушчальнага выкарыстання" "Пашыраныя налады" "Аналітыка" + "Знешні выгляд" "Аўдыё" "Бурбалкі" "Рэзервовае капіраванне чата" @@ -137,6 +139,7 @@ "Вынікаў няма" "Па-за сеткай" "Пароль" + "Людзі" "Пастаянная спасылка" "Дазвол" "Усяго галасоў: %1$s" diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index 1578326b9f..9c362e4400 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -57,6 +57,7 @@ "Beitreten" "Mehr erfahren" "Verlassen" + "Unterhaltung verlassen" "Raum verlassen" "Konto verwalten" "Geräte verwalten" @@ -118,6 +119,7 @@ "PIN eingeben" "Fehler" "Alle" + "Favorit" "Datei" "Datei wurde unter Downloads gespeichert" "Nachricht weiterleiten" diff --git a/libraries/ui-strings/src/main/res/values-hu/translations.xml b/libraries/ui-strings/src/main/res/values-hu/translations.xml index 169d5a6877..dfaac3446a 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -57,6 +57,7 @@ "Csatlakozás" "További tudnivalók" "Elhagyás" + "Beszélgetés elhagyása" "Szoba elhagyása" "Fiók kezelése" "Eszközök kezelése" @@ -118,6 +119,7 @@ "Add meg a PIN-kódodat" "Hiba" "Mindenki" + "Kedvenc" "Fájl" "A fájl a Letöltések mappába mentve" "Üzenet továbbítása" diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index 343bbbf3ae..56606bd1c4 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -119,6 +119,7 @@ "Zadajte svoj PIN" "Chyba" "Všetci" + "Obľúbené" "Súbor" "Súbor bol uložený do priečinka Stiahnuté súbory" "Preposlať správu"