diff --git a/CHANGES.md b/CHANGES.md index 2097277556..0cd6932883 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,22 @@ +Changes in Element X v0.6.0 (2024-09-12) +======================================== + +### 🙌 Improvements +* Enables pinned messages feature by default. by @ganfra in https://github.com/element-hq/element-x-android/pull/3439 +* Pinned messages list : hide reactions by @ganfra in https://github.com/element-hq/element-x-android/pull/3430 + +### 🐛 Bugfixes +* Feature/fga/pinned messages fix timeline provider by @ganfra in https://github.com/element-hq/element-x-android/pull/3432 + +### Dependency upgrades +* Update activity to v1.9.2 by @renovate in https://github.com/element-hq/element-x-android/pull/3397 +* Update peter-evans/create-pull-request action to v7 by @renovate in https://github.com/element-hq/element-x-android/pull/3383 +* Rust sdk upgrade to 0.2.43 by @bmarty in https://github.com/element-hq/element-x-android/pull/3446 + +### Others +* DeviceId and cleanup. by @bmarty in https://github.com/element-hq/element-x-android/pull/3442 +* Update application store assets by @bmarty in https://github.com/element-hq/element-x-android/pull/3441 + Changes in Element X v0.5.3 (2024-09-10) ======================================== diff --git a/appconfig/src/main/kotlin/io/element/android/appconfig/OnBoardingConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/OnBoardingConfig.kt index d70f831eaa..7c08b28ce3 100644 --- a/appconfig/src/main/kotlin/io/element/android/appconfig/OnBoardingConfig.kt +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/OnBoardingConfig.kt @@ -12,5 +12,5 @@ object OnBoardingConfig { const val CAN_LOGIN_WITH_QR_CODE = true /** Whether the user can create an account using the app. */ - const val CAN_CREATE_ACCOUNT = false + const val CAN_CREATE_ACCOUNT = true } diff --git a/appnav/build.gradle.kts b/appnav/build.gradle.kts index fae6fc9c15..fd389645a9 100644 --- a/appnav/build.gradle.kts +++ b/appnav/build.gradle.kts @@ -59,6 +59,7 @@ dependencies { testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.oidc.impl) + testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.pushproviders.test) testImplementation(projects.features.networkmonitor.test) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInEvents.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInEvents.kt index cd156bce02..4f60e543bb 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInEvents.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInEvents.kt @@ -9,4 +9,6 @@ package io.element.android.appnav.loggedin sealed interface LoggedInEvents { data class CloseErrorDialog(val doNotShowAgain: Boolean) : LoggedInEvents + data object CheckSlidingSyncProxyAvailability : LoggedInEvents + data object LogoutAndMigrateToNativeSlidingSync : LoggedInEvents } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt index e6779c2c99..c619629152 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt @@ -16,6 +16,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import im.vector.app.features.analytics.plan.CryptoSessionStateChange import im.vector.app.features.analytics.plan.UserProperties import io.element.android.features.networkmonitor.api.NetworkMonitor @@ -29,6 +30,7 @@ import io.element.android.libraries.matrix.api.encryption.RecoveryState 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.preferences.api.store.EnableNativeSlidingSyncUseCase import io.element.android.libraries.push.api.PushService import io.element.android.libraries.pushproviders.api.RegistrationFailure import io.element.android.services.analytics.api.AnalyticsService @@ -48,6 +50,7 @@ class LoggedInPresenter @Inject constructor( private val sessionVerificationService: SessionVerificationService, private val analyticsService: AnalyticsService, private val encryptionService: EncryptionService, + private val enableNativeSlidingSyncUseCase: EnableNativeSlidingSyncUseCase, ) : Presenter { @Composable override fun present(): LoggedInState { @@ -78,6 +81,7 @@ class LoggedInPresenter @Inject constructor( networkStatus == NetworkStatus.Online && syncIndicator == RoomListService.SyncIndicator.Show } } + var forceNativeSlidingSyncMigration by remember { mutableStateOf(false) } LaunchedEffect(Unit) { combine( sessionVerificationService.sessionVerifiedStatus, @@ -97,6 +101,18 @@ class LoggedInPresenter @Inject constructor( } } } + LoggedInEvents.CheckSlidingSyncProxyAvailability -> coroutineScope.launch { + // Force the user to log out if they were using the proxy sliding sync and it's no longer available, but native sliding sync is. + forceNativeSlidingSyncMigration = !matrixClient.isUsingNativeSlidingSync() && + matrixClient.isNativeSlidingSyncSupported() && + !matrixClient.isSlidingSyncProxySupported() + } + LoggedInEvents.LogoutAndMigrateToNativeSlidingSync -> coroutineScope.launch { + // Enable native sliding sync if it wasn't already the case + enableNativeSlidingSyncUseCase() + // Then force the logout + matrixClient.logout(userInitiated = true, ignoreSdkError = true) + } } } @@ -104,6 +120,7 @@ class LoggedInPresenter @Inject constructor( showSyncSpinner = showSyncSpinner, pusherRegistrationState = pusherRegistrationState.value, ignoreRegistrationError = ignoreRegistrationError, + forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration, eventSink = ::handleEvent ) } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt index 7ef0e6f549..2735b02706 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt @@ -13,5 +13,6 @@ data class LoggedInState( val showSyncSpinner: Boolean, val pusherRegistrationState: AsyncData, val ignoreRegistrationError: Boolean, + val forceNativeSlidingSyncMigration: Boolean, val eventSink: (LoggedInEvents) -> Unit, ) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt index bcc88b1c3a..13d100820b 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt @@ -16,15 +16,18 @@ open class LoggedInStateProvider : PreviewParameterProvider { aLoggedInState(), aLoggedInState(showSyncSpinner = true), aLoggedInState(pusherRegistrationState = AsyncData.Failure(PusherRegistrationFailure.NoDistributorsAvailable())), + aLoggedInState(forceNativeSlidingSyncMigration = true), ) } fun aLoggedInState( showSyncSpinner: Boolean = false, pusherRegistrationState: AsyncData = AsyncData.Uninitialized, + forceNativeSlidingSyncMigration: Boolean = false, ) = LoggedInState( showSyncSpinner = showSyncSpinner, pusherRegistrationState = pusherRegistrationState, ignoreRegistrationError = false, + forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration, eventSink = {}, ) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt index 0af150e0b7..3134270965 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt @@ -15,10 +15,14 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.lifecycle.Lifecycle +import io.element.android.appnav.R import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.components.dialogs.ErrorDialogWithDoNotShowAgain import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.matrix.api.exception.isNetworkError import io.element.android.libraries.ui.strings.CommonStrings @@ -28,6 +32,11 @@ fun LoggedInView( navigateToNotificationTroubleshoot: () -> Unit, modifier: Modifier = Modifier ) { + OnLifecycleEvent { _, event -> + if (event == Lifecycle.Event.ON_RESUME) { + state.eventSink(LoggedInEvents.CheckSlidingSyncProxyAvailability) + } + } Box( modifier = modifier .fillMaxSize() @@ -61,6 +70,13 @@ fun LoggedInView( } } } + + // Set the force migration dialog here so it's always displayed over every screen + if (state.forceNativeSlidingSyncMigration) { + ForceNativeSlidingSyncMigrationDialog(onSubmit = { + state.eventSink(LoggedInEvents.LogoutAndMigrateToNativeSlidingSync) + }) + } } private fun Throwable.getReason(): String? { @@ -80,6 +96,19 @@ private fun Throwable.getReason(): String? { } } +@Composable +private fun ForceNativeSlidingSyncMigrationDialog( + onSubmit: () -> Unit, +) { + ErrorDialog( + title = null, + content = stringResource(R.string.banner_migrate_to_native_sliding_sync_force_logout_title), + submitText = stringResource(R.string.banner_migrate_to_native_sliding_sync_action), + onSubmit = onSubmit, + canDismiss = false, + ) +} + @PreviewsDayNight @Composable internal fun LoggedInViewPreview(@PreviewParameter(LoggedInStateProvider::class) state: LoggedInState) = ElementPreview { diff --git a/appnav/src/main/res/values-cs/translations.xml b/appnav/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..e749887bc4 --- /dev/null +++ b/appnav/src/main/res/values-cs/translations.xml @@ -0,0 +1,5 @@ + + + "Odhlásit se a upgradovat" + "Váš domovský server již nepodporuje starý protokol. Chcete-li pokračovat v používání aplikace, odhlaste se a znovu se přihlaste." + diff --git a/appnav/src/main/res/values-de/translations.xml b/appnav/src/main/res/values-de/translations.xml new file mode 100644 index 0000000000..2b33dacaca --- /dev/null +++ b/appnav/src/main/res/values-de/translations.xml @@ -0,0 +1,5 @@ + + + "Abmelden und aktualisieren" + "Dein Homeserver unterstützt das alte Protokoll nicht mehr. Bitte logge dich aus und melde dich wieder an, um die App weiter zu nutzen." + diff --git a/appnav/src/main/res/values-el/translations.xml b/appnav/src/main/res/values-el/translations.xml new file mode 100644 index 0000000000..42925c0b6f --- /dev/null +++ b/appnav/src/main/res/values-el/translations.xml @@ -0,0 +1,5 @@ + + + "Αποσύνδεση & Αναβάθμιση" + "Ο οικιακός διακομιστής σου δεν υποστηρίζει πλέον το παλιό πρωτόκολλο. Αποσυνδέσου και συνδέσου ξανά για να συνεχίσεις να χρησιμοποιείς την εφαρμογή." + diff --git a/appnav/src/main/res/values-et/translations.xml b/appnav/src/main/res/values-et/translations.xml new file mode 100644 index 0000000000..1631134d60 --- /dev/null +++ b/appnav/src/main/res/values-et/translations.xml @@ -0,0 +1,5 @@ + + + "Logi välja ja uuenda" + "Sinu koduserver enam ei toeta vana protokolli. Jätkamaks rakenduse kasutamist palun logi välja ning seejärel tagasi." + diff --git a/appnav/src/main/res/values-fr/translations.xml b/appnav/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..6295bc88df --- /dev/null +++ b/appnav/src/main/res/values-fr/translations.xml @@ -0,0 +1,5 @@ + + + "Déconnecter et mettre à niveau" + "Votre serveur d’accueil ne prend plus en charge l’ancien protocole. Veuillez vous déconnecter puis vous reconnecter pour continuer à utiliser l’application." + diff --git a/appnav/src/main/res/values-hu/translations.xml b/appnav/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..e4b24f811b --- /dev/null +++ b/appnav/src/main/res/values-hu/translations.xml @@ -0,0 +1,5 @@ + + + "Kijelentkezés és frissítés" + "A Matrix-kiszolgáló már nem támogatja a régi protokollt. Az alkalmazás további használatához jelentkezzen ki és be." + diff --git a/appnav/src/main/res/values-ru/translations.xml b/appnav/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..df600ea045 --- /dev/null +++ b/appnav/src/main/res/values-ru/translations.xml @@ -0,0 +1,5 @@ + + + "Выйти и обновить" + "Ваш homeserver больше не поддерживает старый протокол. Пожалуйста, выйдите из системы и войдите снова, чтобы продолжить использование приложения." + diff --git a/appnav/src/main/res/values-sk/translations.xml b/appnav/src/main/res/values-sk/translations.xml new file mode 100644 index 0000000000..d1ac766f8c --- /dev/null +++ b/appnav/src/main/res/values-sk/translations.xml @@ -0,0 +1,5 @@ + + + "Odhlásiť sa a aktualizovať" + "Váš domovský server už nepodporuje starý protokol. Ak chcete pokračovať v používaní aplikácie, odhláste sa a znova sa prihláste." + diff --git a/appnav/src/main/res/values/localazy.xml b/appnav/src/main/res/values/localazy.xml new file mode 100644 index 0000000000..bb6df44051 --- /dev/null +++ b/appnav/src/main/res/values/localazy.xml @@ -0,0 +1,5 @@ + + + "Log Out & Upgrade" + "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app." + diff --git a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt index ee060f3444..519cceddfe 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt @@ -29,6 +29,8 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService +import io.element.android.libraries.preferences.api.store.EnableNativeSlidingSyncUseCase +import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.libraries.push.api.PushService import io.element.android.libraries.push.test.FakePushService import io.element.android.libraries.pushproviders.api.Distributor @@ -42,6 +44,10 @@ import io.element.android.tests.testutils.lambda.any import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -91,7 +97,8 @@ class LoggedInPresenterTest { pushService = FakePushService(), sessionVerificationService = verificationService, analyticsService = analyticsService, - encryptionService = encryptionService + encryptionService = encryptionService, + enableNativeSlidingSyncUseCase = EnableNativeSlidingSyncUseCase(InMemoryAppPreferencesStore(), this), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -487,26 +494,103 @@ class LoggedInPresenterTest { ) } + @Test + fun `present - CheckSlidingSyncProxyAvailability forces the sliding sync migration under the right circumstances`() = runTest { + // The migration will be forced if: + // - The user is not using the native sliding sync + // - The sliding sync proxy is no longer supported + // - The native sliding sync is supported + val matrixClient = FakeMatrixClient( + isUsingNativeSlidingSyncLambda = { false }, + isSlidingSyncProxySupportedLambda = { false }, + isNativeSlidingSyncSupportedLambda = { true }, + ) + val presenter = createLoggedInPresenter(matrixClient = matrixClient) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.forceNativeSlidingSyncMigration).isFalse() + + initialState.eventSink(LoggedInEvents.CheckSlidingSyncProxyAvailability) + + assertThat(awaitItem().forceNativeSlidingSyncMigration).isTrue() + } + } + + @Test + fun `present - CheckSlidingSyncProxyAvailability will not force the migration if native sliding sync is not supported too`() = runTest { + val matrixClient = FakeMatrixClient( + isUsingNativeSlidingSyncLambda = { false }, + isSlidingSyncProxySupportedLambda = { false }, + isNativeSlidingSyncSupportedLambda = { false }, + ) + val presenter = createLoggedInPresenter(matrixClient = matrixClient) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.forceNativeSlidingSyncMigration).isFalse() + + initialState.eventSink(LoggedInEvents.CheckSlidingSyncProxyAvailability) + + expectNoEvents() + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `present - LogoutAndMigrateToNativeSlidingSync enables native sliding sync and logs out the user`() = runTest { + val logoutLambda = lambdaRecorder { userInitiated, ignoreSdkError -> + assertThat(userInitiated).isTrue() + assertThat(ignoreSdkError).isTrue() + null + } + val matrixClient = FakeMatrixClient().apply { + this.logoutLambda = logoutLambda + } + val appPreferencesStore = InMemoryAppPreferencesStore() + val enableNativeSlidingSyncUseCase = EnableNativeSlidingSyncUseCase(appPreferencesStore, this) + val presenter = createLoggedInPresenter(matrixClient = matrixClient, enableNativeSlidingSyncUseCase = enableNativeSlidingSyncUseCase) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + assertThat(appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first()).isFalse() + + initialState.eventSink(LoggedInEvents.LogoutAndMigrateToNativeSlidingSync) + + advanceUntilIdle() + + assertThat(appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first()).isTrue() + assertThat(logoutLambda.assertions().isCalledOnce()) + } + } + private suspend fun ReceiveTurbine.awaitFirstItem(): T { skipItems(1) return awaitItem() } - private fun createLoggedInPresenter( + private fun TestScope.createLoggedInPresenter( roomListService: RoomListService = FakeRoomListService(), networkStatus: NetworkStatus = NetworkStatus.Offline, analyticsService: AnalyticsService = FakeAnalyticsService(), sessionVerificationService: SessionVerificationService = FakeSessionVerificationService(), encryptionService: EncryptionService = FakeEncryptionService(), pushService: PushService = FakePushService(), + enableNativeSlidingSyncUseCase: EnableNativeSlidingSyncUseCase = EnableNativeSlidingSyncUseCase(InMemoryAppPreferencesStore(), this), + matrixClient: MatrixClient = FakeMatrixClient(roomListService = roomListService), ): LoggedInPresenter { return LoggedInPresenter( - matrixClient = FakeMatrixClient(roomListService = roomListService), + matrixClient = matrixClient, networkMonitor = FakeNetworkMonitor(networkStatus), pushService = pushService, sessionVerificationService = sessionVerificationService, analyticsService = analyticsService, - encryptionService = encryptionService + encryptionService = encryptionService, + enableNativeSlidingSyncUseCase = enableNativeSlidingSyncUseCase, ) } } diff --git a/fastlane/metadata/android/en-US/changelogs/40006010.txt b/fastlane/metadata/android/en-US/changelogs/40006010.txt new file mode 100644 index 0000000000..0574894881 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40006010.txt @@ -0,0 +1,2 @@ +Element X is the new generation of Element for professional and personal use on mobile. It’s the fastest Matrix client with a seamless & intuitive user interface. +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/features/analytics/api/src/main/res/values-nl/translations.xml b/features/analytics/api/src/main/res/values-nl/translations.xml index fcd05769c5..996fe846d5 100644 --- a/features/analytics/api/src/main/res/values-nl/translations.xml +++ b/features/analytics/api/src/main/res/values-nl/translations.xml @@ -3,4 +3,5 @@ "Deel anonieme gebruiksgegevens om ons te helpen problemen te identificeren." "Je kunt al onze voorwaarden %1$s lezen." "hier" + "Gebruiksgegevens delen" diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt index 7d0f1f6676..4641e4e7bc 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt @@ -111,7 +111,7 @@ internal fun CallScreenView( is AsyncData.Failure -> ErrorDialog( content = state.urlState.error.message.orEmpty(), - onDismiss = { state.eventSink(CallScreenEvents.Hangup) }, + onSubmit = { state.eventSink(CallScreenEvents.Hangup) }, ) is AsyncData.Success -> Unit } diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt index 7d117dba5e..c44a6cfb4f 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt @@ -112,8 +112,8 @@ class AcceptDeclineInvitePresenter @Inject constructor( private fun CoroutineScope.declineInvite(roomId: RoomId, declinedAction: MutableState>) = launch { suspend { - client.getRoom(roomId)?.use { - it.leave().getOrThrow() + client.getInvitedRoom(roomId)?.use { + it.declineInvite().getOrThrow() notificationCleaner.clearMembershipNotificationForRoom(client.sessionId, roomId) } roomId diff --git a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt index f4dcb4f278..45fb2efc86 100644 --- a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt +++ b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt @@ -21,7 +21,7 @@ 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.FakeMatrixClient -import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.FakeInvitedRoom import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom import io.element.android.libraries.push.api.notifications.NotificationCleaner import io.element.android.libraries.push.test.notifications.FakeNotificationCleaner @@ -83,12 +83,7 @@ class AcceptDeclineInvitePresenterTest { Result.failure(RuntimeException("Failed to leave room")) } val client = FakeMatrixClient().apply { - givenGetRoomResult( - roomId = A_ROOM_ID, - result = FakeMatrixRoom( - leaveRoomLambda = declineInviteFailure - ) - ) + getInvitedRoomResults[A_ROOM_ID] = FakeInvitedRoom(declineInviteResult = declineInviteFailure) } val presenter = createAcceptDeclineInvitePresenter(client = client) presenter.test { @@ -133,12 +128,7 @@ class AcceptDeclineInvitePresenterTest { Result.success(Unit) } val client = FakeMatrixClient().apply { - givenGetRoomResult( - roomId = A_ROOM_ID, - result = FakeMatrixRoom( - leaveRoomLambda = declineInviteSuccess - ) - ) + getInvitedRoomResults[A_ROOM_ID] = FakeInvitedRoom(declineInviteResult = declineInviteSuccess) } val presenter = createAcceptDeclineInvitePresenter( client = client, diff --git a/features/joinroom/impl/src/main/res/values-nl/translations.xml b/features/joinroom/impl/src/main/res/values-nl/translations.xml new file mode 100644 index 0000000000..d778ef8640 --- /dev/null +++ b/features/joinroom/impl/src/main/res/values-nl/translations.xml @@ -0,0 +1,11 @@ + + + "Toetreden tot de kamer" + "Klop om deel te nemen" + "%1$s ondersteunt nog geen spaces. Je kunt spaces benaderen via de webbrowser." + "Spaces worden nog niet ondersteund" + "Klik op de knop hieronder en een kamerbeheerder wordt op de hoogte gebracht. Na goedkeuring kun je deelnemen aan het gesprek." + "Je moet lid zijn van deze kamer om de berichtgeschiedenis te bekijken." + "Wil je tot deze kamer toetreden?" + "Voorbeeld is niet beschikbaar" + diff --git a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt index 6ee2990c5b..a98bfbff2f 100644 --- a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt +++ b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt @@ -105,7 +105,7 @@ private fun LeaveRoomErrorDialog( is LeaveRoomState.Error.Hidden -> {} is LeaveRoomState.Error.Shown -> ErrorDialog( content = stringResource(CommonStrings.error_unknown), - onDismiss = { state.eventSink(LeaveRoomEvent.HideError) } + onSubmit = { state.eventSink(LeaveRoomEvent.HideError) } ) } } 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 b794f2eed4..b3cd532487 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 @@ -116,7 +116,7 @@ private fun SetupPinContent( ErrorDialog( title = state.setupPinFailure.title(), content = state.setupPinFailure.content(), - onDismiss = { + onSubmit = { state.eventSink(SetupPinEvents.ClearFailure) } ) 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 702e253f03..8667b3806a 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 @@ -104,7 +104,7 @@ fun PinUnlockView( if (state.showBiometricUnlockError) { ErrorDialog( content = state.biometricUnlockErrorMessage ?: "", - onDismiss = { state.eventSink(PinUnlockEvents.ClearBiometricError) } + onSubmit = { state.eventSink(PinUnlockEvents.ClearBiometricError) } ) } } @@ -206,7 +206,7 @@ private fun SignOutPrompt( 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, + onSubmit = onSignOut, ) } } diff --git a/features/lockscreen/impl/src/main/res/values-de/translations.xml b/features/lockscreen/impl/src/main/res/values-de/translations.xml index bf89b3f5e4..4042df3d1b 100644 --- a/features/lockscreen/impl/src/main/res/values-de/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-de/translations.xml @@ -28,8 +28,8 @@ Wähle etwas Einprägsames. Bei falscher Eingabe wirst du aus der App ausgeloggt "Du hast %1$d Versuche zum Entsperren" - "Falsche PIN. Du hast %1$d weitere Chance" - "Falsche PIN. Du hast %1$d weitere Chancen" + "Falsche PIN. Du hast %1$d weiteren Versuch" + "Falsche PIN. Du hast %1$d weitere Versuche" "Biometrie verwenden" "PIN verwenden" diff --git a/features/login/impl/build.gradle.kts b/features/login/impl/build.gradle.kts index 6ea1eb7936..fc783e8733 100644 --- a/features/login/impl/build.gradle.kts +++ b/features/login/impl/build.gradle.kts @@ -44,6 +44,7 @@ dependencies { implementation(projects.libraries.oidc.api) implementation(libs.androidx.browser) implementation(platform(libs.network.retrofit.bom)) + implementation(libs.androidx.webkit) implementation(libs.network.retrofit) implementation(libs.serialization.json) api(projects.features.login.api) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt index a118920ebd..c9e97e8995 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt @@ -30,6 +30,7 @@ import io.element.android.features.login.impl.accountprovider.AccountProviderDat import io.element.android.features.login.impl.qrcode.QrCodeLoginFlowNode import io.element.android.features.login.impl.screens.changeaccountprovider.ChangeAccountProviderNode import io.element.android.features.login.impl.screens.confirmaccountprovider.ConfirmAccountProviderNode +import io.element.android.features.login.impl.screens.createaccount.CreateAccountNode import io.element.android.features.login.impl.screens.loginpassword.LoginPasswordNode import io.element.android.features.login.impl.screens.searchaccountprovider.SearchAccountProviderNode import io.element.android.libraries.architecture.BackstackView @@ -109,6 +110,9 @@ class LoginFlowNode @AssistedInject constructor( @Parcelize data object LoginPassword : NavTarget + @Parcelize + data class CreateAccount(val url: String) : NavTarget + @Parcelize data class OidcView(val oidcDetails: OidcDetails) : NavTarget } @@ -140,6 +144,10 @@ class LoginFlowNode @AssistedInject constructor( } } + override fun onCreateAccountContinue(url: String) { + backstack.push(NavTarget.CreateAccount(url)) + } + override fun onLoginPasswordNeeded() { backstack.push(NavTarget.LoginPassword) } @@ -180,6 +188,12 @@ class LoginFlowNode @AssistedInject constructor( is NavTarget.OidcView -> { oidcEntryPoint.createFallbackWebViewNode(this, buildContext, navTarget.oidcDetails.url) } + is NavTarget.CreateAccount -> { + val inputs = CreateAccountNode.Inputs( + url = navTarget.url, + ) + createNode(buildContext, listOf(inputs)) + } } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt index a2c3b48251..c6139ae492 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt @@ -36,7 +36,7 @@ fun ChangeServerView( ErrorDialog( modifier = modifier, content = error.message(), - onDismiss = { + onSubmit = { eventSink.invoke(ChangeServerEvents.ClearError) } ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/network/ElementWellKnown.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/network/ElementWellKnown.kt new file mode 100644 index 0000000000..d149957a55 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/network/ElementWellKnown.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2023, 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.resolver.network + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +/** + * Example: + *
+ * {
+ *     "registration_helper_url": "https://element.io"
+ * }
+ * 
+ * . + */ +@Serializable +data class ElementWellKnown( + @SerialName("registration_helper_url") + val registrationHelperUrl: String? = null, +) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/network/WellknownAPI.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/network/WellknownAPI.kt index 05658e9e7c..11f1621b03 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/network/WellknownAPI.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/network/WellknownAPI.kt @@ -12,4 +12,7 @@ import retrofit2.http.GET internal interface WellknownAPI { @GET(".well-known/matrix/client") suspend fun getWellKnown(): WellKnown + + @GET(".well-known/element/element.json") + suspend fun getElementWellKnown(): ElementWellKnown } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt index 8e865d9e39..454da3f795 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt @@ -43,6 +43,7 @@ class ConfirmAccountProviderNode @AssistedInject constructor( interface Callback : Plugin { fun onLoginPasswordNeeded() fun onOidcDetails(oidcDetails: OidcDetails) + fun onCreateAccountContinue(url: String) fun onChangeAccountProvider() } @@ -54,6 +55,10 @@ class ConfirmAccountProviderNode @AssistedInject constructor( plugins().forEach { it.onLoginPasswordNeeded() } } + private fun onCreateAccountContinue(url: String) { + plugins().forEach { it.onCreateAccountContinue(url) } + } + private fun onChangeAccountProvider() { plugins().forEach { it.onChangeAccountProvider() } } @@ -67,6 +72,7 @@ class ConfirmAccountProviderNode @AssistedInject constructor( modifier = modifier, onOidcDetails = ::onOidcDetails, onNeedLoginPassword = ::onLoginPasswordNeeded, + onCreateAccountContinue = ::onCreateAccountContinue, onChange = ::onChangeAccountProvider, onLearnMoreClick = { openLearnMorePage(context) }, ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt index a51573f602..c15c84744f 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt @@ -21,6 +21,8 @@ import dagger.assisted.AssistedInject import io.element.android.features.login.impl.DefaultLoginUserStory import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.error.ChangeServerError +import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported +import io.element.android.features.login.impl.web.WebClientUrlForAuthenticationRetriever import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState @@ -36,6 +38,7 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor( private val authenticationService: MatrixAuthenticationService, private val oidcActionFlow: OidcActionFlow, private val defaultLoginUserStory: DefaultLoginUserStory, + private val webClientUrlForAuthenticationRetriever: WebClientUrlForAuthenticationRetriever, ) : Presenter { data class Params( val isAccountCreation: Boolean, @@ -90,13 +93,24 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor( if (matrixHomeServerDetails.supportsOidcLogin) { // Retrieve the details right now LoginFlow.OidcFlow(authenticationService.getOidcUrl().getOrThrow()) + } else if (params.isAccountCreation) { + val url = webClientUrlForAuthenticationRetriever.retrieve(homeserverUrl) + LoginFlow.AccountCreationFlow(url) } else if (matrixHomeServerDetails.supportsPasswordLogin) { LoginFlow.PasswordLogin } else { error("Unsupported login flow") } }.getOrThrow() - }.runCatchingUpdatingState(loginFlowAction, errorTransform = ChangeServerError::from) + }.runCatchingUpdatingState( + state = loginFlowAction, + errorTransform = { + when (it) { + is AccountCreationNotSupported -> it + else -> ChangeServerError.from(it) + } + } + ) } private suspend fun onOidcAction( diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt index b73acab4ec..3c80d76d08 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt @@ -24,4 +24,5 @@ data class ConfirmAccountProviderState( sealed interface LoginFlow { data object PasswordLogin : LoginFlow data class OidcFlow(val oidcDetails: OidcDetails) : LoginFlow + data class AccountCreationFlow(val url: String) : LoginFlow } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStateProvider.kt index d643f0001a..5c95823ded 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStateProvider.kt @@ -8,20 +8,33 @@ package io.element.android.features.login.impl.screens.confirmaccountprovider import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.accountprovider.anAccountProvider +import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported import io.element.android.libraries.architecture.AsyncData open class ConfirmAccountProviderStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aConfirmAccountProviderState(), - // Add other state here + aConfirmAccountProviderState( + isAccountCreation = true, + ), + aConfirmAccountProviderState( + isAccountCreation = true, + loginFlow = AsyncData.Failure(AccountCreationNotSupported()) + ), ) } -fun aConfirmAccountProviderState() = ConfirmAccountProviderState( - accountProvider = anAccountProvider(), - isAccountCreation = false, - loginFlow = AsyncData.Uninitialized, - eventSink = {} +private fun aConfirmAccountProviderState( + accountProvider: AccountProvider = anAccountProvider(), + isAccountCreation: Boolean = false, + loginFlow: AsyncData = AsyncData.Uninitialized, + eventSink: (ConfirmAccountProviderEvents) -> Unit = {}, +) = ConfirmAccountProviderState( + accountProvider = accountProvider, + isAccountCreation = isAccountCreation, + loginFlow = loginFlow, + eventSink = eventSink ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt index a516c1b4a6..407ea9d88b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.unit.dp import io.element.android.features.login.impl.R import io.element.android.features.login.impl.dialogs.SlidingSyncNotSupportedDialog import io.element.android.features.login.impl.error.ChangeServerError +import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported 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 @@ -42,6 +43,7 @@ fun ConfirmAccountProviderView( onOidcDetails: (OidcDetails) -> Unit, onNeedLoginPassword: () -> Unit, onLearnMoreClick: () -> Unit, + onCreateAccountContinue: (url: String) -> Unit, onChange: () -> Unit, modifier: Modifier = Modifier, ) { @@ -103,18 +105,29 @@ fun ConfirmAccountProviderView( is ChangeServerError.Error -> { ErrorDialog( content = error.message(), - onDismiss = { + onSubmit = { eventSink.invoke(ConfirmAccountProviderEvents.ClearError) } ) } is ChangeServerError.SlidingSyncAlert -> { - SlidingSyncNotSupportedDialog(onLearnMoreClick = { - onLearnMoreClick() - eventSink(ConfirmAccountProviderEvents.ClearError) - }, onDismiss = { - eventSink(ConfirmAccountProviderEvents.ClearError) - }) + SlidingSyncNotSupportedDialog( + onLearnMoreClick = { + onLearnMoreClick() + eventSink(ConfirmAccountProviderEvents.ClearError) + }, + onDismiss = { + eventSink(ConfirmAccountProviderEvents.ClearError) + } + ) + } + is AccountCreationNotSupported -> { + ErrorDialog( + content = stringResource(CommonStrings.error_account_creation_not_possible), + onSubmit = { + eventSink.invoke(ConfirmAccountProviderEvents.ClearError) + } + ) } } } @@ -123,6 +136,7 @@ fun ConfirmAccountProviderView( when (val loginFlowState = state.loginFlow.data) { is LoginFlow.OidcFlow -> onOidcDetails(loginFlowState.oidcDetails) LoginFlow.PasswordLogin -> onNeedLoginPassword() + is LoginFlow.AccountCreationFlow -> onCreateAccountContinue(loginFlowState.url) } } AsyncData.Uninitialized -> Unit @@ -139,6 +153,7 @@ internal fun ConfirmAccountProviderViewPreview( state = state, onOidcDetails = {}, onNeedLoginPassword = {}, + onCreateAccountContinue = {}, onLearnMoreClick = {}, onChange = {}, ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/AccountCreationNotSupported.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/AccountCreationNotSupported.kt new file mode 100644 index 0000000000..8a299a6d54 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/AccountCreationNotSupported.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.screens.createaccount + +class AccountCreationNotSupported : Exception() diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountEvents.kt new file mode 100644 index 0000000000..8647d71c43 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountEvents.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.screens.createaccount + +sealed interface CreateAccountEvents { + data class SetPageProgress(val progress: Int) : CreateAccountEvents + data class OnMessageReceived(val message: String) : CreateAccountEvents +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountNode.kt new file mode 100644 index 0000000000..b17699e6f8 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountNode.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.screens.createaccount + +import android.app.Activity +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +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.compound.theme.ElementTheme +import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab +import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.inputs +import io.element.android.libraries.di.AppScope + +@ContributesNode(AppScope::class) +class CreateAccountNode @AssistedInject constructor( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + presenterFactory: CreateAccountPresenter.Factory, +) : Node(buildContext, plugins = plugins) { + data class Inputs( + val url: String, + ) : NodeInputs + + private val presenter = presenterFactory.create(inputs().url) + + private fun onOpenExternalUrl(activity: Activity, darkTheme: Boolean, url: String) { + activity.openUrlInChromeCustomTab(null, darkTheme, url) + } + + @Composable + override fun View(modifier: Modifier) { + val activity = LocalContext.current as Activity + val isDark = ElementTheme.isLightTheme.not() + val state = presenter.present() + CreateAccountView( + state = state, + modifier = modifier, + onBackClick = ::navigateUp, + onOpenExternalUrl = { + onOpenExternalUrl(activity, isDark, it) + }, + ) + } +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt new file mode 100644 index 0000000000..69ffb10883 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2023, 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.screens.createaccount + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import io.element.android.features.login.impl.DefaultLoginUserStory +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.core.extensions.flatMap +import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService +import io.element.android.libraries.matrix.api.core.SessionId +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +class CreateAccountPresenter @AssistedInject constructor( + @Assisted private val url: String, + private val authenticationService: MatrixAuthenticationService, + private val defaultLoginUserStory: DefaultLoginUserStory, + private val messageParser: MessageParser, + private val buildMeta: BuildMeta, +) : Presenter { + @AssistedFactory + interface Factory { + fun create(url: String): CreateAccountPresenter + } + + @Composable + override fun present(): CreateAccountState { + val coroutineScope = rememberCoroutineScope() + val pageProgress: MutableState = remember { mutableIntStateOf(0) } + val createAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } + + fun handleEvents(event: CreateAccountEvents) { + when (event) { + is CreateAccountEvents.SetPageProgress -> { + pageProgress.value = event.progress + } + is CreateAccountEvents.OnMessageReceived -> { + // Ignore unexpected message + if (event.message.contains("isTrusted")) return + coroutineScope.importSession(event.message, createAction) + } + } + } + + return CreateAccountState( + url = url, + pageProgress = pageProgress.value, + isDebugBuild = buildMeta.isDebuggable, + createAction = createAction.value, + eventSink = ::handleEvents + ) + } + + private fun CoroutineScope.importSession(message: String, loggedInState: MutableState>) = launch { + loggedInState.value = AsyncAction.Loading + runCatching { + messageParser.parse(message) + }.flatMap { externalSession -> + authenticationService.importCreatedSession(externalSession) + }.onSuccess { sessionId -> + // We will not navigate to the WaitList screen, so the login user story is done + defaultLoginUserStory.setLoginFlowIsDone(true) + loggedInState.value = AsyncAction.Success(sessionId) + }.onFailure { failure -> + loggedInState.value = AsyncAction.Failure(failure) + } + } +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountState.kt new file mode 100644 index 0000000000..de05825d69 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountState.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.screens.createaccount + +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.core.SessionId + +data class CreateAccountState( + val url: String, + val pageProgress: Int, + val createAction: AsyncAction, + val isDebugBuild: Boolean, + val eventSink: (CreateAccountEvents) -> Unit +) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountStateProvider.kt new file mode 100644 index 0000000000..45b3b4d004 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountStateProvider.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2023, 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.screens.createaccount + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.core.SessionId + +open class CreateAccountStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aCreateAccountState(), + aCreateAccountState(pageProgress = 33), + aCreateAccountState(createAction = AsyncAction.Loading), + aCreateAccountState(createAction = AsyncAction.Failure(Throwable("Failed to create account"))), + ) +} + +private fun aCreateAccountState( + pageProgress: Int = 100, + createAction: AsyncAction = AsyncAction.Uninitialized, +) = CreateAccountState( + url = "https://example.com", + isDebugBuild = true, + pageProgress = pageProgress, + createAction = createAction, + eventSink = {} +) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountView.kt new file mode 100644 index 0000000000..aaa67a80f9 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountView.kt @@ -0,0 +1,181 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.screens.createaccount + +import android.annotation.SuppressLint +import android.view.ViewGroup +import android.webkit.JsResult +import android.webkit.WebChromeClient +import android.webkit.WebView +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +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 +import androidx.compose.ui.viewinterop.AndroidView +import io.element.android.compound.theme.ElementTheme +import io.element.android.features.login.impl.R +import io.element.android.libraries.designsystem.components.async.AsyncActionView +import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.aliasScreenTitle +import io.element.android.libraries.designsystem.theme.components.LinearProgressIndicator +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.theme.progressIndicatorTrackColor +import timber.log.Timber + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun CreateAccountView( + state: CreateAccountState, + onBackClick: () -> Unit, + onOpenExternalUrl: (String) -> Unit, + modifier: Modifier = Modifier, +) { + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + title = { + Text( + stringResource(R.string.screen_create_account_title), + style = ElementTheme.typography.aliasScreenTitle, + ) + }, + navigationIcon = { + BackButton(onClick = onBackClick) + }, + ) + } + ) { contentPadding -> + Box( + modifier = Modifier + .padding(contentPadding) + .consumeWindowInsets(contentPadding) + .fillMaxSize() + ) { + CreateAccountWebView( + modifier = Modifier + .fillMaxSize(), + state = state, + onWebViewCreate = { webView -> + WebViewMessageInterceptor( + webView, + state.isDebugBuild, + onOpenExternalUrl = onOpenExternalUrl, + onMessage = { + state.eventSink(CreateAccountEvents.OnMessageReceived(it)) + }, + ) + } + ) + AnimatedVisibility( + visible = state.pageProgress != 100, + // Disable enter animation + enter = fadeIn(initialAlpha = 1f), + exit = fadeOut(), + ) { + LinearProgressIndicator( + modifier = Modifier + .fillMaxWidth() + .height(2.dp), + progress = { state.pageProgress / 100f }, + trackColor = ElementTheme.colors.progressIndicatorTrackColor, + ) + } + } + } + + AsyncActionView( + async = state.createAction, + onSuccess = {}, + onErrorDismiss = onBackClick, + onRetry = null + ) +} + +@Composable +private fun CreateAccountWebView( + state: CreateAccountState, + onWebViewCreate: (WebView) -> Unit, + modifier: Modifier = Modifier, +) { + if (LocalInspectionMode.current) { + Box(modifier = modifier, contentAlignment = Alignment.Center) { + Text("WebView - can't be previewed") + } + } else { + AndroidView( + modifier = modifier, + factory = { context -> + WebView(context).apply { + onWebViewCreate(this) + setup(state) + } + }, + update = { webView -> + if (webView.url != state.url) { + webView.loadUrl(state.url) + } + }, + onRelease = { webView -> + webView.destroy() + } + ) + } +} + +@SuppressLint("SetJavaScriptEnabled") +private fun WebView.setup(state: CreateAccountState) { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + with(settings) { + javaScriptEnabled = true + domStorageEnabled = true + } + + webChromeClient = object : WebChromeClient() { + override fun onProgressChanged(view: WebView?, newProgress: Int) { + super.onProgressChanged(view, newProgress) + state.eventSink(CreateAccountEvents.SetPageProgress(newProgress)) + } + + override fun onJsBeforeUnload(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean { + Timber.w("onJsBeforeUnload, cancelling the dialog, we will open external links in a Custom Chrome Tab") + result?.confirm() + return true + } + } +} + +@PreviewsDayNight +@Composable +internal fun CreateAccountViewPreview(@PreviewParameter(CreateAccountStateProvider::class) state: CreateAccountState) = ElementPreview { + CreateAccountView( + state = state, + onBackClick = {}, + onOpenExternalUrl = {}, + ) +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MessageParser.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MessageParser.kt new file mode 100644 index 0000000000..a47bd8a83c --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MessageParser.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.screens.createaccount + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.matrix.api.auth.external.ExternalSession +import kotlinx.serialization.json.Json +import javax.inject.Inject + +interface MessageParser { + /** + * Parse the message and return the ExternalSession object, or + * throw an exception if the message is invalid. + */ + fun parse(message: String): ExternalSession +} + +@ContributesBinding(AppScope::class) +class DefaultMessageParser @Inject constructor( + private val accountProviderDataSource: AccountProviderDataSource, +) : MessageParser { + override fun parse(message: String): ExternalSession { + val parser = Json { ignoreUnknownKeys = true } + val response = parser.decodeFromString(MobileRegistrationResponse.serializer(), message) + val userId = response.userId ?: error("No user ID in response") + val homeServer = response.homeServer ?: accountProviderDataSource.flow().value.url + val accessToken = response.accessToken ?: error("No access token in response") + val deviceId = response.deviceId ?: error("No device ID in response") + return ExternalSession( + userId = userId, + homeserverUrl = homeServer, + accessToken = accessToken, + deviceId = deviceId, + refreshToken = null, + slidingSyncProxy = null + ) + } +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MobileRegistrationResponse.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MobileRegistrationResponse.kt new file mode 100644 index 0000000000..5db50e1804 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MobileRegistrationResponse.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.screens.createaccount + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +/** + * For ref: + * https://github.com/element-hq/matrix-react-sdk/pull/42/files#diff-2bbba5a742004fd4e924a639ded444279f66f7ad890cb669fbc91ac6b8638c64R56 + */ +@Serializable +data class MobileRegistrationResponse( + @SerialName("user_id") + val userId: String? = null, + @SerialName("home_server") + val homeServer: String? = null, + @SerialName("access_token") + val accessToken: String? = null, + @SerialName("device_id") + val deviceId: String? = null, +) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/WebViewMessageInterceptor.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/WebViewMessageInterceptor.kt new file mode 100644 index 0000000000..6dfe903b09 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/WebViewMessageInterceptor.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2023, 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.screens.createaccount + +import android.graphics.Bitmap +import android.webkit.JavascriptInterface +import android.webkit.WebResourceRequest +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.webkit.WebViewCompat +import androidx.webkit.WebViewFeature + +class WebViewMessageInterceptor( + webView: WebView, + private val debugLog: Boolean, + private val onOpenExternalUrl: (String) -> Unit, + private val onMessage: (String) -> Unit, +) { + companion object { + // We call both the WebMessageListener and the JavascriptInterface objects in JS with this + // 'listenerName' so they can both receive the data from the WebView when + // `${LISTENER_NAME}.postMessage(...)` is called + const val LISTENER_NAME = "elementX" + } + + init { + webView.webViewClient = object : WebViewClient() { + override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { + super.onPageStarted(view, url, favicon) + + // We inject this JS code when the page starts loading to attach a message listener to the window. + view?.evaluateJavascript( + """ + window.addEventListener( + "mobileregistrationresponse", + (event) => { + let json = JSON.stringify(event.detail) + ${"console.log('message sent: ' + json);".takeIf { debugLog }} + $LISTENER_NAME.postMessage(json); + }, + false, + ); + """.trimIndent(), + null + ) + } + + override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { + request ?: return super.shouldOverrideUrlLoading(view, request) + // Load the URL in a Chrome Custom Tab, and return true to cancel the load + onOpenExternalUrl(request.url.toString()) + return true + } + } + + // Use WebMessageListener if supported, otherwise use JavascriptInterface + if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) { + // Create a WebMessageListener, which will receive messages from the WebView and reply to them + val webMessageListener = WebViewCompat.WebMessageListener { _, message, _, _, _ -> + onMessageReceived(message.data) + } + WebViewCompat.addWebMessageListener( + webView, + LISTENER_NAME, + setOf("*"), + webMessageListener + ) + } else { + webView.addJavascriptInterface( + object { + @JavascriptInterface + fun postMessage(json: String?) { + onMessageReceived(json) + } + }, + LISTENER_NAME, + ) + } + } + + private fun onMessageReceived(json: String?) { + // Here is where we would handle the messages from the WebView, passing them to the listener + json?.let { onMessage(it) } + } +} 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 bd6437ddba..48fe71e0fc 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 @@ -283,7 +283,7 @@ private fun LoginErrorDialog(error: Throwable, onDismiss: () -> Unit) { ErrorDialog( title = stringResource(id = CommonStrings.dialog_title_error), content = stringResource(loginError(error)), - onDismiss = onDismiss + onSubmit = onDismiss ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt new file mode 100644 index 0000000000..b7a30ee843 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.web + +import android.net.Uri +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.appconfig.AuthenticationConfig +import io.element.android.features.login.impl.resolver.network.WellknownAPI +import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.network.RetrofitFactory +import timber.log.Timber +import java.net.HttpURLConnection +import javax.inject.Inject + +interface WebClientUrlForAuthenticationRetriever { + suspend fun retrieve(homeServerUrl: String): String +} + +@ContributesBinding(AppScope::class) +class DefaultWebClientUrlForAuthenticationRetriever @Inject constructor( + private val retrofitFactory: RetrofitFactory, +) : WebClientUrlForAuthenticationRetriever { + override suspend fun retrieve(homeServerUrl: String): String { + if (homeServerUrl != AuthenticationConfig.MATRIX_ORG_URL) { + Timber.w("Temporary account creation flow is only supported on matrix.org") + throw AccountCreationNotSupported() + } + val wellknownApi = retrofitFactory.create(homeServerUrl) + .create(WellknownAPI::class.java) + val result = try { + wellknownApi.getElementWellKnown() + } catch (e: retrofit2.HttpException) { + throw when { + e.code() == HttpURLConnection.HTTP_NOT_FOUND -> AccountCreationNotSupported() + else -> e + } + } + val registrationHelperUrl = result.registrationHelperUrl + return if (registrationHelperUrl != null) { + Uri.parse(registrationHelperUrl) + .buildUpon() + .appendQueryParameter("hs_url", homeServerUrl) + .build() + .toString() + } else { + throw AccountCreationNotSupported() + } + } +} diff --git a/features/login/impl/src/main/res/values-be/translations.xml b/features/login/impl/src/main/res/values-be/translations.xml index f8e675854e..777acc423d 100644 --- a/features/login/impl/src/main/res/values-be/translations.xml +++ b/features/login/impl/src/main/res/values-be/translations.xml @@ -78,10 +78,4 @@ "Тут будуць захоўвацца вашыя размовы - сапраўды гэтак жа, як вы выкарыстоўваеце паштовага правайдара для захоўвання сваіх лістоў." "Вы збіраецеся ўвайсці ў %1$s" "Вы збіраецеся стварыць уліковы запіс на %1$s" - "Зараз існуе высокі попыт на %1$s на %2$s. Калі ласка, вярніцеся ў праграму праз некалькі дзён і паспрабуйце зноў. - -Дзякуй за цярпенне!" - "Вітаем у %1$s!" - "Амаль гатова." - "Вы зарэгістраваны." diff --git a/features/login/impl/src/main/res/values-bg/translations.xml b/features/login/impl/src/main/res/values-bg/translations.xml index cfc51c3f8c..eced3ef828 100644 --- a/features/login/impl/src/main/res/values-bg/translations.xml +++ b/features/login/impl/src/main/res/values-bg/translations.xml @@ -24,5 +24,4 @@ "Това е мястото, където ще живеят вашите разговори — точно както бихте използвали имейл доставчик, за да съхранявате вашите имейли." "На път сте да влезете в %1$s" "На път сте да създадете акаунт в %1$s" - "Добре дошли в %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 c0f00a87af..d65a5571e4 100644 --- a/features/login/impl/src/main/res/values-cs/translations.xml +++ b/features/login/impl/src/main/res/values-cs/translations.xml @@ -78,10 +78,4 @@ Zkuste se přihlásit ručně nebo naskenujte QR kód pomocí jiného zařízen "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 služby %1$s" "Chystáte se vytvořit účet na %1$s" - "Na %2$s je momentálně vysoká poptávka po %1$s. Vraťte se do aplikace za pár dní a zkuste to znovu. - -Díky za trpělivost!" - "Vítá vás %1$s!" - "Jste v pořadníku!" - "Jdete do toho!" 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 2399ec86ba..0ead5a8147 100644 --- a/features/login/impl/src/main/res/values-de/translations.xml +++ b/features/login/impl/src/main/res/values-de/translations.xml @@ -21,6 +21,7 @@ "Du kannst nur eine Verbindung zu einem vorhandenen Server herstellen, der Sliding Sync unterstützt. Dein Homeserver-Administrator muss das konfigurieren. %1$s" "Wie lautet die Adresse deines Servers?" "Wähle deinen Server aus" + "Konto erstellen" "Dieses Konto wurde deaktiviert." "Falscher Benutzername und/oder Passwort" "Dies ist keine gültige Benutzerkennung. Erwartetes Format: \'@user:homeserver.org\'" @@ -78,10 +79,4 @@ Versuche, dich manuell anzumelden, oder scanne den QR-Code mit einem anderen Ger "Hier werden deine Gespräche gespeichert - so wie du deine E-Mails bei einem E-Mail-Anbieter aufbewahren würden." "Du bist dabei, dich bei %1$s anzumelden" "Du bist dabei, ein Konto auf %1$s zu erstellen" - "Derzeit besteht eine hohe Nachfrage nach %1$s auf %2$s. Kehre in ein paar Tagen zur App zurück und versuche es erneut. - -Danke für deine Geduld!" - "Willkommen bei %1$s!" - "Du bist fast am Ziel." - "Du bist dabei." diff --git a/features/login/impl/src/main/res/values-el/translations.xml b/features/login/impl/src/main/res/values-el/translations.xml index 8ff0227168..b0ad469732 100644 --- a/features/login/impl/src/main/res/values-el/translations.xml +++ b/features/login/impl/src/main/res/values-el/translations.xml @@ -21,6 +21,7 @@ "Μπορείτε να συνδεθείς μόνο σε υπάρχοντα διακομιστή που υποστηρίζει Sliding sync. Ο διαχειριστής του οικιακού διακομιστή σου θα πρέπει να το ρυθμίσει. %1$s" "Ποια είναι η διεύθυνση του διακομιστή σου;" "Επέλεξε το διακομιστή σου" + "Δημιουργία λογαριασμού" "Αυτός ο λογαριασμός έχει απενεργοποιηθεί." "Λανθασμένο όνομα χρήστη ή κωδικός πρόσβασης" "Αυτό δεν είναι έγκυρο αναγνωριστικό χρήστη. Αναμενόμενη μορφή: \'@χρήστης:homeserver.org\'" @@ -78,10 +79,4 @@ "Εδώ θα ζουν οι συνομιλίες σου - όπως θα χρησιμοποιούσες έναν πάροχο email για να διατηρήσεις τα email σου." "Πρόκειται να συνδεθείς στο %1$s" "Πρόκειται να δημιουργήσεις έναν λογαριασμό στο %1$s" - "Υπάρχει μεγάλη ζήτηση για το %1$s στον %2$s αυτή τη στιγμή. Επέστρεψε στην εφαρμογή σε λίγες μέρες και δοκίμασε ξανά. - -Ευχαριστώ για την υπομονή σου!" - "Καλώς ήρθες στο %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 c7537c9d7e..60324ae89d 100644 --- a/features/login/impl/src/main/res/values-es/translations.xml +++ b/features/login/impl/src/main/res/values-es/translations.xml @@ -37,10 +37,4 @@ "Aquí es donde se alojarán tus conversaciones — justo como utilizarías un proveedor de correo electrónico para guardar tus correos electrónicos." "Estás a punto de iniciar sesión en %1$s" "Estás a punto de crear una cuenta en %1$s" - "Hay una gran demanda para %1$s en %2$s en este momento. Vuelve a la aplicación en unos días e inténtalo de nuevo. - -¡Gracias por tu paciencia!" - "¡Bienvenido a %1$s!" - "Ya casi has terminado." - "Estás dentro." diff --git a/features/login/impl/src/main/res/values-et/translations.xml b/features/login/impl/src/main/res/values-et/translations.xml index 0558b349b4..36ce791bb2 100644 --- a/features/login/impl/src/main/res/values-et/translations.xml +++ b/features/login/impl/src/main/res/values-et/translations.xml @@ -21,6 +21,7 @@ "Sa saad luua ühendust vaid olemasoleva serveriga, mis toetab Sliding sync režiimi. Sinu koduserveri haldur peaks selle seadistama. %1$s" "Mis on sinu koduserveri aadress?" "Vali oma server" + "Loo kasutajakonto" "Konto on kasutusest eemaldatud." "Vigane kasutajanimi ja/või salasõna" "See ei ole korrektne kasutajanimi. Õige vorming on: „@kasutaja:koduserver.ee“" @@ -78,10 +79,4 @@ Proovi käsitsi sisselogimist või skaneeri QR-koodi mõne muu seadmega.""See on koht, kus sinu vestlused elavad – just nagu kasutaksid oma e-kirjade säilitamiseks e-postitenuse pakkujat." "Sa oled sisselogimas koduserverisse %1$s" "Sa oled loomas kasutajakontot koduserveris %1$s" - "%1$s kasutamiseks %2$s koduserveris on hetkel palju huvilisi. Proovi seda samast rakendusest mõne päeva pärast. - -Täname kannatlikkuse eest!" - "Tere tulemast rakendusse %1$s!" - "Peaaegu olemas." - "Oled nüüd jututoas." 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 49e513c2a8..b075da9fda 100644 --- a/features/login/impl/src/main/res/values-fr/translations.xml +++ b/features/login/impl/src/main/res/values-fr/translations.xml @@ -21,6 +21,7 @@ "Vous ne pouvez vous connecter qu’à un serveur existant qui prend en charge le sliding sync. L’administrateur de votre serveur d’accueil devra le configurer. %1$s" "Quelle est l’adresse de votre serveur ?" "Choisissez votre serveur" + "Créer un compte" "Ce compte a été désactivé." "Nom d’utilisateur et/ou mot de passe incorrects" "Il ne s’agit pas d’un identifiant utilisateur valide. Format attendu : « @user:homeserver.org »" @@ -76,10 +77,4 @@ "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 à %1$s" "Vous êtes sur le point de créer un compte sur %1$s" - "Il y a une forte demande pour %1$s sur %2$s à l’heure actuelle. Revenez sur l’application dans quelques jours et réessayez. - -Merci pour votre patience !" - "Bienvenue dans %1$s !" - "Vous y êtes presque." - "Vous y êtes." 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 8d8bf40f3f..1fb7f12d0c 100644 --- a/features/login/impl/src/main/res/values-hu/translations.xml +++ b/features/login/impl/src/main/res/values-hu/translations.xml @@ -21,6 +21,7 @@ "Csak olyan meglévő kiszolgálóhoz csatlakozhat, amely támogatja a Sliding sync protokollt. Ezt a Matrix-kiszolgáló adminisztrátorának kell beállítania. %1$s" "Mi a kiszolgálója címe?" "Válassza ki a kiszolgálóját" + "Fiók létrehozása" "Ez a fiók deaktiválva lett." "Helytelen felhasználónév vagy jelszó" "Ez nem érvényes felhasználóazonosító. A várt formátum: „@user:homeserver.org”" @@ -78,10 +79,4 @@ Próbáljon meg kézileg bejelentkezni, vagy olvassa be a QR-kódot egy másik e "Itt lesznek a beszélgetései – ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez." "Hamarosan bejelentkezik ebbe: %1$s" "Hamarosan létrehoz egy fiókot ezen: %1$s" - "Jelenleg nagy a kereslet a(z) %2$s oldalon futó %1$s iránt. Térjen vissza néhány nap múlva az alkalmazáshoz, és próbálja újra. - -Köszönjük a türelmét!" - "Üdvözli az %1$s!" - "Már majdnem kész van." - "Bent van." 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 97d84ff1f4..749406da04 100644 --- a/features/login/impl/src/main/res/values-in/translations.xml +++ b/features/login/impl/src/main/res/values-in/translations.xml @@ -78,10 +78,4 @@ Coba masuk secara manual, atau pindai kode QR dengan perangkat lain." "Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda." "Anda akan masuk ke %1$s" "Anda akan membuat akun di %1$s" - "Ada permintaan tinggi untuk %1$s di %2$s saat ini. Kembalilah ke aplikasi dalam beberapa hari dan coba lagi. - -Terima kasih atas kesabaran Anda!" - "Selamat datang di %1$s!" - "Anda hampir selesai." - "Anda sudah masuk." 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 dcd9a7c2ae..eaa5313b03 100644 --- a/features/login/impl/src/main/res/values-it/translations.xml +++ b/features/login/impl/src/main/res/values-it/translations.xml @@ -78,10 +78,4 @@ Prova ad accedere manualmente o scansiona il codice QR con un altro dispositivo. "Qui è dove vivranno le tue conversazioni — proprio come useresti un fornitore di posta elettronica per conservare le tue email." "Stai per accedere a %1$s" "Stai per creare un account su %1$s" - "Al momento c\'è una grande richiesta per %1$s su %2$s. Torna a visitare l\'app tra qualche giorno e riprova. - -Grazie per la pazienza!" - "Benvenuti in %1$s!" - "Ci sei quasi." - "Sei dentro." diff --git a/features/login/impl/src/main/res/values-ka/translations.xml b/features/login/impl/src/main/res/values-ka/translations.xml index 84e97e0a34..6fc49e7b41 100644 --- a/features/login/impl/src/main/res/values-ka/translations.xml +++ b/features/login/impl/src/main/res/values-ka/translations.xml @@ -34,10 +34,4 @@ "აქ იქნება თქვენი საუბრები - ისევე, როგორც თქვენ ელ. ფოსტაში ინახება თქვენი ელ.წერილები." "თქვენ აპირებთ შესვლას %1$s-ში" "თქვენ აპირებთ ანგარიშის შექმნას %1$s-ში" - "ახლა დიდი მოთხოვნაა %1$s-ზე %2$s-ში. დაბრუნდით რამდენიმე დღეში და სცადეთ ერთხელაც. - -მადლობა მოთმენისათვის!" - "კეთილი იყოს თქვენი მობრძანება %1$s-ში!" - "თითქმის მზადაა." - "თქვენ შეხვედით." diff --git a/features/login/impl/src/main/res/values-nl/translations.xml b/features/login/impl/src/main/res/values-nl/translations.xml index 80fbe57afb..627f5b873e 100644 --- a/features/login/impl/src/main/res/values-nl/translations.xml +++ b/features/login/impl/src/main/res/values-nl/translations.xml @@ -14,6 +14,8 @@ "Gebruik een andere accountprovider, zoals je eigen privéserver of een zakelijke account." "Wijzig accountprovider" "We konden deze homeserver niet bereiken. Controleer of je de homeserver-URL juist hebt ingevoerd. Als de URL juist is, neem dan contact op met de beheerder van je homeserver voor verdere hulp." + "Sliding sync is niet beschikbaar vanwege een probleem in het well-known bestand: +%1$s" "Deze server ondersteunt op dit moment geen sliding sync." "Homeserver-URL" "Je kunt alleen verbinding maken met een bestaande server die sliding sync ondersteunt. De beheerder van de homeserver moet dit configureren. %1$s" @@ -28,17 +30,45 @@ "Matrix is een open netwerk voor veilige, gedecentraliseerde communicatie." "Welkom terug!" "Inloggen bij %1$s" + "Een beveiligde verbinding tot stand brengen" + "Er kon geen beveiligde verbinding worden gemaakt met het nieuwe apparaat. Je bestaande apparaten zijn nog steeds veilig en je hoeft je daarover geen zorgen te maken." + "Wat nu?" + "Probeer opnieuw in te loggen met een QR-code voor het geval dit een netwerkprobleem was" + "Als je hetzelfde probleem ondervindt, probeer dan een ander wifi-netwerk of gebruik je mobiele data in plaats van wifi." + "Als dat niet werkt, log dan handmatig in" + "Verbinding niet veilig" + "Daar word je gevraagd om de twee cijfers in te voeren die op dit apparaat worden weergegeven." + "Voer het onderstaande nummer in op je andere apparaat" + "De aanmelding is geannuleerd op het andere apparaat." + "Login verzoek geannuleerd" + "De aanmelding is geweigerd op het andere apparaat." + "Aanmelden geweigerd" + "Aanmelden is verlopen. Probeer het opnieuw." + "De aanmelding was niet op tijd voltooid" + "QR-code wordt niet ondersteund" + "Klaar om te scannen" + "Open %1$s op een desktopapparaat" + "Klik op je afbeelding" + "Selecteer %1$s" + "“Nieuw apparaat koppelen”" + "Scan de QR-code met dit apparaat" + "Open %1$s op een ander apparaat om de QR-code te krijgen" + "Gebruik de QR-code die op het andere apparaat wordt weergegeven." "Probeer het opnieuw" + "Verkeerde QR-code" + "Ga naar camera-instellingen" + "Je moet %1$s toestemming geven om de camera van je apparaat te gebruiken om verder te gaan." + "Cameratoegang toestaan om de QR-code te scannen" + "Scan de QR-code" + "Opnieuw beginnen" + "Er is een onverwachte fout opgetreden. Probeer het opnieuw." + "Aan het wachten op je andere apparaat" + "Je accountprovider kan om de volgende code vragen om de aanmelding te verifiëren." + "Je verificatiecode" "Accountprovider wijzigen" "Een privéserver voor medewerkers van Element." "Matrix is een open netwerk voor veilige, gedecentraliseerde communicatie." "Dit is waar je gesprekken zullen worden bewaard — net zoals je een e-mailprovider zou gebruiken om je e-mails te bewaren." "Je staat op het punt je aan te melden bij %1$s" "Je staat op het punt een account aan te maken op %1$s" - "Er is momenteel veel vraag naar %1$s op %2$s. Kom over een paar dagen terug naar de app en probeer het opnieuw. - -Bedankt voor je geduld!" - "Welkom bij %1$s!" - "Je bent er bijna." - "Je bent binnen." diff --git a/features/login/impl/src/main/res/values-pl/translations.xml b/features/login/impl/src/main/res/values-pl/translations.xml index 4d23e15fe8..6990121a43 100644 --- a/features/login/impl/src/main/res/values-pl/translations.xml +++ b/features/login/impl/src/main/res/values-pl/translations.xml @@ -78,10 +78,4 @@ Spróbuj zalogować się ręcznie lub zeskanuj kod QR na innym urządzeniu.""Tutaj będą przechowywane Twoje konwersacje - w podobnej formie jak wiadomości widnieją na skrzynce e-mail." "Zamierzasz się zalogować do %1$s" "Zamierzasz utworzyć konto na %1$s" - "Obecnie istnieje duże zapotrzebowanie na %1$s na %2$s. Wróć do aplikacji za kilka dni i spróbuj ponownie. - -Dziękujemy za Twoją cierpliwość!" - "Witamy w %1$s!" - "Już prawie gotowe!" - "Witamy!" diff --git a/features/login/impl/src/main/res/values-pt-rBR/translations.xml b/features/login/impl/src/main/res/values-pt-rBR/translations.xml index 4cb9b4f7b0..7b753ffb9f 100644 --- a/features/login/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/login/impl/src/main/res/values-pt-rBR/translations.xml @@ -34,10 +34,4 @@ "Aqui é onde suas conversas vão ficar — assim como você usa um provedor de e-mails para manter seus e-mails." "Você está prestes a fazer login em %1$s" "Você está prestes a criar uma conta em %1$s" - "Há uma grande demanda por %1$s sobre %2$s no momento. Volte ao aplicativo em alguns dias e tente novamente. - -Obrigado pela sua paciência!" - "Bem-vindo ao %1$s!" - "Você está quase lá." - "Você está dentro." diff --git a/features/login/impl/src/main/res/values-pt/translations.xml b/features/login/impl/src/main/res/values-pt/translations.xml index f5feb0cbdd..f03b48c5dd 100644 --- a/features/login/impl/src/main/res/values-pt/translations.xml +++ b/features/login/impl/src/main/res/values-pt/translations.xml @@ -78,10 +78,4 @@ Tenta iniciar a sessão manualmente ou digitaliza o código QR com outro disposi "É aqui que as tuas conversas vão ficar — tal como num serviço de e-mail." "Irás iniciar sessão em %1$s" "Irás criar uma conta em %1$s" - "Há uma grande procura pela %1$s no %2$s, de momento. Volta à aplicação daqui a uns dias e tenta novamente. - -Obrigado!" - "Bem-vindo à %1$s!" - "Estás quase lá." - "Estás dentro." 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 1ff3fe2436..261f16d9f6 100644 --- a/features/login/impl/src/main/res/values-ro/translations.xml +++ b/features/login/impl/src/main/res/values-ro/translations.xml @@ -78,10 +78,4 @@ "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." "Sunteți pe cale să vă conectați la %1$s" "Sunteți pe cale să creați un cont pe %1$s" - "Există o cerere mare pentru %1$s pentru %2$s în acest moment. Reveniți la aplicație în câteva zile și încercați din nou. - -Vă mulțumim pentru răbdare!" - "Bun venit la%1$s!" - "Sunteți pe lista de așteptare" - "Sunteți conectat!" 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 c64d683a12..40f3f9b5df 100644 --- a/features/login/impl/src/main/res/values-ru/translations.xml +++ b/features/login/impl/src/main/res/values-ru/translations.xml @@ -21,6 +21,7 @@ "Вы можете подключиться только к существующему серверу, поддерживающему sliding sync. Администратору домашнего сервера потребуется настроить его. %1$s" "Какой адрес у вашего сервера?" "Выберите свой сервер" + "Создать учетную запись" "Данная учетная запись была деактивирована." "Неверное имя пользователя и/или пароль" "Это не корректный идентификатор пользователя. Ожидаемый формат: \'@user:homeserver.org\'" @@ -78,10 +79,4 @@ "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем." "Вы собираетесь войти в %1$s" "Вы собираетесь создать учетную запись на %1$s" - "В настоящее время существует высокий спрос на %1$s на %2$s. Вернитесь в приложение через несколько дней и попробуйте снова. - -Спасибо за терпение!" - "Добро пожаловать в %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 fea7879d01..7cadc5ecb8 100644 --- a/features/login/impl/src/main/res/values-sk/translations.xml +++ b/features/login/impl/src/main/res/values-sk/translations.xml @@ -21,6 +21,7 @@ "Môžete sa pripojiť iba k existujúcemu serveru, ktorý podporuje kĺzavú synchronizáciu. Správca domovského servera ju bude musieť nakonfigurovať. %1$s" "Aká je adresa vášho servera?" "Vyberte svoj server" + "Vytvoriť účet" "Tento účet bol deaktivovaný." "Nesprávne používateľské meno a/alebo heslo" "Toto nie je platný identifikátor používateľa. Očakávaný formát: \'@pouzivatel:homeserver.sk\'" @@ -78,10 +79,4 @@ Skúste sa prihlásiť manuálne alebo naskenujte QR kód pomocou iného zariade "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 %1$s" "Chystáte sa vytvoriť účet na %1$s" - "Momentálne je veľký dopyt po %1$s na %2$s. Vráťte sa do aplikácie za pár dní a skúste to znova. - -Ďakujeme za trpezlivosť!" - "Vitajte v %1$s!" - "Ste na čakanej listine!" - "Ste dnu!" diff --git a/features/login/impl/src/main/res/values-sv/translations.xml b/features/login/impl/src/main/res/values-sv/translations.xml index d50610adcc..de290469ca 100644 --- a/features/login/impl/src/main/res/values-sv/translations.xml +++ b/features/login/impl/src/main/res/values-sv/translations.xml @@ -78,10 +78,4 @@ Prova att logga in manuellt eller skanna QR-koden med en annan enhet." "Det är här dina konversationer kommer att sparas - precis som du skulle använda en e-postleverantör för att spara dina e-brev." "Du är på väg att logga in på %1$s" "Du är på väg att skapa ett konto på %1$s" - "Det finns en stor efterfrågan på %1$s på %2$s just nu. Kom tillbaka till appen om några dagar och försök igen. - -Tack för ditt tålamod!" - "Välkommen till %1$s!" - "Du är nästan framme." - "Du är inne." diff --git a/features/login/impl/src/main/res/values-uk/translations.xml b/features/login/impl/src/main/res/values-uk/translations.xml index 9cb6eb1f5f..df436a9a69 100644 --- a/features/login/impl/src/main/res/values-uk/translations.xml +++ b/features/login/impl/src/main/res/values-uk/translations.xml @@ -78,10 +78,4 @@ "Тут будуть зберігатися Ваші розмови - так само, як Ви використовуєте поштову скриньку для зберігання своїх електронних листів." "Ви збираєтесь увійти в %1$s" "Ви збираєтеся створити обліковий запис на %1$s" - "На цей момент існує високий попит на %1$s в %2$s. Поверніться до застосунку через кілька днів і спробуйте ще раз. - -Дякуємо за терпіння!" - "Ласкаво просимо до %1$s!" - "Майже готово." - "Готово." diff --git a/features/login/impl/src/main/res/values-uz/translations.xml b/features/login/impl/src/main/res/values-uz/translations.xml index 67dc5129d7..db16e17b09 100644 --- a/features/login/impl/src/main/res/values-uz/translations.xml +++ b/features/login/impl/src/main/res/values-uz/translations.xml @@ -33,10 +33,4 @@ "Bu sizning suhbatlaringiz yashaydigan joy - xuddi siz elektron pochta xabarlaringizni saqlash uchun elektron pochta provayderidan foydalanganingiz kabi." "Siz tizimga kirmoqchisiz%1$s" "Hisob yaratmoqchisiz%1$s" - "Hozirgi paytda %2$sga %1$sda talab yuqori. Bir necha kundan keyin ilovaga qayting va qaytadan urining. - -Sabr-toqatingiz uchun rahmat!" - "%1$sga Xush kelibsiz!" - "Siz deyarli keldingiz." - "Siz kirdingiz." 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 d345095c4b..b66cfca31f 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 @@ -29,5 +29,4 @@ "您的所有對話將保存於此,就如同您的電子郵件供應商會保存您的電子郵件一樣。" "您即將登入 %1$s" "您即將在 %1$s 建立帳號" - "歡迎使用 %1$s!" diff --git a/features/login/impl/src/main/res/values-zh/translations.xml b/features/login/impl/src/main/res/values-zh/translations.xml index 0afd6dad93..2f24aacf69 100644 --- a/features/login/impl/src/main/res/values-zh/translations.xml +++ b/features/login/impl/src/main/res/values-zh/translations.xml @@ -78,10 +78,4 @@ "这是您的对话将进行的地方,就像您使用电子邮件提供商来保存电子邮件一样。" "即将登录 %1$s" "即将在 %1$s 上创建一个账户" - "目前 %1$s 上 %2$s 的负载很大。过几天再回来试试吧。 - -感谢您的耐心!" - "欢迎使用 %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 cade81c4f2..a46b083d63 100644 --- a/features/login/impl/src/main/res/values/localazy.xml +++ b/features/login/impl/src/main/res/values/localazy.xml @@ -21,6 +21,7 @@ "You can only connect to an existing server that supports sliding sync. Your homeserver admin will need to configure it. %1$s" "What is the address of your server?" "Select your server" + "Create account" "This account has been deactivated." "Incorrect username and/or password" "This is not a valid user identifier. Expected format: ‘@user:homeserver.org’" @@ -78,10 +79,4 @@ Try signing in manually, or scan the QR code with another device." "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 %1$s" "You’re about to create an account on %1$s" - "There\'s a high demand for %1$s on %2$s at the moment. Come back to the app in a few days and try again. - -Thanks for your patience!" - "Welcome to %1$s!" - "You’re almost there." - "You\'re in." diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt index 1609ffef3c..1d70580e4d 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt @@ -13,7 +13,10 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.login.impl.DefaultLoginUserStory import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported import io.element.android.features.login.impl.util.defaultAccountProvider +import io.element.android.features.login.impl.web.FakeWebClientUrlForAuthenticationRetriever +import io.element.android.features.login.impl.web.WebClientUrlForAuthenticationRetriever import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.test.A_HOMESERVER @@ -255,17 +258,109 @@ class ConfirmAccountProviderPresenterTest { } } + @Test + fun `present - confirm account creation without oidc and without url generates an error`() = runTest { + val authenticationService = FakeMatrixAuthenticationService() + authenticationService.givenHomeserver(A_HOMESERVER) + val presenter = createConfirmAccountProviderPresenter( + params = ConfirmAccountProviderPresenter.Params(isAccountCreation = true), + matrixAuthenticationService = authenticationService, + webClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever { + throw AccountCreationNotSupported() + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(ConfirmAccountProviderEvents.Continue) + skipItems(1) // Loading + // Check an error was returned + val submittedState = awaitItem() + assertThat(submittedState.loginFlow.errorOrNull()).isInstanceOf(AccountCreationNotSupported::class.java) + // Assert the error is then cleared + submittedState.eventSink(ConfirmAccountProviderEvents.ClearError) + val clearedState = awaitItem() + assertThat(clearedState.loginFlow).isEqualTo(AsyncData.Uninitialized) + } + } + + @Test + fun `present - confirm account creation with oidc is successful`() = runTest { + val authenticationService = FakeMatrixAuthenticationService() + authenticationService.givenHomeserver(A_HOMESERVER_OIDC) + val presenter = createConfirmAccountProviderPresenter( + params = ConfirmAccountProviderPresenter.Params(isAccountCreation = true), + matrixAuthenticationService = authenticationService, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(ConfirmAccountProviderEvents.Continue) + skipItems(1) // Loading + val submittedState = awaitItem() + assertThat(submittedState.loginFlow).isInstanceOf(AsyncData.Success::class.java) + assertThat(submittedState.loginFlow.dataOrNull()).isInstanceOf(LoginFlow.OidcFlow::class.java) + } + } + + @Test + fun `present - confirm account creation with oidc and url continues with oidc`() = runTest { + val aUrl = "aUrl" + val authenticationService = FakeMatrixAuthenticationService() + authenticationService.givenHomeserver(A_HOMESERVER_OIDC) + val presenter = createConfirmAccountProviderPresenter( + params = ConfirmAccountProviderPresenter.Params(isAccountCreation = true), + matrixAuthenticationService = authenticationService, + webClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever { aUrl }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(ConfirmAccountProviderEvents.Continue) + skipItems(1) // Loading + val submittedState = awaitItem() + assertThat(submittedState.loginFlow).isInstanceOf(AsyncData.Success::class.java) + assertThat(submittedState.loginFlow.dataOrNull()).isInstanceOf(LoginFlow.OidcFlow::class.java) + } + } + + @Test + fun `present - confirm account creation without oidc and with url continuing with url`() = runTest { + val aUrl = "aUrl" + val authenticationService = FakeMatrixAuthenticationService() + authenticationService.givenHomeserver(A_HOMESERVER) + val presenter = createConfirmAccountProviderPresenter( + params = ConfirmAccountProviderPresenter.Params(isAccountCreation = true), + matrixAuthenticationService = authenticationService, + webClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever { aUrl }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(ConfirmAccountProviderEvents.Continue) + skipItems(1) // Loading + val submittedState = awaitItem() + assertThat(submittedState.loginFlow.dataOrNull()).isEqualTo(LoginFlow.AccountCreationFlow(aUrl)) + } + } + private fun createConfirmAccountProviderPresenter( params: ConfirmAccountProviderPresenter.Params = ConfirmAccountProviderPresenter.Params(isAccountCreation = false), accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(), matrixAuthenticationService: MatrixAuthenticationService = FakeMatrixAuthenticationService(), defaultOidcActionFlow: DefaultOidcActionFlow = DefaultOidcActionFlow(), defaultLoginUserStory: DefaultLoginUserStory = DefaultLoginUserStory(), + webClientUrlForAuthenticationRetriever: WebClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever(), ) = ConfirmAccountProviderPresenter( params = params, accountProviderDataSource = accountProviderDataSource, authenticationService = matrixAuthenticationService, oidcActionFlow = defaultOidcActionFlow, defaultLoginUserStory = defaultLoginUserStory, + webClientUrlForAuthenticationRetriever = webClientUrlForAuthenticationRetriever ) } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenterTest.kt new file mode 100644 index 0000000000..eb89e80638 --- /dev/null +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenterTest.kt @@ -0,0 +1,145 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.screens.createaccount + +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.login.impl.DefaultLoginUserStory +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService +import io.element.android.libraries.matrix.api.auth.external.ExternalSession +import io.element.android.libraries.matrix.test.AN_EXCEPTION +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService +import io.element.android.libraries.matrix.test.core.aBuildMeta +import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test + +class CreateAccountPresenterTest { + @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.url).isEqualTo("aUrl") + assertThat(initialState.pageProgress).isEqualTo(0) + assertThat(initialState.createAction).isEqualTo(AsyncAction.Uninitialized) + assertThat(initialState.isDebugBuild).isTrue() + } + } + + @Test + fun `present - set up progress update the state`() = runTest { + val presenter = createPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(CreateAccountEvents.SetPageProgress(33)) + assertThat(awaitItem().pageProgress).isEqualTo(33) + } + } + + @Test + fun `present - receiving a message not able to be parsed change the state to error`() = runTest { + val presenter = createPresenter( + messageParser = FakeMessageParser { error("An error") } + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(CreateAccountEvents.OnMessageReceived("")) + assertThat(awaitItem().createAction).isInstanceOf(AsyncAction.Failure::class.java) + } + } + + @Test + fun `present - receiving a message containing isTrusted is ignored`() = runTest { + val presenter = createPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(CreateAccountEvents.OnMessageReceived("isTrusted")) + } + } + + @Test + fun `present - receiving a message able to be parsed change the state to success`() = runTest { + val defaultLoginUserStory = DefaultLoginUserStory() + defaultLoginUserStory.setLoginFlowIsDone(false) + assertThat(defaultLoginUserStory.loginFlowIsDone.value).isFalse() + val lambda = lambdaRecorder { _ -> anExternalSession() } + val presenter = createPresenter( + authenticationService = FakeMatrixAuthenticationService( + importCreatedSessionLambda = { Result.success(A_SESSION_ID) } + ), + messageParser = FakeMessageParser(lambda), + defaultLoginUserStory = defaultLoginUserStory, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(CreateAccountEvents.OnMessageReceived("aMessage")) + assertThat(awaitItem().createAction.isLoading()).isTrue() + assertThat(awaitItem().createAction.dataOrNull()).isEqualTo(A_SESSION_ID) + } + lambda.assertions().isCalledOnce().with(value("aMessage")) + assertThat(defaultLoginUserStory.loginFlowIsDone.value).isTrue() + } + + @Test + fun `present - receiving a message able to be parsed but error in importing change the state to error`() = runTest { + val defaultLoginUserStory = DefaultLoginUserStory() + defaultLoginUserStory.setLoginFlowIsDone(false) + assertThat(defaultLoginUserStory.loginFlowIsDone.value).isFalse() + val presenter = createPresenter( + authenticationService = FakeMatrixAuthenticationService( + importCreatedSessionLambda = { Result.failure(AN_EXCEPTION) } + ), + messageParser = FakeMessageParser { anExternalSession() } + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(CreateAccountEvents.OnMessageReceived("")) + assertThat(awaitItem().createAction.isLoading()).isTrue() + assertThat(awaitItem().createAction.errorOrNull()).isNotNull() + } + assertThat(defaultLoginUserStory.loginFlowIsDone.value).isFalse() + } + + private fun createPresenter( + url: String = "aUrl", + authenticationService: MatrixAuthenticationService = FakeMatrixAuthenticationService(), + defaultLoginUserStory: DefaultLoginUserStory = DefaultLoginUserStory(), + messageParser: MessageParser = FakeMessageParser(), + buildMeta: BuildMeta = aBuildMeta(), + ) = CreateAccountPresenter( + url = url, + authenticationService = authenticationService, + defaultLoginUserStory = defaultLoginUserStory, + messageParser = messageParser, + buildMeta = buildMeta, + ) +} diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt new file mode 100644 index 0000000000..44ccb5f0fb --- /dev/null +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.screens.createaccount + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.features.login.impl.util.defaultAccountProvider +import io.element.android.libraries.matrix.api.auth.external.ExternalSession +import kotlinx.serialization.SerializationException +import org.junit.Assert.assertThrows +import org.junit.Test + +class DefaultMessageParserTest { + private val validMessage = """ + { + "user_id": "user_id", + "home_server": "home_server", + "access_token": "access_token", + "device_id": "device_id" + } + """.trimIndent() + + @Test + fun `DefaultMessageParser is able to parse correct message`() { + val sut = DefaultMessageParser( + AccountProviderDataSource() + ) + assertThat(sut.parse(validMessage)).isEqualTo( + anExternalSession( + homeserverUrl = "home_server", + ) + ) + } + + @Test + fun `DefaultMessageParser should throw Exception in case of error`() { + val sut = DefaultMessageParser( + AccountProviderDataSource() + ) + // kotlinx.serialization.json.internal.JsonDecodingException + assertThrows(SerializationException::class.java) { sut.parse("invalid json") } + // missing userId + assertThrows(IllegalStateException::class.java) { sut.parse(validMessage.replace(""""user_id": "user_id",""", "")) } + // missing accessToken + assertThrows(IllegalStateException::class.java) { sut.parse(validMessage.replace(""""access_token": "access_token",""", "")) } + // missing deviceId + assertThrows(IllegalStateException::class.java) { + sut.parse( + validMessage + .replace(""""access_token": "access_token",""", """"access_token": "access_token"""") + .replace(""""device_id": "device_id"""", "") + ) + } + } + + @Test + fun `DefaultMessageParser should be successful even is homeserver url is missing`() { + val sut = DefaultMessageParser( + AccountProviderDataSource() + ) + // missing homeServer + assertThat(sut.parse(validMessage.replace(""""home_server": "home_server",""", ""))).isEqualTo( + anExternalSession( + homeserverUrl = defaultAccountProvider.url, + ) + ) + } +} + +internal fun anExternalSession( + homeserverUrl: String = "home_server", +) = ExternalSession( + userId = "user_id", + homeserverUrl = homeserverUrl, + accessToken = "access_token", + deviceId = "device_id", + refreshToken = null, + slidingSyncProxy = null +) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/FakeMessageParser.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/FakeMessageParser.kt new file mode 100644 index 0000000000..4207d6b102 --- /dev/null +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/FakeMessageParser.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.screens.createaccount + +import io.element.android.libraries.matrix.api.auth.external.ExternalSession +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeMessageParser( + private val parseResult: (String) -> ExternalSession = { lambdaError() } +) : MessageParser { + override fun parse(message: String): ExternalSession { + return parseResult(message) + } +} diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/web/FakeWebClientUrlForAuthenticationRetriever.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/web/FakeWebClientUrlForAuthenticationRetriever.kt new file mode 100644 index 0000000000..c94d114be5 --- /dev/null +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/web/FakeWebClientUrlForAuthenticationRetriever.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.login.impl.web + +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeWebClientUrlForAuthenticationRetriever( + private val retrieveLambda: suspend (homeServerUrl: String) -> String = { lambdaError() } +) : WebClientUrlForAuthenticationRetriever { + override suspend fun retrieve(homeServerUrl: String): String { + return retrieveLambda(homeServerUrl) + } +} 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 8eabf156af..777412e5e3 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 @@ -241,6 +241,9 @@ fun MessagesView( state.customReactionState.eventSink(CustomReactionEvents.ShowCustomReactionSheet(event)) }, onEmojiReactionClick = ::onEmojiReactionClick, + onVerifiedUserSendFailureClick = { event -> + state.timelineState.eventSink(TimelineEvents.ComputeVerifiedUserSendFailure(event)) + }, ) CustomReactionBottomSheet( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt index 840e12583f..39e6cd8836 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt @@ -22,6 +22,8 @@ import io.element.android.features.messages.api.pinned.IsPinnedMessagesFeatureEn import io.element.android.features.messages.impl.UserEventPermissions import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.actionlist.model.TimelineItemActionPostProcessor +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailure +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailureFactory import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemCallNotifyContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent @@ -56,6 +58,7 @@ class DefaultActionListPresenter @AssistedInject constructor( private val appPreferencesStore: AppPreferencesStore, private val isPinnedMessagesFeatureEnabled: IsPinnedMessagesFeatureEnabled, private val room: MatrixRoom, + private val userSendFailureFactory: VerifiedUserSendFailureFactory, ) : ActionListPresenter { @AssistedFactory @ContributesBinding(RoomScope::class) @@ -115,12 +118,14 @@ class DefaultActionListPresenter @AssistedInject constructor( isEventPinned = pinnedEventIds.contains(timelineItem.eventId), ) - val displayEmojiReactions = usersEventPermissions.canSendReaction && - timelineItem.content.canReact() - if (actions.isNotEmpty() || displayEmojiReactions) { + val verifiedUserSendFailure = userSendFailureFactory.create(timelineItem.localSendState) + val displayEmojiReactions = usersEventPermissions.canSendReaction && timelineItem.content.canReact() + + if (actions.isNotEmpty() || displayEmojiReactions || verifiedUserSendFailure != VerifiedUserSendFailure.None) { target.value = ActionListState.Target.Success( event = timelineItem, displayEmojiReactions = displayEmojiReactions, + verifiedUserSendFailure = verifiedUserSendFailure, actions = actions.toImmutableList() ) } else { @@ -190,9 +195,9 @@ private fun List.postFilter(content: TimelineItemEventConten when (content) { is TimelineItemCallNotifyContent, is TimelineItemLegacyCallInviteContent, - is TimelineItemStateContent, + is TimelineItemStateContent -> action == TimelineItemAction.ViewSource is TimelineItemRedactedContent -> { - action == TimelineItemAction.ViewSource + action == TimelineItemAction.ViewSource || action == TimelineItemAction.Unpin } else -> true } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListState.kt index bb3bd92fbe..75c598df36 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListState.kt @@ -9,6 +9,7 @@ package io.element.android.features.messages.impl.actionlist import androidx.compose.runtime.Immutable import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailure import io.element.android.features.messages.impl.timeline.model.TimelineItem import kotlinx.collections.immutable.ImmutableList @@ -17,12 +18,14 @@ data class ActionListState( val target: Target, val eventSink: (ActionListEvents) -> Unit, ) { + @Immutable sealed interface Target { data object None : Target data class Loading(val event: TimelineItem.Event) : Target data class Success( val event: TimelineItem.Event, val displayEmojiReactions: Boolean, + val verifiedUserSendFailure: VerifiedUserSendFailure, val actions: ImmutableList, ) : Target } 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 722d8af11e..4f92bc72ba 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 @@ -9,6 +9,8 @@ package io.element.android.features.messages.impl.actionlist import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailure +import io.element.android.features.messages.impl.crypto.sendfailure.resolve.anUnsignedDeviceSendFailure import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.aTimelineItemReactions import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemAudioContent @@ -35,6 +37,7 @@ open class ActionListStateProvider : PreviewParameterProvider { reactionsState = reactionsState ), displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ) ), @@ -47,6 +50,7 @@ open class ActionListStateProvider : PreviewParameterProvider { reactionsState = reactionsState, ), displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ) ), @@ -56,6 +60,7 @@ open class ActionListStateProvider : PreviewParameterProvider { reactionsState = reactionsState ), displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ) ), @@ -65,6 +70,7 @@ open class ActionListStateProvider : PreviewParameterProvider { reactionsState = reactionsState ), displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ) ), @@ -74,6 +80,7 @@ open class ActionListStateProvider : PreviewParameterProvider { reactionsState = reactionsState ), displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ) ), @@ -83,6 +90,7 @@ open class ActionListStateProvider : PreviewParameterProvider { reactionsState = reactionsState ), displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ) ), @@ -92,6 +100,7 @@ open class ActionListStateProvider : PreviewParameterProvider { reactionsState = reactionsState ), displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ) ), @@ -101,6 +110,7 @@ open class ActionListStateProvider : PreviewParameterProvider { reactionsState = reactionsState ), displayEmojiReactions = false, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ), ), @@ -110,6 +120,7 @@ open class ActionListStateProvider : PreviewParameterProvider { reactionsState = reactionsState ), displayEmojiReactions = false, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemPollActionList(), ), ), @@ -120,6 +131,15 @@ open class ActionListStateProvider : PreviewParameterProvider { messageShield = MessageShield.UnknownDevice(isCritical = true) ), displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, + actions = aTimelineItemActionList(), + ) + ), + anActionListState().copy( + target = ActionListState.Target.Success( + event = aTimelineItemEvent(), + displayEmojiReactions = true, + verifiedUserSendFailure = anUnsignedDeviceSendFailure(), actions = aTimelineItemActionList(), ) ), 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 8316bddb32..8c950012db 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 @@ -27,6 +27,7 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable @@ -46,6 +47,10 @@ import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailure +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailure.ChangedIdentity +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailure.None +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailure.UnsignedDevice import io.element.android.features.messages.impl.timeline.components.MessageShieldView import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent @@ -90,6 +95,7 @@ fun ActionListView( onSelectAction: (action: TimelineItemAction, TimelineItem.Event) -> Unit, onEmojiReactionClick: (String, TimelineItem.Event) -> Unit, onCustomReactionClick: (TimelineItem.Event) -> Unit, + onVerifiedUserSendFailureClick: (TimelineItem.Event) -> Unit, modifier: Modifier = Modifier, ) { val sheetState = rememberModalBottomSheetState() @@ -126,6 +132,14 @@ fun ActionListView( state.eventSink(ActionListEvents.Clear) } + fun onVerifiedUserSendFailureClick() { + if (targetItem == null) return + sheetState.hide(coroutineScope) { + state.eventSink(ActionListEvents.Clear) + onVerifiedUserSendFailureClick(targetItem) + } + } + if (targetItem != null) { ModalBottomSheet( sheetState = sheetState, @@ -137,6 +151,7 @@ fun ActionListView( onActionClick = ::onItemActionClick, onEmojiReactionClick = ::onEmojiReactionClick, onCustomReactionClick = ::onCustomReactionClick, + onVerifiedUserSendFailureClick = ::onVerifiedUserSendFailureClick, modifier = Modifier .navigationBarsPadding() .imePadding() @@ -151,6 +166,7 @@ private fun SheetContent( onActionClick: (TimelineItemAction) -> Unit, onEmojiReactionClick: (String) -> Unit, onCustomReactionClick: () -> Unit, + onVerifiedUserSendFailureClick: () -> Unit, modifier: Modifier = Modifier, ) { when (val target = state.target) { @@ -184,6 +200,16 @@ private fun SheetContent( HorizontalDivider() } } + if (target.verifiedUserSendFailure != None) { + item { + VerifiedUserSendFailureView( + sendFailure = target.verifiedUserSendFailure, + modifier = Modifier.fillMaxWidth(), + onClick = onVerifiedUserSendFailureClick + ) + HorizontalDivider() + } + } if (target.displayEmojiReactions) { item { EmojiReactionsRow( @@ -338,6 +364,42 @@ private fun EmojiReactionsRow( } } +@Composable +private fun VerifiedUserSendFailureView( + sendFailure: VerifiedUserSendFailure, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + @Composable + fun VerifiedUserSendFailure.headline(): String { + return when (this) { + is None -> "" + is UnsignedDevice -> stringResource(CommonStrings.screen_timeline_item_menu_send_failure_unsigned_device, userDisplayName) + is ChangedIdentity -> stringResource(CommonStrings.screen_timeline_item_menu_send_failure_changed_identity, userDisplayName) + } + } + + ListItem( + modifier = modifier + .clickable(onClick = onClick) + .padding(horizontal = 16.dp, vertical = 8.dp), + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Error())), + trailingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.ChevronRight())), + headlineContent = { + Text( + text = sendFailure.headline(), + style = ElementTheme.typography.fontBodySmMedium, + ) + }, + colors = ListItemDefaults.colors( + containerColor = Color.Transparent, + leadingIconColor = ElementTheme.colors.iconCriticalPrimary, + trailingIconColor = ElementTheme.colors.iconPrimary, + headlineColor = ElementTheme.colors.textCriticalPrimary, + ), + ) +} + @Composable private fun EmojiButton( emoji: String, @@ -387,5 +449,6 @@ internal fun SheetContentPreview( onActionClick = {}, onEmojiReactionClick = {}, onCustomReactionClick = {}, + onVerifiedUserSendFailureClick = {}, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/VerifiedUserSendFailure.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/VerifiedUserSendFailure.kt new file mode 100644 index 0000000000..e3c798f7df --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/VerifiedUserSendFailure.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.messages.impl.crypto.sendfailure + +import androidx.compose.runtime.Immutable + +@Immutable +sealed interface VerifiedUserSendFailure { + data object None : VerifiedUserSendFailure + + data class UnsignedDevice( + val userDisplayName: String, + ) : VerifiedUserSendFailure + + data class ChangedIdentity( + val userDisplayName: String, + ) : VerifiedUserSendFailure +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/VerifiedUserSendFailureFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/VerifiedUserSendFailureFactory.kt new file mode 100644 index 0000000000..de5817c909 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/VerifiedUserSendFailureFactory.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.messages.impl.crypto.sendfailure + +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState +import javax.inject.Inject + +class VerifiedUserSendFailureFactory @Inject constructor( + private val room: MatrixRoom, +) { + suspend fun create( + sendState: LocalEventSendState?, + ): VerifiedUserSendFailure { + return when (sendState) { + is LocalEventSendState.Failed.VerifiedUserHasUnsignedDevice -> { + val userId = sendState.devices.keys.firstOrNull() + if (userId == null) { + VerifiedUserSendFailure.None + } else { + val displayName = room.userDisplayName(userId).getOrNull() ?: userId.value + VerifiedUserSendFailure.UnsignedDevice(displayName) + } + } + is LocalEventSendState.Failed.VerifiedUserChangedIdentity -> { + val userId = sendState.users.firstOrNull() + if (userId == null) { + VerifiedUserSendFailure.None + } else { + val displayName = room.userDisplayName(userId).getOrNull() ?: userId.value + VerifiedUserSendFailure.ChangedIdentity(displayName) + } + } + else -> VerifiedUserSendFailure.None + } + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureEvents.kt new file mode 100644 index 0000000000..7743ef9dcd --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureEvents.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.messages.impl.crypto.sendfailure.resolve + +import io.element.android.features.messages.impl.timeline.model.TimelineItem + +sealed interface ResolveVerifiedUserSendFailureEvents { + data class ComputeForMessage( + val messageEvent: TimelineItem.Event, + ) : ResolveVerifiedUserSendFailureEvents + + data object ResolveAndResend : ResolveVerifiedUserSendFailureEvents + data object Retry : ResolveVerifiedUserSendFailureEvents + data object Dismiss : ResolveVerifiedUserSendFailureEvents +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailurePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailurePresenter.kt new file mode 100644 index 0000000000..c96e695375 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailurePresenter.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.messages.impl.crypto.sendfailure.resolve + +import androidx.compose.runtime.Composable +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.setValue +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailure +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailureFactory +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.runUpdatingState +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState +import kotlinx.coroutines.launch +import javax.inject.Inject + +class ResolveVerifiedUserSendFailurePresenter @Inject constructor( + private val room: MatrixRoom, + private val verifiedUserSendFailureFactory: VerifiedUserSendFailureFactory, +) : Presenter { + @Composable + override fun present(): ResolveVerifiedUserSendFailureState { + var resolver by remember { + mutableStateOf(null) + } + val verifiedUserSendFailure by produceState(VerifiedUserSendFailure.None, resolver?.currentSendFailure?.value) { + val currentSendFailure = resolver?.currentSendFailure?.value + value = verifiedUserSendFailureFactory.create(currentSendFailure) + } + + val resolveAction = remember { + mutableStateOf>(AsyncAction.Uninitialized) + } + val retryAction = remember { + mutableStateOf>(AsyncAction.Uninitialized) + } + val coroutineScope = rememberCoroutineScope() + + fun handleEvents(event: ResolveVerifiedUserSendFailureEvents) { + when (event) { + is ResolveVerifiedUserSendFailureEvents.ComputeForMessage -> { + val sendState = event.messageEvent.localSendState as? LocalEventSendState.Failed.VerifiedUser + val transactionId = event.messageEvent.transactionId + resolver = if (sendState != null && transactionId != null) { + VerifiedUserSendFailureResolver( + room = room, + transactionId = transactionId, + iterator = VerifiedUserSendFailureIterator.from(sendState) + ) + } else { + null + } + } + ResolveVerifiedUserSendFailureEvents.Dismiss -> { + resolver = null + } + ResolveVerifiedUserSendFailureEvents.Retry -> { + coroutineScope.launch { + resolver?.run { + runUpdatingState(retryAction) { + resend() + } + } + } + } + ResolveVerifiedUserSendFailureEvents.ResolveAndResend -> { + coroutineScope.launch { + resolver?.run { + runUpdatingState(resolveAction) { + resolveAndResend() + } + } + } + } + } + } + + return ResolveVerifiedUserSendFailureState( + verifiedUserSendFailure = verifiedUserSendFailure, + resolveAction = resolveAction.value, + retryAction = retryAction.value, + eventSink = ::handleEvents + ) + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureState.kt new file mode 100644 index 0000000000..44ec6640b0 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureState.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.messages.impl.crypto.sendfailure.resolve + +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailure +import io.element.android.libraries.architecture.AsyncAction + +data class ResolveVerifiedUserSendFailureState( + val verifiedUserSendFailure: VerifiedUserSendFailure, + val resolveAction: AsyncAction, + val retryAction: AsyncAction, + val eventSink: (ResolveVerifiedUserSendFailureEvents) -> Unit +) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureStateProvider.kt new file mode 100644 index 0000000000..1f8335b648 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureStateProvider.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.messages.impl.crypto.sendfailure.resolve + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailure +import io.element.android.libraries.architecture.AsyncAction + +open class ResolveVerifiedUserSendFailureStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aResolveVerifiedUserSendFailureState(), + aResolveVerifiedUserSendFailureState( + verifiedUserSendFailure = anUnsignedDeviceSendFailure() + ), + aResolveVerifiedUserSendFailureState( + verifiedUserSendFailure = aChangedIdentitySendFailure() + ) + ) +} + +fun aResolveVerifiedUserSendFailureState( + verifiedUserSendFailure: VerifiedUserSendFailure = VerifiedUserSendFailure.None, + resolveAction: AsyncAction = AsyncAction.Uninitialized, + retryAction: AsyncAction = AsyncAction.Uninitialized, + eventSink: (ResolveVerifiedUserSendFailureEvents) -> Unit = {} +) = ResolveVerifiedUserSendFailureState( + verifiedUserSendFailure = verifiedUserSendFailure, + resolveAction = resolveAction, + retryAction = retryAction, + eventSink = eventSink +) + +fun anUnsignedDeviceSendFailure(userDisplayName: String = "Alice") = VerifiedUserSendFailure.UnsignedDevice( + userDisplayName = userDisplayName, +) + +fun aChangedIdentitySendFailure(userDisplayName: String = "Alice") = VerifiedUserSendFailure.ChangedIdentity( + userDisplayName = userDisplayName, +) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureView.kt new file mode 100644 index 0000000000..4c1d40cc87 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureView.kt @@ -0,0 +1,156 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.messages.impl.crypto.sendfailure.resolve + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.rememberModalBottomSheetState +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 androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailure +import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule +import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule +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.ModalBottomSheet +import io.element.android.libraries.designsystem.theme.components.OutlinedButton +import io.element.android.libraries.designsystem.theme.components.TextButton +import io.element.android.libraries.ui.strings.CommonStrings + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ResolveVerifiedUserSendFailureView( + state: ResolveVerifiedUserSendFailureState, + modifier: Modifier = Modifier, +) { + val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + var showSheet by remember { mutableStateOf(false) } + + fun dismiss() { + state.eventSink(ResolveVerifiedUserSendFailureEvents.Dismiss) + } + + fun onRetryClick() { + state.eventSink(ResolveVerifiedUserSendFailureEvents.Retry) + } + + fun onResolveAndResendClick() { + state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend) + } + + LaunchedEffect(state.verifiedUserSendFailure) { + if (state.verifiedUserSendFailure is VerifiedUserSendFailure.None) { + sheetState.hide() + showSheet = false + } else { + showSheet = true + } + } + + Box(modifier = modifier) { + if (showSheet) { + ModalBottomSheet( + modifier = Modifier + .systemBarsPadding() + .navigationBarsPadding(), + sheetState = sheetState, + onDismissRequest = ::dismiss, + ) { + IconTitleSubtitleMolecule( + modifier = Modifier.padding(24.dp), + title = state.verifiedUserSendFailure.title(), + subTitle = state.verifiedUserSendFailure.subtitle(), + iconImageVector = CompoundIcons.Error(), + iconTint = ElementTheme.colors.iconCriticalPrimary, + iconBackgroundTint = ElementTheme.colors.bgCriticalSubtle, + ) + ButtonColumnMolecule( + modifier = Modifier.padding(horizontal = 16.dp, vertical = 16.dp), + ) { + Button( + modifier = Modifier.fillMaxWidth(), + text = state.verifiedUserSendFailure.resolveAction(), + showProgress = state.resolveAction.isLoading(), + onClick = ::onResolveAndResendClick + ) + OutlinedButton( + modifier = Modifier.fillMaxWidth(), + text = stringResource(id = CommonStrings.action_retry), + showProgress = state.retryAction.isLoading(), + onClick = ::onRetryClick + ) + TextButton( + modifier = Modifier.fillMaxWidth(), + text = stringResource(id = CommonStrings.action_cancel_for_now), + onClick = ::dismiss, + ) + } + } + } + } +} + +@Composable +private fun VerifiedUserSendFailure.title(): String { + return when (this) { + is VerifiedUserSendFailure.UnsignedDevice -> stringResource(id = CommonStrings.screen_resolve_send_failure_unsigned_device_title, userDisplayName) + is VerifiedUserSendFailure.ChangedIdentity -> stringResource( + id = CommonStrings.screen_resolve_send_failure_changed_identity_title, + userDisplayName + ) + VerifiedUserSendFailure.None -> error("This method should never be called for this state") + } +} + +@Composable +private fun VerifiedUserSendFailure.subtitle(): String { + return when (this) { + is VerifiedUserSendFailure.UnsignedDevice -> stringResource( + id = CommonStrings.screen_resolve_send_failure_unsigned_device_subtitle, + userDisplayName, + userDisplayName, + ) + is VerifiedUserSendFailure.ChangedIdentity -> stringResource( + id = CommonStrings.screen_resolve_send_failure_changed_identity_subtitle, + userDisplayName + ) + VerifiedUserSendFailure.None -> error("This method should never be called for this state") + } +} + +@Composable +private fun VerifiedUserSendFailure.resolveAction(): String { + return when (this) { + is VerifiedUserSendFailure.UnsignedDevice -> stringResource(id = CommonStrings.screen_resolve_send_failure_unsigned_device_primary_button_title) + is VerifiedUserSendFailure.ChangedIdentity -> stringResource(id = CommonStrings.screen_resolve_send_failure_changed_identity_primary_button_title) + VerifiedUserSendFailure.None -> error("This method should never be called for this state") + } +} + +@PreviewsDayNight +@Composable +internal fun ResolveVerifiedUserSendFailureViewPreview( + @PreviewParameter(ResolveVerifiedUserSendFailureStateProvider::class) state: ResolveVerifiedUserSendFailureState +) = ElementPreview { + ResolveVerifiedUserSendFailureView(state) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/VerifiedUserSendFailureIterator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/VerifiedUserSendFailureIterator.kt new file mode 100644 index 0000000000..8b438808ff --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/VerifiedUserSendFailureIterator.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.messages.impl.crypto.sendfailure.resolve + +import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState +import timber.log.Timber + +/** + * Iterator for [LocalEventSendState.Failed.VerifiedUser] + * Allow to iterate through the internal state of the failure. + * This is useful to allow solving the failure step by step (e.g. for each user). + */ +interface VerifiedUserSendFailureIterator : Iterator { + companion object { + fun from(failure: LocalEventSendState.Failed.VerifiedUser): VerifiedUserSendFailureIterator { + return when (failure) { + is LocalEventSendState.Failed.VerifiedUserHasUnsignedDevice -> UnsignedDeviceSendFailureIterator(failure) + is LocalEventSendState.Failed.VerifiedUserChangedIdentity -> ChangedIdentitySendFailureIterator(failure) + } + } + } +} + +class UnsignedDeviceSendFailureIterator( + failure: LocalEventSendState.Failed.VerifiedUserHasUnsignedDevice +) : VerifiedUserSendFailureIterator { + private val iterator = failure.devices.iterator() + + init { + if (!hasNext()) { + Timber.w("Got $failure without any devices, shouldn't happen.") + } + } + + override fun hasNext(): Boolean { + return iterator.hasNext() + } + + override fun next(): LocalEventSendState.Failed.VerifiedUser { + val (userId, deviceIds) = iterator.next() + return LocalEventSendState.Failed.VerifiedUserHasUnsignedDevice( + mapOf(userId to deviceIds) + ) + } +} + +class ChangedIdentitySendFailureIterator( + failure: LocalEventSendState.Failed.VerifiedUserChangedIdentity +) : VerifiedUserSendFailureIterator { + private val iterator = failure.users.iterator() + + init { + if (!hasNext()) { + Timber.w("Got $failure without any users, shouldn't happen.") + } + } + + override fun hasNext(): Boolean { + return iterator.hasNext() + } + + override fun next(): LocalEventSendState.Failed.VerifiedUser { + val userId = iterator.next() + return LocalEventSendState.Failed.VerifiedUserChangedIdentity( + listOf(userId) + ) + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/VerifiedUserSendFailureResolver.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/VerifiedUserSendFailureResolver.kt new file mode 100644 index 0000000000..be775ed122 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/VerifiedUserSendFailureResolver.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.messages.impl.crypto.sendfailure.resolve + +import androidx.compose.runtime.mutableStateOf +import io.element.android.libraries.matrix.api.core.TransactionId +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState +import timber.log.Timber + +/** + * This class is responsible for resolving and resending a failed message sent to a verified user. + * It also allow to resend the message without resolving the failure, for example if the user has in the meantime verified their device again. + * It's using the [VerifiedUserSendFailureIterator] to iterate over the different failures (ie. the different users concerned by the failure). + * This way, the user can resolve and resend the message for each user concerned, one by one. + */ +class VerifiedUserSendFailureResolver( + private val room: MatrixRoom, + private val transactionId: TransactionId, + private val iterator: VerifiedUserSendFailureIterator, +) { + val currentSendFailure = mutableStateOf(null) + + init { + if (iterator.hasNext()) { + currentSendFailure.value = iterator.next() + } + } + + suspend fun resend(): Result { + return room.retrySendMessage(transactionId) + .onSuccess { + Timber.d("Succeed to resend message with transactionId: $transactionId") + currentSendFailure.value = null + } + .onFailure { + Timber.e(it, "Failed to resend message with transactionId: $transactionId") + } + } + + suspend fun resolveAndResend(): Result { + return when (val failure = currentSendFailure.value) { + is LocalEventSendState.Failed.VerifiedUserHasUnsignedDevice -> { + room.ignoreDeviceTrustAndResend(failure.devices, transactionId) + } + is LocalEventSendState.Failed.VerifiedUserChangedIdentity -> { + room.withdrawVerificationAndResend(failure.users, transactionId) + } + else -> { + Result.failure(IllegalStateException("Unknown send failure type")) + } + }.onSuccess { + Timber.d("Succeed to resolve and resend message with transactionId: $transactionId") + if (iterator.hasNext()) { + val failure = iterator.next() + currentSendFailure.value = failure + } else { + currentSendFailure.value = null + Timber.d("No more failure to resolve for transactionId: $transactionId") + } + }.onFailure { + Timber.e(it, "Failed to resolve and resend message with transactionId: $transactionId") + } + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/di/MessagesModule.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/di/MessagesModule.kt index e6cb916aef..8c488d61da 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/di/MessagesModule.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/di/MessagesModule.kt @@ -10,6 +10,8 @@ package io.element.android.features.messages.impl.di import com.squareup.anvil.annotations.ContributesTo import dagger.Binds import dagger.Module +import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailurePresenter +import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureState import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerPresenter import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerState import io.element.android.libraries.architecture.Presenter @@ -20,4 +22,7 @@ import io.element.android.libraries.di.RoomScope interface MessagesModule { @Binds fun bindPinnedMessagesBannerPresenter(presenter: PinnedMessagesBannerPresenter): Presenter + + @Binds + fun bindResolveVerifiedUserSendFailurePresenter(presenter: ResolveVerifiedUserSendFailurePresenter): Presenter } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt index b31e38d99f..6c8c52a443 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt @@ -119,7 +119,6 @@ class PinnedMessagesListPresenter @AssistedInject constructor( targetEvent: TimelineItem.Event, ) = launch { when (action) { - TimelineItemAction.Redact -> handleActionRedact(targetEvent) TimelineItemAction.ViewSource -> { navigator.onShowEventDebugInfoClick(targetEvent.eventId, targetEvent.debugInfo) } @@ -149,13 +148,6 @@ class PinnedMessagesListPresenter @AssistedInject constructor( } } - private suspend fun handleActionRedact(event: TimelineItem.Event) { - timelineProvider.invokeOnTimeline { - redactEvent(eventId = event.eventId, transactionId = event.transactionId, reason = null) - .onFailure { Timber.e(it) } - } - } - @Composable private fun userEventPermissions(updateKey: Long): State { return produceState(UserEventPermissions.DEFAULT, key1 = updateKey) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListTimelineActionPostProcessor.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListTimelineActionPostProcessor.kt index 3e88a9d716..48fdb83d79 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListTimelineActionPostProcessor.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListTimelineActionPostProcessor.kt @@ -17,7 +17,6 @@ class PinnedMessagesListTimelineActionPostProcessor : TimelineItemActionPostProc actions.firstOrNull { it is TimelineItemAction.Unpin }?.let(::add) actions.firstOrNull { it is TimelineItemAction.Forward }?.let(::add) actions.firstOrNull { it is TimelineItemAction.ViewSource }?.let(::add) - actions.firstOrNull { it is TimelineItemAction.Redact }?.let(::add) } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt index 8a03ed857c..2562de95f8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt @@ -108,7 +108,7 @@ private fun PinnedMessagesListContent( ErrorDialog( title = stringResource(id = CommonStrings.error_unknown), content = stringResource(id = CommonStrings.error_failed_loading_messages), - onDismiss = onErrorDismiss + onSubmit = onErrorDismiss ) } PinnedMessagesListState.Empty -> PinnedMessagesListEmpty() @@ -181,6 +181,7 @@ private fun PinnedMessagesListLoaded( onSelectAction = ::onActionSelected, onCustomReactionClick = {}, onEmojiReactionClick = { _, _ -> }, + onVerifiedUserSendFailureClick = {} ) LazyColumn( modifier = modifier.fillMaxSize(), @@ -199,19 +200,18 @@ private fun PinnedMessagesListLoaded( renderReadReceipts = false, isLastOutgoingMessage = false, focusedEventId = null, - onClick = onEventClick, - onLongClick = ::onMessageLongClick, onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, + onClick = onEventClick, + onLongClick = ::onMessageLongClick, inReplyToClick = {}, onReactionClick = { _, _ -> }, onReactionLongClick = { _, _ -> }, onMoreReactionsClick = {}, onReadReceiptClick = {}, - eventSink = {}, onSwipeToReply = {}, onJoinCallClick = {}, - onShieldClick = {}, + eventSink = {}, eventContentView = { event, contentModifier, onContentLayoutChange -> TimelineItemEventContentViewWrapper( event = event, 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 1aa1afe0f0..e886251b64 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 @@ -7,6 +7,7 @@ package io.element.android.features.messages.impl.timeline +import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield @@ -19,7 +20,6 @@ sealed interface TimelineEvents { data object OnFocusEventRender : TimelineEvents data object JumpToLive : TimelineEvents - data class ShowShieldDialog(val messageShield: MessageShield) : TimelineEvents data object HideShieldDialog : TimelineEvents /** @@ -27,6 +27,8 @@ sealed interface TimelineEvents { */ sealed interface EventFromTimelineItem : TimelineEvents + data class ComputeVerifiedUserSendFailure(val event: TimelineItem.Event) : EventFromTimelineItem + data class ShowShieldDialog(val messageShield: MessageShield) : EventFromTimelineItem data class LoadMore(val direction: Timeline.PaginationDirection) : EventFromTimelineItem /** 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 e4287cb3ed..088354f245 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 @@ -21,6 +21,8 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.element.android.features.messages.impl.MessagesNavigator +import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureEvents +import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureState import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig import io.element.android.features.messages.impl.timeline.model.NewEventState @@ -66,6 +68,7 @@ class TimelinePresenter @AssistedInject constructor( private val endPollAction: EndPollAction, private val sessionPreferencesStore: SessionPreferencesStore, private val timelineController: TimelineController, + private val resolveVerifiedUserSendFailurePresenter: Presenter, ) : Presenter { @AssistedFactory interface Factory { @@ -101,6 +104,7 @@ class TimelinePresenter @AssistedInject constructor( val newEventState = remember { mutableStateOf(NewEventState.None) } val messageShield: MutableState = remember { mutableStateOf(null) } + val resolveVerifiedUserSendFailureState = resolveVerifiedUserSendFailurePresenter.present() val isSendPublicReadReceiptsEnabled by sessionPreferencesStore.isSendPublicReadReceiptsEnabled().collectAsState(initial = true) val renderReadReceipts by sessionPreferencesStore.isRenderReadReceiptsEnabled().collectAsState(initial = true) val isLive by timelineController.isLive().collectAsState(initial = true) @@ -156,6 +160,9 @@ class TimelinePresenter @AssistedInject constructor( } TimelineEvents.HideShieldDialog -> messageShield.value = null is TimelineEvents.ShowShieldDialog -> messageShield.value = event.messageShield + is TimelineEvents.ComputeVerifiedUserSendFailure -> { + resolveVerifiedUserSendFailureState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(event.event)) + } } } @@ -232,6 +239,7 @@ class TimelinePresenter @AssistedInject constructor( isLive = isLive, focusRequestState = focusRequestState.value, messageShield = messageShield.value, + resolveVerifiedUserSendFailureState = resolveVerifiedUserSendFailureState, 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 114cbf8128..18630f62d4 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 @@ -8,6 +8,7 @@ package io.element.android.features.messages.impl.timeline import androidx.compose.runtime.Immutable +import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureState import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.matrix.api.core.EventId @@ -25,6 +26,7 @@ data class TimelineState( val focusRequestState: FocusRequestState, // If not null, info will be rendered in a dialog val messageShield: MessageShield?, + val resolveVerifiedUserSendFailureState: ResolveVerifiedUserSendFailureState, val eventSink: (TimelineEvents) -> Unit, ) { val hasAnyEvent = timelineItems.any { it is TimelineItem.Event } 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 516559cc37..3317c2976c 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 @@ -7,6 +7,8 @@ package io.element.android.features.messages.impl.timeline +import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureState +import io.element.android.features.messages.impl.crypto.sendfailure.resolve.aResolveVerifiedUserSendFailureState import io.element.android.features.messages.impl.timeline.components.receipt.aReadReceiptData import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.ReadReceiptData @@ -44,6 +46,7 @@ fun aTimelineState( focusedEventIndex: Int = -1, isLive: Boolean = true, messageShield: MessageShield? = null, + resolveVerifiedUserSendFailureState: ResolveVerifiedUserSendFailureState = aResolveVerifiedUserSendFailureState(), eventSink: (TimelineEvents) -> Unit = {}, ): TimelineState { val focusedEventId = timelineItems.filterIsInstance().getOrNull(focusedEventIndex)?.eventId @@ -60,6 +63,7 @@ fun aTimelineState( isLive = isLive, focusRequestState = focusRequestState, messageShield = messageShield, + resolveVerifiedUserSendFailureState = resolveVerifiedUserSendFailureState, eventSink = eventSink, ) } 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 c3f7d53bb6..17ce5264fc 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 @@ -48,6 +48,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureView import io.element.android.features.messages.impl.timeline.components.TimelineItemRow import io.element.android.features.messages.impl.timeline.components.toText import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories @@ -127,8 +128,8 @@ fun TimelineView( Box(modifier) { LazyColumn( modifier = Modifier - .fillMaxSize() - .nestedScroll(nestedScrollConnection), + .fillMaxSize() + .nestedScroll(nestedScrollConnection), state = lazyListState, reverseLayout = useReverseLayout, contentPadding = PaddingValues(vertical = 8.dp), @@ -150,19 +151,18 @@ fun TimelineView( isLastOutgoingMessage = (timelineItem as? TimelineItem.Event)?.isMine == true && state.timelineItems.first().identifier() == timelineItem.identifier(), focusedEventId = state.focusedEventId, - onClick = onMessageClick, - onLongClick = onMessageLongClick, - onShieldClick = ::onShieldClick, onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, + onClick = onMessageClick, + onLongClick = onMessageLongClick, inReplyToClick = ::inReplyToClick, onReactionClick = onReactionClick, onReactionLongClick = onReactionLongClick, onMoreReactionsClick = onMoreReactionsClick, onReadReceiptClick = onReadReceiptClick, - eventSink = state.eventSink, onSwipeToReply = onSwipeToReply, onJoinCallClick = onJoinCallClick, + eventSink = state.eventSink, ) } } @@ -186,6 +186,8 @@ fun TimelineView( } } + ResolveVerifiedUserSendFailureView(state = state.resolveVerifiedUserSendFailureState) + MessageShieldDialog(state) } @@ -267,8 +269,8 @@ private fun BoxScope.TimelineScrollHelper( // Use inverse of canAutoScroll otherwise we might briefly see the before the scroll animation is triggered isVisible = !canAutoScroll || forceJumpToBottomVisibility || !isLive, modifier = Modifier - .align(Alignment.BottomEnd) - .padding(end = 24.dp, bottom = 12.dp), + .align(Alignment.BottomEnd) + .padding(end = 24.dp, bottom = 12.dp), onClick = { jumpToBottom() }, ) } @@ -295,8 +297,8 @@ private fun JumpToBottomButton( ) { Icon( modifier = Modifier - .size(24.dp) - .rotate(90f), + .size(24.dp) + .rotate(90f), imageVector = CompoundIcons.ArrowRight(), contentDescription = stringResource(id = CommonStrings.a11y_jump_to_bottom) ) 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 a73dbef79c..0db7a8ac52 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 @@ -28,9 +28,8 @@ internal fun ATimelineItemEventRow( isHighlighted = isHighlighted, onClick = {}, onLongClick = {}, - onShieldClick = {}, - onUserDataClick = {}, onLinkClick = {}, + onUserDataClick = {}, inReplyToClick = {}, onReactionClick = { _, _ -> }, onReactionLongClick = { _, _ -> }, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt index 08c72a1060..a6692bd4ec 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.isEdited import io.element.android.libraries.core.bool.orFalse @@ -31,14 +32,13 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState -import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield import io.element.android.libraries.matrix.api.timeline.item.event.isCritical import io.element.android.libraries.ui.strings.CommonStrings @Composable fun TimelineEventTimestampView( event: TimelineItem.Event, - onShieldClick: (MessageShield) -> Unit, + eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit, modifier: Modifier = Modifier, ) { val formattedTime = event.sentTime @@ -48,8 +48,8 @@ fun TimelineEventTimestampView( val tint = if (hasError || hasEncryptionCritical) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.secondary Row( modifier = Modifier - .padding(PaddingValues(start = TimelineEventTimestampViewDefaults.spacing)) - .then(modifier), + .padding(PaddingValues(start = TimelineEventTimestampViewDefaults.spacing)) + .then(modifier), verticalAlignment = Alignment.CenterVertically, ) { if (isMessageEdited) { @@ -66,12 +66,17 @@ fun TimelineEventTimestampView( color = tint, ) if (hasError) { + val isVerifiedUserSendFailure = event.localSendState is LocalEventSendState.Failed.VerifiedUser Spacer(modifier = Modifier.width(2.dp)) Icon( imageVector = CompoundIcons.Error(), contentDescription = stringResource(id = CommonStrings.common_sending_failed), tint = tint, - modifier = Modifier.size(15.dp, 18.dp), + modifier = Modifier + .size(15.dp, 18.dp) + .clickable(isVerifiedUserSendFailure) { + eventSink(TimelineEvents.ComputeVerifiedUserSendFailure(event)) + }, ) } event.messageShield?.let { shield -> @@ -80,8 +85,10 @@ fun TimelineEventTimestampView( imageVector = shield.toIcon(), contentDescription = shield.toText(), modifier = Modifier - .size(15.dp) - .clickable { onShieldClick(shield) }, + .size(15.dp) + .clickable { + eventSink(TimelineEvents.ShowShieldDialog(shield)) + }, tint = shield.toIconColor(), ) Spacer(modifier = Modifier.width(4.dp)) @@ -94,7 +101,7 @@ fun TimelineEventTimestampView( internal fun TimelineEventTimestampViewPreview(@PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event) = ElementPreview { TimelineEventTimestampView( event = event, - onShieldClick = {}, + eventSink = {}, ) } 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 c03162efc6..e068eb5522 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 @@ -81,7 +81,6 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails import io.element.android.libraries.matrix.ui.messages.reply.InReplyToView @@ -110,7 +109,6 @@ fun TimelineItemEventRow( isHighlighted: Boolean, onClick: () -> Unit, onLongClick: () -> Unit, - onShieldClick: (MessageShield) -> Unit, onLinkClick: (String) -> Unit, onUserDataClick: (UserId) -> Unit, inReplyToClick: (EventId) -> Unit, @@ -161,6 +159,17 @@ fun TimelineItemEventRow( ReplySwipeIndicator({ offset / 120 }) } TimelineItemEventRowContent( + event = event, + isHighlighted = isHighlighted, + timelineRoomInfo = timelineRoomInfo, + interactionSource = interactionSource, + onClick = onClick, + onLongClick = onLongClick, + inReplyToClick = ::inReplyToClick, + onUserDataClick = ::onUserDataClick, + onReactionClick = { emoji -> onReactionClick(emoji, event) }, + onReactionLongClick = { emoji -> onReactionLongClick(emoji, event) }, + onMoreReactionsClick = { onMoreReactionsClick(event) }, modifier = Modifier .absoluteOffset { IntOffset(x = offset.roundToInt(), y = 0) } .draggable( @@ -176,18 +185,7 @@ fun TimelineItemEventRow( }, state = state.draggableState, ), - event = event, - isHighlighted = isHighlighted, - timelineRoomInfo = timelineRoomInfo, - interactionSource = interactionSource, - onClick = onClick, - onLongClick = onLongClick, - onShieldClick = onShieldClick, - inReplyToClick = ::inReplyToClick, - onUserDataClick = ::onUserDataClick, - onReactionClick = { emoji -> onReactionClick(emoji, event) }, - onReactionLongClick = { emoji -> onReactionLongClick(emoji, event) }, - onMoreReactionsClick = { onMoreReactionsClick(event) }, + eventSink = eventSink, eventContentView = eventContentView, ) } @@ -200,12 +198,12 @@ fun TimelineItemEventRow( interactionSource = interactionSource, onClick = onClick, onLongClick = onLongClick, - onShieldClick = onShieldClick, inReplyToClick = ::inReplyToClick, onUserDataClick = ::onUserDataClick, onReactionClick = { emoji -> onReactionClick(emoji, event) }, onReactionLongClick = { emoji -> onReactionLongClick(emoji, event) }, onMoreReactionsClick = { onMoreReactionsClick(event) }, + eventSink = eventSink, eventContentView = eventContentView, ) } @@ -255,12 +253,12 @@ private fun TimelineItemEventRowContent( interactionSource: MutableInteractionSource, onClick: () -> Unit, onLongClick: () -> Unit, - onShieldClick: (MessageShield) -> Unit, inReplyToClick: () -> Unit, onUserDataClick: () -> Unit, onReactionClick: (emoji: String) -> Unit, onReactionLongClick: (emoji: String) -> Unit, onMoreReactionsClick: (event: TimelineItem.Event) -> Unit, + eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit, modifier: Modifier = Modifier, eventContentView: @Composable (Modifier, (ContentAvoidingLayoutData) -> Unit) -> Unit, ) { @@ -322,9 +320,9 @@ private fun TimelineItemEventRowContent( ) { MessageEventBubbleContent( event = event, - onShieldClick = onShieldClick, onMessageLongClick = onLongClick, inReplyToClick = inReplyToClick, + eventSink = eventSink, eventContentView = eventContentView, ) } @@ -382,9 +380,9 @@ private fun MessageSenderInformation( @Composable private fun MessageEventBubbleContent( event: TimelineItem.Event, - onShieldClick: (MessageShield) -> Unit, onMessageLongClick: () -> Unit, inReplyToClick: () -> Unit, + eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit, @SuppressLint("ModifierParameter") // need to rename this modifier to prevent linter false positives @Suppress("ModifierNaming") @@ -422,7 +420,7 @@ private fun MessageEventBubbleContent( @Composable fun WithTimestampLayout( timestampPosition: TimestampPosition, - onShieldClick: (MessageShield) -> Unit, + eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit, modifier: Modifier = Modifier, canShrinkContent: Boolean = false, content: @Composable (onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit) -> Unit, @@ -433,7 +431,7 @@ private fun MessageEventBubbleContent( content {} TimelineEventTimestampView( event = event, - onShieldClick = onShieldClick, + eventSink = eventSink, modifier = Modifier // Outer padding .padding(horizontal = 4.dp, vertical = 4.dp) @@ -454,7 +452,7 @@ private fun MessageEventBubbleContent( overlay = { TimelineEventTimestampView( event = event, - onShieldClick = onShieldClick, + eventSink = eventSink, modifier = Modifier .padding(horizontal = 8.dp, vertical = 4.dp) ) @@ -465,7 +463,7 @@ private fun MessageEventBubbleContent( content {} TimelineEventTimestampView( event = event, - onShieldClick = onShieldClick, + eventSink = eventSink, modifier = Modifier .align(Alignment.End) .padding(horizontal = 8.dp, vertical = 4.dp) @@ -513,7 +511,7 @@ private fun MessageEventBubbleContent( val contentWithTimestamp = @Composable { WithTimestampLayout( timestampPosition = timestampPosition, - onShieldClick = onShieldClick, + eventSink = eventSink, canShrinkContent = canShrinkContent, modifier = timestampLayoutModifier, content = { onContentLayoutChange -> 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 000ce19bd2..75f1ccc02c 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 @@ -29,7 +29,6 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield @Composable fun TimelineItemGroupedEventsRow( @@ -40,7 +39,6 @@ fun TimelineItemGroupedEventsRow( focusedEventId: EventId?, onClick: (TimelineItem.Event) -> Unit, onLongClick: (TimelineItem.Event) -> Unit, - onShieldClick: (MessageShield) -> Unit, inReplyToClick: (EventId) -> Unit, onUserDataClick: (UserId) -> Unit, onLinkClick: (String) -> Unit, @@ -77,7 +75,6 @@ fun TimelineItemGroupedEventsRow( isLastOutgoingMessage = isLastOutgoingMessage, onClick = onClick, onLongClick = onLongClick, - onShieldClick = onShieldClick, inReplyToClick = inReplyToClick, onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, @@ -102,7 +99,6 @@ private fun TimelineItemGroupedEventsRowContent( isLastOutgoingMessage: Boolean, onClick: (TimelineItem.Event) -> Unit, onLongClick: (TimelineItem.Event) -> Unit, - onShieldClick: (MessageShield) -> Unit, inReplyToClick: (EventId) -> Unit, onUserDataClick: (UserId) -> Unit, onLinkClick: (String) -> Unit, @@ -143,19 +139,18 @@ private fun TimelineItemGroupedEventsRowContent( renderReadReceipts = renderReadReceipts, isLastOutgoingMessage = isLastOutgoingMessage, focusedEventId = focusedEventId, - onClick = onClick, - onLongClick = onLongClick, - onShieldClick = onShieldClick, - inReplyToClick = inReplyToClick, onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, + onClick = onClick, + onLongClick = onLongClick, + inReplyToClick = inReplyToClick, onReactionClick = onReactionClick, onReactionLongClick = onReactionLongClick, onMoreReactionsClick = onMoreReactionsClick, onReadReceiptClick = onReadReceiptClick, - eventSink = eventSink, onSwipeToReply = {}, onJoinCallClick = {}, + eventSink = eventSink, eventContentView = eventContentView, ) } @@ -188,7 +183,6 @@ internal fun TimelineItemGroupedEventsRowContentExpandedPreview() = ElementPrevi isLastOutgoingMessage = false, onClick = {}, onLongClick = {}, - onShieldClick = {}, inReplyToClick = {}, onUserDataClick = {}, onLinkClick = {}, @@ -213,7 +207,6 @@ internal fun TimelineItemGroupedEventsRowContentCollapsePreview() = ElementPrevi isLastOutgoingMessage = false, onClick = {}, onLongClick = {}, - onShieldClick = {}, inReplyToClick = {}, onUserDataClick = {}, onLinkClick = {}, 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 4af30081a0..56f509f9fb 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,7 +30,6 @@ import io.element.android.libraries.designsystem.text.toPx import io.element.android.libraries.designsystem.theme.highlightedMessageBackgroundColor import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield @Composable internal fun TimelineItemRow( @@ -43,7 +42,6 @@ internal fun TimelineItemRow( onLinkClick: (String) -> Unit, onClick: (TimelineItem.Event) -> Unit, onLongClick: (TimelineItem.Event) -> Unit, - onShieldClick: (MessageShield) -> Unit, inReplyToClick: (EventId) -> Unit, onReactionClick: (key: String, TimelineItem.Event) -> Unit, onReactionLongClick: (key: String, TimelineItem.Event) -> Unit, @@ -115,9 +113,8 @@ internal fun TimelineItemRow( isHighlighted = timelineItem.isEvent(focusedEventId), onClick = { onClick(timelineItem) }, onLongClick = { onLongClick(timelineItem) }, - onShieldClick = onShieldClick, - onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, + onUserDataClick = onUserDataClick, inReplyToClick = inReplyToClick, onReactionClick = onReactionClick, onReactionLongClick = onReactionLongClick, @@ -141,7 +138,6 @@ internal fun TimelineItemRow( focusedEventId = focusedEventId, onClick = onClick, onLongClick = onLongClick, - onShieldClick = onShieldClick, inReplyToClick = inReplyToClick, onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateView.kt index 7bd5301208..75d4d5ebac 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateView.kt @@ -36,7 +36,7 @@ fun FocusRequestStateView( } ErrorDialog( content = errorMessage, - onDismiss = onClearFocusRequestState, + onSubmit = onClearFocusRequestState, modifier = modifier, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageSendingFailedDialog.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageSendingFailedDialog.kt index f2cc385553..ffed5f31ac 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageSendingFailedDialog.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageSendingFailedDialog.kt @@ -19,7 +19,7 @@ internal fun VoiceMessageSendingFailedDialog( ErrorDialog( title = stringResource(CommonStrings.common_error), content = stringResource(CommonStrings.error_failed_uploading_voice_message), - onDismiss = onDismiss, + onSubmit = onDismiss, submitText = stringResource(CommonStrings.action_ok), ) } 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 df873d2328..3d6af28f3d 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 @@ -16,6 +16,7 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.actionlist.ActionListState import io.element.android.features.messages.impl.actionlist.FakeActionListPresenter import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction +import io.element.android.features.messages.impl.crypto.sendfailure.resolve.aResolveVerifiedUserSendFailureState import io.element.android.features.messages.impl.draft.FakeComposerDraftService import io.element.android.features.messages.impl.fixtures.aMessageEvent import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactoryCreator @@ -1035,6 +1036,7 @@ class MessagesPresenterTest { sessionPreferencesStore = sessionPreferencesStore, timelineItemIndexer = TimelineItemIndexer(), timelineController = TimelineController(matrixRoom), + resolveVerifiedUserSendFailurePresenter = { aResolveVerifiedUserSendFailureState() }, ) val timelinePresenterFactory = object : TimelinePresenter.Factory { override fun create(navigator: MessagesNavigator): TimelinePresenter { 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 52387f821a..210ffb4fe6 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 @@ -34,6 +34,8 @@ 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.crypto.sendfailure.VerifiedUserSendFailure +import io.element.android.features.messages.impl.crypto.sendfailure.resolve.aChangedIdentitySendFailure import io.element.android.features.messages.impl.messagecomposer.aMessageComposerState import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerItem import io.element.android.features.messages.impl.pinned.banner.aLoadedPinnedMessagesBannerState @@ -329,6 +331,7 @@ class MessagesViewTest { event = timelineItem, displayEmojiReactions = true, actions = persistentListOf(TimelineItemAction.Edit), + verifiedUserSendFailure = VerifiedUserSendFailure.None, ) ), ) @@ -399,6 +402,7 @@ class MessagesViewTest { target = ActionListState.Target.Success( event = timelineItem, displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf(TimelineItemAction.Edit), ), ), @@ -416,6 +420,32 @@ class MessagesViewTest { eventsRecorder.assertSingle(CustomReactionEvents.ShowCustomReactionSheet(timelineItem)) } + @Test + fun `clicking on verified user send failure from action list emits the expected Event`() { + val eventsRecorder = EventsRecorder() + val state = aMessagesState() + val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event + val stateWithActionListState = state.copy( + actionListState = anActionListState( + target = ActionListState.Target.Success( + event = timelineItem, + displayEmojiReactions = true, + verifiedUserSendFailure = aChangedIdentitySendFailure(), + actions = persistentListOf(), + ), + ), + timelineState = aTimelineState(eventSink = eventsRecorder) + ) + rule.setMessagesView( + state = stateWithActionListState, + ) + val verifiedUserSendFailure = rule.activity.getString(CommonStrings.screen_timeline_item_menu_send_failure_changed_identity, "Alice") + rule.onNodeWithText(verifiedUserSendFailure).performClick() + // Give time for the close animation to complete + rule.mainClock.advanceTimeBy(milliseconds = 1_000) + eventsRecorder.assertSingle(TimelineEvents.ComputeVerifiedUserSendFailure(timelineItem)) + } + @Test fun `clicking on a custom emoji emits the expected Events`() { val aUnicode = "🙈" diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt index bd580b5f98..078fb9e676 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt @@ -14,6 +14,8 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.aUserEventPermissions import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.actionlist.model.TimelineItemActionPostProcessor +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailure +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailureFactory import io.element.android.features.messages.impl.fixtures.aMessageEvent import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemCallNotifyContent @@ -25,8 +27,10 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemVoiceContent import io.element.android.features.poll.api.pollcontent.aPollAnswerItemList import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_MESSAGE +import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore @@ -79,6 +83,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = false, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.ViewSource, ) @@ -120,6 +125,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = false, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.ViewSource, ) @@ -161,6 +167,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.Reply, TimelineItemAction.Forward, @@ -208,6 +215,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.Forward, TimelineItemAction.Pin, @@ -252,6 +260,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.Reply, TimelineItemAction.Forward, @@ -298,6 +307,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = false, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.Reply, TimelineItemAction.Forward, @@ -345,6 +355,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.Reply, TimelineItemAction.Forward, @@ -392,6 +403,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.Reply, TimelineItemAction.Forward, @@ -439,6 +451,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.Reply, TimelineItemAction.Forward, @@ -484,6 +497,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = stateEvent, displayEmojiReactions = false, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.ViewSource, ) @@ -553,6 +567,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.Reply, TimelineItemAction.Forward, @@ -599,6 +614,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.Reply, TimelineItemAction.Forward, @@ -652,6 +668,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.Reply, TimelineItemAction.Forward, @@ -748,6 +765,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.Edit, TimelineItemAction.Copy, @@ -787,6 +805,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.Reply, TimelineItemAction.Edit, @@ -829,6 +848,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.Reply, TimelineItemAction.EndPoll, @@ -870,6 +890,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.Reply, TimelineItemAction.Pin, @@ -910,6 +931,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.Reply, TimelineItemAction.Forward, @@ -949,6 +971,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( event = messageEvent, displayEmojiReactions = false, + verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.ViewSource ) @@ -956,6 +979,32 @@ class ActionListPresenterTest { ) } } + + @Test + fun `present - compute for verified user send failure`() = runTest { + val room = FakeMatrixRoom( + userDisplayNameResult = { Result.success("Alice") } + ) + val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = false, room = room) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + val messageEvent = aMessageEvent( + sendState = LocalEventSendState.Failed.VerifiedUserChangedIdentity(users = listOf(A_USER_ID)), + ) + initialState.eventSink.invoke( + ActionListEvents.ComputeForMessage( + event = messageEvent, + userEventPermissions = aUserEventPermissions(), + ) + ) + skipItems(1) + val successState = awaitItem() + val target = successState.target as ActionListState.Target.Success + assertThat(target.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.ChangedIdentity(userDisplayName = "Alice")) + } + } } private fun createActionListPresenter( @@ -968,6 +1017,7 @@ private fun createActionListPresenter( postProcessor = TimelineItemActionPostProcessor.Default, appPreferencesStore = preferencesStore, isPinnedMessagesFeatureEnabled = { isPinFeatureEnabled }, - room = room + room = room, + userSendFailureFactory = VerifiedUserSendFailureFactory(room) ) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailurePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailurePresenterTest.kt new file mode 100644 index 0000000000..8d5884cf08 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailurePresenterTest.kt @@ -0,0 +1,353 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.messages.impl.crypto.sendfailure.resolve + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailure +import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailureFactory +import io.element.android.features.messages.impl.fixtures.aMessageEvent +import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_TRANSACTION_ID +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.room.FakeMatrixRoom +import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.test +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test + +class ResolveVerifiedUserSendFailurePresenterTest { + @get:Rule + val warmUpRule = WarmUpRule() + + @Test + fun `present - initial state`() = runTest { + val presenter = createResolveVerifiedUserSendFailurePresenter() + presenter.test { + val initialState = awaitItem() + assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + } + } + + @Test + fun `present - remote message scenario`() = runTest { + val presenter = createResolveVerifiedUserSendFailurePresenter() + presenter.test { + val sentMessage = aMessageEvent() + val initialState = awaitItem() + assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(sentMessage)) + ensureAllEventsConsumed() + } + } + + @Test + fun `present - sent message scenario`() = runTest { + val presenter = createResolveVerifiedUserSendFailurePresenter() + presenter.test { + val sentMessage = aMessageEvent( + sendState = LocalEventSendState.Sent(AN_EVENT_ID) + ) + val initialState = awaitItem() + assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(sentMessage)) + ensureAllEventsConsumed() + } + } + + @Test + fun `present - unknown failed message scenario`() = runTest { + val presenter = createResolveVerifiedUserSendFailurePresenter() + presenter.test { + val failedMessage = aMessageEvent( + sendState = LocalEventSendState.Failed.Unknown("") + ) + val initialState = awaitItem() + assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage)) + ensureAllEventsConsumed() + } + } + + @Test + fun `present - verified user unsigned device failure dismiss scenario`() = runTest { + val room = FakeMatrixRoom( + userDisplayNameResult = { userId -> + Result.success(userId.value) + }, + ) + val presenter = createResolveVerifiedUserSendFailurePresenter(room) + presenter.test { + val failedMessage = aVerifiedUserHasUnsignedDeviceFailedMessage() + val initialState = awaitItem() + assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage)) + skipItems(1) + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice(A_USER_ID.value)) + state.eventSink(ResolveVerifiedUserSendFailureEvents.Dismiss) + } + skipItems(1) + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + } + ensureAllEventsConsumed() + } + } + + @Test + fun `present - verified user unsigned device failure retry scenario`() = runTest { + val room = FakeMatrixRoom( + userDisplayNameResult = { userId -> + Result.success(userId.value) + }, + retrySendMessageResult = { + Result.success(Unit) + }, + ) + val presenter = createResolveVerifiedUserSendFailurePresenter(room) + presenter.test { + val failedMessage = aVerifiedUserHasUnsignedDeviceFailedMessage() + val initialState = awaitItem() + assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage)) + + skipItems(1) + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice(A_USER_ID.value)) + state.eventSink(ResolveVerifiedUserSendFailureEvents.Retry) + } + awaitItem().also { state -> + assertThat(state.retryAction).isEqualTo(AsyncAction.Loading) + } + skipItems(2) + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + assertThat(state.retryAction).isEqualTo(AsyncAction.Success(Unit)) + } + ensureAllEventsConsumed() + } + } + + @Test + fun `present - verified user unsigned device failure resolve and resend scenario`() = runTest { + val room = FakeMatrixRoom( + userDisplayNameResult = { userId -> + Result.success(userId.value) + }, + ignoreDeviceTrustAndResendResult = { _, _ -> + Result.success(Unit) + }, + ) + val presenter = createResolveVerifiedUserSendFailurePresenter(room) + presenter.test { + val failedMessage = aVerifiedUserHasUnsignedDeviceFailedMessage() + val initialState = awaitItem() + assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage)) + + skipItems(1) + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice(A_USER_ID.value)) + state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend) + } + awaitItem().also { state -> + assertThat(state.resolveAction).isEqualTo(AsyncAction.Loading) + } + // This should move to the next user + skipItems(2) + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice(A_USER_ID_2.value)) + assertThat(state.resolveAction).isEqualTo(AsyncAction.Success(Unit)) + state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend) + } + + skipItems(3) + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + } + ensureAllEventsConsumed() + } + } + + @Test + fun `present - verified user unsigned device failure resolve and resend scenario with error`() = runTest { + val room = FakeMatrixRoom( + userDisplayNameResult = { userId -> + Result.success(userId.value) + }, + ignoreDeviceTrustAndResendResult = { _, _ -> + Result.failure(Exception()) + }, + ) + val presenter = createResolveVerifiedUserSendFailurePresenter(room) + presenter.test { + val failedMessage = aVerifiedUserHasUnsignedDeviceFailedMessage() + val initialState = awaitItem() + assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage)) + + skipItems(1) + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice(A_USER_ID.value)) + state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend) + } + awaitItem().also { state -> + assertThat(state.resolveAction).isEqualTo(AsyncAction.Loading) + } + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice(A_USER_ID.value)) + assertThat(state.resolveAction).isInstanceOf(AsyncAction.Failure::class.java) + } + ensureAllEventsConsumed() + } + } + + @Test + fun `present - verified user changed identity failure retry scenario`() = runTest { + val room = FakeMatrixRoom( + userDisplayNameResult = { userId -> + Result.success(userId.value) + }, + retrySendMessageResult = { + Result.success(Unit) + }, + ) + val presenter = createResolveVerifiedUserSendFailurePresenter(room) + presenter.test { + val failedMessage = aVerifiedUserChangedIdentityMessage() + val initialState = awaitItem() + assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage)) + + skipItems(1) + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.ChangedIdentity(A_USER_ID.value)) + state.eventSink(ResolveVerifiedUserSendFailureEvents.Retry) + } + awaitItem().also { state -> + assertThat(state.retryAction).isEqualTo(AsyncAction.Loading) + } + skipItems(2) + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + assertThat(state.retryAction).isEqualTo(AsyncAction.Success(Unit)) + } + ensureAllEventsConsumed() + } + } + + @Test + fun `present - verified user changed identity failure resolve and resend scenario`() = runTest { + val room = FakeMatrixRoom( + userDisplayNameResult = { userId -> + Result.success(userId.value) + }, + withdrawVerificationAndResendResult = { _, _ -> + Result.success(Unit) + }, + ) + val presenter = createResolveVerifiedUserSendFailurePresenter(room) + presenter.test { + val failedMessage = aVerifiedUserChangedIdentityMessage() + val initialState = awaitItem() + assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage)) + + skipItems(1) + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.ChangedIdentity(A_USER_ID.value)) + state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend) + } + awaitItem().also { state -> + assertThat(state.resolveAction).isEqualTo(AsyncAction.Loading) + } + // This should move to the next user + skipItems(2) + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.ChangedIdentity(A_USER_ID_2.value)) + assertThat(state.resolveAction).isEqualTo(AsyncAction.Success(Unit)) + state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend) + } + + skipItems(3) + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + } + ensureAllEventsConsumed() + } + } + + @Test + fun `present - verified user changed identity failure resolve and resend scenario with error`() = runTest { + val room = FakeMatrixRoom( + userDisplayNameResult = { userId -> + Result.success(userId.value) + }, + withdrawVerificationAndResendResult = { _, _ -> + Result.failure(Exception()) + }, + ) + val presenter = createResolveVerifiedUserSendFailurePresenter(room) + presenter.test { + val failedMessage = aVerifiedUserChangedIdentityMessage() + val initialState = awaitItem() + assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None) + initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage)) + + skipItems(1) + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.ChangedIdentity(A_USER_ID.value)) + state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend) + } + awaitItem().also { state -> + assertThat(state.resolveAction).isEqualTo(AsyncAction.Loading) + } + awaitItem().also { state -> + assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.ChangedIdentity(A_USER_ID.value)) + assertThat(state.resolveAction).isInstanceOf(AsyncAction.Failure::class.java) + } + ensureAllEventsConsumed() + } + } + + private fun aVerifiedUserHasUnsignedDeviceFailedMessage(): TimelineItem.Event { + return aMessageEvent( + transactionId = A_TRANSACTION_ID, + sendState = LocalEventSendState.Failed.VerifiedUserHasUnsignedDevice( + mapOf( + A_USER_ID to emptyList(), + A_USER_ID_2 to emptyList() + ) + ) + ) + } + + private fun aVerifiedUserChangedIdentityMessage(): TimelineItem.Event { + return aMessageEvent( + transactionId = A_TRANSACTION_ID, + sendState = LocalEventSendState.Failed.VerifiedUserChangedIdentity( + listOf(A_USER_ID, A_USER_ID_2) + ) + ) + } + + private fun createResolveVerifiedUserSendFailurePresenter( + room: MatrixRoom = FakeMatrixRoom(), + ): ResolveVerifiedUserSendFailurePresenter { + return ResolveVerifiedUserSendFailurePresenter( + room = room, + verifiedUserSendFailureFactory = VerifiedUserSendFailureFactory(room), + ) + } +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureViewTest.kt new file mode 100644 index 0000000000..e5d67848dd --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureViewTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.messages.impl.crypto.sendfailure.resolve + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +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.EventsRecorder +import io.element.android.tests.testutils.clickOn +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ResolveVerifiedUserSendFailureViewTest { + @get:Rule val rule = createAndroidComposeRule() + + @Test + fun `clicking on resolve and resend emit the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setResolveVerifiedUserSendFailureView( + state = aResolveVerifiedUserSendFailureState( + verifiedUserSendFailure = aChangedIdentitySendFailure(), + eventSink = eventsRecorder, + ), + ) + + rule.clickOn(res = CommonStrings.screen_resolve_send_failure_changed_identity_primary_button_title) + eventsRecorder.assertSingle(ResolveVerifiedUserSendFailureEvents.ResolveAndResend) + } + + @Test + fun `clicking on retry emit the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setResolveVerifiedUserSendFailureView( + state = aResolveVerifiedUserSendFailureState( + verifiedUserSendFailure = aChangedIdentitySendFailure(), + eventSink = eventsRecorder, + ), + ) + + rule.clickOn(res = CommonStrings.action_retry) + eventsRecorder.assertSingle(ResolveVerifiedUserSendFailureEvents.Retry) + } + + private fun AndroidComposeTestRule.setResolveVerifiedUserSendFailureView( + state: ResolveVerifiedUserSendFailureState, + ) { + setContent { + ResolveVerifiedUserSendFailureView(state = state) + } + } +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt index 5c84fec8ac..efbe67a337 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt @@ -19,7 +19,6 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatch import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService 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.room.MatrixRoom import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo @@ -138,34 +137,6 @@ class PinnedMessagesListPresenterTest { } } - @Test - fun `present - redact event`() = runTest { - val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(true) } - val pinnedEventsTimeline = createPinnedMessagesTimeline().apply { - this.redactEventLambda = redactEventLambda - } - val room = FakeMatrixRoom( - pinnedEventsTimelineResult = { Result.success(pinnedEventsTimeline) }, - canRedactOwnResult = { Result.success(true) }, - canRedactOtherResult = { Result.success(true) }, - canUserPinUnpinResult = { Result.success(true) }, - ).apply { - givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID))) - } - val presenter = createPinnedMessagesListPresenter(room = room, isFeatureEnabled = true) - presenter.test { - skipItems(3) - val filledState = awaitItem() as PinnedMessagesListState.Filled - val eventItem = filledState.timelineItems.first() as TimelineItem.Event - filledState.eventSink(PinnedMessagesListEvents.HandleAction(TimelineItemAction.Redact, eventItem)) - advanceUntilIdle() - cancelAndIgnoreRemainingEvents() - assert(redactEventLambda) - .isCalledOnce() - .with(value(AN_EVENT_ID), value(null), value(null)) - } - } - @Test fun `present - unpin event`() = runTest { val successUnpinEventLambda = lambdaRecorder { _: EventId? -> Result.success(true) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt index 9df9dfc9f8..a0712907cc 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt @@ -13,6 +13,7 @@ import app.cash.turbine.ReceiveTurbine import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.FakeMessagesNavigator +import io.element.android.features.messages.impl.crypto.sendfailure.resolve.aResolveVerifiedUserSendFailureState import io.element.android.features.messages.impl.fixtures.aMessageEvent import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactoryCreator import io.element.android.features.messages.impl.timeline.components.aCriticalShield @@ -680,6 +681,7 @@ import kotlin.time.Duration.Companion.seconds sessionPreferencesStore = sessionPreferencesStore, timelineItemIndexer = timelineItemIndexer, timelineController = TimelineController(room), + resolveVerifiedUserSendFailurePresenter = { aResolveVerifiedUserSendFailureState() }, ) } } 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 8ba1679a5e..b63ca0a12f 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 @@ -39,8 +39,8 @@ import io.element.android.libraries.designsystem.theme.components.Button 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.IconSource -import io.element.android.libraries.designsystem.theme.components.OutlinedButton import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings @@ -171,10 +171,9 @@ private fun OnBoardingButtons( .testTag(TestTags.onBoardingSignIn) ) if (state.canCreateAccount) { - OutlinedButton( + TextButton( text = stringResource(id = R.string.screen_onboarding_sign_up), onClick = onCreateAccount, - enabled = true, modifier = Modifier .fillMaxWidth() ) diff --git a/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt b/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt index fff5763221..8791992021 100644 --- a/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt +++ b/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt @@ -11,6 +11,7 @@ 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.appconfig.OnBoardingConfig import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService @@ -46,7 +47,7 @@ class OnBoardingPresenterTest { assertThat(initialState.isDebugBuild).isTrue() assertThat(initialState.canLoginWithQrCode).isFalse() assertThat(initialState.productionApplicationName).isEqualTo("B") - assertThat(initialState.canCreateAccount).isFalse() + assertThat(initialState.canCreateAccount).isEqualTo(OnBoardingConfig.CAN_CREATE_ACCOUNT) assertThat(awaitItem().canLoginWithQrCode).isTrue() } 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 25cc166103..22884bc6ed 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 @@ -259,7 +259,7 @@ private fun InvalidNotificationSettingsView( ErrorDialog( title = stringResource(id = CommonStrings.dialog_title_error), content = stringResource(id = R.string.screen_notification_settings_failed_fixing_configuration), - onDismiss = onDismissError + onSubmit = onDismissError ) } } diff --git a/features/preferences/impl/src/main/res/values-nl/translations.xml b/features/preferences/impl/src/main/res/values-nl/translations.xml index 6296b409b2..06f4c6cd26 100644 --- a/features/preferences/impl/src/main/res/values-nl/translations.xml +++ b/features/preferences/impl/src/main/res/values-nl/translations.xml @@ -6,12 +6,14 @@ "Aangepaste basis-URL voor Element Call" "Stel een aangepaste basis-URL in voor Element Call." "Ongeldige URL, zorg ervoor dat je het protocol (http/https) en het juiste adres invult." + "Push-meldingen provider" "Schakel de uitgebreide tekstverwerker uit om Markdown handmatig te typen." "Leesbevestigingen" "Indien uitgeschakeld worden er geen leesbevestigingen verstuurd. Je ontvangt nog steeds leesbevestigingen van andere gebruikers." "Aanwezigheid delen" "Indien uitgeschakeld kun je geen leesbevestigingen en typmeldingen verzenden of ontvangen." "Schakel optie in om de berichtbron in de tijdlijn te bekijken." + "Je hebt geen geblokkeerde gebruikers." "Deblokkeren" "Je zult alle berichten van hen weer kunnen zien." "Gebruiker deblokkeren" @@ -49,4 +51,6 @@ Als je doorgaat, kunnen sommige van je instellingen veranderen." "systeeminstellingen" "Systeemmeldingen uitgeschakeld" "Meldingen" + "Problemen oplossen" + "Problemen met meldingen oplossen" diff --git a/features/roomaliasresolver/impl/src/main/res/values-nl/translations.xml b/features/roomaliasresolver/impl/src/main/res/values-nl/translations.xml new file mode 100644 index 0000000000..9bf0f7156b --- /dev/null +++ b/features/roomaliasresolver/impl/src/main/res/values-nl/translations.xml @@ -0,0 +1,4 @@ + + + "Kan het kameradres niet vinden." + diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt index bfc19026a9..a3f39895df 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt @@ -124,7 +124,7 @@ fun RolesAndPermissionsView( is AsyncAction.Failure -> { ErrorDialog( content = stringResource(CommonStrings.error_unknown), - onDismiss = { state.eventSink(RolesAndPermissionsEvents.CancelPendingAction) } + onSubmit = { state.eventSink(RolesAndPermissionsEvents.CancelPendingAction) } ) } else -> Unit diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesView.kt index 2f7cfb16a0..ce362373f2 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesView.kt @@ -210,7 +210,7 @@ fun ChangeRolesView( is AsyncAction.Failure -> { ErrorDialog( content = stringResource(CommonStrings.error_unknown), - onDismiss = { state.eventSink(ChangeRolesEvent.ClearError) } + onSubmit = { state.eventSink(ChangeRolesEvent.ClearError) } ) } is AsyncAction.Success -> { diff --git a/features/roomdetails/impl/src/main/res/values-nl/translations.xml b/features/roomdetails/impl/src/main/res/values-nl/translations.xml index 117ef56b9f..a3a4dc965a 100644 --- a/features/roomdetails/impl/src/main/res/values-nl/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-nl/translations.xml @@ -3,14 +3,41 @@ "Er is een fout opgetreden bij het bijwerken van de meldingsinstelling." "Je homeserver ondersteunt deze optie niet in versleutelde kamers; in sommige kamers krijg je mogelijk geen meldingen." "Peilingen" + "Alleen beheerders" + "Personen verbannen" + "Berichten verwijderen" "Iedereen" + "Personen uitnodigen" + "Moderatie van leden" + "Berichten en inhoud" + "Beheerders en moderators" + "Personen verwijderen" + "Kamerafbeelding wijzigen" + "Kamergegevens" + "Kamernaam wijzigen" + "Kameronderwerp wijzigen" + "Berichten verzenden" + "Beheerders bewerken" "Je kunt deze actie niet ongedaan maken. Je bevordert deze gebruiker tot hetzelfde machtsniveau als jij." "Beheerder toevoegen?" + "Degraderen" + "Je kunt deze wijziging niet ongedaan maken omdat je jezelf degradeert. Als je de laatste gebruiker met bevoegdheden in de kamer bent, is het onmogelijk om deze bevoegdheden terug te krijgen." "Jezelf degraderen?" + "%1$s (In behandeling)" + "(In afwachting)" + "Beheerders hebben automatisch moderatorrechten" + "Moderators bewerken" + "Beheerders" + "Moderators" "Leden" + "Je hebt niet-opgeslagen wijzigingen" + "Wijzigingen opslaan?" "Onderwerp toevoegen" "Reeds lid" "Reeds uitgenodigd" + "Versleuteld" + "Niet versleuteld" + "Openbare kamer" "Kamer bewerken" "Er is een onbekende fout opgetreden en de informatie kon niet worden gewijzigd." "Kan kamer niet bijwerken" @@ -25,20 +52,30 @@ "Aangepast" "Standaard" "Meldingen" + "Rollen en rechten" "Naam van de kamer" "Beveiliging" "Kamer delen" + "Kamer info" "Onderwerp" "Kamer bijwerken…" + "Verbannen" + "Ze kunnen niet meer toetreden tot deze kamer als ze worden uitgenodigd." + "Weet je zeker dat je dit lid wilt verbannen?" + "Er zijn geen verbannen gebruikers in deze kamer." + "%1$s verbannen" "%1$d persoon" "%1$d personen" + "Lid verwijderen en verbannen" "Verwijderen uit kamer" "Lid verwijderen en verbannen" "Alleen lid verwijderen" "Lid verwijderen en toekomstige deelname verbieden?" "Ontbannen" + "Ze kunnen opnieuw tot de kamer toetreden als ze worden uitgenodigd." + "Ontban gebruiker" "Profiel bekijken" "Verbannen" "Leden" @@ -47,6 +84,7 @@ "Beheerder" "Moderator" "Kamerleden" + "%1$s ontbannen" "Aanpassen toestaan" "Als je dit inschakelt, wordt je standaardinstelling overschreven" "Stuur me een melding in deze chat voor" @@ -61,4 +99,18 @@ "Alle berichten" "Alleen vermeldingen en trefwoorden" "In deze kamer, stuur me een melding voor" + "Beheerders" + "Mijn rol wijzigen" + "Degraderen tot lid" + "Degraderen tot moderator" + "Moderatie van leden" + "Berichten en inhoud" + "Moderators" + "Rechten" + "Rechten opnieuw instellen" + "Als je de rechten opnieuw instelt, raak je de huidige instellingen kwijt." + "Rechten opnieuw instellen?" + "Rollen" + "Kamergegevens" + "Rollen en rechten" diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/MatrixRoomFixture.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/MatrixRoomFixture.kt index 0bcb881af6..8ff03a69b9 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/MatrixRoomFixture.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/MatrixRoomFixture.kt @@ -31,7 +31,7 @@ fun aMatrixRoom( emitRoomInfo: Boolean = false, canInviteResult: (UserId) -> Result = { lambdaError() }, canSendStateResult: (UserId, StateEventType) -> Result = { _, _ -> lambdaError() }, - userDisplayNameResult: () -> Result = { lambdaError() }, + userDisplayNameResult: (UserId) -> Result = { lambdaError() }, userAvatarUrlResult: () -> Result = { lambdaError() }, setNameResult: (String) -> Result = { lambdaError() }, setTopicResult: (String) -> Result = { lambdaError() }, diff --git a/features/roomdirectory/impl/src/main/res/values-nl/translations.xml b/features/roomdirectory/impl/src/main/res/values-nl/translations.xml new file mode 100644 index 0000000000..a2c6da3d09 --- /dev/null +++ b/features/roomdirectory/impl/src/main/res/values-nl/translations.xml @@ -0,0 +1,5 @@ + + + "Laden mislukt" + "Kamergids" + 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 69779542a9..52a18f8e9c 100644 --- a/features/roomlist/impl/src/main/res/values-cs/translations.xml +++ b/features/roomlist/impl/src/main/res/values-cs/translations.xml @@ -1,5 +1,9 @@ + "Odhlásit se a upgradovat" + "Váš server nyní podporuje nový, rychlejší protokol. Chcete-li upgradovat, odhlaste se a znovu se přihlaste. Pokud to uděláte nyní, pomůže vám vyhnout se nucenému odhlášení, když bude starý protokol později odstraněn." + "Váš domovský server již nepodporuje starý protokol. Chcete-li pokračovat v používání aplikace, odhlaste se a znovu se přihlaste." + "Upgrade k dispozici" "Vygenerujte nový klíč pro obnovení, který lze použít k obnovení historie šifrovaných zpráv v případě, že ztratíte přístup ke svým zařízením." "Nastavení obnovy" "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í." 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 6215b3d4e4..372e918e09 100644 --- a/features/roomlist/impl/src/main/res/values-de/translations.xml +++ b/features/roomlist/impl/src/main/res/values-de/translations.xml @@ -1,5 +1,10 @@ + "Abmelden und aktualisieren" + "Dein Server unterstützt jetzt ein neues, schnelleres Protokoll. Melde dich ab und melde dich wieder an, um zu aktualisieren. Wenn du das jetzt tust, vermeidest du eine erzwungene Abmeldung, wenn das alte Protokoll später entfernt wird." + "Dein Homeserver unterstützt das alte Protokoll nicht mehr. Bitte logge dich aus und melde dich wieder an, um die App weiter zu nutzen." + "Aktualisierung verfügbar" + "Wiederherstellung einrichten" "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." "Damit du keinen wichtigen Anruf verpasst, ändere bitte deine Einstellungen so, dass du bei gesperrtem Telefon Benachrichtigungen im Vollbildmodus erhältst." diff --git a/features/roomlist/impl/src/main/res/values-el/translations.xml b/features/roomlist/impl/src/main/res/values-el/translations.xml index 71aca711ad..a7e2a0f79f 100644 --- a/features/roomlist/impl/src/main/res/values-el/translations.xml +++ b/features/roomlist/impl/src/main/res/values-el/translations.xml @@ -1,5 +1,11 @@ + "Αποσύνδεση & Αναβάθμιση" + "Ο διακομιστής σου υποστηρίζει τώρα ένα νέο, ταχύτερο πρωτόκολλο. Αποσυνδέσου και συνδέσου ξανά για αναβάθμιση τώρα. Κάνοντας αυτό τώρα θα σε βοηθήσει να αποφύγεις μια αναγκαστική αποσύνδεση όταν το παλιό πρωτόκολλο καταργηθεί αργότερα." + "Ο οικιακός διακομιστής σου δεν υποστηρίζει πλέον το παλιό πρωτόκολλο. Αποσυνδέσου και συνδέσου ξανά για να συνεχίσεις να χρησιμοποιείς την εφαρμογή." + "Διαθέσιμη αναβάθμιση" + "Δημιούργησε ένα νέο κλειδί ανάκτησης που μπορεί να χρησιμοποιηθεί για την επαναφορά του ιστορικού των κρυπτογραφημένων μηνυμάτων σου σε περίπτωση που χάσεις την πρόσβαση στις συσκευές σου." + "Ρύθμιση ανάκτησης" "Το αντίγραφο ασφαλείας της συνομιλίας σου δεν είναι συγχρονισμένο αυτήν τη στιγμή. Πρέπει να εισαγάγεις το κλειδί ανάκτησης για να διατηρήσεις την πρόσβαση στο αντίγραφο ασφαλείας της συνομιλίας σου." "Εισήγαγε το κλειδί ανάκτησης" "Για να διασφαλίσεις ότι δεν θα χάσεις ποτέ μια σημαντική κλήση, άλλαξε τις ρυθμίσεις σου για να επιτρέψεις τις ειδοποιήσεις πλήρους οθόνης όταν το τηλέφωνό σου είναι κλειδωμένο." diff --git a/features/roomlist/impl/src/main/res/values-et/translations.xml b/features/roomlist/impl/src/main/res/values-et/translations.xml index e64e0fc6e0..a8f61ea37a 100644 --- a/features/roomlist/impl/src/main/res/values-et/translations.xml +++ b/features/roomlist/impl/src/main/res/values-et/translations.xml @@ -1,5 +1,9 @@ + "Logi välja ja uuenda" + "Sinu koduserver toetab uut ja kiiremat protokolli. Uuendamiseks logi korraks rakendusest välja ja siis tagasi. Mingil hetkel tulevikus vana protokoll eemaldatakse kasutusest ja tehes uuenduse nüüd väldid hilisemat sundkorras uuendust." + "Sinu koduserver enam ei toeta vana protokolli. Jätkamaks rakenduse kasutamist palun logi välja ning seejärel tagasi." + "Saadaval on uuendus" "Loo uus taastevõti, mida saad kasutada oma krüptitud sõnumite ajaloo taastamisel olukorras, kus kaotad ligipääsu oma seadmetele." "Seadista taastamine" "Sinu vestluste varukoopia pole hetkel sünkroonis. Säilitamaks ligipääsu vestluse varukoopiale palun sisesta oma taastevõti." 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 e2f3b22fe1..fb2188a8a4 100644 --- a/features/roomlist/impl/src/main/res/values-fr/translations.xml +++ b/features/roomlist/impl/src/main/res/values-fr/translations.xml @@ -1,5 +1,11 @@ + "Déconnecter et mettre à niveau" + "Votre serveur prend désormais en charge un nouveau protocole plus rapide. Déconnectez-vous, puis reconnectez-vous pour effectuer la mise à niveau dès maintenant. En le faisant tout de suite, vous éviterez une déconnexion forcée lorsque l’ancien protocole sera supprimé." + "Votre serveur d’accueil ne prend plus en charge l’ancien protocole. Veuillez vous déconnecter puis vous reconnecter pour continuer à utiliser l’application." + "Mise à niveau disponible" + "Générez une nouvelle clé de récupération qui peut être utilisée pour restaurer l’historique de vos messages chiffrés au cas où vous perdriez l’accès à vos appareils." + "Configurer la récupération" "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" "Afin de ne jamais manquer un appel important, veuillez modifier vos paramètres pour autoriser les notifications en plein écran lorsque votre appareil est verrouillé." 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 2325b7ae21..20006fd466 100644 --- a/features/roomlist/impl/src/main/res/values-hu/translations.xml +++ b/features/roomlist/impl/src/main/res/values-hu/translations.xml @@ -1,5 +1,9 @@ + "Kijelentkezés és frissítés" + "A kiszolgálója mostantól egy új, gyorsabb protokollt támogat. A frissítéshez jelentkezzen ki, majd jelentkezzen be újra. Ha ezt most megteszi, elkerülheti a kényszerített kijelentkeztetést a régi protokollt eltávolításakor." + "A Matrix-kiszolgáló már nem támogatja a régi protokollt. Az alkalmazás további használatához jelentkezzen ki és be." + "Frissítés érhető el" "Hozzon létre egy új helyreállítási kulcsot, amellyel visszaállíthatja a titkosított üzenetek előzményeit, ha elveszíti az eszközökhöz való hozzáférést." "Helyreállítás beállítása" "A csevegés biztonsági mentése 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." diff --git a/features/roomlist/impl/src/main/res/values-nl/translations.xml b/features/roomlist/impl/src/main/res/values-nl/translations.xml index a20e0a695c..b20bad6cdd 100644 --- a/features/roomlist/impl/src/main/res/values-nl/translations.xml +++ b/features/roomlist/impl/src/main/res/values-nl/translations.xml @@ -14,13 +14,25 @@ "Ga aan de slag door iemand een bericht te sturen." "Nog geen chats." "Favorieten" + "Je kunt een chat toevoegen aan je favorieten in de chatinstellingen. +Voor nu kun je filters deselecteren om je andere chats te zien" + "Je hebt nog geen favoriete chats" + "Uitnodigingen" + "Je hebt geen openstaande uitnodigingen." "Lage prioriteit" + "Je kunt filters deselecteren om je andere chats te zien" + "Je hebt geen chats voor deze selectie" "Personen" + "Je hebt nog geen directe chats" "Kamers" + "Je zit nog niet in een kamer" "Ongelezen" + "Gefeliciteerd! +Je hebt geen ongelezen berichten!" "Chats" "Markeren als gelezen" "Markeren als ongelezen" + "Blader door alle kamers" "Het lijkt erop dat je een nieuw apparaat gebruikt. Verifieer met een ander apparaat om toegang te krijgen tot je versleutelde berichten." "Verifieer dat jij het bent" 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 c9ccc1f44f..a057f9284d 100644 --- a/features/roomlist/impl/src/main/res/values-ru/translations.xml +++ b/features/roomlist/impl/src/main/res/values-ru/translations.xml @@ -1,5 +1,11 @@ + "Выйти и обновить" + "Теперь ваш сервер поддерживает новый, более быстрый протокол. Выйдите из системы и снова войдите в систему для обновления прямо сейчас. Сделав это сейчас, вы сможете избежать принудительного выхода из системы при последующем удалении старого протокола." + "Ваш homeserver больше не поддерживает старый протокол. Пожалуйста, выйдите из системы и войдите снова, чтобы продолжить использование приложения." + "Доступно обновление" + "Создайте новый ключ восстановления, который можно использовать для восстановления зашифрованной истории сообщений в случае потери доступа к своим устройствам." + "Настроить восстановление" "В настоящее время резервная копия вашего чата не синхронизирована. Требуется подтвердить вашим ключом восстановления, чтобы сохранить доступ к резервной копии чата." "Введите " 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 3d6e215cc6..f1d15ebbf1 100644 --- a/features/roomlist/impl/src/main/res/values-sk/translations.xml +++ b/features/roomlist/impl/src/main/res/values-sk/translations.xml @@ -1,5 +1,9 @@ + "Odhlásiť sa a aktualizovať" + "Váš server teraz podporuje nový, rýchlejší protokol. Odhláste sa a prihláste sa znova, aby ste mohli aktualizovať. Ak to urobíte teraz, pomôže vám vyhnúť sa nútenému odhláseniu, keď sa starý protokol neskôr odstráni." + "Váš domovský server už nepodporuje starý protokol. Ak chcete pokračovať v používaní aplikácie, odhláste sa a znova sa prihláste." + "Aktualizácia je k dispozícii" "Vytvorte nový kľúč na obnovenie, ktorý môžete použiť na obnovenie vašej histórie šifrovaných správ v prípade straty prístupu k vašim zariadeniam." "Nastaviť obnovenie" "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." diff --git a/features/roomlist/impl/src/main/res/values/localazy.xml b/features/roomlist/impl/src/main/res/values/localazy.xml index ff6c626c63..36dc4b8d65 100644 --- a/features/roomlist/impl/src/main/res/values/localazy.xml +++ b/features/roomlist/impl/src/main/res/values/localazy.xml @@ -2,6 +2,7 @@ "Log Out & Upgrade" "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later." + "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app." "Upgrade available" "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices." "Set up recovery" diff --git a/features/securebackup/impl/src/main/res/values-be/translations.xml b/features/securebackup/impl/src/main/res/values-be/translations.xml index 75e4c2ccb9..910350f41e 100644 --- a/features/securebackup/impl/src/main/res/values-be/translations.xml +++ b/features/securebackup/impl/src/main/res/values-be/translations.xml @@ -38,7 +38,6 @@ "Паўтарыце спробу, каб пацвердзіць доступ да рэзервовай копіі чата." "Няправільны ключ аднаўлення" "Калі ў вас ёсць ключ аднаўлення або парольная фраза, гэта таксама будзе працаваць." - "Ключ аднаўлення або код доступу" "Увесці…" "Страцілі ключ аднаўлення?" "Ключ аднаўлення пацверджаны" diff --git a/features/securebackup/impl/src/main/res/values-cs/translations.xml b/features/securebackup/impl/src/main/res/values-cs/translations.xml index 6425f52b66..ba113f9c89 100644 --- a/features/securebackup/impl/src/main/res/values-cs/translations.xml +++ b/features/securebackup/impl/src/main/res/values-cs/translations.xml @@ -39,7 +39,6 @@ "Zkuste prosím znovu potvrdit přístup k záloze chatu." "Nesprávný klíč pro obnovení" "Pokud máte bezpečnostní klíč nebo bezpečnostní frázi, bude to fungovat také." - "Klíč pro obnovení nebo přístupový kód" "Zadejte…" "Ztratili jste klíč pro obnovení?" "Klíč pro obnovení potvrzen" diff --git a/features/securebackup/impl/src/main/res/values-de/translations.xml b/features/securebackup/impl/src/main/res/values-de/translations.xml index c95e51db7e..3b95814763 100644 --- a/features/securebackup/impl/src/main/res/values-de/translations.xml +++ b/features/securebackup/impl/src/main/res/values-de/translations.xml @@ -29,13 +29,13 @@ "Wiederherstellungsschlüssel" " mit einem anderen Gerät" + "Zurücksetzen fortsetzen" "Ausschalten" "Du verlierst deine verschlüsselten Nachrichten, wenn du auf allen Geräten abgemeldet bist." "Bist du sicher, dass du das Backup deaktivieren willst?" - "Wenn du das Backup deaktivierst, wird dein aktuelles Backup des Verschlüsselungsschlüssels entfernt und andere Sicherheitsfunktionen werden deaktiviert. -Das bedeutet:" - "Keine Historie für verschlüsselte Nachrichten auf neuen Geräten ." - "Du verlierst den Zugriff auf deine verschlüsselten Nachrichten, wenn du dich überall von %1$s abmeldest" + "Wenn du das Backup deaktivierst, wird dein aktuelles Schlüssel Backup für die Verschlüsselung entfernt und andere Sicherheitsfunktionen werden deaktiviert. Das bedeutet:" + "Keinen Nachrichtenverlauf für verschlüsselte Nachrichten auf neuen Geräten ." + "Verlust des Zugriffs auf Deine verschlüsselten Nachrichten, wenn Du Dich überall von %1$s abmeldest" "Bist du sicher, dass du das Backup deaktivieren willst?" "Hier kannst Du einen neuen Wiederherstellungsschlüssel erstellen. Nachdem Du einen neuen Wiederherstellungsschlüssel erstellt hast, funktioniert dein alter Schlüssel nicht mehr." "Wiederherstellungsschlüssel erstellen" @@ -51,10 +51,6 @@ Das bedeutet:" "Bitte versuche es noch einmal, um den Zugriff auf dein Chat-Backup zu bestätigen." "Falscher Wiederherstellungsschlüssel" "Dies funktioniert auch mit einem Sicherheitsschlüssel oder Sicherheitsphrase." - - "Wiederherstellungsschlüssel" - " oder Passcode" - "Eingeben…" "Hast du deinen Wiederherstellungschlüssel vergessen?" "Wiederherstellungsschlüssel bestätigt" @@ -72,4 +68,6 @@ Das bedeutet:" "Stelle sicher, dass du deinen Wiederherstellungsschlüssel an einem sicheren Ort aufbewahren kannst" "Einrichtung der Wiederherstellung erfolgreich" "Wiederherstellung einrichten" + "Es ist ein unbekannter Fehler aufgetreten. Bitte überprüfe das Passwort deines Kontos und versuche es erneut." + "Eingeben…" diff --git a/features/securebackup/impl/src/main/res/values-el/translations.xml b/features/securebackup/impl/src/main/res/values-el/translations.xml index e62f465743..f4574b523d 100644 --- a/features/securebackup/impl/src/main/res/values-el/translations.xml +++ b/features/securebackup/impl/src/main/res/values-el/translations.xml @@ -16,6 +16,12 @@ "Ακολούθησε τις οδηγίες για να δημιουργήσεις ένα νέο κλειδί ανάκτησης" "Αποθήκευσε το νέο κλειδί ανάκτησης σε έναν διαχειριστή κωδικών πρόσβασης ή σε κρυπτογραφημένη σημείωση" "Επανάφερε την κρυπτογράφηση για το λογαριασμό σου χρησιμοποιώντας άλλη συσκευή" + "Συνέχιση επαναφοράς" + "Τα στοιχεία του λογαριασμού σου, οι επαφές, οι προτιμήσεις και η λίστα συνομιλιών θα διατηρηθούν" + "Θα χάσεις το υπάρχον ιστορικό μηνυμάτων σου" + "Θα χρειαστεί να επαληθεύσεις ξανά όλες τις υπάρχουσες συσκευές και επαφές σου" + "Επανάφερε την ταυτότητά σου μόνο εάν δεν έχεις πρόσβαση σε άλλη συνδεδεμένη συσκευή και έχεις χάσει το κλειδί ανάκτησης." + "Δεν μπορείς να επιβεβαιώσεις; Θα χρειαστεί να επαναφέρεις την ταυτότητά σου." "Απενεργοποίηση" "Θα χάσεις τα κρυπτογραφημένα μηνύματά σου εάν αποσυνδεθείς από όλες τις συσκευές." "Σίγουρα θες να απενεργοποιήσεις τα αντίγραφα ασφαλείας;" @@ -33,7 +39,6 @@ "Προσπάθησε ξανά για να επιβεβαιώσεις την πρόσβαση στο αντίγραφο ασφαλείας της συνομιλίας σου." "Λανθασμένο κλειδί ανάκτησης" "Εάν έχεις ένα κλειδί ασφαλείας ή μια φράση ασφαλείας, θα λειτουργήσει επίσης." - "Κλειδί ανάκτησης ή κωδικός πρόσβασης" "Εισαγωγή…" "Έχασες το κλειδί ανάκτησης;" "Επιβεβαιώθηκε το κλειδί ανάκτησης" @@ -51,4 +56,11 @@ "Βεβαιώσου ότι μπορείς να αποθηκεύσεις το κλειδί ανάκτησης κάπου ασφαλές" "Επιτυχής ρύθμιση ανάκτησης" "Ρύθμιση ανάκτησης" + "Ναι, επαναφορά τώρα" + "Η διαδικασία είναι μη αναστρέψιμη." + "Σίγουρα θες να επαναφέρεις την ταυτότητά σου;" + "Συνέβη ένα άγνωστο σφάλμα. Έλεγξε ότι ο κωδικός πρόσβασης του λογαριασμού σου είναι σωστός και δοκίμασε ξανά." + "Εισαγωγή…" + "Επιβεβαίωσε ότι θες να επαναφέρεις την ταυτότητά σου." + "Εισήγαγε τον κωδικό πρόσβασης του λογαριασμού σου για να συνεχίσεις" diff --git a/features/securebackup/impl/src/main/res/values-et/translations.xml b/features/securebackup/impl/src/main/res/values-et/translations.xml index 2c3aa2d12b..b618432839 100644 --- a/features/securebackup/impl/src/main/res/values-et/translations.xml +++ b/features/securebackup/impl/src/main/res/values-et/translations.xml @@ -39,7 +39,6 @@ "Kinnitamaks ligipääsu sinu vestluse varukoopiale, palun proovi uuesti" "Vigane taastevõti" "Kui sul on turvavõti või turvafraas, siis need toimivad ka." - "Taastevõti või turvafraas" "Sisesta…" "Kas sa oled taastevõtme kaotanud?" "Taastevõti on kinnitatud" diff --git a/features/securebackup/impl/src/main/res/values-fr/translations.xml b/features/securebackup/impl/src/main/res/values-fr/translations.xml index 6656e0a8e3..346ad49329 100644 --- a/features/securebackup/impl/src/main/res/values-fr/translations.xml +++ b/features/securebackup/impl/src/main/res/values-fr/translations.xml @@ -16,6 +16,7 @@ "Suivez les instructions pour créer une nouvelle clé de récupération" "Enregistrez votre nouvelle clé dans un gestionnaire de mots de passe ou dans une note chiffrée" "Réinitialisez le chiffrement de votre compte en utilisant un autre appareil" + "Continuer la réinitialisation" "Les détails de votre compte, vos contacts, vos préférences et votre liste de discussions seront conservés" "Vous perdrez l’historique de vos messages" "Vous devrez vérifier à nouveau tous vos appareils et tous vos contacts" @@ -37,7 +38,6 @@ "Veuillez réessayer afin de pouvoir accéder à vos anciens messages." "Clé de récupération incorrecte" "Si vous avez une clé de sécurité ou une phrase de sécurité, cela fonctionnera également." - "Clé de récupération" "Saisissez la clé ici…" "Clé de récupération perdue?" "Clé de récupération confirmée" diff --git a/features/securebackup/impl/src/main/res/values-hu/translations.xml b/features/securebackup/impl/src/main/res/values-hu/translations.xml index 20575540b7..48fe08a31b 100644 --- a/features/securebackup/impl/src/main/res/values-hu/translations.xml +++ b/features/securebackup/impl/src/main/res/values-hu/translations.xml @@ -39,7 +39,6 @@ "Próbálja meg újra megerősíteni a csevegés biztonsági mentéséhez való hozzáférését." "Helytelen helyreállítási kulcs" "Ha van biztonsági kulcsa vagy biztonsági jelmondata, akkor ez is fog működni." - "Helyreállítási kulcs vagy jelkód" "Megadás…" "Elvesztette a helyreállítási kulcsát?" "Helyreállítási kulcs megerősítve" diff --git a/features/securebackup/impl/src/main/res/values-in/translations.xml b/features/securebackup/impl/src/main/res/values-in/translations.xml index 83fa1f8092..62968d3471 100644 --- a/features/securebackup/impl/src/main/res/values-in/translations.xml +++ b/features/securebackup/impl/src/main/res/values-in/translations.xml @@ -33,7 +33,6 @@ "Silakan coba lagi untuk mengonfirmasi akses ke cadangan percakapan Anda." "Kunci pemulihan salah" "Jika Anda memiliki kunci keamanan atau frasa keamanan, ini juga bisa digunakan." - "Kunci pemulihan atau kode sandi" "Masukkan…" "Kehilangan kunci pemulihan Anda?" "Kunci pemulihan dikonfirmasi" diff --git a/features/securebackup/impl/src/main/res/values-it/translations.xml b/features/securebackup/impl/src/main/res/values-it/translations.xml index c1d67299ba..0e069b0ab2 100644 --- a/features/securebackup/impl/src/main/res/values-it/translations.xml +++ b/features/securebackup/impl/src/main/res/values-it/translations.xml @@ -38,7 +38,6 @@ "Riprova per confermare l\'accesso al backup della chat." "Chiave di recupero errata" "Se hai una chiave di sicurezza o una password, andrà bene anche questo." - "Chiave di recupero o codice di accesso" "Inserisci…" "Hai perso la chiave di recupero?" "Chiave di recupero confermata" diff --git a/features/securebackup/impl/src/main/res/values-nl/translations.xml b/features/securebackup/impl/src/main/res/values-nl/translations.xml index b4dbb23562..388230dd7d 100644 --- a/features/securebackup/impl/src/main/res/values-nl/translations.xml +++ b/features/securebackup/impl/src/main/res/values-nl/translations.xml @@ -9,6 +9,13 @@ "Je chatback-up is momenteel niet gesynchroniseerd." "Herstelmogelijkheid instellen" "Krijg toegang tot je versleutelde berichten als je al je apparaten kwijtraakt of overal uit %1$s bent uitgelogd." + "Open %1$s op een desktopapparaat" + "Log opnieuw in op je account" + "Wanneer je wordt gevraagd om je apparaat te verifiëren, selecteer %1$s" + "“Alles opnieuw instellen”" + "Volg de instructies om een nieuwe herstelsleutel te maken" + "Sla je nieuwe herstelsleutel op in een wachtwoordmanager of versleutelde notitie" + "Stel de versleuteling voor je account opnieuw in met een ander apparaat" "Uitschakelen" "Je verliest je versleutelde berichten als je bent uitgelogd op alle apparaten." "Weet je zeker dat je de back-up wilt uitschakelen?" @@ -21,11 +28,13 @@ "Zorg ervoor dat je je herstelsleutel op een veilige plek kunt bewaren" "Herstelsleutel gewijzigd" "Herstelsleutel wijzigen?" + "Maak een nieuwe herstelsleutel" "Zorg ervoor dat niemand dit scherm kan zien!" "Probeer het opnieuw om toegang tot je chatback-up te bevestigen." "Onjuiste herstelsleutel" "Als je een beveiligingssleutel of beveiligingszin hebt, werkt dit ook." "Voer in…" + "Herstelsleutel kwijt?" "Herstelsleutel bevestigd" "Voer je herstelsleutel in" "Herstelsleutel gekopieerd" diff --git a/features/securebackup/impl/src/main/res/values-pl/translations.xml b/features/securebackup/impl/src/main/res/values-pl/translations.xml index eea550627f..d708e18bab 100644 --- a/features/securebackup/impl/src/main/res/values-pl/translations.xml +++ b/features/securebackup/impl/src/main/res/values-pl/translations.xml @@ -38,7 +38,6 @@ "Spróbuj ponownie, aby potwierdzić dostęp do backupu czatu." "Nieprawidłowy klucz przywracania" "To też zadziała, jeśli posiadasz klucz lub frazę bezpieczeństwa." - "Klucz przywracania lub hasło" "Wprowadź…" "Zgubiłeś swój kod przywracania?" "Potwierdzono klucz przywracania" diff --git a/features/securebackup/impl/src/main/res/values-pt/translations.xml b/features/securebackup/impl/src/main/res/values-pt/translations.xml index f93e5dedf4..0e409ea22c 100644 --- a/features/securebackup/impl/src/main/res/values-pt/translations.xml +++ b/features/securebackup/impl/src/main/res/values-pt/translations.xml @@ -38,7 +38,6 @@ "Por favor, tenta novamente para confirmar o acesso à tua cópia de segurança das conversas." "Chave de recuperação incorreta" "Também funciona se tiveres uma chave ou frase de segurança." - "Chave ou código de recuperação" "Inserir…" "Perdeste a tua chave?" "Chave de recuperação confirmada" diff --git a/features/securebackup/impl/src/main/res/values-ro/translations.xml b/features/securebackup/impl/src/main/res/values-ro/translations.xml index 6b2f2e31dc..7266533d78 100644 --- a/features/securebackup/impl/src/main/res/values-ro/translations.xml +++ b/features/securebackup/impl/src/main/res/values-ro/translations.xml @@ -33,7 +33,6 @@ "Vă rugăm să încercați din nou să confirmați accesul la backup." "Cheie de recuperare incorectă" "Dacă aveți o cheie de securitate sau o frază de securitate, aceasta va funcționa și ea." - "Cheie de recuperare sau cod de acces" "Introduceți…" "Ați pierdut cheia de recuperare?" "Cheia de recuperare confirmată" diff --git a/features/securebackup/impl/src/main/res/values-ru/translations.xml b/features/securebackup/impl/src/main/res/values-ru/translations.xml index 8866dc9613..52d239069d 100644 --- a/features/securebackup/impl/src/main/res/values-ru/translations.xml +++ b/features/securebackup/impl/src/main/res/values-ru/translations.xml @@ -26,6 +26,7 @@ " в менеджере паролей или зашифрованной заметке" "Сбросьте шифрование вашей учетной записи с помощью другого устройства." + "Продолжить сброс" "Данные вашей учетной записи, контакты, настройки и список чатов будут сохранены" "Вы потеряете существующую историю сообщений" "Вам нужно будет заново подтвердить все существующие устройства и контакты." @@ -60,10 +61,6 @@ "ключ восстановления" "Если у вас есть пароль для восстановления или секретный пароль/ключ, это тоже сработает." - - "Ключ восстановления" - " или пароль" - "Вход…" "Потеряли ключ восстановления?" diff --git a/features/securebackup/impl/src/main/res/values-sk/translations.xml b/features/securebackup/impl/src/main/res/values-sk/translations.xml index 8a088f4245..24d80c6b00 100644 --- a/features/securebackup/impl/src/main/res/values-sk/translations.xml +++ b/features/securebackup/impl/src/main/res/values-sk/translations.xml @@ -39,7 +39,6 @@ "Skúste prosím znova potvrdiť prístup k vašej zálohe konverzácie." "Nesprávny kľúč na obnovenie" "Ak máte bezpečnostný kľúč alebo bezpečnostnú frázu, bude to fungovať tiež." - "Kľúč na obnovenie alebo prístupový kód" "Zadať…" "Stratili ste kľúč na obnovenie?" "Kľúč na obnovu potvrdený" diff --git a/features/securebackup/impl/src/main/res/values-sv/translations.xml b/features/securebackup/impl/src/main/res/values-sv/translations.xml index 3599320cd8..7b2364f450 100644 --- a/features/securebackup/impl/src/main/res/values-sv/translations.xml +++ b/features/securebackup/impl/src/main/res/values-sv/translations.xml @@ -39,7 +39,6 @@ "Vänligen pröva igen för att bekräfta åtkomsten till din chattsäkerhetskopia." "Felaktig återställningsnyckel" "Om du har en säkerhetsnyckel eller säkerhetsfras så funkar den också." - "Återställningsnyckel eller lösenkod" "Ange …" "Blivit av med din återställningsnyckel?" "Återställningsnyckel bekräftad" diff --git a/features/securebackup/impl/src/main/res/values-uk/translations.xml b/features/securebackup/impl/src/main/res/values-uk/translations.xml index 14e47f8bad..7d88980f8b 100644 --- a/features/securebackup/impl/src/main/res/values-uk/translations.xml +++ b/features/securebackup/impl/src/main/res/values-uk/translations.xml @@ -38,7 +38,6 @@ "Будь ласка, спробуйте ще раз, щоб підтвердити доступ до резервної копії чату." "Неправильний ключ відновлення" "Якщо у вас є ключ безпеки або фраза безпеки, це теж спрацює." - "Ключ відновлення або код допуску" "Ввести…" "Загубили ключ відновлення?" "Ключ відновлення підтверджено" diff --git a/features/securebackup/impl/src/main/res/values-zh/translations.xml b/features/securebackup/impl/src/main/res/values-zh/translations.xml index 0b4f12b17a..c8f2b7d9ed 100644 --- a/features/securebackup/impl/src/main/res/values-zh/translations.xml +++ b/features/securebackup/impl/src/main/res/values-zh/translations.xml @@ -38,7 +38,6 @@ "请重试以访问您的聊天备份。" "恢复密钥不正确" "如果您有安全密钥或安全短语,也可以用。" - "恢复密钥或密码" "输入……" "丢失了恢复密钥?" "恢复密钥已确认" diff --git a/features/securebackup/impl/src/main/res/values/localazy.xml b/features/securebackup/impl/src/main/res/values/localazy.xml index f4795c23da..55c6c547f5 100644 --- a/features/securebackup/impl/src/main/res/values/localazy.xml +++ b/features/securebackup/impl/src/main/res/values/localazy.xml @@ -39,7 +39,6 @@ "Please try again to confirm access to your chat backup." "Incorrect recovery key" "If you have a security key or security phrase, this will work too." - "Recovery key or passcode" "Enter…" "Lost your recovery key?" "Recovery key confirmed" diff --git a/features/verifysession/impl/src/main/res/values-de/translations.xml b/features/verifysession/impl/src/main/res/values-de/translations.xml index 5f0d9504f4..1ddda0126b 100644 --- a/features/verifysession/impl/src/main/res/values-de/translations.xml +++ b/features/verifysession/impl/src/main/res/values-de/translations.xml @@ -4,6 +4,7 @@ "Verifiziere dieses Gerät, um sicheres Messaging einzurichten." "Bestätige, dass du es bist" "Ein anderes Gerät verwenden" + "Wiederherstellungsschlüssel verwenden" "Du kannst nun verschlüsselte Nachrichten lesen oder versenden." "Gerät verifiziert" "Ein anderes Gerät verwenden" diff --git a/features/verifysession/impl/src/main/res/values-el/translations.xml b/features/verifysession/impl/src/main/res/values-el/translations.xml index cc99091441..e53bd54e00 100644 --- a/features/verifysession/impl/src/main/res/values-el/translations.xml +++ b/features/verifysession/impl/src/main/res/values-el/translations.xml @@ -1,9 +1,11 @@ + "Δεν μπορείς να επιβεβαιώσεις;" "Δημιουργία νέου κλειδιού ανάκτησης" "Επαλήθευσε αυτήν τη συσκευή για να ρυθμίσεις την ασφαλή επικοινωνία." "Επιβεβαίωσε ότι είσαι εσύ" "Χρήση άλλης συσκευής" + "Χρήση κλειδιού ανάκτησης" "Τώρα μπορείς να διαβάζεις ή να στέλνεις μηνύματα με ασφάλεια και επίσης μπορεί να εμπιστευτεί αυτήν τη συσκευή οποιοσδήποτε με τον οποίο συνομιλείς." "Επαληθευμένη συσκευή" "Χρήση άλλης συσκευής" diff --git a/features/verifysession/impl/src/main/res/values-nl/translations.xml b/features/verifysession/impl/src/main/res/values-nl/translations.xml index 6330ad2fc6..bebac551e2 100644 --- a/features/verifysession/impl/src/main/res/values-nl/translations.xml +++ b/features/verifysession/impl/src/main/res/values-nl/translations.xml @@ -1,12 +1,18 @@ + "Maak een nieuwe herstelsleutel" + "Verifieer dit apparaat om beveiligde berichten in te stellen." + "Bevestig dat jij het bent" + "Nu kun je veilig berichten lezen of verzenden, en iedereen met wie je chat kan dit apparaat ook vertrouwen." + "Apparaat geverifieerd" + "Wachten op ander apparaat…" "Er lijkt iets niet goed te gaan. Of er is een time-out opgetreden of het verzoek is geweigerd." "Bevestig dat de emoji\'s hieronder overeenkomen met de emoji\'s in je andere sessie." "Vergelijk emoji\'s" "Bevestig dat de onderstaande cijfers overeenkomen met de cijfers die worden weergegeven in je andere sessie." "Vergelijk getallen" "Je nieuwe sessie is nu geverifieerd. Het heeft toegang tot je versleutelde berichten en andere gebruikers zullen het als vertrouwd beschouwen." - "Voer recovery key in" + "Voer herstelsleutel in" "Bewijs dat jij het bent om toegang te krijgen tot je versleutelde berichtgeschiedenis." "Open een bestaande sessie" "Verificatie opnieuw proberen" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f01f7ce1a6..14d38d3a2c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -162,7 +162,7 @@ jsoup = "org.jsoup:jsoup:1.18.1" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0" timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.43" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.46" 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" } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoundedIconAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoundedIconAtom.kt index 9dc739da96..c21e1a5f38 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoundedIconAtom.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoundedIconAtom.kt @@ -37,6 +37,7 @@ import io.element.android.libraries.designsystem.theme.temporaryColorBgSpecial * @param resourceId the resource id of the icon to display, exclusive with [imageVector] * @param imageVector the image vector of the icon to display, exclusive with [resourceId] * @param tint the tint to apply to the icon + * @param backgroundTint the tint to apply to the icon background */ @Composable fun RoundedIconAtom( @@ -44,13 +45,14 @@ fun RoundedIconAtom( size: RoundedIconAtomSize = RoundedIconAtomSize.Large, resourceId: Int? = null, imageVector: ImageVector? = null, - tint: Color = MaterialTheme.colorScheme.secondary + tint: Color = MaterialTheme.colorScheme.secondary, + backgroundTint: Color = ElementTheme.colors.temporaryColorBgSpecial, ) { Box( modifier = modifier .size(size.toContainerSize()) .background( - color = ElementTheme.colors.temporaryColorBgSpecial, + color = backgroundTint, shape = RoundedCornerShape(size.toCornerSize()) ) ) { diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt index f4c88ab59b..4812eaddee 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt @@ -27,6 +27,7 @@ import io.element.android.libraries.designsystem.icons.CompoundDrawables 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.designsystem.theme.temporaryColorBgSpecial /** * IconTitleSubtitleMolecule is a molecule which displays an icon, a title and a subtitle. @@ -37,6 +38,7 @@ import io.element.android.libraries.designsystem.theme.components.Text * @param iconResourceId the resource id of the icon to display, exclusive with [iconImageVector] * @param iconImageVector the image vector of the icon to display, exclusive with [iconResourceId] * @param iconTint the tint to apply to the icon + * @param iconBackgroundTint the tint to apply to the icon background */ @Composable fun IconTitleSubtitleMolecule( @@ -46,6 +48,7 @@ fun IconTitleSubtitleMolecule( iconResourceId: Int? = null, iconImageVector: ImageVector? = null, iconTint: Color = MaterialTheme.colorScheme.primary, + iconBackgroundTint: Color = ElementTheme.colors.temporaryColorBgSpecial, ) { Column(modifier) { RoundedIconAtom( @@ -55,6 +58,7 @@ fun IconTitleSubtitleMolecule( resourceId = iconResourceId, imageVector = iconImageVector, tint = iconTint, + backgroundTint = iconBackgroundTint, ) Spacer(modifier = Modifier.height(16.dp)) Text( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt index 08c2f352e6..6e42909f0b 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt @@ -48,7 +48,7 @@ fun AsyncActionView( ErrorDialog( title = errorTitle(async.error), content = errorMessage(async.error), - onDismiss = onErrorDismiss + onSubmit = onErrorDismiss ) } else { RetryDialog( 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 acef5bf9f9..add7bf2904 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 @@ -13,6 +13,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.window.DialogProperties import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup @@ -25,17 +26,23 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun ErrorDialog( content: String, - onDismiss: () -> Unit, + onSubmit: () -> Unit, modifier: Modifier = Modifier, - title: String = ErrorDialogDefaults.title, + title: String? = ErrorDialogDefaults.title, submitText: String = ErrorDialogDefaults.submitText, + onDismiss: () -> Unit = onSubmit, + canDismiss: Boolean = true, ) { - BasicAlertDialog(modifier = modifier, onDismissRequest = onDismiss) { + BasicAlertDialog( + modifier = modifier, + onDismissRequest = onDismiss, + properties = DialogProperties(dismissOnClickOutside = canDismiss, dismissOnBackPress = canDismiss) + ) { ErrorDialogContent( title = title, content = content, submitText = submitText, - onSubmitClick = onDismiss, + onSubmitClick = onSubmit, ) } } @@ -44,7 +51,7 @@ fun ErrorDialog( private fun ErrorDialogContent( content: String, onSubmitClick: () -> Unit, - title: String = ErrorDialogDefaults.title, + title: String? = ErrorDialogDefaults.title, submitText: String = ErrorDialogDefaults.submitText, ) { SimpleAlertDialogContent( @@ -78,6 +85,6 @@ internal fun ErrorDialogContentPreview() { internal fun ErrorDialogPreview() = ElementPreview { ErrorDialog( content = "Content", - onDismiss = {}, + onSubmit = {}, ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt index 25f5009299..f09ccad5d9 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt @@ -65,7 +65,41 @@ fun ListItem( disabledLeadingIconColor = ListItemDefaultColors.iconDisabled, disabledTrailingIconColor = ListItemDefaultColors.iconDisabled, ) + ListItem( + headlineContent = headlineContent, + modifier = modifier, + supportingContent = supportingContent, + leadingContent = leadingContent, + trailingContent = trailingContent, + colors = colors, + enabled = enabled, + onClick = onClick, + ) +} +/** + * A List Item component to be used in lists and menus with simple layouts, matching the Material 3 guidelines. + * @param headlineContent The main content of the list item, usually a text. + * @param colors The colors to use for the list item. You can use [ListItemDefaults.colors] to create this. + * @param modifier The modifier to be applied to the list item. + * @param supportingContent The content to be displayed below the headline content. + * @param leadingContent The content to be displayed before the headline content. + * @param trailingContent The content to be displayed after the headline content. + * @param enabled Whether the list item is enabled. When disabled, will change the color of the headline content and the leading content to use disabled tokens. + * @param onClick The callback to be called when the list item is clicked. + */ +@Suppress("LongParameterList") +@Composable +fun ListItem( + headlineContent: @Composable () -> Unit, + colors: ListItemColors, + modifier: Modifier = Modifier, + supportingContent: @Composable (() -> Unit)? = null, + leadingContent: ListItemContent? = null, + trailingContent: ListItemContent? = null, + enabled: Boolean = true, + onClick: (() -> Unit)? = null, +) { // We cannot just pass the disabled colors, they must be set manually: https://issuetracker.google.com/issues/280480132 val headlineColor = if (enabled) colors.headlineColor else colors.disabledHeadlineColor val leadingContentColor = if (enabled) colors.leadingIconColor else colors.disabledLeadingIconColor @@ -378,8 +412,8 @@ private object PreviewItems { ) { ElementThemedPreview { ListItem( - headlineContent = PreviewItems.headline(), - supportingContent = PreviewItems.text(), + headlineContent = headline(), + supportingContent = text(), leadingContent = leadingContent, trailingContent = trailingContent, style = style, @@ -397,8 +431,8 @@ private object PreviewItems { ) { ElementThemedPreview { ListItem( - headlineContent = PreviewItems.headline(), - supportingContent = PreviewItems.textSingleLine(), + headlineContent = headline(), + supportingContent = textSingleLine(), leadingContent = leadingContent, trailingContent = trailingContent, style = style, @@ -417,7 +451,7 @@ private object PreviewItems { ) { ElementThemedPreview { ListItem( - headlineContent = PreviewItems.headline(), + headlineContent = headline(), leadingContent = leadingContent, trailingContent = trailingContent, enabled = enabled, 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 597632a1f6..1b99fd2e5d 100644 --- a/libraries/eventformatter/impl/src/main/res/values-de/translations.xml +++ b/libraries/eventformatter/impl/src/main/res/values-de/translations.xml @@ -45,6 +45,12 @@ "Du hast den Raum-Namen entfernt" "%1$shat keine Änderungen vorgenommen" "Du hast keine Änderungen vorgenommen" + "%1$s hat die fixierten Nachrichten geändert" + "Du hast die fixierten Nachrichten geändert" + "%1$s fixierte eine Nachricht" + "Du hast eine Nachricht fixiert" + "%1$s löste eine Nachricht" + "Du hast eine Nachricht gelöst" "%1$s hat die Einladung abgelehnt" "Du hast die Einladung abgelehnt" "%1$s hat %2$s entfernt" diff --git a/libraries/eventformatter/impl/src/main/res/values-el/translations.xml b/libraries/eventformatter/impl/src/main/res/values-el/translations.xml index 71adc9ed56..4e476d3c4b 100644 --- a/libraries/eventformatter/impl/src/main/res/values-el/translations.xml +++ b/libraries/eventformatter/impl/src/main/res/values-el/translations.xml @@ -45,6 +45,12 @@ "Αφαίρεσες το όνομα του δωματίου" "Ο χρήστης %1$s δεν έκανε καμία αλλαγή" "Δεν έκανες καμία αλλαγή" + "Ο χρήστης %1$s άλλαξε τα καρφιτσωμένα μηνύματα" + "Άλλαξες τα καρφιτσωμένα μηνύματα" + "Ο χρήστης %1$s καρφίτσωσε ένα μήνυμα" + "Καρφίτσωσες ένα μήνυμα" + "Ο χρήστης %1$s ξεκαρφίτσωσε ένα μήνυμα" + "Ξεκαρφίτσωσες ένα μήνυμα" "Ο χρήστης %1$s απέρριψε την πρόσκληση" "Απέρριψες την πρόσκληση" "Ο χρήστης %1$s αφαίρεσε τον χρήστη %2$s" diff --git a/libraries/eventformatter/impl/src/main/res/values-nl/translations.xml b/libraries/eventformatter/impl/src/main/res/values-nl/translations.xml index b10c806572..dba242a458 100644 --- a/libraries/eventformatter/impl/src/main/res/values-nl/translations.xml +++ b/libraries/eventformatter/impl/src/main/res/values-nl/translations.xml @@ -3,12 +3,16 @@ "(afbeelding is ook gewijzigd)" "%1$s wijzigde van afbeelding" "Je hebt je afbeelding gewijzigd" + "%1$s werd gedegradeerd tot lid" + "%1$s werd gedegradeerd tot moderator" "%1$s heeft de weergavenaam aangepast van %2$s naar %3$s" "Je hebt je weergavenaam aangepast van %1$s naar %2$s" "%1$s heeft de weergavenaam verwijderd (dit was %2$s)" "Je hebt je weergavenaam verwijderd (dit was %1$s)" "%1$s heeft de weergavenaam %2$s aangenomen" "Je hebt de weergavenaam %1$s aangenomen" + "%1$s werd bevorderd tot beheerder" + "%1$s werd bevorderd tot moderator" "%1$s heeft de kamerafbeelding gewijzigd" "Je hebt de kamerafbeelding gewijzigd" "%1$s heeft de kamerafbeelding verwijderd" 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 124d3a3318..d71c0de360 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 @@ -13,8 +13,6 @@ import io.element.android.libraries.core.meta.BuildType /** * To enable or disable a FeatureFlags, change the `defaultValue` value. - * Warning: to enable a flag for the release app, you MUST update the file - * [io.element.android.libraries.featureflag.impl.StaticFeatureFlagProvider] */ enum class FeatureFlags( override val key: String, @@ -125,4 +123,13 @@ enum class FeatureFlags( defaultValue = { true }, isFinished = false, ), + InvisibleCrypto( + key = "feature.invisibleCrypto", + title = "Invisible Crypto", + description = "This setting controls how end-to-end encryption (E2E) keys are shared." + + " Enabling it will prevent the inclusion of devices that have not been explicitly verified by their owners." + + " You'll have to stop and re-open the app manually for that setting to take effect.", + defaultValue = { false }, + isFinished = false, + ), } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index e45686b1a2..ca44838553 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.oidc.AccountManagementAction import io.element.android.libraries.matrix.api.pusher.PushersService +import io.element.android.libraries.matrix.api.room.InvitedRoom import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.RoomMembershipObserver @@ -49,6 +50,7 @@ interface MatrixClient : Closeable { val sessionCoroutineScope: CoroutineScope val ignoredUsersFlow: StateFlow> suspend fun getRoom(roomId: RoomId): MatrixRoom? + suspend fun getInvitedRoom(roomId: RoomId): InvitedRoom? suspend fun findDM(userId: UserId): RoomId? suspend fun ignoreUser(userId: UserId): Result suspend fun unignoreUser(userId: UserId): Result @@ -131,6 +133,9 @@ interface MatrixClient : Closeable { /** Returns `true` if the home server supports native sliding sync. */ suspend fun isNativeSlidingSyncSupported(): Boolean - /** Returns `true` if the current session is using native sliding sync. */ + /** Returns `true` if the home server supports sliding sync using a proxy. */ + suspend fun isSlidingSyncProxySupported(): Boolean + + /** Returns `true` if the current session is using native sliding sync, `false` if it's using a proxy. */ fun isUsingNativeSlidingSync(): Boolean } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt index bf0955098b..bb3d396202 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt @@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.api.auth import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClientProvider +import io.element.android.libraries.matrix.api.auth.external.ExternalSession import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep import io.element.android.libraries.matrix.api.core.SessionId @@ -30,6 +31,11 @@ interface MatrixAuthenticationService { suspend fun setHomeserver(homeserver: String): Result suspend fun login(username: String, password: String): Result + /** + * Import a session that was created using another client, for instance Element Web. + */ + suspend fun importCreatedSession(externalSession: ExternalSession): Result + /* * OIDC part. */ diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/external/ExternalSession.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/external/ExternalSession.kt new file mode 100644 index 0000000000..d85135b26e --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/external/ExternalSession.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.matrix.api.auth.external + +/*** + * Represents a session data of a session created by another client. + */ +data class ExternalSession( + val userId: String, + val deviceId: String, + val accessToken: String, + val refreshToken: String?, + val homeserverUrl: String, + val slidingSyncProxy: String? +) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/InvitedRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/InvitedRoom.kt new file mode 100644 index 0000000000..7e1dd5d10d --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/InvitedRoom.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.matrix.api.room + +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.SessionId + +/** A reference to a room the current user has been invited to, with the ability to decline the invite. */ +interface InvitedRoom : AutoCloseable { + val sessionId: SessionId + val roomId: RoomId + + /** Decline the invite to this room. */ + suspend fun declineInvite(): Result +} 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 2d8c807ce0..d266b45d35 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 @@ -7,6 +7,7 @@ package io.element.android.libraries.matrix.api.room +import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.core.RoomAlias @@ -349,5 +350,24 @@ interface MatrixRoom : Closeable { */ suspend fun clearComposerDraft(): Result + /** + * Ignore the local trust for the given devices and resend messages that failed to send because said devices are unverified. + * + * @param devices The map of users identifiers to device identifiers received in the error + * @param transactionId The send queue transaction identifier of the local echo the send error applies to. + * + */ + suspend fun ignoreDeviceTrustAndResend(devices: Map>, transactionId: TransactionId): Result + + /** + * Remove verification requirements for the given users and + * resend messages that failed to send because their identities were no longer verified. + * + * @param userIds The list of users identifiers received in the error. + * @param transactionId The send queue transaction identifier of the local echo the send error applies to. + * + */ + suspend fun withdrawVerificationAndResend(userIds: List, transactionId: TransactionId): Result + override fun close() = destroy() } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt index 32eddf7b00..b466a28da6 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt @@ -44,5 +44,6 @@ data class MatrixRoomInfo( val hasRoomCall: Boolean, val activeRoomCallParticipants: ImmutableList, val heroes: ImmutableList, - val pinnedEventIds: ImmutableList + val pinnedEventIds: ImmutableList, + val creator: UserId?, ) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/LocalEventSendState.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/LocalEventSendState.kt index 92242faf18..452073bb25 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/LocalEventSendState.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/LocalEventSendState.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.matrix.api.timeline.item.event import androidx.compose.runtime.Immutable +import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId @@ -18,21 +19,24 @@ sealed interface LocalEventSendState { data class Unknown(val error: String) : Failed data object CrossSigningNotSetup : Failed data object SendingFromUnverifiedDevice : Failed + + sealed interface VerifiedUser : Failed data class VerifiedUserHasUnsignedDevice( /** * The unsigned devices belonging to verified users. A map from user ID * to a list of device IDs. */ - val devices: Map> - ) : Failed + val devices: Map> + ) : VerifiedUser data class VerifiedUserChangedIdentity( /** * The users that were previously verified but are no longer. */ val users: List - ) : Failed + ) : VerifiedUser } + data class Sent( val eventId: EventId ) : LocalEventSendState 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 0fc9f6695b..9e77dcff4c 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 @@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.notificationsettings.Notification import io.element.android.libraries.matrix.api.oidc.AccountManagementAction import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.CurrentUserMembership +import io.element.android.libraries.matrix.api.room.InvitedRoom import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.RoomMembershipObserver @@ -245,6 +246,10 @@ class RustMatrixClient( return roomFactory.create(roomId) } + override suspend fun getInvitedRoom(roomId: RoomId): InvitedRoom? { + return roomFactory.createInvitedRoom(roomId) + } + /** * Wait for the room to be available in the room list, with a membership for the current user of [CurrentUserMembership.JOINED]. * @param roomIdOrAlias the room id or alias to wait for @@ -267,6 +272,8 @@ class RustMatrixClient( .filter(predicate) .first() .first() + // Ensure that the room is ready + .also { client.awaitRoomRemoteEcho(it.roomId.value) } } } @@ -534,6 +541,10 @@ class RustMatrixClient( return client.availableSlidingSyncVersions().contains(SlidingSyncVersion.Native) } + override suspend fun isSlidingSyncProxySupported(): Boolean { + return client.availableSlidingSyncVersions().any { it is SlidingSyncVersion.Proxy } + } + override fun isUsingNativeSlidingSync(): Boolean { return client.session().slidingSyncVersion == SlidingSyncVersion.Native } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt index 2fe4284f98..512b6e8ea2 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt @@ -7,9 +7,10 @@ package io.element.android.libraries.matrix.impl -import io.element.android.appconfig.AuthenticationConfig import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.di.CacheDirectory +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.impl.analytics.UtdTracker import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider import io.element.android.libraries.matrix.impl.paths.SessionPaths @@ -17,12 +18,10 @@ import io.element.android.libraries.matrix.impl.paths.getSessionPaths import io.element.android.libraries.matrix.impl.proxy.ProxyProvider import io.element.android.libraries.matrix.impl.util.anonymizedTokens import io.element.android.libraries.network.useragent.UserAgentProvider -import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.sessionstorage.api.SessionData import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.services.toolbox.api.systemclock.SystemClock import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.ClientBuilder import org.matrix.rustcomponents.sdk.Session @@ -30,6 +29,7 @@ import org.matrix.rustcomponents.sdk.SlidingSyncVersion import org.matrix.rustcomponents.sdk.SlidingSyncVersionBuilder import org.matrix.rustcomponents.sdk.use import timber.log.Timber +import uniffi.matrix_sdk_crypto.CollectStrategy import java.io.File import javax.inject.Inject @@ -44,14 +44,14 @@ class RustMatrixClientFactory @Inject constructor( private val proxyProvider: ProxyProvider, private val clock: SystemClock, private val utdTracker: UtdTracker, - private val appPreferencesStore: AppPreferencesStore, + private val featureFlagService: FeatureFlagService, ) { suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) { val sessionDelegate = RustClientSessionDelegate(sessionStore, appCoroutineScope, coroutineDispatchers) val client = getBaseClientBuilder( sessionPaths = sessionData.getSessionPaths(), passphrase = sessionData.passphrase, - restore = true, + slidingSyncType = ClientBuilderSlidingSync.Restored, ) .homeserverUrl(sessionData.homeserverUrl) .username(sessionData.userId) @@ -84,16 +84,8 @@ class RustMatrixClientFactory @Inject constructor( internal suspend fun getBaseClientBuilder( sessionPaths: SessionPaths, passphrase: String?, - restore: Boolean, + slidingSyncType: ClientBuilderSlidingSync, ): ClientBuilder { - val slidingSync = when { - // Always check restore first, since otherwise other values could accidentally override the already persisted config - restore -> ClientBuilderSlidingSync.Restored - AuthenticationConfig.SLIDING_SYNC_PROXY_URL != null -> ClientBuilderSlidingSync.CustomProxy(AuthenticationConfig.SLIDING_SYNC_PROXY_URL!!) - appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first() -> ClientBuilderSlidingSync.Simplified - else -> ClientBuilderSlidingSync.Discovered - } - return ClientBuilder() .sessionPaths( dataPath = sessionPaths.fileDirectory.absolutePath, @@ -104,11 +96,18 @@ class RustMatrixClientFactory @Inject constructor( .addRootCertificates(userCertificatesProvider.provides()) .autoEnableBackups(true) .autoEnableCrossSigning(true) + .roomKeyRecipientStrategy( + strategy = if (featureFlagService.isFeatureEnabled(FeatureFlags.InvisibleCrypto)) { + CollectStrategy.IdentityBasedStrategy + } else { + CollectStrategy.DeviceBasedStrategy(onlyAllowTrustedDevices = false, errorOnVerifiedUserProblem = true) + } + ) .run { // Apply sliding sync version settings - when (slidingSync) { + when (slidingSyncType) { ClientBuilderSlidingSync.Restored -> this - is ClientBuilderSlidingSync.CustomProxy -> slidingSyncVersionBuilder(SlidingSyncVersionBuilder.Proxy(slidingSync.url)) + is ClientBuilderSlidingSync.CustomProxy -> slidingSyncVersionBuilder(SlidingSyncVersionBuilder.Proxy(slidingSyncType.url)) ClientBuilderSlidingSync.Discovered -> slidingSyncVersionBuilder(SlidingSyncVersionBuilder.DiscoverProxy) ClientBuilderSlidingSync.Simplified -> slidingSyncVersionBuilder(SlidingSyncVersionBuilder.DiscoverNative) ClientBuilderSlidingSync.ForcedSimplified -> slidingSyncVersionBuilder(SlidingSyncVersionBuilder.Native) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index 344ae588a1..d1f0d29295 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.matrix.impl.auth import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.appconfig.AuthenticationConfig import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.di.AppScope @@ -16,9 +17,11 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.external.ExternalSession import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.impl.ClientBuilderSlidingSync import io.element.android.libraries.matrix.impl.RustMatrixClientFactory import io.element.android.libraries.matrix.impl.auth.qrlogin.QrErrorMapper import io.element.android.libraries.matrix.impl.auth.qrlogin.SdkQrCodeLoginData @@ -28,6 +31,7 @@ import io.element.android.libraries.matrix.impl.keys.PassphraseGenerator import io.element.android.libraries.matrix.impl.mapper.toSessionData import io.element.android.libraries.matrix.impl.paths.SessionPaths import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory +import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.sessionstorage.api.LoggedInState import io.element.android.libraries.sessionstorage.api.LoginType import io.element.android.libraries.sessionstorage.api.SessionStore @@ -35,9 +39,13 @@ import kotlinx.coroutines.CancellationException import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.Client +import org.matrix.rustcomponents.sdk.ClientBuilder import org.matrix.rustcomponents.sdk.HumanQrLoginException +import org.matrix.rustcomponents.sdk.OidcConfiguration +import org.matrix.rustcomponents.sdk.QrCodeData import org.matrix.rustcomponents.sdk.QrCodeDecodeException import org.matrix.rustcomponents.sdk.QrLoginProgress import org.matrix.rustcomponents.sdk.QrLoginProgressListener @@ -55,6 +63,7 @@ class RustMatrixAuthenticationService @Inject constructor( private val rustMatrixClientFactory: RustMatrixClientFactory, private val passphraseGenerator: PassphraseGenerator, private val oidcConfigurationProvider: OidcConfigurationProvider, + private val appPreferencesStore: AppPreferencesStore, ) : MatrixAuthenticationService { // Passphrase which will be used for new sessions. Existing sessions will use the passphrase // stored in the SessionData. @@ -117,9 +126,10 @@ class RustMatrixAuthenticationService @Inject constructor( withContext(coroutineDispatchers.io) { val emptySessionPath = rotateSessionPath() runCatching { - val client = getBaseClientBuilder(emptySessionPath) - .serverNameOrHomeserverUrl(homeserver) - .build() + val client = makeClient(sessionPaths = emptySessionPath) { + serverNameOrHomeserverUrl(homeserver) + } + currentClient = client val homeServerDetails = client.homeserverLoginDetails().map() currentHomeserver.value = homeServerDetails.copy(url = homeserver) @@ -151,6 +161,23 @@ class RustMatrixAuthenticationService @Inject constructor( } } + override suspend fun importCreatedSession(externalSession: ExternalSession): Result = + withContext(coroutineDispatchers.io) { + runCatching { + currentClient ?: error("You need to call `setHomeserver()` first") + val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first") + val sessionData = externalSession.toSessionData( + isTokenValid = true, + loginType = LoginType.PASSWORD, + passphrase = pendingPassphrase, + sessionPaths = currentSessionPaths, + ) + clear() + sessionStore.storeData(sessionData) + SessionId(sessionData.userId) + } + } + private var pendingOidcAuthorizationData: OidcAuthorizationData? = null override suspend fun getOidcUrl(): Result { @@ -207,23 +234,24 @@ class RustMatrixAuthenticationService @Inject constructor( override suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit) = withContext(coroutineDispatchers.io) { + val sdkQrCodeLoginData = (qrCodeData as SdkQrCodeLoginData).rustQrCodeData val emptySessionPaths = rotateSessionPath() + val oidcConfiguration = oidcConfigurationProvider.get() + val progressListener = object : QrLoginProgressListener { + override fun onUpdate(state: QrLoginProgress) { + Timber.d("QR Code login progress: $state") + progress(state.toStep()) + } + } runCatching { - val client = rustMatrixClientFactory.getBaseClientBuilder( + val client = makeQrCodeLoginClient( sessionPaths = emptySessionPaths, passphrase = pendingPassphrase, - restore = false, + qrCodeData = sdkQrCodeLoginData, + oidcConfiguration = oidcConfiguration, + progressListener = progressListener, ) - .buildWithQrCode( - qrCodeData = (qrCodeData as SdkQrCodeLoginData).rustQrCodeData, - oidcConfiguration = oidcConfigurationProvider.get(), - progressListener = object : QrLoginProgressListener { - override fun onUpdate(state: QrLoginProgress) { - Timber.d("QR Code login progress: $state") - progress(state.toStep()) - } - } - ) + client.use { rustClient -> val sessionData = rustClient.session() .toSessionData( @@ -249,14 +277,80 @@ class RustMatrixAuthenticationService @Inject constructor( } } - private suspend fun getBaseClientBuilder( + private suspend fun makeClient( sessionPaths: SessionPaths, - ) = rustMatrixClientFactory - .getBaseClientBuilder( - sessionPaths = sessionPaths, - passphrase = pendingPassphrase, - restore = false, - ) + config: suspend ClientBuilder.() -> ClientBuilder, + ): Client { + val slidingSyncType = getSlidingSyncType() + if (slidingSyncType is ClientBuilderSlidingSync.Simplified) { + Timber.d("Creating client with simplified sliding sync") + try { + return rustMatrixClientFactory + .getBaseClientBuilder( + sessionPaths = sessionPaths, + passphrase = pendingPassphrase, + slidingSyncType = slidingSyncType, + ) + .run { config() } + .build() + } catch (e: HumanQrLoginException.SlidingSyncNotAvailable) { + Timber.e(e, "Failed to create client with simplified sliding sync, trying with Proxy now") + } + } + Timber.d("Creating client with Proxy sliding sync") + return rustMatrixClientFactory + .getBaseClientBuilder( + sessionPaths = sessionPaths, + passphrase = pendingPassphrase, + slidingSyncType = getSlidingSyncProxy(), + ) + .run { config() } + .build() + } + + private suspend fun makeQrCodeLoginClient( + sessionPaths: SessionPaths, + passphrase: String?, + qrCodeData: QrCodeData, + oidcConfiguration: OidcConfiguration, + progressListener: QrLoginProgressListener, + ): Client { + val slidingSyncType = getSlidingSyncType() + if (slidingSyncType is ClientBuilderSlidingSync.Simplified) { + Timber.d("Creating client for QR Code login with simplified sliding sync") + try { + return rustMatrixClientFactory + .getBaseClientBuilder( + sessionPaths = sessionPaths, + passphrase = pendingPassphrase, + slidingSyncType = slidingSyncType, + ) + .passphrase(passphrase) + .buildWithQrCode(qrCodeData, oidcConfiguration, progressListener) + } catch (e: HumanQrLoginException.SlidingSyncNotAvailable) { + Timber.e(e, "Failed to create client with simplified sliding sync, trying with Proxy now") + } + } + Timber.d("Creating client for QR Code login with Proxy sliding sync") + return rustMatrixClientFactory + .getBaseClientBuilder( + sessionPaths = sessionPaths, + passphrase = pendingPassphrase, + slidingSyncType = getSlidingSyncProxy(), + ) + .passphrase(passphrase) + .buildWithQrCode(qrCodeData, oidcConfiguration, progressListener) + } + + private suspend fun getSlidingSyncType(nativeSlidingSyncFailed: Boolean = false) = when { + appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first() && !nativeSlidingSyncFailed -> ClientBuilderSlidingSync.Simplified + else -> getSlidingSyncProxy() + } + + private fun getSlidingSyncProxy() = when { + AuthenticationConfig.SLIDING_SYNC_PROXY_URL != null -> ClientBuilderSlidingSync.CustomProxy(AuthenticationConfig.SLIDING_SYNC_PROXY_URL!!) + else -> ClientBuilderSlidingSync.Discovered + } private fun clear() { currentClient?.close() diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt index 95bf823872..0f9bd5c87d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt @@ -7,6 +7,7 @@ package io.element.android.libraries.matrix.impl.mapper +import io.element.android.libraries.matrix.api.auth.external.ExternalSession import io.element.android.libraries.matrix.impl.paths.SessionPaths import io.element.android.libraries.sessionstorage.api.LoginType import io.element.android.libraries.sessionstorage.api.SessionData @@ -35,3 +36,24 @@ internal fun Session.toSessionData( sessionPath = sessionPaths.fileDirectory.absolutePath, cachePath = sessionPaths.cacheDirectory.absolutePath, ) + +internal fun ExternalSession.toSessionData( + isTokenValid: Boolean, + loginType: LoginType, + passphrase: String?, + sessionPaths: SessionPaths, +) = SessionData( + userId = userId, + deviceId = deviceId, + accessToken = accessToken, + refreshToken = refreshToken, + homeserverUrl = homeserverUrl, + oidcData = null, + slidingSyncProxy = slidingSyncProxy, + loginTimestamp = Date(), + isTokenValid = isTokenValid, + loginType = loginType, + passphrase = passphrase, + sessionPath = sessionPaths.fileDirectory.absolutePath, + cachePath = sessionPaths.cacheDirectory.absolutePath, +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt index d5a0b86083..7fcef163b6 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt @@ -28,6 +28,7 @@ class MatrixRoomInfoMapper { fun map(rustRoomInfo: RustRoomInfo): MatrixRoomInfo = rustRoomInfo.let { return MatrixRoomInfo( id = RoomId(it.id), + creator = it.creator?.let(::UserId), name = it.displayName, rawName = it.rawName, topic = it.topic, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustInvitedRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustInvitedRoom.kt new file mode 100644 index 0000000000..67e9e5e7a5 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustInvitedRoom.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.room + +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.room.InvitedRoom +import org.matrix.rustcomponents.sdk.Room + +class RustInvitedRoom( + override val sessionId: SessionId, + private val invitedRoom: Room, +) : InvitedRoom { + override val roomId = RoomId(invitedRoom.id()) + + override suspend fun declineInvite(): Result = runCatching { + invitedRoom.leave() + } + + override fun close() { + invitedRoom.destroy() + } +} 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 aad3777071..b871062f70 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 @@ -459,8 +459,8 @@ class RustMatrixRoom( return liveTimeline.forwardEvent(eventId, roomIds) } - override suspend fun retrySendMessage(transactionId: TransactionId): Result { - return Result.failure(UnsupportedOperationException("Not supported")) + override suspend fun retrySendMessage(transactionId: TransactionId): Result = runCatching { + innerRoom.tryResend(transactionId.value) } override suspend fun cancelSend(transactionId: TransactionId): Result { @@ -645,6 +645,22 @@ class RustMatrixRoom( innerRoom.clearComposerDraft() } + override suspend fun ignoreDeviceTrustAndResend(devices: Map>, transactionId: TransactionId) = runCatching { + innerRoom.ignoreDeviceTrustAndResend( + devices = devices.entries.associate { entry -> + entry.key.value to entry.value.map { it.value } + }, + transactionId = transactionId.value + ) + } + + override suspend fun withdrawVerificationAndResend(userIds: List, transactionId: TransactionId) = runCatching { + innerRoom.withdrawVerificationAndResend( + userIds = userIds.map { it.value }, + transactionId = transactionId.value + ) + } + private fun createTimeline( timeline: InnerTimeline, mode: Timeline.Mode, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt index 04210d6959..a440578513 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt @@ -14,6 +14,7 @@ import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService +import io.element.android.libraries.matrix.api.room.InvitedRoom import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.awaitLoaded @@ -27,6 +28,7 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.FilterTimelineEventType +import org.matrix.rustcomponents.sdk.Membership import org.matrix.rustcomponents.sdk.Room import org.matrix.rustcomponents.sdk.RoomListException import org.matrix.rustcomponents.sdk.RoomListItem @@ -123,6 +125,33 @@ class RustRoomFactory( } } + suspend fun createInvitedRoom(roomId: RoomId): InvitedRoom? = withContext(dispatcher) { + if (isDestroyed) { + Timber.d("Room factory is destroyed, returning null for $roomId") + return@withContext null + } + val roomListItem = innerRoomListService.roomOrNull(roomId.value) + if (roomListItem == null) { + Timber.d("Room not found for $roomId") + return@withContext null + } + if (roomListItem.membership() != Membership.INVITED) { + Timber.d("Room $roomId is not in invited state") + return@withContext null + } + val invitedRoom = try { + roomListItem.invitedRoom() + } catch (e: RoomListException) { + Timber.e(e, "Failed to get invited room for $roomId") + return@withContext null + } + + RustInvitedRoom( + sessionId = sessionId, + invitedRoom = invitedRoom, + ) + } + private suspend fun getRoomReferences(roomId: RoomId): RustRoomReferences? { cache[roomId]?.let { Timber.d("Room found in cache for $roomId") diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt index 25d7c52c7b..1fa43c4e7c 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt @@ -86,7 +86,7 @@ class RustTimeline( onNewSyncedEvent: () -> Unit, ) : Timeline { private val initLatch = CompletableDeferred() - private val isInit = MutableStateFlow(false) + private val isTimelineInitialized = MutableStateFlow(false) private val _timelineItems: MutableStateFlow> = MutableStateFlow(emptyList()) @@ -110,7 +110,7 @@ class RustTimeline( timelineCoroutineScope = coroutineScope, timelineDiffProcessor = timelineDiffProcessor, initLatch = initLatch, - isInit = isInit, + isTimelineInitialized = isTimelineInitialized, dispatcher = dispatcher, onNewSyncedEvent = onNewSyncedEvent, ) @@ -189,7 +189,7 @@ class RustTimeline( } private fun canPaginate(direction: Timeline.PaginationDirection): Boolean { - if (!isInit.value) return false + if (!isTimelineInitialized.value) return false return when (direction) { Timeline.PaginationDirection.BACKWARDS -> backPaginationStatus.value.canPaginate Timeline.PaginationDirection.FORWARDS -> forwardPaginationStatus.value.canPaginate @@ -207,23 +207,37 @@ class RustTimeline( _timelineItems, backPaginationStatus.map { it.hasMoreToLoad }.distinctUntilChanged(), forwardPaginationStatus.map { it.hasMoreToLoad }.distinctUntilChanged(), - isInit, - ) { timelineItems, hasMoreToLoadBackward, hasMoreToLoadForward, isInit -> + matrixRoom.roomInfoFlow.map { it.creator }, + isTimelineInitialized, + ) { timelineItems, + hasMoreToLoadBackward, + hasMoreToLoadForward, + roomCreator, + isTimelineInitialized -> withContext(dispatcher) { timelineItems - .process { items -> + .let { items -> roomBeginningPostProcessor.process( items = items, isDm = matrixRoom.isDm, - hasMoreToLoadBackwards = hasMoreToLoadBackward + roomCreator = roomCreator, + hasMoreToLoadBackwards = hasMoreToLoadBackward, ) } - .process(predicate = isInit) { items -> - loadingIndicatorsPostProcessor.process(items, hasMoreToLoadBackward, hasMoreToLoadForward) + .let { items -> + loadingIndicatorsPostProcessor.process( + items = items, + isTimelineInitialized = isTimelineInitialized, + hasMoreToLoadBackward = hasMoreToLoadBackward, + hasMoreToLoadForward = hasMoreToLoadForward + ) } // Keep lastForwardIndicatorsPostProcessor last - .process(predicate = isInit) { items -> - lastForwardIndicatorsPostProcessor.process(items) + .let { items -> + lastForwardIndicatorsPostProcessor.process( + items = items, + isTimelineInitialized = isTimelineInitialized, + ) } } }.onStart { @@ -542,14 +556,3 @@ class RustTimeline( } } } - -private suspend fun List.process( - predicate: Boolean = true, - processor: suspend (List) -> List -): List { - return if (predicate) { - processor(this) - } else { - this - } -} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/TimelineItemsSubscriber.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/TimelineItemsSubscriber.kt index 0269c48bae..5e9c6ac37a 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/TimelineItemsSubscriber.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/TimelineItemsSubscriber.kt @@ -38,7 +38,7 @@ internal class TimelineItemsSubscriber( private val timeline: Timeline, private val timelineDiffProcessor: MatrixTimelineDiffProcessor, private val initLatch: CompletableDeferred, - private val isInit: MutableStateFlow, + private val isTimelineInitialized: MutableStateFlow, private val onNewSyncedEvent: () -> Unit, ) { private var subscriptionCount = 0 @@ -85,13 +85,13 @@ internal class TimelineItemsSubscriber( ensureActive() timelineDiffProcessor.postItems(it) } - isInit.value = true + isTimelineInitialized.value = true initLatch.complete(Unit) } private suspend fun postDiffs(diffs: List) { val diffsToProcess = diffs.toMutableList() - if (!isInit.value) { + if (!isTimelineInitialized.value) { val resetDiff = diffsToProcess.firstOrNull { it.change() == TimelineChange.RESET } if (resetDiff != null) { // Keep using the postItems logic so we can post the timelineItems asap. diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt index 5f170c9387..f83d6d0672 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt @@ -7,6 +7,7 @@ package io.element.android.libraries.matrix.impl.timeline.item.event +import io.element.android.libraries.matrix.api.core.DeviceId 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.core.UserId @@ -87,7 +88,9 @@ fun RustEventSendState?.map(): LocalEventSendState? { } is RustEventSendState.VerifiedUserHasUnsignedDevice -> { LocalEventSendState.Failed.VerifiedUserHasUnsignedDevice( - devices = devices.mapKeys { UserId(it.key) } + devices = devices.entries.associate { entry -> + UserId(entry.key) to entry.value.map { DeviceId(it) } + } ) } EventSendState.CrossSigningNotSetup -> LocalEventSendState.Failed.CrossSigningNotSetup diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/LastForwardIndicatorsPostProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/LastForwardIndicatorsPostProcessor.kt index 193682f81b..7f89c40a0e 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/LastForwardIndicatorsPostProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/LastForwardIndicatorsPostProcessor.kt @@ -22,7 +22,9 @@ class LastForwardIndicatorsPostProcessor( fun process( items: List, + isTimelineInitialized: Boolean, ): List { + if (!isTimelineInitialized) return items // We don't need to add the last forward indicator if we are not in the FOCUSED_ON_EVENT mode if (mode != Timeline.Mode.FOCUSED_ON_EVENT) { return items diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/LoadingIndicatorsPostProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/LoadingIndicatorsPostProcessor.kt index a4aa6bf05a..216ccd6f89 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/LoadingIndicatorsPostProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/LoadingIndicatorsPostProcessor.kt @@ -16,9 +16,11 @@ import io.element.android.services.toolbox.api.systemclock.SystemClock class LoadingIndicatorsPostProcessor(private val systemClock: SystemClock) { fun process( items: List, + isTimelineInitialized: Boolean, hasMoreToLoadBackward: Boolean, hasMoreToLoadForward: Boolean, ): List { + if (!isTimelineInitialized) return items val shouldAddForwardLoadingIndicator = hasMoreToLoadForward && items.isNotEmpty() val currentTimestamp = systemClock.epochMillis() return buildList { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt index 2fb41a745c..7ed9de2fe3 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt @@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.impl.timeline.postprocessor import androidx.annotation.VisibleForTesting import io.element.android.libraries.matrix.api.core.UniqueId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange @@ -25,12 +26,14 @@ class RoomBeginningPostProcessor(private val mode: Timeline.Mode) { fun process( items: List, isDm: Boolean, - hasMoreToLoadBackwards: Boolean + roomCreator: UserId?, + hasMoreToLoadBackwards: Boolean, ): List { return when { + items.isEmpty() -> items mode == Timeline.Mode.PINNED_EVENTS -> items + isDm -> processForDM(items, roomCreator) hasMoreToLoadBackwards -> items - isDm -> processForDM(items) else -> processForRoom(items) } } @@ -40,15 +43,18 @@ class RoomBeginningPostProcessor(private val mode: Timeline.Mode) { return listOf(roomBeginningItem) + items } - private fun processForDM(items: List): List { - // Find room creation event. This is usually index 0 + private fun processForDM(items: List, roomCreator: UserId?): List { + // Find room creation event. + // This is usually the first MatrixTimelineItem.Event (so index 1, index 0 is a date) val roomCreationEventIndex = items.indexOfFirst { val stateEventContent = (it as? MatrixTimelineItem.Event)?.event?.content as? StateContent stateEventContent?.content is OtherState.RoomCreate } - // Find self-join event for room creator. This is usually index 1 - val roomCreatorUserId = (items.getOrNull(roomCreationEventIndex) as? MatrixTimelineItem.Event)?.event?.sender + // If the parameter roomCreator is null, the creator is the sender of the RoomCreate Event. + val roomCreatorUserId = roomCreator ?: (items.getOrNull(roomCreationEventIndex) as? MatrixTimelineItem.Event)?.event?.sender + // Find self-join event for the room creator. + // This is usually the second MatrixTimelineItem.Event (so index 2) val selfUserJoinedEventIndex = roomCreatorUserId?.let { creatorUserId -> items.indexOfFirst { val stateEventContent = (it as? MatrixTimelineItem.Event)?.event?.content as? RoomMembershipContent @@ -56,6 +62,9 @@ class RoomBeginningPostProcessor(private val mode: Timeline.Mode) { } } ?: -1 + if (roomCreationEventIndex == -1 && selfUserJoinedEventIndex == -1) { + return items + } // Remove items at the indices we found val newItems = items.toMutableList() if (selfUserJoinedEventIndex in newItems.indices) { diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/call/DefaultElementCallBaseUrlProviderTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/call/DefaultElementCallBaseUrlProviderTest.kt index 03174d5359..a5333d3c1f 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/call/DefaultElementCallBaseUrlProviderTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/call/DefaultElementCallBaseUrlProviderTest.kt @@ -91,8 +91,9 @@ class DefaultElementCallBaseUrlProviderTest { private fun createElementWellKnown(widgetUrl: String): ElementWellKnown { return ElementWellKnown( call = ElementCallWellKnown( - widgetUrl = widgetUrl - ) + widgetUrl = widgetUrl, + ), + registrationHelperUrl = null, ) } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTest.kt index f4b7f8a1d0..faa8a1102e 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTest.kt @@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.impl.roomlist import com.google.common.truth.Truth.assertThat import com.sun.jna.Pointer import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID_2 @@ -215,6 +216,7 @@ private fun aRustRoomInfo( numUnreadNotifications: ULong = 0uL, numUnreadMentions: ULong = 0uL, pinnedEventIds: List = listOf(), + roomCreator: UserId? = null, ) = RoomInfo( id = id, displayName = displayName, @@ -245,6 +247,7 @@ private fun aRustRoomInfo( numUnreadNotifications = numUnreadNotifications, numUnreadMentions = numUnreadMentions, pinnedEventIds = pinnedEventIds, + creator = roomCreator?.value, ) class FakeRoomListItem( diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt index db93b86142..fadc174cd1 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt @@ -22,85 +22,174 @@ import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import org.junit.Test class RoomBeginningPostProcessorTest { + private val roomCreateEvent = MatrixTimelineItem.Event( + uniqueId = UniqueId("m.room.create"), + event = anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate)) + ) + private val roomCreatorJoinEvent = MatrixTimelineItem.Event( + uniqueId = UniqueId("m.room.member"), + event = anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED)) + ) + private val otherMemberJoinEvent = MatrixTimelineItem.Event( + uniqueId = UniqueId("m.room.member_other"), + event = anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, null, MembershipChange.JOINED)) + ) + private val messageEvent = MatrixTimelineItem.Event( + uniqueId = UniqueId("m.room.message"), + event = anEventTimelineItem(content = aMessageContent("hi")) + ) + + @Test + fun `processor returns empty list when empty list is provided`() { + val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) + val processedItems = processor.process( + items = emptyList(), + isDm = true, + roomCreator = A_USER_ID, + hasMoreToLoadBackwards = false, + ) + assertThat(processedItems).isEmpty() + } + + @Test + fun `processor returns the provided list when it only contains a message`() { + val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) + val processedItems = processor.process( + items = listOf(messageEvent), + isDm = true, + roomCreator = A_USER_ID, + hasMoreToLoadBackwards = false, + ) + assertThat(processedItems).isEqualTo(listOf(messageEvent)) + } + + @Test + fun `processor returns the provided list when it only contains a message and the roomCreator is not provided`() { + val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) + val processedItems = processor.process( + items = listOf(messageEvent), + isDm = true, + roomCreator = null, + hasMoreToLoadBackwards = false, + ) + assertThat(processedItems).isEqualTo(listOf(messageEvent)) + } + @Test fun `processor removes room creation event and self-join event from DM timeline`() { val timelineItems = listOf( - MatrixTimelineItem.Event(UniqueId("m.room.create"), anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))), - MatrixTimelineItem.Event(UniqueId("m.room.member"), anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), + roomCreateEvent, + roomCreatorJoinEvent, ) val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) - val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = false) + val processedItems = processor.process( + items = timelineItems, + isDm = true, + roomCreator = A_USER_ID, + hasMoreToLoadBackwards = false, + ) assertThat(processedItems).isEmpty() } + @Test + fun `processor does not remove anything with PINNED_EVENTS mode`() { + val timelineItems = listOf( + roomCreateEvent, + roomCreatorJoinEvent, + ) + val processor = RoomBeginningPostProcessor(Timeline.Mode.PINNED_EVENTS) + val processedItems = processor.process( + items = timelineItems, + isDm = true, + roomCreator = A_USER_ID, + hasMoreToLoadBackwards = false, + ) + assertThat(processedItems).isEqualTo(timelineItems) + } + @Test fun `processor removes room creation event and self-join event from DM timeline even if they're not the first items`() { val timelineItems = listOf( - MatrixTimelineItem.Event( - UniqueId("m.room.member_other"), - anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, null, MembershipChange.JOINED)) - ), - MatrixTimelineItem.Event(UniqueId("m.room.create"), anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))), - MatrixTimelineItem.Event(UniqueId("m.room.message"), anEventTimelineItem(content = aMessageContent("hi"))), - MatrixTimelineItem.Event(UniqueId("m.room.member"), anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), + otherMemberJoinEvent, + roomCreateEvent, + messageEvent, + roomCreatorJoinEvent, ) val expected = listOf( - MatrixTimelineItem.Event( - UniqueId("m.room.member_other"), - anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, null, MembershipChange.JOINED)) - ), - MatrixTimelineItem.Event(UniqueId("m.room.message"), anEventTimelineItem(content = aMessageContent("hi"))), + otherMemberJoinEvent, + messageEvent, ) val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) - val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = false) + val processedItems = processor.process(timelineItems, isDm = true, roomCreator = A_USER_ID, hasMoreToLoadBackwards = false) assertThat(processedItems).isEqualTo(expected) } @Test fun `processor will add beginning of room item if it's not a DM`() { val timelineItems = listOf( - MatrixTimelineItem.Event(UniqueId("m.room.create"), anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))), - MatrixTimelineItem.Event(UniqueId("m.room.member"), anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), + roomCreateEvent, + roomCreatorJoinEvent, ) val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) - val processedItems = processor.process(timelineItems, isDm = false, hasMoreToLoadBackwards = false) + val processedItems = processor.process(timelineItems, isDm = false, roomCreator = A_USER_ID, hasMoreToLoadBackwards = false) assertThat(processedItems).isEqualTo( listOf(processor.createRoomBeginningItem()) + timelineItems ) } @Test - fun `processor won't remove items if it's not at the start of the timeline`() { + fun `processor will not add beginning of room item if it's not a DM but the room has more to load`() { val timelineItems = listOf( - MatrixTimelineItem.Event(UniqueId("m.room.create"), anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))), - MatrixTimelineItem.Event(UniqueId("m.room.member"), anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), + roomCreateEvent, + roomCreatorJoinEvent, ) val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) - val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = true) + val processedItems = processor.process(timelineItems, isDm = false, roomCreator = A_USER_ID, hasMoreToLoadBackwards = true) assertThat(processedItems).isEqualTo(timelineItems) } @Test - fun `processor won't remove the first member join event if it can't find the room creation event`() { + fun `processor will add beginning of room item if it's not a DM, when the parameter roomCreator is null`() { val timelineItems = listOf( - MatrixTimelineItem.Event(UniqueId("m.room.member"), anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), + roomCreateEvent, + roomCreatorJoinEvent, ) val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) - val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = true) - assertThat(processedItems).isEqualTo(timelineItems) + val processedItems = processor.process(timelineItems, isDm = false, roomCreator = null, hasMoreToLoadBackwards = false) + assertThat(processedItems).isEqualTo( + listOf(processor.createRoomBeginningItem()) + timelineItems + ) + } + + @Test + fun `processor removes items event it's not at the start of the timeline`() { + val timelineItems = listOf( + roomCreateEvent, + roomCreatorJoinEvent, + ) + val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) + val processedItems = processor.process(timelineItems, isDm = true, roomCreator = A_USER_ID, hasMoreToLoadBackwards = true) + assertThat(processedItems).isEmpty() + } + + @Test + fun `processor removes the first member join event if it matches the roomCreator parameter`() { + val timelineItems = listOf( + roomCreatorJoinEvent, + ) + val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) + val processedItems = processor.process(timelineItems, isDm = true, roomCreator = A_USER_ID, hasMoreToLoadBackwards = true) + assertThat(processedItems).isEmpty() } @Test fun `processor won't remove the first member join event if it's not from the room creator`() { val timelineItems = listOf( - MatrixTimelineItem.Event(UniqueId("m.room.create"), anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))), - MatrixTimelineItem.Event( - UniqueId("m.room.member"), - anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, null, MembershipChange.JOINED)) - ), + roomCreateEvent, + otherMemberJoinEvent, ) val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) - val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = true) - assertThat(processedItems).isEqualTo(timelineItems) + val processedItems = processor.process(timelineItems, isDm = true, roomCreator = A_USER_ID, hasMoreToLoadBackwards = true) + assertThat(processedItems).isEqualTo(listOf(otherMemberJoinEvent)) } } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index ed484f4426..908cb443ea 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -22,6 +22,7 @@ import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.oidc.AccountManagementAction import io.element.android.libraries.matrix.api.pusher.PushersService +import io.element.android.libraries.matrix.api.room.InvitedRoom import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.RoomMembershipObserver @@ -79,6 +80,7 @@ class FakeMatrixClient( private val userIdServerNameLambda: () -> String = { lambdaError() }, private val getUrlLambda: (String) -> Result = { lambdaError() }, var isNativeSlidingSyncSupportedLambda: suspend () -> Boolean = { true }, + var isSlidingSyncProxySupportedLambda: suspend () -> Boolean = { true }, var isUsingNativeSlidingSyncLambda: () -> Boolean = { true }, ) : MatrixClient { var setDisplayNameCalled: Boolean = false @@ -98,6 +100,7 @@ class FakeMatrixClient( private var createDmResult: Result = Result.success(A_ROOM_ID) private var findDmResult: RoomId? = A_ROOM_ID private val getRoomResults = mutableMapOf() + val getInvitedRoomResults = mutableMapOf() private val searchUserResults = mutableMapOf>() private val getProfileResults = mutableMapOf>() private var uploadMediaResult: Result = Result.success(AN_AVATAR_URL) @@ -124,6 +127,10 @@ class FakeMatrixClient( return getRoomResults[roomId] } + override suspend fun getInvitedRoom(roomId: RoomId): InvitedRoom? { + return getInvitedRoomResults[roomId] + } + override suspend fun findDM(userId: UserId): RoomId? { return findDmResult } @@ -324,6 +331,10 @@ class FakeMatrixClient( return isNativeSlidingSyncSupportedLambda() } + override suspend fun isSlidingSyncProxySupported(): Boolean { + return isSlidingSyncProxySupportedLambda() + } + override fun isUsingNativeSlidingSync(): Boolean { return isUsingNativeSlidingSyncLambda() } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt index 6f5ffa3928..8c18629817 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt @@ -11,12 +11,14 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.external.ExternalSession import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep import io.element.android.libraries.matrix.api.core.SessionId 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.sessionstorage.api.LoggedInState +import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.simulateLongTask import kotlinx.coroutines.flow.Flow @@ -30,6 +32,7 @@ class FakeMatrixAuthenticationService( var matrixClientResult: ((SessionId) -> Result)? = null, var loginWithQrCodeResult: (qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit) -> Result = lambdaRecorder Unit, Result> { _, _ -> Result.success(A_SESSION_ID) }, + private val importCreatedSessionLambda: (ExternalSession) -> Result = { lambdaError() } ) : MatrixAuthenticationService { private val homeserver = MutableStateFlow(null) private var oidcError: Throwable? = null @@ -73,6 +76,10 @@ class FakeMatrixAuthenticationService( loginError?.let { Result.failure(it) } ?: Result.success(A_USER_ID) } + override suspend fun importCreatedSession(externalSession: ExternalSession): Result = simulateLongTask { + return importCreatedSessionLambda(externalSession) + } + override suspend fun getOidcUrl(): Result = simulateLongTask { oidcError?.let { Result.failure(it) } ?: Result.success(A_OIDC_DATA) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt new file mode 100644 index 0000000000..3224d1fd2f --- /dev/null +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.matrix.test.room + +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.room.InvitedRoom +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeInvitedRoom( + override val sessionId: SessionId = A_SESSION_ID, + override val roomId: RoomId = A_ROOM_ID, + private val declineInviteResult: () -> Result = { lambdaError() } +) : InvitedRoom { + override suspend fun declineInvite(): Result { + return declineInviteResult() + } + + override fun close() = Unit +} 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 8418d0c047..54b52c6fb7 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 @@ -7,6 +7,7 @@ package io.element.android.libraries.matrix.test.room +import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.core.RoomAlias @@ -79,7 +80,7 @@ class FakeMatrixRoom( private var roomPermalinkResult: () -> Result = { lambdaError() }, private var eventPermalinkResult: (EventId) -> Result = { lambdaError() }, private val sendCallNotificationIfNeededResult: () -> Result = { lambdaError() }, - private val userDisplayNameResult: () -> Result = { lambdaError() }, + private val userDisplayNameResult: (UserId) -> Result = { lambdaError() }, private val userAvatarUrlResult: () -> Result = { lambdaError() }, private val userRoleResult: () -> Result = { lambdaError() }, private val getUpdatedMemberResult: (UserId) -> Result = { lambdaError() }, @@ -134,7 +135,9 @@ class FakeMatrixRoom( private val loadComposerDraftLambda: () -> Result = { Result.success(null) }, private val clearComposerDraftLambda: () -> Result = { Result.success(Unit) }, private val subscribeToSyncLambda: () -> Unit = { lambdaError() }, -) : MatrixRoom { + private val ignoreDeviceTrustAndResendResult: (Map>, TransactionId) -> Result = { _, _ -> lambdaError() }, + private val withdrawVerificationAndResendResult: (List, TransactionId) -> Result = { _, _ -> lambdaError() }, + ) : MatrixRoom { private val _roomInfoFlow: MutableSharedFlow = MutableSharedFlow(replay = 1) override val roomInfoFlow: Flow = _roomInfoFlow @@ -199,7 +202,7 @@ class FakeMatrixRoom( override fun destroy() = Unit override suspend fun userDisplayName(userId: UserId): Result = simulateLongTask { - userDisplayNameResult() + userDisplayNameResult(userId) } override suspend fun userAvatarUrl(userId: UserId): Result = simulateLongTask { @@ -226,7 +229,7 @@ class FakeMatrixRoom( return toggleReactionResult(emoji, uniqueId) } - override suspend fun retrySendMessage(transactionId: TransactionId): Result { + override suspend fun retrySendMessage(transactionId: TransactionId): Result = simulateLongTask { return retrySendMessageResult(transactionId) } @@ -492,6 +495,14 @@ class FakeMatrixRoom( return getWidgetDriverResult(widgetSettings) } + override suspend fun ignoreDeviceTrustAndResend(devices: Map>, transactionId: TransactionId): Result = simulateLongTask { + return ignoreDeviceTrustAndResendResult(devices, transactionId) + } + + override suspend fun withdrawVerificationAndResend(userIds: List, transactionId: TransactionId): Result = simulateLongTask { + return withdrawVerificationAndResendResult(userIds, transactionId) + } + fun givenRoomMembersState(state: MatrixRoomMembersState) { membersStateFlow.value = state } @@ -523,6 +534,7 @@ fun aRoomInfo( activeRoomCallParticipants: List = emptyList(), heroes: List = emptyList(), pinnedEventIds: List = emptyList(), + roomCreator: UserId? = null, ) = MatrixRoomInfo( id = id, name = name, @@ -549,6 +561,7 @@ fun aRoomInfo( activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(), heroes = heroes.toImmutableList(), pinnedEventIds = pinnedEventIds.toImmutableList(), + creator = roomCreator, ) fun defaultRoomPowerLevels() = MatrixRoomPowerLevels( diff --git a/libraries/permissions/impl/src/main/res/values-nl/translations.xml b/libraries/permissions/impl/src/main/res/values-nl/translations.xml new file mode 100644 index 0000000000..b23c075bae --- /dev/null +++ b/libraries/permissions/impl/src/main/res/values-nl/translations.xml @@ -0,0 +1,5 @@ + + + "Controleren of de applicatie meldingen kan weergeven." + "Controleer machtigingen" + diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt index 8e09547fb4..ab985e798d 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt @@ -87,7 +87,7 @@ class DefaultAppPreferencesStore @Inject constructor( override fun isSimplifiedSlidingSyncEnabledFlow(): Flow { return store.data.map { prefs -> - prefs[simplifiedSlidingSyncKey] ?: false + prefs[simplifiedSlidingSyncKey] ?: true } } 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 35924e6247..fc5edbf1d9 100644 --- a/libraries/push/impl/src/main/res/values-de/translations.xml +++ b/libraries/push/impl/src/main/res/values-de/translations.xml @@ -34,6 +34,7 @@ "Schnelle Antwort" "Du wurdest eingeladen, den Raum zu betreten" "Ich" + "%1$s hat Dich erwähnt oder geantwortet" "Du siehst dir die Benachrichtigung an! Klicke hier!" "%1$s: %2$s" "%1$s: %2$s %3$s" diff --git a/libraries/push/impl/src/main/res/values-el/translations.xml b/libraries/push/impl/src/main/res/values-el/translations.xml index 081adb46b8..b008447c80 100644 --- a/libraries/push/impl/src/main/res/values-el/translations.xml +++ b/libraries/push/impl/src/main/res/values-el/translations.xml @@ -34,6 +34,7 @@ "Γρήγορη απάντηση" "Σέ προσκάλεσε να συμμετάσχεις στο δωμάτιο" "Εγώ" + "Ο χρήστης %1$s αναφέρθηκε ή απάντησε" "Βλέπεις την ειδοποίηση! Κάνε μου κλικ!" "%1$s: %2$s" "%1$s: %2$s %3$s" diff --git a/libraries/push/impl/src/main/res/values-nl/translations.xml b/libraries/push/impl/src/main/res/values-nl/translations.xml index d9ed0161ed..425973551a 100644 --- a/libraries/push/impl/src/main/res/values-nl/translations.xml +++ b/libraries/push/impl/src/main/res/values-nl/translations.xml @@ -49,4 +49,28 @@ "Achtergrondsynchronisatie" "Google-services" "Geen geldige Google Play-services gevonden. Meldingen werken mogelijk niet goed." + "Naam van de huidige provider aan het ophalen." + "Er zijn geen push-providers geselecteerd." + "Huidige push-provider: %1$s." + "Huidige push-provider" + "Zorg ervoor dat de applicatie minimaal één push-provider heeft." + "Geen push-providers gevonden." + + "%1$d push-provider gevonden: %2$s" + "%1$d push-providers gevonden: %2$s" + + "Push-providers detecteren" + "Controleer of de applicatie een melding kan weergeven." + "Er is niet op de melding geklikt." + "Kan de melding niet weergeven." + "Er is op de melding geklikt!" + "Melding weergeven" + "Klik op de melding om verder te gaan met de test." + "Ervoor zorgen dat de applicatie pushberichten ontvangt." + "Fout: pusher heeft het verzoek afgewezen." + "Fout: %1$s." + "Fout, kan push niet testen." + "Fout, time-out tijdens het wachten op push." + "Push terugkoppeling duurde %1$d ms." + "Test Push terugkoppeling" diff --git a/libraries/pushproviders/firebase/src/main/res/values-nl/translations.xml b/libraries/pushproviders/firebase/src/main/res/values-nl/translations.xml new file mode 100644 index 0000000000..d847c39901 --- /dev/null +++ b/libraries/pushproviders/firebase/src/main/res/values-nl/translations.xml @@ -0,0 +1,11 @@ + + + "Zorg ervoor dat Firebase beschikbaar is." + "Firebase is niet beschikbaar." + "Firebase is beschikbaar." + "Firebase controleren" + "Zorg ervoor dat de Firebase-token beschikbaar is." + "Firebase-token is niet bekend." + "Firebase-token: %1$s." + "Firebase-token controleren" + diff --git a/libraries/pushproviders/unifiedpush/src/main/res/values-nl/translations.xml b/libraries/pushproviders/unifiedpush/src/main/res/values-nl/translations.xml new file mode 100644 index 0000000000..afe222b147 --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/main/res/values-nl/translations.xml @@ -0,0 +1,10 @@ + + + "Ervoor zorgen dat UnifiedPush verdelers beschikbaar zijn." + "Geen push-verdelers gevonden." + + "%1$d verdeler gevonden: %2$s." + "%1$d verdelers gevonden: %2$s." + + "UnifiedPush controleren" + diff --git a/libraries/troubleshoot/impl/src/main/res/values-nl/translations.xml b/libraries/troubleshoot/impl/src/main/res/values-nl/translations.xml new file mode 100644 index 0000000000..5ce2bd89ad --- /dev/null +++ b/libraries/troubleshoot/impl/src/main/res/values-nl/translations.xml @@ -0,0 +1,11 @@ + + + "Tests uitvoeren" + "Tests opnieuw uitvoeren" + "Sommige tests zijn mislukt. Controleer de details." + "Voer de tests uit om problemen in je configuratie op te sporen waardoor meldingen mogelijk niet naar verwachting werken." + "Probeer het op te lossen" + "Alle tests zijn geslaagd." + "Problemen met meldingen oplossen" + "Sommige tests vereisen je aandacht. Controleer de details." + 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 6fcecc495b..164aed50f3 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -38,6 +38,7 @@ "Zpět" "Hovor" "Zrušit" + "Prozatím zrušit" "Vybrat fotku" "Vymazat" "Zavřít" @@ -288,6 +289,12 @@ Důvod: %1$s." "Připnuté zprávy" "Chystáte se přejít na svůj %1$s účet a obnovit svou identitu. Poté budete přesměrováni zpět do aplikace." "Nemůžete to potvrdit? Přejděte na svůj účet a resetujte svou identitu." + "Zrušit ověření a odeslat" + "Ověření můžete zrušit a přesto odeslat tuto zprávu, nebo můžete prozatím zrušit a zkusit to znovu později po opětovném ověření %1$s." + "Vaše zpráva nebyla odeslána, protože ověřená identita uživatele %1$s se změnila" + "Přesto odeslat zprávu" + "%1$s používá jedno nebo více neověřených zařízení. Zprávu můžete přesto odeslat, nebo můžete prozatím zrušit a zkusit to znovu později poté, co %2$s ověří všechna svá zařízení." + "Vaše zpráva nebyla odeslána, protože%1$s neověřil jedno nebo více zařízení" "Připnuté zprávy" "Nahrání média se nezdařilo, zkuste to prosím znovu." "Nepodařilo se načíst údaje o uživateli" @@ -309,6 +316,8 @@ Důvod: %1$s." "Otevřít v Mapách Google" "Otevřít v OpenStreetMap" "Sdílet tuto polohu" + "Zpráva nebyla odeslána, protože ověřená identita uživatele %1$s se změnila." + "Zpráva nebyla odeslána, protože%1$s neověřil jedno nebo více zařízení." "Poloha" "Verze: %1$s (%2$s)" "en" 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 dcd41ca7a6..13bd0b7d93 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -36,6 +36,7 @@ "Zurück" "Anruf" "Abbrechen" + "Jetzt abbrechen" "Foto auswählen" "Löschen" "Schließen" @@ -80,6 +81,7 @@ "Ok" "Einstellungen öffnen" "Öffnen mit" + "Fixieren" "Schnelle Antwort" "Zitat" "Reagieren" @@ -109,7 +111,8 @@ "Foto aufnehmen" "Für Optionen tippen" "Erneut versuchen" - "Entpinnen" + "Lösen" + "Im Chatverlauf anzeigen" "Quellcode anzeigen" "Ja" "Über" @@ -175,6 +178,7 @@ Grund: %1$s." "Personen" "Permalink" "Erlaubnis" + "Fixiert" "Bitte warten …" "Bist du sicher, dass du diese Umfrage beenden möchtest?" "Umfrage: %1$s" @@ -246,6 +250,7 @@ Grund: %1$s." "Warnung" "Deine Änderungen wurden nicht gespeichert. Bist du sicher, dass du zurückgehen willst?" "Änderungen speichern?" + "Dein Homeserver muss aktualisiert werden, um den Matrix Authentication Services und die Erstellung von Konten zu unterstützen." "Fehler beim Erstellen des Permalinks" "%1$s konnte die Karte nicht laden. Bitte versuche es später erneut." "Fehler beim Laden der Nachrichten" @@ -258,6 +263,8 @@ Grund: %1$s." "Einige Nachrichten wurden nicht gesendet" "Entschuldigung, es ist ein Fehler aufgetreten" "Die Authentizität dieser verschlüsselten Nachricht kann auf diesem Gerät nicht garantiert werden." + "Verschlüsselt von einem zuvor verifizierten Nutzer." + "Unverschlüsselt." "Verschlüsselt von einem unbekannten oder gelöschten Gerät." "Verschlüsselt durch ein Gerät, das nicht von seinem Besitzer verifiziert wurde." "Verschlüsselt durch einen nicht verifizierten Benutzer." @@ -268,6 +275,22 @@ Grund: %1$s." "Medienauswahl fehlgeschlagen, bitte versuche es erneut." "Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut." "Das Hochladen der Medien ist fehlgeschlagen. Bitte versuche es erneut." + "Drücke auf eine Nachricht und wähle “%1$s”, um sie hier einzufügen." + "Fixiere wichtige Nachrichten, so dass sie leicht gefunden werden können" + + "%1$d fixierte Nachricht" + "%1$d fixierte Nachrichten" + + "Fixierte Nachrichten" + "Du wirst jetzt zu deinem %1$s Konto geleitet, um deine Identität zurückzusetzen. Danach wirst du zur App zurückgebracht." + "Kannst du das nicht bestätigen? Gehe zu deinem Konto, um deine Identität zurückzusetzen." + "Verifizierung zurückziehen und senden" + "Du kannst deine Verifizierung zurückziehen und diese Nachricht trotzdem senden, oder du kannst sie vorerst abbrechen und es später noch einmal versuchen, nachdem du %1$s erneut verifiziert hast." + "Deine Nachricht wurde nicht gesendet, da sich die verifizierte Identität von %1$s geändert hat" + "Nachricht trotzdem senden" + "%1$s verwendet wenigstens ein nicht verifiziertes Gerät. Du kannst die Nachricht trotzdem verschicken, oder vorerst abbrechen und später erneut versuchen, nachdem %2$s alle Geräte verifiziert hat." + "Deine Nachricht wurde nicht gesendet, weil %1$s nicht alle Geräte verifiziert hat" + "Fixierte Nachrichten" "Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut." "Benutzerdetails konnten nicht abgerufen werden" "Blockieren" @@ -279,6 +302,7 @@ Grund: %1$s." "Blockierung aufheben" "%1$s von %2$s" "%1$s Angepinnte Nachrichten" + "Nachricht wird geladen…" "Alle anzeigen" "Chat" "Standort teilen" @@ -287,6 +311,8 @@ Grund: %1$s." "In Google Maps öffnen" "In OpenStreetMap öffnen" "Diesen Standort teilen" + "Nachricht nicht gesendet, weil sich die verifizierte Identität von %1$s geändert hat." + "Nachricht wurde nicht gesendet, weil %1$s nicht alle Geräte verifiziert hat" "Standort" "Version: %1$s (%2$s)" "en" diff --git a/libraries/ui-strings/src/main/res/values-el/translations.xml b/libraries/ui-strings/src/main/res/values-el/translations.xml index b38211a60d..d0fd3cbdab 100644 --- a/libraries/ui-strings/src/main/res/values-el/translations.xml +++ b/libraries/ui-strings/src/main/res/values-el/translations.xml @@ -36,6 +36,7 @@ "Πίσω" "Κάλεσε" "Άκυρο" + "Ακύρωση προς το παρόν" "Επιλογή φωτογραφίας" "Εκκαθάριση" "Κλείσιμο" @@ -91,6 +92,7 @@ "Αναφορά σφάλματος" "Αναφορά περιεχομένου" "Επαναφορά" + "Επαναφορά ταυτότητας" "Επανάληψη" "Επανάληψη αποκρυπτογράφησης" "Αποθήκευση" @@ -111,6 +113,7 @@ "Πάτα για επιλογές" "Προσπάθησε ξανά" "Ξεκαρφίτσωμα" + "Προβολή στο χρονοδιάγραμμα" "Προβολή πηγής" "Ναι" "Σχετικά" @@ -176,6 +179,7 @@ "Άτομα" "Μόνιμος σύνδεσμος" "Αδεια" + "Καρφιτσωμένο" "Παρακαλώ περίμενε…" "Θες σίγουρα να τερματίσεις αυτή τη δημοσκόπηση;" "Δημοσκόπηση: %1$s" @@ -247,6 +251,7 @@ "Προειδοποίηση" "Οι αλλαγές σου δεν έχουν αποθηκευτεί. Σίγουρα θες να πας πίσω;" "Αποθήκευση αλλαγών;" + "Ο οικιακός διακομιστής σου πρέπει να αναβαθμιστεί για να υποστηρίζει το Matrix Authentication Server και τη δημιουργία λογαριασμού." "Αποτυχία δημιουργίας του μόνιμου συνδέσμου" "%1$s δεν ήταν δυνατή η φόρτωση του χάρτη. Παρακαλώ δοκίμασε ξανά αργότερα." "Αποτυχία φόρτωσης μηνυμάτων" @@ -259,6 +264,8 @@ "Ορισμένα μηνύματα δεν έχουν σταλεί" "Λυπούμαστε, παρουσιάστηκε σφάλμα" "Η αυθεντικότητα αυτού του κρυπτογραφημένου μηνύματος δεν είναι εγγυημένη σε αυτήν τη συσκευή." + "Κρυπτογραφημένο από έναν προηγουμένως επαληθευμένο χρήστη." + "Μη κρυπτογραφημένο." "Κρυπτογραφημένο από άγνωστη ή διαγεγραμμένη συσκευή." "Κρυπτογραφημένο από μια συσκευή που δεν έχει επαληθευτεί από τον ιδιοκτήτη της." "Κρυπτογραφημένο από μη επαληθευμένο χρήστη." @@ -269,6 +276,22 @@ "Αποτυχία επιλογής πολυμέσου, δοκίμασε ξανά." "Αποτυχία μεταφόρτωσης μέσου, δοκίμασε ξανά." "Αποτυχία μεταφόρτωσης πολυμέσων, δοκίμασε ξανά." + "Πάτα σε ένα μήνυμα και επέλεξε «%1$s» για να συμπεριληφθεί εδώ." + "Καρφίτσωσε σημαντικά μηνύματα, ώστε να μπορούν να εντοπιστούν εύκολα" + + "%1$d Καρφιτσωμένο μήνυμα" + "%1$d Καρφιτσωμένα μηνύματα" + + "Καρφιτσωμένα μηνύματα" + "Πρόκειται να μεταβείς στον λογαριασμό σου %1$s για να επαναφέρεις την ταυτότητά σου. Στη συνέχεια, θα επιστρέψεις στην εφαρμογή." + "Δεν μπορείς να επιβεβαιώσεις; Πήγαινε στον λογαριασμό σου για να επαναφέρεις την ταυτότητά σου." + "Ανάκληση επαλήθευσης και αποστολή" + "Μπορείτε να ανακαλέσεις την επαλήθευσή σου και να στείλεις αυτό το μήνυμα όπως και να \'χει ή μπορείς να το ακυρώσεις προς το παρόν και να προσπαθήσεις ξανά αργότερα μετά την επαλήθευση του χρήστη %1$s." + "Το μήνυμά σου δεν στάλθηκε επειδή η επαληθευμένη ταυτότητα του χρήστη %1$s έχει αλλάξει" + "Αποστολή μηνύματος ούτως ή άλλως" + "Ο χρήστης %1$s χρησιμοποιεί τουλάχιστον μία μη επαληθευμένη συσκευή. Μπορείς να στείλεις το μήνυμα όπως και να \'χει ή μπορείς να το ακυρώσεις προς το παρόν και να δοκιμάσεις ξανά αργότερα αφού ο χρήστης %2$s επαληθεύσει όλες τις συσκευές του." + "Το μήνυμά σου δεν στάλθηκε επειδή ο χρήστης %1$s δεν έχει επαληθεύσει τουλάχιστον μία συσκευή" + "Καρφιτσωμένα μηνύματα" "Αποτυχία μεταφόρτωσης μέσου, δοκίμασε ξανά." "Δεν ήταν δυνατή η ανάκτηση στοιχείων χρήστη" "Αποκλεισμός" @@ -280,6 +303,7 @@ "Κατάργηση αποκλεισμού χρήστη" "%1$s από %2$s" "%1$s Καρφιτσωμένα μηνύματα" + "Φόρτωση μηνύματος…" "Προβολή Όλων" "Συνομιλία" "Κοινή χρήση τοποθεσίας" @@ -288,6 +312,8 @@ "Άνοιγμα στο Google Maps" "Άνοιγμα στο OpenStreetMap" "Κοινή χρήση αυτής της τοποθεσίας" + "Το μήνυμα δεν στάλθηκε επειδή η επαληθευμένη ταυτότητα του χρήστη %1$s έχει αλλάξει." + "Το μήνυμα δεν στάλθηκε επειδή ο χρήστης %1$s δεν έχει επαληθεύσει τουλάχιστον μία συσκευή." "Τοποθεσία" "Έκδοση: %1$s (%2$s)" "el" diff --git a/libraries/ui-strings/src/main/res/values-et/translations.xml b/libraries/ui-strings/src/main/res/values-et/translations.xml index 8f6c8740e6..1de14e5f1f 100644 --- a/libraries/ui-strings/src/main/res/values-et/translations.xml +++ b/libraries/ui-strings/src/main/res/values-et/translations.xml @@ -36,6 +36,7 @@ "Tagasi" "Helista kasutajale" "Loobu" + "Hetkel jäta tegemata" "Vali foto" "Selge" "Sulge" @@ -250,6 +251,7 @@ Põhjus: %1$s." "Hoiatus" "Sinu tehtud muudatused pole veel salvestatud. Kas sa oled kindel, et soovid minna tagasi?" "Kas salvestame muudatused?" + "Selleks et koos kasutajakonto loomisega toimiks Matrix Authentication Service\'i tugi, vajab sinu koduserver uuendamist." "Püsilingi loomine ei õnnestumud" "%1$s kaardi laadimine ei õnnestunud. Palun proovi hiljem uuesti." "Sõnumite laadimine ei õnnestunud" @@ -283,6 +285,12 @@ Põhjus: %1$s." "Esiletõstetud sõnumid" "Oma võrguidentiteedi lähtestamiseks suuname sind %1$s kasutajakonto halduse lehele. Hiljem suunatakse sind tagasi sama rakenduse juurde." "Sa ei saa seda kinnitada? Ava oma kasutajakonto haldus ja lähtesta oma võrguidentiteet." + "Unusta verifitseerimine ja saada ikkagi" + "Sa võid jätta verifitseerimisvea tähelepanuta ja sõnumi ikkagi saata või katkestad saatmise ja peale kasutaja %1$s verifitseerimist proovid seda uuesti." + "Sinu sõnum on saatmata, kuna kasutaja %1$s verifitseeritud identiteet on muutunud." + "Saada sõnum ikkagi" + "%1$s kasutab ühte või enamat verifitseerimata seadet. Sa võid sõnumi ikkagi saata või katkestad selle ning ootad kuni %2$s on kõik oma seadmed verifitseerinud ning proovid seejärel uuesti." + "Sinu sõnum on saatmata, kuna %1$s pole verifitseerinud ühte või enamat oma seadet" "Esiletõstetud sõnumid" "Meediafaili töötlemine enne üleslaadimist ei õnnestunud. Palun proovi uuesti." "Kasutaja andmete laadimine ei õnnestunud" @@ -304,6 +312,8 @@ Põhjus: %1$s." "Ava Google Mapsis" "Ava OpenStreetMapis" "Jaga seda asukohta" + "Sõnum on saatmata, kuna kasutaja %1$s verifitseeritud identiteet on muutunud." + "Sõnum on saatmata, kuna %1$s pole verifitseerinud ühte või enamat oma seadet." "Asukoht" "Versioon: %1$s (%2$s)" "et" 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 258f386137..8c41675598 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -36,6 +36,7 @@ "Retour" "Appel" "Annuler" + "Annuler pour l’instant" "Choisir une photo" "Effacer" "Fermer" @@ -249,6 +250,7 @@ Raison: %1$s." "Attention" "Vos modifications n’ont pas été enregistrées. Êtes-vous certain de vouloir quitter?" "Enregistrer les changements?" + "Votre serveur d’accueil doit être mis à jour pour prendre en charge le protocole MAS (Matrix Authentication Service) et la création de compte." "Échec de la création du permalien" "%1$s n’a pas pu charger la carte. Veuillez réessayer ultérieurement." "Échec du chargement des messages" @@ -280,6 +282,14 @@ Raison: %1$s." "%1$d messages épinglés" "Messages épinglés" + "Vous êtes sur le point d’accéder à votre compte %1$s pour réinitialiser votre identité. Vous serez ensuite redirigé vers l’application." + "Vous ne pouvez pas confirmer ? Accédez à votre compte pour réinitialiser votre identité." + "Révoquer la verification et envoyer" + "Vous pouvez révoquer la verification et envoyer ce message, ou vous pouvez annuler pour l’instant et réessayer plus tard après avoir vérifié à nouveau %1$s." + "Votre message n’a pas été envoyé car l’identité vérifiée de %1$s a changé" + "Envoyer le message quand même" + "%1$s utilise un ou plusieurs appareils non vérifiés. Vous pouvez quand même envoyer le message, ou vous pouvez annuler pour l’instant et réessayer plus tard après que %2$s vérifie tous ses appareils." + "Votre message n’a pas été envoyé car %1$s n’a pas vérifié tous ses appareils" "Messages épinglés" "Échec du traitement des médias à télécharger, veuillez réessayer." "Impossible de récupérer les détails de l’utilisateur" @@ -301,6 +311,8 @@ Raison: %1$s." "Ouvrir dans Google Maps" "Ouvrir dans OpenStreetMap" "Partager cette position" + "Le message n’a pas été envoyé car l’identité vérifiée de %1$s a changé." + "Le message n’a pas été envoyé car %1$s n’a pas vérifié tous ses appareils." "Position" "Version : %1$s ( %2$s )" "fr" 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 fff7fcbe20..977c6c8928 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -36,6 +36,7 @@ "Vissza" "Hívás" "Mégse" + "Egyelőre nem" "Fénykép kiválasztása" "Törlés" "Bezárás" @@ -250,6 +251,7 @@ Ok: %1$s." "Figyelmeztetés" "A módosítások nem lettek mentve. Biztos, hogy visszalép?" "Menti a módosításokat?" + "A Matrix-kiszolgálót frissíteni kell a Matrix Authentication Service és a fióklétrehozás támogatásához." "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." "Nem sikerült betölteni az üzeneteket" @@ -283,6 +285,12 @@ Ok: %1$s." "Kitűzött üzenetek" "Arra készül, hogy belépjen a(z) %1$s fiókjába, hogy visszaállítsa a személyazonosságát. Ezután vissza fog térni az alkalmazásba." "Nem tudja megerősíteni? Ugorjon a fiókjához, és állítsa vissza a személyazonosságát." + "Ellenőrzés visszavonása és elküldés" + "Visszavonhatja az ellenőrzést, és ennek ellenére elküldheti ezt az üzenetet, vagy egyelőre törölheti, és %1$s újbóli ellenőrzése után újra megpróbálhatja." + "Az üzenete nem lett elküldve, mert %1$s ellenőrzött személyazonossága megváltozott." + "Üzenet elküldése mindenképp" + "%1$s egy vagy több ellenőrizetlen eszközt használ. Így is elküldheti az üzenetet, vagy megszakíthatja most, és megpróbálhatja újra, miután %2$s ellenőrizte az összes eszközét." + "Az üzenet nem lett elküldve, mert %1$s nem ellenőrizte az összes eszközét" "Kitűzött üzenetek" "Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra." "Nem sikerült letölteni a felhasználói adatokat" @@ -304,6 +312,8 @@ Ok: %1$s." "Megnyitás a Google Mapsben" "Megnyitás az OpenStreetMapen" "E hely megosztása" + "Az üzenet nem lett elküldve, mert %1$s ellenőrzött személyazonossága megváltozott." + "Az üzenet nem lett elküldve, mert %1$s nem ellenőrizte az összes eszközét." "Hely" "Verzió: %1$s (%2$s)" "hu" diff --git a/libraries/ui-strings/src/main/res/values-nl/translations.xml b/libraries/ui-strings/src/main/res/values-nl/translations.xml index 7b9034291f..a1d8c194a4 100644 --- a/libraries/ui-strings/src/main/res/values-nl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-nl/translations.xml @@ -34,6 +34,7 @@ "Accepteren" "Toevoegen aan tijdlijn" "Terug" + "Bellen" "Annuleren" "Kies foto" "Wissen" @@ -49,6 +50,7 @@ "Weigeren" "Peiling verwijderen" "Uitschakelen" + "Verwerpen" "Gereed" "Bewerken" "Peiling wijzigen" @@ -57,6 +59,7 @@ "Voer pincode in" "Wachtwoord vergeten?" "Doorsturen" + "Terug" "Uitnodigen" "Mensen uitnodigen" "Nodig mensen uit voor %1$s" @@ -70,6 +73,7 @@ "Meer laden" "Account beheren" "Apparaten beheren" + "Bericht" "Volgende" "Nee" "Niet nu" @@ -84,6 +88,7 @@ "Antwoord in subchat" "Probleem melden" "Inhoud melden" + "Opnieuw instellen" "Opnieuw proberen" "Decryptie opnieuw proberen" "Opslaan" @@ -108,10 +113,13 @@ "Over" "Beleid inzake redelijk gebruik" "Geavanceerde instellingen" + "Gebruiksgegevens" "Weergave" "Geluid" "Geblokkeerde gebruikers" "Bubbels" + "Gesprek bezig (niet ondersteund)" + "Oproep gestart" "Chat back-up" "Copyright" "Kamer maken…" @@ -153,12 +161,14 @@ "Modern" "Dempen" "Geen resultaten" + "Geen kamernaam" "Offline" "of" "Wachtwoord" "Personen" "Permalink" "Toestemming" + "Even geduld…" "Weet je zeker dat je deze peiling wilt beëindigen?" "Peiling: %1$s" "Totaal aantal stemmen: %1$s" @@ -180,6 +190,8 @@ "Kamer" "Naam van de kamer" "bijv. de naam van je project" + "Wijzigingen opgeslagen" + "Opslaan" "Schermvergrendeling" "Iemand zoeken" "Zoekresultaten" @@ -193,6 +205,7 @@ "Instellingen" "Gedeelde locatie" "Uitloggen" + "Er is iets misgegaan" "Chat starten…" "Sticker" "Geslaagd" @@ -205,6 +218,7 @@ "Onderwerp" "Waar gaat deze kamer over?" "Kan niet ontsleutelen" + "Je hebt geen toegang tot dit bericht" "Uitnodigingen konden niet naar een of meerdere gebruikers worden verzonden." "Kan uitnodiging(en) niet verzenden" "Ontgrendelen" @@ -222,11 +236,14 @@ "Fout" "Geslaagd" "Waarschuwing" + "Je wijzigingen zijn niet opgeslagen. Weet je zeker dat je terug wilt gaan?" + "Wijzigingen opslaan?" "Het aanmaken van de permanente link is mislukt" "%1$s kon de kaart niet laden. Probeer het later opnieuw." "Het laden van berichten is mislukt" "%1$s had geen toegang tot je locatie. Probeer het later opnieuw." "Het uploaden van je spraakbericht is mislukt." + "Bericht niet gevonden" "%1$s heeft geen toegang tot je locatie. Je kunt dit inschakelen bij Instellingen." "%1$s heeft geen toegang tot je locatie. Schakel toegang hieronder in." "%1$s heeft geen toegang tot je microfoon. Schakel toegang in om een spraakbericht op te nemen." @@ -244,9 +261,11 @@ "Blokkeren" "Geblokkeerde gebruikers kunnen je geen berichten sturen en al hun berichten worden verborgen. Je kunt ze op elk moment deblokkeren." "Gebruiker blokkeren" + "Profiel" "Deblokkeren" "Je zult alle berichten van hen weer kunnen zien." "Gebruiker deblokkeren" + "Chat" "Locatie delen" "Deel mijn locatie" "Openen in Apple Maps" 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 6dd12d85df..407ce90c96 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -38,6 +38,7 @@ "Назад" "Позвонить" "Отмена" + "Отмените сейчас" "Выбрать фото" "Очистить" "Закрыть" @@ -256,6 +257,7 @@ "Предупреждение" "Изменения не сохранены. Вы действительно хотите вернуться?" "Сохранить изменения?" + "Ваш homeserver необходимо обновить, чтобы он поддерживал Matrix Authentication Service и создание учетной записи." "Не удалось создать постоянную ссылку" "Не удалось загрузить карту %1$s. Пожалуйста, повторите попытку позже." "Не удалось загрузить сообщения" @@ -290,6 +292,12 @@ "Закрепленные сообщения" "Вы собираетесь перейти в свою учетную запись %1$s, чтобы сбросить идентификацию. После этого вы вернетесь в приложение." "Не можете подтвердить? Перейдите в свою учетную запись, чтобы сбросить свою идентификацию." + "Отозвать верификацию и отправить" + "Вы можете отозвать свою верификацию и отправить это сообщение в любом случае или вы можете отменить ее сейчас и повторить попытку позже после повторной верификации %1$s." + "Ваше сообщение не было отправлено, потому что изменилась подтвержденная личность %1$s" + "Отправь сообщение в любом случае" + "%1$s использует одно или несколько непроверенных устройств. Вы все равно можете отправить сообщение или отменить его пока и повторить попытку позже %2$s, проверив все устройства пользователя." + "Ваше сообщение не было отправлено, потому что %1$s не проверил одно или несколько устройств" "Закрепленные сообщения" "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." "Не удалось получить данные о пользователе" @@ -311,6 +319,8 @@ "Открыть в Google Maps" "Открыть в OpenStreetMap" "Поделиться этим местоположением" + "Сообщение не отправлено, потому что верифицированная личность %1$s изменилась." + "Сообщение не отправлено, потому что %1$s не проверил одно или несколько устройств." "Местоположение" "Версия: %1$s (%2$s)" "ru" 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 5fbe3c55e7..d184c35e0c 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -38,6 +38,7 @@ "Späť" "Zavolať" "Zrušiť" + "Zatiaľ zrušiť" "Vybrať fotku" "Vyčistiť" "Zavrieť" @@ -254,6 +255,7 @@ Dôvod: %1$s." "Upozornenie" "Vaše zmeny neboli uložené. Naozaj sa chcete vrátiť?" "Uložiť zmeny?" + "Váš domovský server musí byť aktualizovaný tak, aby podporoval Matrix Authentication Server a vytvorenie účtu." "Nepodarilo sa vytvoriť trvalý odkaz" "%1$s nedokázal načítať mapu. Skúste to prosím neskôr." "Načítanie správ zlyhalo" @@ -288,6 +290,12 @@ Dôvod: %1$s." "Pripnuté správy" "Chystáte sa prejsť na svoj %1$s účet, aby ste obnovili svoju identitu. Potom budete vrátení späť do aplikácie." "Neviete potvrdiť? Prejdite do svojho účtu a obnovte svoju identitu." + "Odvolať overenie a odoslať" + "Svoje overenie môžete odvolať a odoslať túto správu aj tak, alebo ju môžete zatiaľ zrušiť a po opätovnom overení to skúsiť znova %1$s ." + "Vaša správa nebola odoslaná, pretože sa zmenila overená totožnosť používateľa %1$s." + "Odoslať správu aj tak" + "%1$s používa jedno alebo viac neoverených zariadení. Správu môžete odoslať aj tak, alebo ju môžete zatiaľ zrušiť a skúsiť to znova neskôr po %2$s overení všetkých zariadení." + "Vaša správa nebola odoslaná, pretože %1$s neoveril/a jedno alebo viac zariadení" "Pripnuté správy" "Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova." "Nepodarilo sa získať údaje o používateľovi" @@ -309,6 +317,8 @@ Dôvod: %1$s." "Otvoriť v Mapách Google" "Otvoriť v OpenStreetMap" "Zdieľajte túto polohu" + "Správa nebola odoslaná, pretože sa zmenila overená totožnosť používateľa %1$s." + "Správa nebola odoslaná, pretože %1$s neoveril/a jedno alebo viac zariadení." "Poloha" "Verzia: %1$s (%2$s)" "sk" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index f7c3213ba4..8e2e7822bf 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -36,6 +36,7 @@ "Back" "Call" "Cancel" + "Cancel for now" "Choose photo" "Clear" "Close" @@ -250,6 +251,7 @@ Reason: %1$s." "Warning" "Your changes have not been saved. Are you sure you want to go back?" "Save changes?" + "Your homeserver needs to be upgraded to support Matrix Authentication Service and account creation." "Failed creating the permalink" "%1$s could not load the map. Please try again later." "Failed loading messages" @@ -283,6 +285,12 @@ Reason: %1$s." "Pinned messages" "You\'re about to go to your %1$s account to reset your identity. Afterwards you\'ll be taken back to the app." "Can\'t confirm? Go to your account to reset your identity." + "Withdraw verification and send" + "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$s." + "Your message was not sent because %1$s’s verified identity has changed" + "Send message anyway" + "%1$s is using one or more unverified devices. You can send the message anyway, or you can cancel for now and try again later after %2$s has verified all their devices." + "Your message was not sent because %1$s has not verified all devices" "Pinned messages" "Failed processing media to upload, please try again." "Could not retrieve user details" @@ -304,6 +312,8 @@ Reason: %1$s." "Open in Google Maps" "Open in OpenStreetMap" "Share this location" + "Message not sent because %1$s’s verified identity has changed." + "Message not sent because %1$s has not verified all devices." "Location" "Version: %1$s (%2$s)" "en" diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 59048156b3..0ea95396de 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -47,7 +47,7 @@ private const val versionMinor = 6 // Note: even values are reserved for regular release, odd values for hotfix release. // When creating a hotfix, you should decrease the value, since the current value // is the value for the next regular release. -private const val versionPatch = 0 +private const val versionPatch = 1 object Versions { val versionCode = 4_000_000 + versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/AlwaysEnabledFeatureFlagService.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/AlwaysEnabledFeatureFlagService.kt new file mode 100644 index 0000000000..67151d5aa4 --- /dev/null +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/AlwaysEnabledFeatureFlagService.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.samples.minimal + +import io.element.android.libraries.featureflag.api.Feature +import io.element.android.libraries.featureflag.api.FeatureFlagService +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf + +class AlwaysEnabledFeatureFlagService : FeatureFlagService { + override fun isFeatureEnabledFlow(feature: Feature): Flow { + return flowOf(true) + } + + override suspend fun setFeatureEnabled(feature: Feature, enabled: Boolean): Boolean { + return true + } +} diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt index e1654a8fb7..19e1390f47 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt @@ -55,10 +55,11 @@ class MainActivity : ComponentActivity() { proxyProvider = proxyProvider, clock = DefaultSystemClock(), utdTracker = UtdTracker(NoopAnalyticsService()), - appPreferencesStore = InMemoryAppPreferencesStore(), + featureFlagService = AlwaysEnabledFeatureFlagService(), ), passphraseGenerator = NullPassphraseGenerator(), oidcConfigurationProvider = OidcConfigurationProvider(baseDirectory), + appPreferencesStore = InMemoryAppPreferencesStore(), ) } 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 70f2a1f8a4..c6a2d49c89 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 @@ -25,8 +25,6 @@ import io.element.android.features.roomlist.impl.filters.selection.DefaultFilter import io.element.android.features.roomlist.impl.search.RoomListSearchDataSource import io.element.android.features.roomlist.impl.search.RoomListSearchPresenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.core.meta.BuildMeta -import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.dateformatter.impl.DateFormatters import io.element.android.libraries.dateformatter.impl.DefaultLastMessageTimestampFormatter import io.element.android.libraries.dateformatter.impl.LocalDateTimeProvider @@ -35,8 +33,6 @@ import io.element.android.libraries.eventformatter.impl.DefaultRoomLastMessageFo import io.element.android.libraries.eventformatter.impl.ProfileChangeContentFormatter import io.element.android.libraries.eventformatter.impl.RoomMembershipContentFormatter import io.element.android.libraries.eventformatter.impl.StateContentFormatter -import io.element.android.libraries.featureflag.impl.DefaultFeatureFlagService -import io.element.android.libraries.featureflag.impl.PreferencesFeatureFlagProvider import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsPresenter import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState import io.element.android.libraries.indicator.impl.DefaultIndicatorService @@ -70,13 +66,7 @@ class RoomListScreen( private val sessionVerificationService = matrixClient.sessionVerificationService() private val encryptionService = matrixClient.encryptionService() private val stringProvider = AndroidStringProvider(context.resources) - private val buildMeta = getBuildMeta(context) - private val featureFlagService = DefaultFeatureFlagService( - providers = setOf( - PreferencesFeatureFlagProvider(context = context, buildMeta = buildMeta) - ), - buildMeta = buildMeta, - ) + private val featureFlagService = AlwaysEnabledFeatureFlagService() private val roomListRoomSummaryFactory = RoomListRoomSummaryFactory( lastMessageTimestampFormatter = DefaultLastMessageTimestampFormatter( localDateTimeProvider = dateTimeProvider, @@ -191,25 +181,4 @@ class RoomListScreen( } } } - - private fun getBuildMeta(context: Context): BuildMeta { - val buildType = BuildType.valueOf(BuildConfig.BUILD_TYPE.uppercase()) - val name = context.getString(R.string.app_name) - return BuildMeta( - isDebuggable = BuildConfig.DEBUG, - buildType = buildType, - applicationName = name, - productionApplicationName = name, - desktopApplicationName = name, - applicationId = BuildConfig.APPLICATION_ID, - lowPrivacyLoggingEnabled = false, - versionName = BuildConfig.VERSION_NAME, - versionCode = BuildConfig.VERSION_CODE.toLong(), - gitRevision = "", - gitBranchName = "", - flavorDescription = "", - flavorShortDescription = "", - isEnterpriseBuild = false, - ) - } } diff --git a/screenshots/de/appnav.loggedin_LoggedInView_Day_3_de.png b/screenshots/de/appnav.loggedin_LoggedInView_Day_3_de.png new file mode 100644 index 0000000000..03db0d9f7d --- /dev/null +++ b/screenshots/de/appnav.loggedin_LoggedInView_Day_3_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f535f7d2913ec47e30a45b9734f1905844fb49b5f2e20915f308c7a3aaf0633a +size 28227 diff --git a/screenshots/de/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_de.png b/screenshots/de/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_de.png index 08fa1da445..4d58cac782 100644 --- a/screenshots/de/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_de.png +++ b/screenshots/de/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae53bc6a3d857c58ae51b28a954d07ea1b6532e11034a82203d30273071ff3ec -size 23562 +oid sha256:707e640a752ec0e320fb1a20debaf7ceddf2b36a76c0059d82b421a5baa49d20 +size 23784 diff --git a/screenshots/de/features.lockscreen.impl.unlock_PinUnlockView_Day_2_de.png b/screenshots/de/features.lockscreen.impl.unlock_PinUnlockView_Day_2_de.png index a4d7e88514..f8ba109911 100644 --- a/screenshots/de/features.lockscreen.impl.unlock_PinUnlockView_Day_2_de.png +++ b/screenshots/de/features.lockscreen.impl.unlock_PinUnlockView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82b87880816463b56ad04aa1453b6fb74b64d1fe2436e8ee8015f9e918dbc483 -size 39094 +oid sha256:a0626ba6f85dafa09da2d8df68086593aab5c6af87be82f0a38564ae5c2e2a02 +size 39176 diff --git a/screenshots/de/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_de.png b/screenshots/de/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_de.png new file mode 100644 index 0000000000..90bdfbd9d9 --- /dev/null +++ b/screenshots/de/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c060f0778f1cc237cc6392e53acf5a6b543d3acd2bc17d0c3ce044d2dd9a028 +size 40383 diff --git a/screenshots/de/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_de.png b/screenshots/de/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_de.png new file mode 100644 index 0000000000..6b5271c39f --- /dev/null +++ b/screenshots/de/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbc451dbdbf48cbcf5851863970cfb2d5728d6ad1f0fbf8207b26cb9e8ace861 +size 39645 diff --git a/screenshots/de/features.login.impl.screens.createaccount_CreateAccountView_Day_0_de.png b/screenshots/de/features.login.impl.screens.createaccount_CreateAccountView_Day_0_de.png new file mode 100644 index 0000000000..1de44959ed --- /dev/null +++ b/screenshots/de/features.login.impl.screens.createaccount_CreateAccountView_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fc44751070bfec3956fcd02393cbdf62436a1f065cb00f5407cf46b4b0eb3101 +size 13406 diff --git a/screenshots/de/features.login.impl.screens.createaccount_CreateAccountView_Day_1_de.png b/screenshots/de/features.login.impl.screens.createaccount_CreateAccountView_Day_1_de.png new file mode 100644 index 0000000000..db5f9573f6 --- /dev/null +++ b/screenshots/de/features.login.impl.screens.createaccount_CreateAccountView_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b345f6492eb7ecf24226050d3278e7b5fd78736c20173fb8d7a03d1bf71e2cae +size 13450 diff --git a/screenshots/de/features.login.impl.screens.createaccount_CreateAccountView_Day_2_de.png b/screenshots/de/features.login.impl.screens.createaccount_CreateAccountView_Day_2_de.png new file mode 100644 index 0000000000..e23afae05a --- /dev/null +++ b/screenshots/de/features.login.impl.screens.createaccount_CreateAccountView_Day_2_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:69629592939063f0916ab2e77e69b98fa8e51f2ba5346f6be32ba2612054d441 +size 13372 diff --git a/screenshots/de/features.login.impl.screens.createaccount_CreateAccountView_Day_3_de.png b/screenshots/de/features.login.impl.screens.createaccount_CreateAccountView_Day_3_de.png new file mode 100644 index 0000000000..0f7d3b0478 --- /dev/null +++ b/screenshots/de/features.login.impl.screens.createaccount_CreateAccountView_Day_3_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd07fc9ba41cabd1894139517fe3b13a92e300bd5eee5d9e27f836489bbde9cb +size 15526 diff --git a/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_12_de.png b/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_12_de.png new file mode 100644 index 0000000000..2fd9274774 --- /dev/null +++ b/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_12_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dcd09b6f69e80f03ea6e81c64a7da582dc732a6288fb0660bddb6a2b4fe41a07 +size 51531 diff --git a/screenshots/de/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_de.png b/screenshots/de/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_de.png new file mode 100644 index 0000000000..937f0b8b00 --- /dev/null +++ b/screenshots/de/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b73f8ee5792e95bb189e3dfdb615e30d2089afb879ed05958245ddba0251aae +size 63856 diff --git a/screenshots/de/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_de.png b/screenshots/de/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_de.png new file mode 100644 index 0000000000..9d990d6e19 --- /dev/null +++ b/screenshots/de/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:54ad73d4aa324d71a5a6f718b8fb508b6a90aaf3f433f0e35ac621ca713ca0a1 +size 67574 diff --git a/screenshots/de/features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_de.png b/screenshots/de/features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_de.png new file mode 100644 index 0000000000..6a9e2c93f8 --- /dev/null +++ b/screenshots/de/features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a970f4f1ce3cbbbfde86774d40f68cd35537586dd6c43c7de84ae422c813b91 +size 8529 diff --git a/screenshots/de/features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_de.png b/screenshots/de/features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_de.png index 96bc10b985..0911cafa8f 100644 --- a/screenshots/de/features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_de.png +++ b/screenshots/de/features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e615668e59a443185ea1151820f08951b37ea5bbe394e049262be04cc2a00cdb -size 12285 +oid sha256:e1790f4c2d4a6b54c4e20dd44b9d14f38b5bec555f65c5f30a17439fddab8842 +size 12768 diff --git a/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_de.png b/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_de.png index ac3a73d739..ed4e20aae2 100644 --- a/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_de.png +++ b/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ccd86242d92f1a773f35ee6f4fabc44699fb4ed7744ef41282b880e671699f3 -size 24182 +oid sha256:68447035a33d0acad5d3055808b77bd30dc876c00a28ecd06546d592811625bd +size 24294 diff --git a/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_de.png b/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_de.png new file mode 100644 index 0000000000..2c5b8d4c12 --- /dev/null +++ b/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d0f5407369b9d57c271a432046b373e9e0a8177ec8931b8dcc4893e577d205f +size 9070 diff --git a/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_de.png b/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_de.png new file mode 100644 index 0000000000..7a27e388ea --- /dev/null +++ b/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b5c48945b82ddae5217f6b0bce3f74e81bfa7b2eb929deb697dc936ccbf2bac +size 35386 diff --git a/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_de.png b/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_de.png new file mode 100644 index 0000000000..541b33f5d3 --- /dev/null +++ b/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0927e2dece4cca09e69aea086f2b026ec6143a47d7ca34cd3d9edab22ecf3b31 +size 42816 diff --git a/screenshots/de/features.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_Day_0_de.png b/screenshots/de/features.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_Day_0_de.png deleted file mode 100644 index b4f8b0f49a..0000000000 --- a/screenshots/de/features.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_Day_0_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:705b83e220abea5b572dab15e9a08414dbbe6beaa135086b9200653c781a51c9 -size 12658 diff --git a/screenshots/de/features.messages.impl.timeline.components_MessageShieldView_Day_0_de.png b/screenshots/de/features.messages.impl.timeline.components_MessageShieldView_Day_0_de.png index 04d3bf0665..6b3a059bf1 100644 --- a/screenshots/de/features.messages.impl.timeline.components_MessageShieldView_Day_0_de.png +++ b/screenshots/de/features.messages.impl.timeline.components_MessageShieldView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bdf88e9ea1157af338533be63f6df5845a40352babe56cba9c03ecfc7539a0a -size 47175 +oid sha256:282e9afb40069c21fce9c8df11e9d1f98d07184fa0600ec8488b1a667ee0d1e0 +size 48184 diff --git a/screenshots/de/features.onboarding.impl_OnBoardingView_Day_2_de.png b/screenshots/de/features.onboarding.impl_OnBoardingView_Day_2_de.png index fc2434d46c..6121562597 100644 --- a/screenshots/de/features.onboarding.impl_OnBoardingView_Day_2_de.png +++ b/screenshots/de/features.onboarding.impl_OnBoardingView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8856d920221c12eb15e4b539e9b13e0f692d1f7fd7184214efa7cc387179a851 -size 312746 +oid sha256:11eeb61c2d3ad494bb9b106ac1443df0c98f5f77d5b70ec4f0f617c1dcceb5fc +size 311194 diff --git a/screenshots/de/features.onboarding.impl_OnBoardingView_Day_3_de.png b/screenshots/de/features.onboarding.impl_OnBoardingView_Day_3_de.png index 70dfd4f2ec..e2bd3aa8f1 100644 --- a/screenshots/de/features.onboarding.impl_OnBoardingView_Day_3_de.png +++ b/screenshots/de/features.onboarding.impl_OnBoardingView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4b4a054a736fda19b44fb1aed86df28a63ce00704789a9828b6d7f933c7081a -size 306739 +oid sha256:1d4ebb057326ef42c95a77a46a3b5ab183a83b6fd734e11c598222f4cae4a07a +size 305270 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png index fa370d2d6c..355a348677 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6121327494502b096c25dceece3e42e45eb3cf5b98578e8be7f7a96d006240be -size 44157 +oid sha256:8b7ff61b4b4281fdce700003fdaad4138b75075e10a66b9e4e0de8ae487d9d84 +size 44271 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png index 59bb9b9897..1a28f57540 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23e0b1b6e830eba355a0b7b139aafc1be11b781e6ef03df01786f5d6aa0c1b21 -size 46343 +oid sha256:384654c739d25cfd314ecde1fedaa6c8c0beccdfabfb0b0f0faacf1f1d10f6e1 +size 46456 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png index 4d57857101..6a7dcf16e7 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22beba9c2412749866f57de99ee180a78761d2c47c06784a5d161a0aa91a3328 -size 44832 +oid sha256:9ff26f20abce5bfa8e936057d70258bbac919cc47c7aa27b374176f7d618c7ba +size 44947 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png index 098b141519..53674500dd 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ade9b2a3b5f32e65876becaf64ba6fb4627b9e4e6f2f3075634c6db9589798f -size 48494 +oid sha256:9cc4178e91b11dd83bcfc07e291a989317e744fbfcfb802e913875988d7f1685 +size 48606 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png index 2be7ff6576..a0eb722a41 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e93f96555973aa1c30fc5ce2ea981bb26203e129a8cee2255ae3acfc53225e55 -size 46831 +oid sha256:edb4fe7262c508c066c11cebbf578e2b4851a871d5c438085bb3caea1dfae14a +size 46936 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png index 1e435d5e0f..a8632d499b 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31e5144d7d0e819a4979fb3747d1b33f7dd1a12e23316598b908ef1db3f970b0 -size 42706 +oid sha256:da8ed936f17cd7b016aa5c2e45fe1df9bfbe149c686ab04903fafb537cf322c0 +size 42808 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png index 5f9ee3c8ff..6b21e09888 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73618da5786309a1c338f77d18e88b82a25e228256c336714b5dbbd039937adc -size 43723 +oid sha256:e65461ca9a5aa89268fdfe04759d4c0dd5918ca395470d58c95a9f50eefb4ee4 +size 43835 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png index 7d713991e5..667414400c 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0cc61ea48b4ed1e8a31c46996afb5c547ba2c6f75b98c4af2fb00b9921896ba3 -size 41780 +oid sha256:bec0e6890c5da8790c3b787d05ea7dab0e1084e8c1f34e98fdf6a478adfeff95 +size 41892 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png index ceedaf8a5d..2a431ede3d 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fca83c8ab2de5105cba3eb2884d9b73501f5d83c7d9c4764dbbc35b387646d5f -size 47170 +oid sha256:30dd58fbdb510dce6e75347fcf9cca171770f285aa4abe981624b49c7ea45ab0 +size 47282 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png index 1b784506e8..07c6683054 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:403b8193a9bac5094515c7911c4f58859d12c3491f52e1d942b132a3de7d0445 -size 44718 +oid sha256:1ad624a4ae1c20addec10174f60e2115649bb8440d9d1b742091a5a2a8b3e68b +size 44834 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png index 1b784506e8..07c6683054 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:403b8193a9bac5094515c7911c4f58859d12c3491f52e1d942b132a3de7d0445 -size 44718 +oid sha256:1ad624a4ae1c20addec10174f60e2115649bb8440d9d1b742091a5a2a8b3e68b +size 44834 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png index 746506a9bd..b7d93442f2 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c18704beb6082319d63d176d7e1cc333ffe1b14e18aeb1326ed736afb0fc9eb5 -size 47737 +oid sha256:84a24a5c58ecfb144caf04c0e8652d290c0574ff6cb1dbed7b701df2fa9e195b +size 47850 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png index ea4ed4d191..4d9d503a0d 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c016d05fc5dccf7e9d9e619c42279e677b92e74af8f4a72108e2117d402c99fd -size 46665 +oid sha256:8a20b95ef4d95bdc045505ed01199ec219d5698a8295f3301e4f260ad926a043 +size 46780 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png index 81d7577a9b..d0c7127489 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c734cbce4733caaf9ba237fe9ce76a0e1d081ab9fcf348bdc69e27468fb677bc -size 45709 +oid sha256:0dfce3cff7725fc9e5778c813be51fc159817ecdecfd14fe62af7905213d3c4f +size 45824 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png index ef718f3558..31cc642e29 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:073ea237aede57bd96b74b154c845e06066131f608d228d74430922686b10002 -size 45170 +oid sha256:d5d7311a5e7fd9e5e950128960089555ac4d84341c2d9107e94d1396c9d1175a +size 45326 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png index 387d0fa600..fb144576a1 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e73c7eb3bea900153fc3ced3b3e0fdc7b8948934783b870394587500035c2c3 -size 47443 +oid sha256:b80349e217e0649d360502c881abdca61008ac0a52a9860cb223588174673850 +size 47606 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png index 5fd1221199..0ad7508787 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c09c681362992445b2ba1a7f66ede83a1e934b6c58a63e29dad6a01d8fd91c09 -size 45899 +oid sha256:dac10588ea7f2d0909356bdd24ac789959ebbc52853c6d6904a7e3899eba3313 +size 46058 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png index 7d8aacdd38..ec420272df 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d70a4c8e5f753f9f8564b74e780d40beecf46efd5127a80a947736f6ae104d04 -size 49565 +oid sha256:3f9797f47aea0f543849c333616a55b73cd5afcd236498f9e85129720c0246c9 +size 49720 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png index bd5263e590..05ce4083ee 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a95e349b642b9d00db353dcef8544ae7953725c8b46372812fba3eeb652d0923 -size 47918 +oid sha256:c202ce10bcadc283985cdbc537db571b9cd3c452bc3f99bf0587c9beb354bd0f +size 48091 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png index b1395c8bb8..5691f770cd 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2ce330939fb18280697f35bef030723fb8658225e750eb46a4023c9cec0cc6e -size 43836 +oid sha256:9f17d568db0c791191fd171ff60a7f1f9dda733610a8cdc4fc13128625b45507 +size 43984 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png index a887b71d4c..019cc7168b 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ed6dea45912127b7eca3a89759241963915ed157f0b7722d8981bbfa1f748aa -size 44920 +oid sha256:1df92fa426acbbdfecb208e03cd98e0e699816cb7e9f4780479710dd0ac9a866 +size 45077 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png index 5091e52d62..28b88db2ae 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3407b1e432f4d5a90ed0be8ee529364ad61e5d190d2a614530d5d929273bd712 -size 42655 +oid sha256:d43f43946824fa36994303c527ed6ac4396b1d3c71d3562ec456a7daecd6fec2 +size 42816 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png index af53a9d8ad..a731314fc9 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:864a597bb95aec9d8ba3a677617b9fd3cafe863ae5fe336c4db165fc7038fb0f -size 48284 +oid sha256:c6a6d1abea617ec460b96d70a2fb197926714a3b120cc7217e15031232438078 +size 48442 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png index 6a2d10d4b1..5f85ce08f7 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4ac057ad0587edc72bef380cc2446df7152588aa20c259fba76e255c7d05a53 -size 45627 +oid sha256:725c2b15c127b3b7e455f07de17d582d2b7aa04cfcb13cea1b56f6f6ab2ecdaa +size 45783 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png index 6a2d10d4b1..5f85ce08f7 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4ac057ad0587edc72bef380cc2446df7152588aa20c259fba76e255c7d05a53 -size 45627 +oid sha256:725c2b15c127b3b7e455f07de17d582d2b7aa04cfcb13cea1b56f6f6ab2ecdaa +size 45783 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png index ad016b5911..84fcc21085 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81881ff88e285e6bf4f13e0dfe746842b6a7a33d4babb5f579a5be31ae0cca3b -size 48955 +oid sha256:633389d3bd3878fb2aa7e75359967fb72b168a769ee99bdf2581661915fad981 +size 49103 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png index 6669a44928..066b9b403b 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:facf28de76d249f99e5b4f32bfa46c932cc77bb4bebe5a496abdeaf1cabff1c4 -size 47823 +oid sha256:f47f49ff6c3aa8ba703b2ffdd6bc08d3c389f8bd79abb276a437571f78f08ee0 +size 47984 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png index 0b62a52c4c..c0c98b9a73 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a054d80a79ca39138c830df9d3dde06ca8900e37989dabd4ef4af8a963a415eb -size 46763 +oid sha256:6542cfb73e64d5022a089872f80793e34244c530609d5c408872f249f53f8337 +size 46926 diff --git a/screenshots/de/features.roomlist.impl.components_NativeSlidingSyncMigrationBanner_Day_0_de.png b/screenshots/de/features.roomlist.impl.components_NativeSlidingSyncMigrationBanner_Day_0_de.png new file mode 100644 index 0000000000..3b132a6365 --- /dev/null +++ b/screenshots/de/features.roomlist.impl.components_NativeSlidingSyncMigrationBanner_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4edb266d9c0e99cf4104509169407379d6bcf75889abd0b4ebfa9f800e23b5aa +size 45278 diff --git a/screenshots/de/features.roomlist.impl.components_RoomListContentView_Day_4_de.png b/screenshots/de/features.roomlist.impl.components_RoomListContentView_Day_4_de.png new file mode 100644 index 0000000000..7f02a36973 --- /dev/null +++ b/screenshots/de/features.roomlist.impl.components_RoomListContentView_Day_4_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca791ab27e69f4a84eecfe420aba93fd7aedbcca93914e8aceb8781dd0409981 +size 82377 diff --git a/screenshots/de/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_de.png b/screenshots/de/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_de.png index 193d5a5010..082adc23cb 100644 --- a/screenshots/de/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_de.png +++ b/screenshots/de/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:284e0f25302bddb07a48154b1dedc5b651881901ef45c065f67ac9ba19523ee8 -size 28834 +oid sha256:75b3f513dcbf3a7fd948f20b7ed438669ae827111da49941de11f28a80bad250 +size 30064 diff --git a/screenshots/de/features.roomlist.impl_RoomListView_Day_10_de.png b/screenshots/de/features.roomlist.impl_RoomListView_Day_10_de.png index 71fb3198ec..0405427877 100644 --- a/screenshots/de/features.roomlist.impl_RoomListView_Day_10_de.png +++ b/screenshots/de/features.roomlist.impl_RoomListView_Day_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5592b558284e74ab31471943c4484efe60a373f8136b2af9493e853029cb5435 -size 102313 +oid sha256:04d51057429c916bda03e771ca7da818fe32f9766257eb4813fcba924e5dd5d4 +size 103878 diff --git a/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_0_de.png b/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_0_de.png index 46a6198a48..e2838bdd57 100644 --- a/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_0_de.png +++ b/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42ceb0bf684dcb97456ee904342afa81e966de5e595fbf741f26a25536f2700d -size 71135 +oid sha256:5108d337185d1cd70063fbf34a5510a5e3d441f001f164f61042bbd2fbe93e1f +size 72102 diff --git a/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_1_de.png b/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_1_de.png index ac3fdcdf6d..dfb1308cdd 100644 --- a/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_1_de.png +++ b/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be22cf2ce23200a147bffc00d7bb7879957c046d44f97f46237783eeacfd6802 -size 54437 +oid sha256:857e94824a054038e372ddfc47b64e3132f1ea9d35da6de8f371c7a5ce67fef5 +size 54421 diff --git a/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_2_de.png b/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_2_de.png index a39ca0c09d..e64607ce87 100644 --- a/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_2_de.png +++ b/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8334e9951e681fc87b1713ad9f2dee7b1bf288f479c9b77f026a7779ff3ad8d0 -size 71639 +oid sha256:7fa5a407d658c99463e30074041a643d6e826427b5171061e345af5419f9bbbf +size 72612 diff --git a/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_3_de.png b/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_3_de.png index d5a14dcbe8..69f9af3f5c 100644 --- a/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_3_de.png +++ b/screenshots/de/features.securebackup.impl.disable_SecureBackupDisableView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c730cf5ceebede7f6a9a02edad610ae9c7ec42e77a37cec222bda261b48cacc -size 42017 +oid sha256:770cb9396ba855398d65d18e7794c771b8cfc26aa73a7dcb04b49ac3bacd63ac +size 41944 diff --git a/screenshots/de/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_de.png b/screenshots/de/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_de.png index c068c315bf..a8bc71740a 100644 --- a/screenshots/de/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_de.png +++ b/screenshots/de/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:836d97a08320f602807201ac740eb417ba173ddfd4821455f9d85ca8945c0d64 -size 39351 +oid sha256:0f6d57c36c609cd6d810368df13f7bfb15b7366ffdbe17dfb4c6be64b8bd33b8 +size 41658 diff --git a/screenshots/html/data.js b/screenshots/html/data.js index 4e00986bda..6ac9ca9bfe 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -1,41 +1,41 @@ // Generated file, do not edit export const screenshots = [ ["en","en-dark","de",], -["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",19972,], +["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",19983,], ["features.invite.impl.response_AcceptDeclineInviteView_Day_0_en","features.invite.impl.response_AcceptDeclineInviteView_Night_0_en",0,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_1_en","features.invite.impl.response_AcceptDeclineInviteView_Night_1_en",19972,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_2_en","features.invite.impl.response_AcceptDeclineInviteView_Night_2_en",19972,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_3_en","features.invite.impl.response_AcceptDeclineInviteView_Night_3_en",19972,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_4_en","features.invite.impl.response_AcceptDeclineInviteView_Night_4_en",19972,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_1_en","features.invite.impl.response_AcceptDeclineInviteView_Night_1_en",19983,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_2_en","features.invite.impl.response_AcceptDeclineInviteView_Night_2_en",19983,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_3_en","features.invite.impl.response_AcceptDeclineInviteView_Night_3_en",19983,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_4_en","features.invite.impl.response_AcceptDeclineInviteView_Night_4_en",19983,], ["features.login.impl.accountprovider_AccountProviderView_Day_0_en","features.login.impl.accountprovider_AccountProviderView_Night_0_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_1_en","features.login.impl.accountprovider_AccountProviderView_Night_1_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_2_en","features.login.impl.accountprovider_AccountProviderView_Night_2_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_3_en","features.login.impl.accountprovider_AccountProviderView_Night_3_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_4_en","features.login.impl.accountprovider_AccountProviderView_Night_4_en",0,], -["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",19972,], -["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",19972,], -["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",19972,], -["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",19972,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en",19972,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en",19972,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en",19972,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en",19972,], -["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",19972,], -["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",19972,], -["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",19972,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",19972,], -["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",19972,], -["services.apperror.impl_AppErrorView_Day_0_en","services.apperror.impl_AppErrorView_Night_0_en",19972,], +["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",19983,], +["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",19983,], +["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",19983,], +["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",19983,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en",19983,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en",19983,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en",19983,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en",19983,], +["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",19983,], +["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",19983,], +["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",19983,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",19983,], +["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",19983,], +["services.apperror.impl_AppErrorView_Day_0_en","services.apperror.impl_AppErrorView_Night_0_en",19983,], ["libraries.designsystem.components.async_AsyncActionView_Day_0_en","libraries.designsystem.components.async_AsyncActionView_Night_0_en",0,], -["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",19972,], +["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",19983,], ["libraries.designsystem.components.async_AsyncActionView_Day_2_en","libraries.designsystem.components.async_AsyncActionView_Night_2_en",0,], -["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",19972,], +["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",19983,], ["libraries.designsystem.components.async_AsyncActionView_Day_4_en","libraries.designsystem.components.async_AsyncActionView_Night_4_en",0,], -["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",19972,], +["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",19983,], ["libraries.designsystem.components.async_AsyncIndicatorFailure_Day_0_en","libraries.designsystem.components.async_AsyncIndicatorFailure_Night_0_en",0,], ["libraries.designsystem.components.async_AsyncIndicatorLoading_Day_0_en","libraries.designsystem.components.async_AsyncIndicatorLoading_Night_0_en",0,], ["libraries.designsystem.components.async_AsyncLoading_Day_0_en","libraries.designsystem.components.async_AsyncLoading_Night_0_en",0,], -["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",19972,], +["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",19983,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_0_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_0_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_1_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_1_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_2_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_2_en",0,], @@ -45,11 +45,11 @@ export const screenshots = [ ["libraries.matrix.ui.components_AttachmentThumbnail_Day_6_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_6_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_7_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_7_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_8_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_8_en",0,], -["features.messages.impl.attachments.preview_AttachmentsView_0_en","",19972,], -["features.messages.impl.attachments.preview_AttachmentsView_1_en","",19972,], -["features.messages.impl.attachments.preview_AttachmentsView_2_en","",19972,], -["features.messages.impl.attachments.preview_AttachmentsView_3_en","",19972,], -["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",19972,], +["features.messages.impl.attachments.preview_AttachmentsView_0_en","",19983,], +["features.messages.impl.attachments.preview_AttachmentsView_1_en","",19983,], +["features.messages.impl.attachments.preview_AttachmentsView_2_en","",19983,], +["features.messages.impl.attachments.preview_AttachmentsView_3_en","",19983,], +["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",19983,], ["libraries.designsystem.components.avatar_Avatar_Avatars_0_en","",0,], ["libraries.designsystem.components.avatar_Avatar_Avatars_10_en","",0,], ["libraries.designsystem.components.avatar_Avatar_Avatars_11_en","",0,], @@ -129,13 +129,13 @@ export const screenshots = [ ["libraries.designsystem.components_Badge_Day_0_en","libraries.designsystem.components_Badge_Night_0_en",0,], ["libraries.designsystem.components_BigCheckmark_Day_0_en","libraries.designsystem.components_BigCheckmark_Night_0_en",0,], ["libraries.designsystem.components_BigIcon_Day_0_en","libraries.designsystem.components_BigIcon_Night_0_en",0,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",19972,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",19972,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",19972,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",19972,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",19972,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",19972,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",19972,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",19983,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",19983,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",19983,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",19983,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",19983,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",19983,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",19983,], ["libraries.designsystem.components_BloomInitials_Day_0_en","libraries.designsystem.components_BloomInitials_Night_0_en",0,], ["libraries.designsystem.components_BloomInitials_Day_1_en","libraries.designsystem.components_BloomInitials_Night_1_en",0,], ["libraries.designsystem.components_BloomInitials_Day_2_en","libraries.designsystem.components_BloomInitials_Night_2_en",0,], @@ -146,87 +146,93 @@ export const screenshots = [ ["libraries.designsystem.components_BloomInitials_Day_7_en","libraries.designsystem.components_BloomInitials_Night_7_en",0,], ["libraries.designsystem.components_Bloom_Day_0_en","libraries.designsystem.components_Bloom_Night_0_en",0,], ["libraries.designsystem.theme.components_BottomSheetDragHandle_Day_0_en","libraries.designsystem.theme.components_BottomSheetDragHandle_Night_0_en",0,], -["features.rageshake.impl.bugreport_BugReportView_Day_0_en","features.rageshake.impl.bugreport_BugReportView_Night_0_en",19972,], -["features.rageshake.impl.bugreport_BugReportView_Day_1_en","features.rageshake.impl.bugreport_BugReportView_Night_1_en",19972,], -["features.rageshake.impl.bugreport_BugReportView_Day_2_en","features.rageshake.impl.bugreport_BugReportView_Night_2_en",19972,], -["features.rageshake.impl.bugreport_BugReportView_Day_3_en","features.rageshake.impl.bugreport_BugReportView_Night_3_en",19972,], -["features.rageshake.impl.bugreport_BugReportView_Day_4_en","features.rageshake.impl.bugreport_BugReportView_Night_4_en",19972,], +["features.rageshake.impl.bugreport_BugReportView_Day_0_en","features.rageshake.impl.bugreport_BugReportView_Night_0_en",19983,], +["features.rageshake.impl.bugreport_BugReportView_Day_1_en","features.rageshake.impl.bugreport_BugReportView_Night_1_en",19983,], +["features.rageshake.impl.bugreport_BugReportView_Day_2_en","features.rageshake.impl.bugreport_BugReportView_Night_2_en",19983,], +["features.rageshake.impl.bugreport_BugReportView_Day_3_en","features.rageshake.impl.bugreport_BugReportView_Night_3_en",19983,], +["features.rageshake.impl.bugreport_BugReportView_Day_4_en","features.rageshake.impl.bugreport_BugReportView_Night_4_en",19983,], ["libraries.designsystem.atomic.molecules_ButtonColumnMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ButtonColumnMolecule_Night_0_en",0,], ["libraries.designsystem.atomic.molecules_ButtonRowMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ButtonRowMolecule_Night_0_en",0,], ["features.call.impl.ui_CallScreenPipView_Day_0_en","features.call.impl.ui_CallScreenPipView_Night_0_en",0,], ["features.call.impl.ui_CallScreenPipView_Day_1_en","features.call.impl.ui_CallScreenPipView_Night_1_en",0,], ["features.call.impl.ui_CallScreenView_Day_0_en","features.call.impl.ui_CallScreenView_Night_0_en",0,], -["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",19972,], -["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",19972,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",19972,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_0_en",19972,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_10_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_10_en",19972,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_1_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_1_en",19972,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_2_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_2_en",19972,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_3_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_3_en",19972,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_4_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_4_en",19972,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_5_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_5_en",19972,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_6_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_6_en",19972,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_7_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_7_en",19972,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_8_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_8_en",19972,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_9_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_9_en",19972,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en",19972,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en",19972,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en",19972,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en",19972,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en",19972,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en",19972,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en",19972,], +["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",19983,], +["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",19983,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",19983,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_0_en",19983,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_10_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_10_en",19983,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_1_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_1_en",19983,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_2_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_2_en",19983,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_3_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_3_en",19983,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_4_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_4_en",19983,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_5_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_5_en",19983,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_6_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_6_en",19983,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_7_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_7_en",19983,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_8_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_8_en",19983,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_9_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_9_en",19983,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en",19983,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en",19983,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en",19983,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en",19983,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en",19983,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en",19983,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en",19983,], ["features.login.impl.changeserver_ChangeServerView_Day_0_en","features.login.impl.changeserver_ChangeServerView_Night_0_en",0,], -["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",19972,], -["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",19972,], +["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",19983,], +["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",19983,], ["libraries.matrix.ui.components_CheckableResolvedUserRow_en","",0,], -["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",19972,], +["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",19983,], ["libraries.designsystem.theme.components_Checkboxes_Toggles_en","",0,], ["libraries.designsystem.theme.components_CircularProgressIndicator_Progress Indicators_en","",0,], ["libraries.designsystem.components_ClickableLinkText_Text_en","",0,], ["libraries.designsystem.theme_ColorAliases_Day_0_en","libraries.designsystem.theme_ColorAliases_Night_0_en",0,], ["libraries.textcomposer.components_ComposerOptionsButton_Day_0_en","libraries.textcomposer.components_ComposerOptionsButton_Night_0_en",0,], ["libraries.designsystem.components.avatar_CompositeAvatar_Avatars_en","",0,], -["features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en","features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en",19972,], -["features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en","features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en",19972,], +["features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en","features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en",19983,], +["features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en","features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en",19983,], ["features.preferences.impl.developer.tracing_ConfigureTracingView_Day_0_en","features.preferences.impl.developer.tracing_ConfigureTracingView_Night_0_en",0,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",19972,], -["features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",19972,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",19983,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",19983,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",19983,], +["features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",19983,], ["libraries.designsystem.components.dialogs_ConfirmationDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ConfirmationDialog_Day_0_en","libraries.designsystem.components.dialogs_ConfirmationDialog_Night_0_en",0,], ["features.networkmonitor.api.ui_ConnectivityIndicatorView_Day_0_en","features.networkmonitor.api.ui_ConnectivityIndicatorView_Night_0_en",0,], -["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",19972,], -["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",19972,], -["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",19972,], -["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",19972,], -["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",19972,], -["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",19972,], -["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",19972,], -["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",19972,], -["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",19972,], -["features.createroom.impl.root_CreateRoomRootView_Day_0_en","features.createroom.impl.root_CreateRoomRootView_Night_0_en",19972,], -["features.createroom.impl.root_CreateRoomRootView_Day_1_en","features.createroom.impl.root_CreateRoomRootView_Night_1_en",19972,], -["features.createroom.impl.root_CreateRoomRootView_Day_2_en","features.createroom.impl.root_CreateRoomRootView_Night_2_en",19972,], -["features.createroom.impl.root_CreateRoomRootView_Day_3_en","features.createroom.impl.root_CreateRoomRootView_Night_3_en",19972,], -["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime pickers_en","",19972,], -["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime pickers_en","",19972,], +["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",19983,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",19983,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",19983,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",19983,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",19983,], +["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",19983,], +["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",19983,], +["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",19983,], +["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",19983,], +["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",19983,], +["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",19983,], +["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",19983,], +["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",19983,], +["features.createroom.impl.root_CreateRoomRootView_Day_0_en","features.createroom.impl.root_CreateRoomRootView_Night_0_en",19983,], +["features.createroom.impl.root_CreateRoomRootView_Day_1_en","features.createroom.impl.root_CreateRoomRootView_Night_1_en",19983,], +["features.createroom.impl.root_CreateRoomRootView_Day_2_en","features.createroom.impl.root_CreateRoomRootView_Night_2_en",19983,], +["features.createroom.impl.root_CreateRoomRootView_Day_3_en","features.createroom.impl.root_CreateRoomRootView_Night_3_en",19983,], +["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime pickers_en","",19983,], +["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime pickers_en","",19983,], ["features.logout.impl.direct_DefaultDirectLogoutView_Day_0_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_0_en",0,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",19972,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",19972,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",19972,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",19983,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",19983,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",19983,], ["features.logout.impl.direct_DefaultDirectLogoutView_Day_4_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_4_en",0,], -["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",19972,], -["features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en",19972,], -["features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en",19972,], +["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",19983,], +["features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en",19983,], +["features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en",19983,], ["features.licenses.impl.details_DependenciesDetailsView_Day_0_en","features.licenses.impl.details_DependenciesDetailsView_Night_0_en",0,], -["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",19975,], -["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",19975,], -["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",19975,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",19972,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",19972,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",19972,], -["libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Day_0_en","libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Night_0_en",19972,], +["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",19983,], +["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",19983,], +["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",19983,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",19983,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",19983,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",19983,], +["libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Day_0_en","libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Night_0_en",19983,], ["libraries.designsystem.theme.components_DialogWithDestructiveButton_Dialog with destructive button_Dialogs_en","",0,], ["libraries.designsystem.theme.components_DialogWithOnlyMessageAndOkButton_Dialog with only message and ok button_Dialogs_en","",0,], ["libraries.designsystem.theme.components_DialogWithThirdButton_Dialog with third button_Dialogs_en","",0,], @@ -238,12 +244,12 @@ export const screenshots = [ ["libraries.designsystem.text_DpScale_1_0f__en","",0,], ["libraries.designsystem.text_DpScale_1_5f__en","",0,], ["libraries.designsystem.theme.components_DropdownMenuItem_Menus_en","",0,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",19972,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",19972,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",19972,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",19972,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",19972,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",19972,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",19983,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",19983,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",19983,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",19983,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",19983,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",19983,], ["libraries.matrix.ui.components_EditableAvatarView_Day_0_en","libraries.matrix.ui.components_EditableAvatarView_Night_0_en",0,], ["libraries.matrix.ui.components_EditableAvatarView_Day_1_en","libraries.matrix.ui.components_EditableAvatarView_Night_1_en",0,], ["libraries.matrix.ui.components_EditableAvatarView_Day_2_en","libraries.matrix.ui.components_EditableAvatarView_Night_2_en",0,], @@ -253,10 +259,9 @@ export const screenshots = [ ["libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Night_0_en",0,], ["features.messages.impl.timeline.components.customreaction_EmojiItem_Day_0_en","features.messages.impl.timeline.components.customreaction_EmojiItem_Night_0_en",0,], ["features.messages.impl.timeline.components.customreaction_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction_EmojiPicker_Night_0_en",0,], -["features.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_Day_0_en","features.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_Night_0_en",19972,], -["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",19972,], -["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",19972,], -["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",19972,], +["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",19983,], +["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",19983,], +["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",19983,], ["features.messages.impl.timeline.debug_EventDebugInfoView_Day_0_en","features.messages.impl.timeline.debug_EventDebugInfoView_Night_0_en",0,], ["libraries.featureflag.ui_FeatureListView_Day_0_en","libraries.featureflag.ui_FeatureListView_Night_0_en",0,], ["libraries.designsystem.theme.components_FilledButtonLargeLowPadding_Buttons_en","",0,], @@ -267,15 +272,15 @@ export const screenshots = [ ["libraries.designsystem.theme.components_FloatingActionButton_Floating Action Buttons_en","",0,], ["libraries.designsystem.atomic.pages_FlowStepPage_Day_0_en","libraries.designsystem.atomic.pages_FlowStepPage_Night_0_en",0,], ["features.messages.impl.timeline.focus_FocusRequestStateView_Day_0_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_0_en",0,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",19972,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",19972,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",19972,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",19983,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",19983,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",19983,], ["libraries.textcomposer.components_FormattingOption_Day_0_en","libraries.textcomposer.components_FormattingOption_Night_0_en",0,], ["features.messages.impl.forward_ForwardMessagesView_Day_0_en","features.messages.impl.forward_ForwardMessagesView_Night_0_en",0,], ["features.messages.impl.forward_ForwardMessagesView_Day_1_en","features.messages.impl.forward_ForwardMessagesView_Night_1_en",0,], ["features.messages.impl.forward_ForwardMessagesView_Day_2_en","features.messages.impl.forward_ForwardMessagesView_Night_2_en",0,], -["features.messages.impl.forward_ForwardMessagesView_Day_3_en","features.messages.impl.forward_ForwardMessagesView_Night_3_en",19972,], -["features.roomlist.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.roomlist.impl.components_FullScreenIntentPermissionBanner_Night_0_en",19972,], +["features.messages.impl.forward_ForwardMessagesView_Day_3_en","features.messages.impl.forward_ForwardMessagesView_Night_3_en",19983,], +["features.roomlist.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.roomlist.impl.components_FullScreenIntentPermissionBanner_Night_0_en",19983,], ["libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Day_0_en","libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Night_0_en",0,], ["libraries.designsystem.components.button_GradientFloatingActionButton_Day_0_en","libraries.designsystem.components.button_GradientFloatingActionButton_Night_0_en",0,], ["features.messages.impl.timeline.components.group_GroupHeaderView_Day_0_en","features.messages.impl.timeline.components.group_GroupHeaderView_Night_0_en",0,], @@ -302,37 +307,37 @@ export const screenshots = [ ["libraries.matrix.ui.messages.reply_InReplyToView_Day_1_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_1_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_2_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_2_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_3_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_3_en",0,], -["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",19972,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",19983,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_5_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_5_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_6_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_6_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_7_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_7_en",0,], -["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",19972,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",19983,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_9_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_9_en",0,], -["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",19972,], +["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",19983,], ["libraries.designsystem.atomic.molecules_InfoListItemMolecule_Day_0_en","libraries.designsystem.atomic.molecules_InfoListItemMolecule_Night_0_en",0,], ["libraries.designsystem.atomic.organisms_InfoListOrganism_Day_0_en","libraries.designsystem.atomic.organisms_InfoListOrganism_Night_0_en",0,], -["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",19972,], +["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",19983,], ["features.joinroom.impl_JoinRoomView_Day_0_en","features.joinroom.impl_JoinRoomView_Night_0_en",0,], ["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",0,], -["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",19972,], -["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",19972,], -["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",19972,], -["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",19972,], -["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",19972,], -["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",19972,], -["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",19972,], -["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",19972,], -["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",19972,], +["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",19983,], +["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",19983,], +["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",19983,], +["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",19983,], +["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",19983,], +["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",19983,], +["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",19983,], +["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",19983,], +["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",19983,], ["libraries.designsystem.components_LabelledCheckbox_Toggles_en","",0,], ["libraries.designsystem.components_LabelledOutlinedTextField_Day_0_en","libraries.designsystem.components_LabelledOutlinedTextField_Night_0_en",0,], ["libraries.designsystem.components_LabelledTextField_Day_0_en","libraries.designsystem.components_LabelledTextField_Night_0_en",0,], ["features.leaveroom.api_LeaveRoomView_Day_0_en","features.leaveroom.api_LeaveRoomView_Night_0_en",0,], -["features.leaveroom.api_LeaveRoomView_Day_1_en","features.leaveroom.api_LeaveRoomView_Night_1_en",19972,], -["features.leaveroom.api_LeaveRoomView_Day_2_en","features.leaveroom.api_LeaveRoomView_Night_2_en",19972,], -["features.leaveroom.api_LeaveRoomView_Day_3_en","features.leaveroom.api_LeaveRoomView_Night_3_en",19972,], -["features.leaveroom.api_LeaveRoomView_Day_4_en","features.leaveroom.api_LeaveRoomView_Night_4_en",19972,], -["features.leaveroom.api_LeaveRoomView_Day_5_en","features.leaveroom.api_LeaveRoomView_Night_5_en",19972,], -["features.leaveroom.api_LeaveRoomView_Day_6_en","features.leaveroom.api_LeaveRoomView_Night_6_en",19972,], +["features.leaveroom.api_LeaveRoomView_Day_1_en","features.leaveroom.api_LeaveRoomView_Night_1_en",19983,], +["features.leaveroom.api_LeaveRoomView_Day_2_en","features.leaveroom.api_LeaveRoomView_Night_2_en",19983,], +["features.leaveroom.api_LeaveRoomView_Day_3_en","features.leaveroom.api_LeaveRoomView_Night_3_en",19983,], +["features.leaveroom.api_LeaveRoomView_Day_4_en","features.leaveroom.api_LeaveRoomView_Night_4_en",19983,], +["features.leaveroom.api_LeaveRoomView_Day_5_en","features.leaveroom.api_LeaveRoomView_Night_5_en",19983,], +["features.leaveroom.api_LeaveRoomView_Day_6_en","features.leaveroom.api_LeaveRoomView_Night_6_en",19983,], ["libraries.designsystem.background_LightGradientBackground_Day_0_en","libraries.designsystem.background_LightGradientBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_LinearProgressIndicator_Progress Indicators_en","",0,], ["libraries.designsystem.components.dialogs_ListDialogContent_Dialogs_en","",0,], @@ -383,28 +388,29 @@ export const screenshots = [ ["libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List supporting text - small padding_List sections_en","",0,], ["libraries.textcomposer.components_LiveWaveformView_Day_0_en","libraries.textcomposer.components_LiveWaveformView_Night_0_en",0,], ["appnav.room.joined_LoadingRoomNodeView_Day_0_en","appnav.room.joined_LoadingRoomNodeView_Night_0_en",0,], -["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",19972,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",19972,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",19972,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",19972,], +["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",19983,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",19983,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",19983,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",19983,], ["appnav.loggedin_LoggedInView_Day_0_en","appnav.loggedin_LoggedInView_Night_0_en",0,], -["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",19972,], -["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",19972,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",19972,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",19972,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",19972,], -["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",19972,], -["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",19972,], -["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",19972,], -["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",19972,], -["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",19972,], -["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",19972,], -["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",19972,], -["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",19972,], -["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",19972,], -["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",19972,], +["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",19983,], +["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",19983,], +["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",19983,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",19983,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",19983,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",19983,], +["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",19983,], +["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",19983,], +["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",19983,], +["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",19983,], +["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",19983,], +["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",19983,], +["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",19983,], +["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",19983,], +["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",19983,], +["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",19983,], ["libraries.designsystem.components.button_MainActionButton_Buttons_en","",0,], -["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",19972,], +["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",19983,], ["libraries.textcomposer.components.markdown_MarkdownTextInput_Day_0_en","libraries.textcomposer.components.markdown_MarkdownTextInput_Night_0_en",0,], ["libraries.matrix.ui.components_MatrixUserHeaderPlaceholder_Day_0_en","libraries.matrix.ui.components_MatrixUserHeaderPlaceholder_Night_0_en",0,], ["libraries.matrix.ui.components_MatrixUserHeader_Day_0_en","libraries.matrix.ui.components_MatrixUserHeader_Night_0_en",0,], @@ -414,7 +420,7 @@ export const screenshots = [ ["libraries.mediaviewer.api.viewer_MediaViewerView_0_en","",0,], ["libraries.mediaviewer.api.viewer_MediaViewerView_10_en","",0,], ["libraries.mediaviewer.api.viewer_MediaViewerView_1_en","",0,], -["libraries.mediaviewer.api.viewer_MediaViewerView_2_en","",19972,], +["libraries.mediaviewer.api.viewer_MediaViewerView_2_en","",19983,], ["libraries.mediaviewer.api.viewer_MediaViewerView_3_en","",0,], ["libraries.mediaviewer.api.viewer_MediaViewerView_4_en","",0,], ["libraries.mediaviewer.api.viewer_MediaViewerView_5_en","",0,], @@ -426,7 +432,7 @@ export const screenshots = [ ["libraries.textcomposer.mentions_MentionSpanTheme_Day_0_en","libraries.textcomposer.mentions_MentionSpanTheme_Night_0_en",0,], ["libraries.designsystem.theme.components.previews_Menu_Menus_en","",0,], ["features.messages.impl.messagecomposer_MessageComposerViewVoice_Day_0_en","features.messages.impl.messagecomposer_MessageComposerViewVoice_Night_0_en",0,], -["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",19972,], +["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",19983,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_0_en","features.messages.impl.timeline.components_MessageEventBubble_Night_0_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_10_en","features.messages.impl.timeline.components_MessageEventBubble_Night_10_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_11_en","features.messages.impl.timeline.components_MessageEventBubble_Night_11_en",0,], @@ -443,7 +449,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessageEventBubble_Day_7_en","features.messages.impl.timeline.components_MessageEventBubble_Night_7_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_8_en","features.messages.impl.timeline.components_MessageEventBubble_Night_8_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_9_en","features.messages.impl.timeline.components_MessageEventBubble_Night_9_en",0,], -["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",19972,], +["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",19983,], ["features.messages.impl.timeline.components_MessageStateEventContainer_Day_0_en","features.messages.impl.timeline.components_MessageStateEventContainer_Night_0_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButtonAdd_Day_0_en","features.messages.impl.timeline.components_MessagesReactionButtonAdd_Night_0_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButtonExtra_Day_0_en","features.messages.impl.timeline.components_MessagesReactionButtonExtra_Night_0_en",0,], @@ -451,25 +457,25 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessagesReactionButton_Day_1_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_1_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButton_Day_2_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_2_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButton_Day_3_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_3_en",0,], -["features.messages.impl.typing_MessagesViewWithTyping_Day_0_en","features.messages.impl.typing_MessagesViewWithTyping_Night_0_en",19972,], -["features.messages.impl.typing_MessagesViewWithTyping_Day_1_en","features.messages.impl.typing_MessagesViewWithTyping_Night_1_en",19972,], -["features.messages.impl.typing_MessagesViewWithTyping_Day_2_en","features.messages.impl.typing_MessagesViewWithTyping_Night_2_en",19972,], -["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",19972,], -["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",19972,], -["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",19972,], -["features.messages.impl_MessagesView_Day_12_en","features.messages.impl_MessagesView_Night_12_en",19972,], -["features.messages.impl_MessagesView_Day_13_en","features.messages.impl_MessagesView_Night_13_en",19972,], -["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",19972,], -["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",19972,], -["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",19972,], -["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",19972,], -["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",19972,], -["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",19972,], -["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",19972,], -["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",19972,], -["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",19972,], +["features.messages.impl.typing_MessagesViewWithTyping_Day_0_en","features.messages.impl.typing_MessagesViewWithTyping_Night_0_en",19983,], +["features.messages.impl.typing_MessagesViewWithTyping_Day_1_en","features.messages.impl.typing_MessagesViewWithTyping_Night_1_en",19983,], +["features.messages.impl.typing_MessagesViewWithTyping_Day_2_en","features.messages.impl.typing_MessagesViewWithTyping_Night_2_en",19983,], +["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",19983,], +["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",19983,], +["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",19983,], +["features.messages.impl_MessagesView_Day_12_en","features.messages.impl_MessagesView_Night_12_en",19983,], +["features.messages.impl_MessagesView_Day_13_en","features.messages.impl_MessagesView_Night_13_en",19983,], +["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",19983,], +["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",19983,], +["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",19983,], +["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",19983,], +["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",19983,], +["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",19983,], +["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",19983,], +["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",19983,], +["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",19983,], ["features.migration.impl_MigrationView_Day_0_en","features.migration.impl_MigrationView_Night_0_en",0,], -["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",19972,], +["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",19983,], ["libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom Sheets_en","",0,], ["libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom Sheets_en","",0,], ["appicon.element_MonochromeIcon_en","",0,], @@ -478,28 +484,29 @@ export const screenshots = [ ["libraries.designsystem.components.list_MutipleSelectionListItemSelectedTrailingContent_Multiple selection List item - selection in trailing content_List items_en","",0,], ["libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple selection List item - selection in supporting text_List items_en","",0,], ["libraries.designsystem.components.list_MutipleSelectionListItem_Multiple selection List item - no selection_List items_en","",0,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",19972,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",19972,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",19972,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",19972,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",19972,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",19972,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",19972,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",19972,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",19972,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",19972,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",19972,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",19972,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",19972,], -["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",19972,], +["features.roomlist.impl.components_NativeSlidingSyncMigrationBanner_Day_0_en","features.roomlist.impl.components_NativeSlidingSyncMigrationBanner_Night_0_en",19983,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",19983,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",19983,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",19983,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",19983,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",19983,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",19983,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",19983,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",19983,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",19983,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",19983,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",19983,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",19983,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",19983,], +["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",19983,], ["libraries.oidc.impl.webview_OidcView_Day_0_en","libraries.oidc.impl.webview_OidcView_Night_0_en",0,], ["libraries.oidc.impl.webview_OidcView_Day_1_en","libraries.oidc.impl.webview_OidcView_Night_1_en",0,], ["libraries.designsystem.atomic.pages_OnBoardingPage_Day_0_en","libraries.designsystem.atomic.pages_OnBoardingPage_Night_0_en",0,], -["features.onboarding.impl_OnBoardingView_Day_0_en","features.onboarding.impl_OnBoardingView_Night_0_en",19972,], -["features.onboarding.impl_OnBoardingView_Day_1_en","features.onboarding.impl_OnBoardingView_Night_1_en",19972,], -["features.onboarding.impl_OnBoardingView_Day_2_en","features.onboarding.impl_OnBoardingView_Night_2_en",19972,], -["features.onboarding.impl_OnBoardingView_Day_3_en","features.onboarding.impl_OnBoardingView_Night_3_en",19972,], -["features.onboarding.impl_OnBoardingView_Day_4_en","features.onboarding.impl_OnBoardingView_Night_4_en",19972,], +["features.onboarding.impl_OnBoardingView_Day_0_en","features.onboarding.impl_OnBoardingView_Night_0_en",19983,], +["features.onboarding.impl_OnBoardingView_Day_1_en","features.onboarding.impl_OnBoardingView_Night_1_en",19983,], +["features.onboarding.impl_OnBoardingView_Day_2_en","features.onboarding.impl_OnBoardingView_Night_2_en",19983,], +["features.onboarding.impl_OnBoardingView_Day_3_en","features.onboarding.impl_OnBoardingView_Night_3_en",19983,], +["features.onboarding.impl_OnBoardingView_Day_4_en","features.onboarding.impl_OnBoardingView_Night_4_en",19983,], ["libraries.designsystem.background_OnboardingBackground_Day_0_en","libraries.designsystem.background_OnboardingBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_OutlinedButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonLarge_Buttons_en","",0,], @@ -514,62 +521,62 @@ export const screenshots = [ ["libraries.designsystem.components_PageTitleWithIconFull_Day_3_en","libraries.designsystem.components_PageTitleWithIconFull_Night_3_en",0,], ["libraries.designsystem.components_PageTitleWithIconFull_Day_4_en","libraries.designsystem.components_PageTitleWithIconFull_Night_4_en",0,], ["libraries.designsystem.components_PageTitleWithIconMinimal_Day_0_en","libraries.designsystem.components_PageTitleWithIconMinimal_Night_0_en",0,], -["features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Night_0_en",19972,], -["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",19972,], -["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",19972,], -["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",19972,], -["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",19972,], +["features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Night_0_en",19983,], +["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",19983,], +["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",19983,], +["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",19983,], +["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",19983,], ["features.lockscreen.impl.components_PinEntryTextField_Day_0_en","features.lockscreen.impl.components_PinEntryTextField_Night_0_en",0,], ["libraries.designsystem.components_PinIcon_Day_0_en","libraries.designsystem.components_PinIcon_Night_0_en",0,], ["features.lockscreen.impl.unlock.keypad_PinKeypad_Day_0_en","features.lockscreen.impl.unlock.keypad_PinKeypad_Night_0_en",0,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",19972,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",19972,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",19972,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",19972,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",19972,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",19972,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",19972,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",19972,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",19972,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",19972,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",19972,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",19972,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",19972,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",19972,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",19983,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",19983,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",19983,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",19983,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",19983,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",19983,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",19983,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",19983,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",19983,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",19983,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",19983,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",19983,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",19983,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",19983,], ["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_0_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_0_en",0,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",19975,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",0,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",19972,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",19972,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",19972,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",19972,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",19972,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",19972,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",19972,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",19972,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",19975,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",0,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",0,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",0,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",19983,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",19983,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",19983,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",19983,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",19983,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",19983,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",19983,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",19983,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",19983,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",19983,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",19983,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",19983,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",19983,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",19983,], ["libraries.designsystem.atomic.atoms_PlaceholderAtom_Day_0_en","libraries.designsystem.atomic.atoms_PlaceholderAtom_Night_0_en",0,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",19972,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",19972,], -["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",19972,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",19972,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",19972,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",19983,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",19983,], +["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",19983,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",19983,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",19983,], ["features.poll.api.pollcontent_PollAnswerViewUndisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewUndisclosedNotSelected_Night_0_en",0,], ["features.poll.api.pollcontent_PollAnswerViewUndisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewUndisclosedSelected_Night_0_en",0,], -["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",19972,], -["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",19972,], -["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",19972,], -["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",19972,], -["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",19972,], -["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",19972,], -["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",19972,], -["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",19972,], -["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",19972,], -["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",19972,], -["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",19972,], +["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",19983,], +["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",19983,], +["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",19983,], +["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",19983,], +["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",19983,], +["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",19983,], +["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",19983,], +["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",19983,], +["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",19983,], +["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",19983,], +["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",19983,], ["features.poll.api.pollcontent_PollTitleView_Day_0_en","features.poll.api.pollcontent_PollTitleView_Night_0_en",0,], ["libraries.designsystem.components.preferences_PreferenceCategory_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceCheckbox_Preferences_en","",0,], @@ -586,191 +593,195 @@ export const screenshots = [ ["libraries.designsystem.components.preferences_PreferenceTextLight_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceTextWithEndBadgeDark_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceTextWithEndBadgeLight_Preferences_en","",0,], -["features.preferences.impl.root_PreferencesRootViewDark_0_en","",19972,], -["features.preferences.impl.root_PreferencesRootViewDark_1_en","",19972,], -["features.preferences.impl.root_PreferencesRootViewLight_0_en","",19972,], -["features.preferences.impl.root_PreferencesRootViewLight_1_en","",19972,], +["features.preferences.impl.root_PreferencesRootViewDark_0_en","",19983,], +["features.preferences.impl.root_PreferencesRootViewDark_1_en","",19983,], +["features.preferences.impl.root_PreferencesRootViewLight_0_en","",19983,], +["features.preferences.impl.root_PreferencesRootViewLight_1_en","",19983,], ["features.messages.impl.timeline.components.event_ProgressButton_Day_0_en","features.messages.impl.timeline.components.event_ProgressButton_Night_0_en",0,], -["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",19972,], -["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",19972,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",19972,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",19972,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",19972,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",19972,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",19972,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",19972,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",19972,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",19972,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",19972,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",19972,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",19972,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",19972,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",19972,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",19972,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",19972,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",19972,], +["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",19983,], +["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",19983,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",19983,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",19983,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",19983,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",19983,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",19983,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",19983,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",19983,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",19983,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",19983,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",19983,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",19983,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",19983,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",19983,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",19983,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",19983,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",19983,], ["libraries.designsystem.theme.components_RadioButton_Toggles_en","",0,], -["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",19972,], -["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",19972,], +["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",19983,], +["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",19983,], ["features.rageshake.api.preferences_RageshakePreferencesView_Day_1_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_1_en",0,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",19972,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",19972,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",19972,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",19972,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",19972,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",19972,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",19972,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",19972,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",19972,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",19975,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",19975,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",19972,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",19972,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",19972,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",19972,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",19972,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",19972,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",19972,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",19972,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",19972,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",19983,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",19983,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",19983,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",19983,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",19983,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",19983,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",19983,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",19983,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",19983,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",19983,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",19983,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",19983,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",19983,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",19983,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",19983,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",19983,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",19983,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",19983,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",19983,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",19983,], ["libraries.designsystem.atomic.atoms_RedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_RedIndicatorAtom_Night_0_en",0,], ["features.messages.impl.timeline.components_ReplySwipeIndicator_Day_0_en","features.messages.impl.timeline.components_ReplySwipeIndicator_Night_0_en",0,], -["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",19972,], -["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",19972,], -["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",19972,], -["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",19972,], -["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",19972,], -["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",19972,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",19972,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",19972,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",19972,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",19972,], +["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",19983,], +["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",19983,], +["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",19983,], +["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",19983,], +["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",19983,], +["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",19983,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",19983,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",19983,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",19983,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",19983,], ["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",0,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",19972,], -["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",19972,], -["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",19972,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en",19972,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en",19972,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en",19972,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en",19972,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en",19972,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en",19972,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en",19972,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en",19972,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",19983,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_0_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_0_en",0,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",19983,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",19983,], +["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",19983,], +["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",19983,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en",19983,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en",19983,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en",19983,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en",19983,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en",19983,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en",19983,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en",19983,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en",19983,], ["features.roomaliasresolver.impl_RoomAliasResolverView_Day_0_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_0_en",0,], ["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",0,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",19972,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",19983,], ["features.roomdetails.impl.components_RoomBadgeNegative_Day_0_en","features.roomdetails.impl.components_RoomBadgeNegative_Night_0_en",0,], ["features.roomdetails.impl.components_RoomBadgeNeutral_Day_0_en","features.roomdetails.impl.components_RoomBadgeNeutral_Night_0_en",0,], ["features.roomdetails.impl.components_RoomBadgePositive_Day_0_en","features.roomdetails.impl.components_RoomBadgePositive_Night_0_en",0,], -["features.roomdetails.impl_RoomDetailsDark_0_en","",19972,], -["features.roomdetails.impl_RoomDetailsDark_10_en","",19972,], -["features.roomdetails.impl_RoomDetailsDark_11_en","",19972,], -["features.roomdetails.impl_RoomDetailsDark_12_en","",19972,], -["features.roomdetails.impl_RoomDetailsDark_13_en","",19975,], -["features.roomdetails.impl_RoomDetailsDark_1_en","",19972,], -["features.roomdetails.impl_RoomDetailsDark_2_en","",19972,], -["features.roomdetails.impl_RoomDetailsDark_3_en","",19972,], -["features.roomdetails.impl_RoomDetailsDark_4_en","",19972,], -["features.roomdetails.impl_RoomDetailsDark_5_en","",19972,], -["features.roomdetails.impl_RoomDetailsDark_6_en","",19972,], -["features.roomdetails.impl_RoomDetailsDark_7_en","",19972,], -["features.roomdetails.impl_RoomDetailsDark_8_en","",19972,], -["features.roomdetails.impl_RoomDetailsDark_9_en","",19972,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_0_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_0_en",19972,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_1_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_1_en",19972,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_2_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_2_en",19972,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_3_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_3_en",19972,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_4_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_4_en",19972,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_5_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_5_en",19972,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_6_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_6_en",19972,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_7_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_7_en",19972,], -["features.roomdetails.impl_RoomDetails_0_en","",19972,], -["features.roomdetails.impl_RoomDetails_10_en","",19972,], -["features.roomdetails.impl_RoomDetails_11_en","",19972,], -["features.roomdetails.impl_RoomDetails_12_en","",19972,], -["features.roomdetails.impl_RoomDetails_13_en","",19975,], -["features.roomdetails.impl_RoomDetails_1_en","",19972,], -["features.roomdetails.impl_RoomDetails_2_en","",19972,], -["features.roomdetails.impl_RoomDetails_3_en","",19972,], -["features.roomdetails.impl_RoomDetails_4_en","",19972,], -["features.roomdetails.impl_RoomDetails_5_en","",19972,], -["features.roomdetails.impl_RoomDetails_6_en","",19972,], -["features.roomdetails.impl_RoomDetails_7_en","",19972,], -["features.roomdetails.impl_RoomDetails_8_en","",19972,], -["features.roomdetails.impl_RoomDetails_9_en","",19972,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",19972,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",19972,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",19972,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",19972,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",19972,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",19972,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",19972,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_4_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_4_en",19972,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_5_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_5_en",19972,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_6_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_6_en",19972,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_7_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_7_en",19972,], -["features.roomlist.impl.components_RoomListContentView_Day_0_en","features.roomlist.impl.components_RoomListContentView_Night_0_en",19972,], -["features.roomlist.impl.components_RoomListContentView_Day_1_en","features.roomlist.impl.components_RoomListContentView_Night_1_en",19972,], +["features.roomdetails.impl_RoomDetailsDark_0_en","",19983,], +["features.roomdetails.impl_RoomDetailsDark_10_en","",19983,], +["features.roomdetails.impl_RoomDetailsDark_11_en","",19983,], +["features.roomdetails.impl_RoomDetailsDark_12_en","",19983,], +["features.roomdetails.impl_RoomDetailsDark_13_en","",19983,], +["features.roomdetails.impl_RoomDetailsDark_1_en","",19983,], +["features.roomdetails.impl_RoomDetailsDark_2_en","",19983,], +["features.roomdetails.impl_RoomDetailsDark_3_en","",19983,], +["features.roomdetails.impl_RoomDetailsDark_4_en","",19983,], +["features.roomdetails.impl_RoomDetailsDark_5_en","",19983,], +["features.roomdetails.impl_RoomDetailsDark_6_en","",19983,], +["features.roomdetails.impl_RoomDetailsDark_7_en","",19983,], +["features.roomdetails.impl_RoomDetailsDark_8_en","",19983,], +["features.roomdetails.impl_RoomDetailsDark_9_en","",19983,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_0_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_0_en",19983,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_1_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_1_en",19983,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_2_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_2_en",19983,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_3_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_3_en",19983,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_4_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_4_en",19983,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_5_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_5_en",19983,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_6_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_6_en",19983,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_7_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_7_en",19983,], +["features.roomdetails.impl_RoomDetails_0_en","",19983,], +["features.roomdetails.impl_RoomDetails_10_en","",19983,], +["features.roomdetails.impl_RoomDetails_11_en","",19983,], +["features.roomdetails.impl_RoomDetails_12_en","",19983,], +["features.roomdetails.impl_RoomDetails_13_en","",19983,], +["features.roomdetails.impl_RoomDetails_1_en","",19983,], +["features.roomdetails.impl_RoomDetails_2_en","",19983,], +["features.roomdetails.impl_RoomDetails_3_en","",19983,], +["features.roomdetails.impl_RoomDetails_4_en","",19983,], +["features.roomdetails.impl_RoomDetails_5_en","",19983,], +["features.roomdetails.impl_RoomDetails_6_en","",19983,], +["features.roomdetails.impl_RoomDetails_7_en","",19983,], +["features.roomdetails.impl_RoomDetails_8_en","",19983,], +["features.roomdetails.impl_RoomDetails_9_en","",19983,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",19983,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",19983,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",19983,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",19983,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",19983,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",19983,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",19983,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_4_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_4_en",19983,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_5_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_5_en",19983,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_6_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_6_en",19983,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_7_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_7_en",19983,], +["features.roomlist.impl.components_RoomListContentView_Day_0_en","features.roomlist.impl.components_RoomListContentView_Night_0_en",19983,], +["features.roomlist.impl.components_RoomListContentView_Day_1_en","features.roomlist.impl.components_RoomListContentView_Night_1_en",19983,], ["features.roomlist.impl.components_RoomListContentView_Day_2_en","features.roomlist.impl.components_RoomListContentView_Night_2_en",0,], -["features.roomlist.impl.components_RoomListContentView_Day_3_en","features.roomlist.impl.components_RoomListContentView_Night_3_en",19972,], -["features.roomlist.impl.filters_RoomListFiltersView_Day_0_en","features.roomlist.impl.filters_RoomListFiltersView_Night_0_en",19972,], -["features.roomlist.impl.filters_RoomListFiltersView_Day_1_en","features.roomlist.impl.filters_RoomListFiltersView_Night_1_en",19972,], -["features.roomlist.impl_RoomListModalBottomSheetContent_Day_0_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_0_en",19972,], -["features.roomlist.impl_RoomListModalBottomSheetContent_Day_1_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_1_en",19972,], -["features.roomlist.impl_RoomListModalBottomSheetContent_Day_2_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_2_en",19972,], +["features.roomlist.impl.components_RoomListContentView_Day_3_en","features.roomlist.impl.components_RoomListContentView_Night_3_en",19983,], +["features.roomlist.impl.components_RoomListContentView_Day_4_en","features.roomlist.impl.components_RoomListContentView_Night_4_en",19983,], +["features.roomlist.impl.filters_RoomListFiltersView_Day_0_en","features.roomlist.impl.filters_RoomListFiltersView_Night_0_en",19983,], +["features.roomlist.impl.filters_RoomListFiltersView_Day_1_en","features.roomlist.impl.filters_RoomListFiltersView_Night_1_en",19983,], +["features.roomlist.impl_RoomListModalBottomSheetContent_Day_0_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_0_en",19983,], +["features.roomlist.impl_RoomListModalBottomSheetContent_Day_1_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_1_en",19983,], +["features.roomlist.impl_RoomListModalBottomSheetContent_Day_2_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_2_en",19983,], ["features.roomlist.impl.search_RoomListSearchContent_Day_0_en","features.roomlist.impl.search_RoomListSearchContent_Night_0_en",0,], -["features.roomlist.impl.search_RoomListSearchContent_Day_1_en","features.roomlist.impl.search_RoomListSearchContent_Night_1_en",19972,], -["features.roomlist.impl.search_RoomListSearchContent_Day_2_en","features.roomlist.impl.search_RoomListSearchContent_Night_2_en",19972,], -["features.roomlist.impl_RoomListView_Day_0_en","features.roomlist.impl_RoomListView_Night_0_en",19972,], -["features.roomlist.impl_RoomListView_Day_10_en","features.roomlist.impl_RoomListView_Night_10_en",19975,], -["features.roomlist.impl_RoomListView_Day_1_en","features.roomlist.impl_RoomListView_Night_1_en",19972,], -["features.roomlist.impl_RoomListView_Day_2_en","features.roomlist.impl_RoomListView_Night_2_en",19972,], -["features.roomlist.impl_RoomListView_Day_3_en","features.roomlist.impl_RoomListView_Night_3_en",19972,], -["features.roomlist.impl_RoomListView_Day_4_en","features.roomlist.impl_RoomListView_Night_4_en",19972,], -["features.roomlist.impl_RoomListView_Day_5_en","features.roomlist.impl_RoomListView_Night_5_en",19972,], -["features.roomlist.impl_RoomListView_Day_6_en","features.roomlist.impl_RoomListView_Night_6_en",19972,], -["features.roomlist.impl_RoomListView_Day_7_en","features.roomlist.impl_RoomListView_Night_7_en",19972,], +["features.roomlist.impl.search_RoomListSearchContent_Day_1_en","features.roomlist.impl.search_RoomListSearchContent_Night_1_en",19983,], +["features.roomlist.impl.search_RoomListSearchContent_Day_2_en","features.roomlist.impl.search_RoomListSearchContent_Night_2_en",19983,], +["features.roomlist.impl_RoomListView_Day_0_en","features.roomlist.impl_RoomListView_Night_0_en",19983,], +["features.roomlist.impl_RoomListView_Day_10_en","features.roomlist.impl_RoomListView_Night_10_en",19983,], +["features.roomlist.impl_RoomListView_Day_1_en","features.roomlist.impl_RoomListView_Night_1_en",19983,], +["features.roomlist.impl_RoomListView_Day_2_en","features.roomlist.impl_RoomListView_Night_2_en",19983,], +["features.roomlist.impl_RoomListView_Day_3_en","features.roomlist.impl_RoomListView_Night_3_en",19983,], +["features.roomlist.impl_RoomListView_Day_4_en","features.roomlist.impl_RoomListView_Night_4_en",19983,], +["features.roomlist.impl_RoomListView_Day_5_en","features.roomlist.impl_RoomListView_Night_5_en",19983,], +["features.roomlist.impl_RoomListView_Day_6_en","features.roomlist.impl_RoomListView_Night_6_en",19983,], +["features.roomlist.impl_RoomListView_Day_7_en","features.roomlist.impl_RoomListView_Night_7_en",19983,], ["features.roomlist.impl_RoomListView_Day_8_en","features.roomlist.impl_RoomListView_Night_8_en",0,], ["features.roomlist.impl_RoomListView_Day_9_en","features.roomlist.impl_RoomListView_Night_9_en",0,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_0_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_0_en",19972,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_1_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_1_en",19972,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_2_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_2_en",19972,], -["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",19972,], -["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",19972,], -["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",19972,], -["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",19972,], -["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",19972,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_0_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_0_en",19983,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_1_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_1_en",19983,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_2_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_2_en",19983,], +["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",19983,], +["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",19983,], +["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",19983,], +["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",19983,], +["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",19983,], ["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",0,], -["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",19972,], -["features.roomdetails.impl.members_RoomMemberListView_Day_7_en","features.roomdetails.impl.members_RoomMemberListView_Night_7_en",19972,], -["features.roomdetails.impl.members_RoomMemberListView_Day_8_en","features.roomdetails.impl.members_RoomMemberListView_Night_8_en",19972,], +["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",19983,], +["features.roomdetails.impl.members_RoomMemberListView_Day_7_en","features.roomdetails.impl.members_RoomMemberListView_Night_7_en",19983,], +["features.roomdetails.impl.members_RoomMemberListView_Day_8_en","features.roomdetails.impl.members_RoomMemberListView_Night_8_en",19983,], ["libraries.designsystem.atomic.molecules_RoomMembersCountMolecule_Day_0_en","libraries.designsystem.atomic.molecules_RoomMembersCountMolecule_Night_0_en",0,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en",19972,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en",19972,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en",19972,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_3_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_3_en",19972,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_4_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_4_en",19972,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en",19972,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_6_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_6_en",19972,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_7_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_7_en",19972,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_8_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_8_en",19972,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en",19983,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en",19983,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en",19983,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_3_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_3_en",19983,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_4_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_4_en",19983,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en",19983,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_6_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_6_en",19983,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_7_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_7_en",19983,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_8_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_8_en",19983,], ["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_9_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_9_en",0,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",19972,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",19972,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",19972,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",19972,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",19972,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",19972,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",19972,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",19972,], -["features.createroom.impl.components_RoomPrivacyOption_Day_0_en","features.createroom.impl.components_RoomPrivacyOption_Night_0_en",19972,], -["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",19972,], -["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",19972,], -["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",19972,], -["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",19972,], -["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",19972,], -["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",19972,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",19983,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",19983,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",19983,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",19983,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",19983,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",19983,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",19983,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",19983,], +["features.createroom.impl.components_RoomPrivacyOption_Day_0_en","features.createroom.impl.components_RoomPrivacyOption_Night_0_en",19983,], +["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",19983,], +["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",19983,], +["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",19983,], +["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",19983,], +["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",19983,], +["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",19983,], ["features.roomlist.impl.components_RoomSummaryPlaceholderRow_Day_0_en","features.roomlist.impl.components_RoomSummaryPlaceholderRow_Night_0_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_0_en","features.roomlist.impl.components_RoomSummaryRow_Night_0_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_10_en","features.roomlist.impl.components_RoomSummaryRow_Night_10_en",0,], @@ -793,10 +804,10 @@ export const screenshots = [ ["features.roomlist.impl.components_RoomSummaryRow_Day_26_en","features.roomlist.impl.components_RoomSummaryRow_Night_26_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_27_en","features.roomlist.impl.components_RoomSummaryRow_Night_27_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_28_en","features.roomlist.impl.components_RoomSummaryRow_Night_28_en",0,], -["features.roomlist.impl.components_RoomSummaryRow_Day_29_en","features.roomlist.impl.components_RoomSummaryRow_Night_29_en",19972,], -["features.roomlist.impl.components_RoomSummaryRow_Day_2_en","features.roomlist.impl.components_RoomSummaryRow_Night_2_en",19972,], -["features.roomlist.impl.components_RoomSummaryRow_Day_30_en","features.roomlist.impl.components_RoomSummaryRow_Night_30_en",19972,], -["features.roomlist.impl.components_RoomSummaryRow_Day_31_en","features.roomlist.impl.components_RoomSummaryRow_Night_31_en",19972,], +["features.roomlist.impl.components_RoomSummaryRow_Day_29_en","features.roomlist.impl.components_RoomSummaryRow_Night_29_en",19983,], +["features.roomlist.impl.components_RoomSummaryRow_Day_2_en","features.roomlist.impl.components_RoomSummaryRow_Night_2_en",19983,], +["features.roomlist.impl.components_RoomSummaryRow_Day_30_en","features.roomlist.impl.components_RoomSummaryRow_Night_30_en",19983,], +["features.roomlist.impl.components_RoomSummaryRow_Day_31_en","features.roomlist.impl.components_RoomSummaryRow_Night_31_en",19983,], ["features.roomlist.impl.components_RoomSummaryRow_Day_3_en","features.roomlist.impl.components_RoomSummaryRow_Night_3_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_4_en","features.roomlist.impl.components_RoomSummaryRow_Night_4_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_5_en","features.roomlist.impl.components_RoomSummaryRow_Night_5_en",0,], @@ -804,64 +815,64 @@ export const screenshots = [ ["features.roomlist.impl.components_RoomSummaryRow_Day_7_en","features.roomlist.impl.components_RoomSummaryRow_Night_7_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_8_en","features.roomlist.impl.components_RoomSummaryRow_Night_8_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_9_en","features.roomlist.impl.components_RoomSummaryRow_Night_9_en",0,], -["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",19972,], -["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",19972,], -["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",19972,], +["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",19983,], +["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",19983,], +["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",19983,], ["appicon.element_RoundIcon_en","",0,], ["appicon.enterprise_RoundIcon_en","",0,], ["libraries.designsystem.atomic.atoms_RoundedIconAtom_Day_0_en","libraries.designsystem.atomic.atoms_RoundedIconAtom_Night_0_en",0,], -["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",19972,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",19972,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",19972,], +["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",19983,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",19983,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",19983,], ["libraries.designsystem.theme.components_SearchBarActiveNoneQuery_Search views_en","",0,], ["libraries.designsystem.theme.components_SearchBarActiveWithContent_Search views_en","",0,], -["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search views_en","",19972,], +["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search views_en","",19983,], ["libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search views_en","",0,], ["libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search views_en","",0,], ["libraries.designsystem.theme.components_SearchBarInactive_Search views_en","",0,], -["features.createroom.impl.components_SearchMultipleUsersResultItem_en","",19972,], -["features.createroom.impl.components_SearchSingleUserResultItem_en","",19972,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",19972,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",19972,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",19972,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",19972,], -["features.securebackup.impl.enable_SecureBackupEnableView_Day_0_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_0_en",19972,], -["features.securebackup.impl.enable_SecureBackupEnableView_Day_1_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_1_en",19972,], -["features.securebackup.impl.enable_SecureBackupEnableView_Day_2_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_2_en",19972,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",19972,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",19972,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",19972,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",19972,], -["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",19972,], -["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",19972,], -["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",19972,], -["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",19972,], -["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",19972,], -["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",19972,], -["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",19972,], -["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",19972,], -["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",19972,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",19972,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",19972,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",19972,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",19972,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",19972,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",19972,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",19972,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",19972,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",19972,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",19972,], +["features.createroom.impl.components_SearchMultipleUsersResultItem_en","",19983,], +["features.createroom.impl.components_SearchSingleUserResultItem_en","",19983,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",19983,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",19983,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",19983,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",19983,], +["features.securebackup.impl.enable_SecureBackupEnableView_Day_0_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_0_en",19983,], +["features.securebackup.impl.enable_SecureBackupEnableView_Day_1_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_1_en",19983,], +["features.securebackup.impl.enable_SecureBackupEnableView_Day_2_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_2_en",19983,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",19983,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",19983,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",19983,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",19983,], +["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",19983,], +["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",19983,], +["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",19983,], +["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",19983,], +["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",19983,], +["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",19983,], +["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",19983,], +["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",19983,], +["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",19983,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",19983,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",19983,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",19983,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",19983,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",19983,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",19983,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",19983,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",19983,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",19983,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",19983,], ["libraries.matrix.ui.components_SelectedRoom_Day_0_en","libraries.matrix.ui.components_SelectedRoom_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoom_Day_1_en","libraries.matrix.ui.components_SelectedRoom_Night_1_en",0,], ["libraries.matrix.ui.components_SelectedUserCannotRemove_Day_0_en","libraries.matrix.ui.components_SelectedUserCannotRemove_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedUser_Day_0_en","libraries.matrix.ui.components_SelectedUser_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedUsersRowList_Day_0_en","libraries.matrix.ui.components_SelectedUsersRowList_Night_0_en",0,], ["libraries.textcomposer.components_SendButton_Day_0_en","libraries.textcomposer.components_SendButton_Night_0_en",0,], -["features.location.impl.send_SendLocationView_Day_0_en","features.location.impl.send_SendLocationView_Night_0_en",19972,], -["features.location.impl.send_SendLocationView_Day_1_en","features.location.impl.send_SendLocationView_Night_1_en",19972,], -["features.location.impl.send_SendLocationView_Day_2_en","features.location.impl.send_SendLocationView_Night_2_en",19972,], -["features.location.impl.send_SendLocationView_Day_3_en","features.location.impl.send_SendLocationView_Night_3_en",19972,], -["features.location.impl.send_SendLocationView_Day_4_en","features.location.impl.send_SendLocationView_Night_4_en",19972,], +["features.location.impl.send_SendLocationView_Day_0_en","features.location.impl.send_SendLocationView_Night_0_en",19983,], +["features.location.impl.send_SendLocationView_Day_1_en","features.location.impl.send_SendLocationView_Night_1_en",19983,], +["features.location.impl.send_SendLocationView_Day_2_en","features.location.impl.send_SendLocationView_Night_2_en",19983,], +["features.location.impl.send_SendLocationView_Day_3_en","features.location.impl.send_SendLocationView_Night_3_en",19983,], +["features.location.impl.send_SendLocationView_Day_4_en","features.location.impl.send_SendLocationView_Night_4_en",19983,], ["libraries.matrix.ui.messages.sender_SenderName_Day_0_en","libraries.matrix.ui.messages.sender_SenderName_Night_0_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_1_en","libraries.matrix.ui.messages.sender_SenderName_Night_1_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_2_en","libraries.matrix.ui.messages.sender_SenderName_Night_2_en",0,], @@ -871,39 +882,40 @@ export const screenshots = [ ["libraries.matrix.ui.messages.sender_SenderName_Day_6_en","libraries.matrix.ui.messages.sender_SenderName_Night_6_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_7_en","libraries.matrix.ui.messages.sender_SenderName_Night_7_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_8_en","libraries.matrix.ui.messages.sender_SenderName_Night_8_en",0,], -["features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en",19975,], -["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",19972,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",19972,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",19972,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",19972,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",19972,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",19972,], +["features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en",19983,], +["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",19983,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",19983,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",19983,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",19983,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",19983,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",19983,], ["features.share.impl_ShareView_Day_0_en","features.share.impl_ShareView_Night_0_en",0,], ["features.share.impl_ShareView_Day_1_en","features.share.impl_ShareView_Night_1_en",0,], ["features.share.impl_ShareView_Day_2_en","features.share.impl_ShareView_Night_2_en",0,], -["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",19972,], +["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",19983,], ["features.messages.impl.actionlist_SheetContent_Day_0_en","features.messages.impl.actionlist_SheetContent_Night_0_en",0,], ["features.messages.impl.timeline.components.reactionsummary_SheetContent_Day_0_en","features.messages.impl.timeline.components.reactionsummary_SheetContent_Night_0_en",0,], -["features.messages.impl.actionlist_SheetContent_Day_10_en","features.messages.impl.actionlist_SheetContent_Night_10_en",19972,], -["features.messages.impl.actionlist_SheetContent_Day_11_en","features.messages.impl.actionlist_SheetContent_Night_11_en",19972,], +["features.messages.impl.actionlist_SheetContent_Day_10_en","features.messages.impl.actionlist_SheetContent_Night_10_en",19983,], +["features.messages.impl.actionlist_SheetContent_Day_11_en","features.messages.impl.actionlist_SheetContent_Night_11_en",19983,], +["features.messages.impl.actionlist_SheetContent_Day_12_en","features.messages.impl.actionlist_SheetContent_Night_12_en",19983,], ["features.messages.impl.actionlist_SheetContent_Day_1_en","features.messages.impl.actionlist_SheetContent_Night_1_en",0,], -["features.messages.impl.actionlist_SheetContent_Day_2_en","features.messages.impl.actionlist_SheetContent_Night_2_en",19972,], -["features.messages.impl.actionlist_SheetContent_Day_3_en","features.messages.impl.actionlist_SheetContent_Night_3_en",19972,], -["features.messages.impl.actionlist_SheetContent_Day_4_en","features.messages.impl.actionlist_SheetContent_Night_4_en",19972,], -["features.messages.impl.actionlist_SheetContent_Day_5_en","features.messages.impl.actionlist_SheetContent_Night_5_en",19972,], -["features.messages.impl.actionlist_SheetContent_Day_6_en","features.messages.impl.actionlist_SheetContent_Night_6_en",19972,], -["features.messages.impl.actionlist_SheetContent_Day_7_en","features.messages.impl.actionlist_SheetContent_Night_7_en",19972,], -["features.messages.impl.actionlist_SheetContent_Day_8_en","features.messages.impl.actionlist_SheetContent_Night_8_en",19972,], -["features.messages.impl.actionlist_SheetContent_Day_9_en","features.messages.impl.actionlist_SheetContent_Night_9_en",19972,], -["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",19972,], -["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",19972,], -["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",19972,], -["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",19972,], -["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",19972,], -["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",19972,], -["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",19972,], -["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",19972,], -["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",19972,], +["features.messages.impl.actionlist_SheetContent_Day_2_en","features.messages.impl.actionlist_SheetContent_Night_2_en",19983,], +["features.messages.impl.actionlist_SheetContent_Day_3_en","features.messages.impl.actionlist_SheetContent_Night_3_en",19983,], +["features.messages.impl.actionlist_SheetContent_Day_4_en","features.messages.impl.actionlist_SheetContent_Night_4_en",19983,], +["features.messages.impl.actionlist_SheetContent_Day_5_en","features.messages.impl.actionlist_SheetContent_Night_5_en",19983,], +["features.messages.impl.actionlist_SheetContent_Day_6_en","features.messages.impl.actionlist_SheetContent_Night_6_en",19983,], +["features.messages.impl.actionlist_SheetContent_Day_7_en","features.messages.impl.actionlist_SheetContent_Night_7_en",19983,], +["features.messages.impl.actionlist_SheetContent_Day_8_en","features.messages.impl.actionlist_SheetContent_Night_8_en",19983,], +["features.messages.impl.actionlist_SheetContent_Day_9_en","features.messages.impl.actionlist_SheetContent_Night_9_en",19983,], +["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",19983,], +["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",19983,], +["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",19983,], +["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",19983,], +["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",19983,], +["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",19983,], +["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",19983,], +["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",19983,], +["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",19983,], ["libraries.designsystem.components.dialogs_SingleSelectionDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_SingleSelectionDialog_Day_0_en","libraries.designsystem.components.dialogs_SingleSelectionDialog_Night_0_en",0,], ["libraries.designsystem.components.list_SingleSelectionListItemCustomFormattert_Single selection List item - custom formatter_List items_en","",0,], @@ -912,7 +924,7 @@ export const screenshots = [ ["libraries.designsystem.components.list_SingleSelectionListItemUnselectedWithSupportingText_Single selection List item - no selection, supporting text_List items_en","",0,], ["libraries.designsystem.components.list_SingleSelectionListItem_Single selection List item - no selection_List items_en","",0,], ["libraries.designsystem.theme.components_Sliders_Sliders_en","",0,], -["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",19972,], +["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",19983,], ["libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar with action and close button_Snackbars_en","",0,], ["libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar with action and close button on new line_Snackbars_en","",0,], ["libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar with action on new line_Snackbars_en","",0,], @@ -922,37 +934,37 @@ export const screenshots = [ ["libraries.designsystem.modifiers_SquareSizeModifierLargeHeight_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeWidth_en","",0,], ["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",0,], -["features.location.api.internal_StaticMapPlaceholder_Day_1_en","features.location.api.internal_StaticMapPlaceholder_Night_1_en",19972,], +["features.location.api.internal_StaticMapPlaceholder_Day_1_en","features.location.api.internal_StaticMapPlaceholder_Night_1_en",19983,], ["features.location.api_StaticMapView_Day_0_en","features.location.api_StaticMapView_Night_0_en",0,], -["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",19972,], +["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",19983,], ["libraries.designsystem.atomic.pages_SunsetPage_Day_0_en","libraries.designsystem.atomic.pages_SunsetPage_Night_0_en",0,], ["libraries.designsystem.components.button_SuperButton_Day_0_en","libraries.designsystem.components.button_SuperButton_Night_0_en",0,], ["libraries.designsystem.theme.components_Surface_en","",0,], ["libraries.designsystem.theme.components_Switch_Toggles_en","",0,], -["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",19972,], +["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",19983,], ["libraries.designsystem.theme.components_TextButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonSmall_Buttons_en","",0,], -["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",19972,], -["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",19972,], -["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",19972,], -["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",19972,], -["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",19972,], -["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",19972,], -["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",19972,], -["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",19972,], -["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",19972,], -["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",19972,], -["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",19972,], -["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",19972,], -["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",19972,], -["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",19972,], -["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",19972,], -["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",19972,], -["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",19972,], -["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",19972,], +["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",19983,], +["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",19983,], +["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",19983,], +["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",19983,], +["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",19983,], +["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",19983,], +["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",19983,], +["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",19983,], +["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",19983,], +["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",19983,], +["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",19983,], +["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",19983,], +["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",19983,], +["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",19983,], +["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",19983,], +["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",19983,], +["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",19983,], +["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",19983,], ["libraries.textcomposer_TextComposerVoice_Day_0_en","libraries.textcomposer_TextComposerVoice_Night_0_en",0,], ["libraries.designsystem.theme.components_TextDark_Text_en","",0,], ["libraries.designsystem.theme.components_TextFieldDark_TextFields_en","",0,], @@ -964,26 +976,26 @@ export const screenshots = [ ["libraries.designsystem.theme.components_TextFieldValueTextFieldDark_TextFields_en","",0,], ["libraries.textcomposer.components_TextFormatting_Day_0_en","libraries.textcomposer.components_TextFormatting_Night_0_en",0,], ["libraries.designsystem.theme.components_TextLight_Text_en","",0,], -["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime pickers_en","",19972,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime pickers_en","",19972,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime pickers_en","",19972,], +["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime pickers_en","",19983,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime pickers_en","",19983,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime pickers_en","",19983,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_0_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_1_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_2_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",19972,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",19972,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",19983,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",19983,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_5_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_6_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_6_en",0,], ["features.messages.impl.timeline.components.event_TimelineImageWithCaptionRow_Day_0_en","features.messages.impl.timeline.components.event_TimelineImageWithCaptionRow_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",19972,], +["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",19983,], ["features.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_Night_0_en",0,], ["features.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_Day_1_en","features.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_Night_1_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",19972,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",19972,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",19972,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",19983,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",19983,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",19983,], ["features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowLongSenderName_en","",0,], @@ -991,16 +1003,16 @@ export const screenshots = [ ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",19972,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",19972,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",19983,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",19983,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_5_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_6_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_6_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",19972,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",19983,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",19972,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",19972,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",19983,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",19983,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en",0,], @@ -1009,36 +1021,36 @@ export const screenshots = [ ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",19972,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",19983,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",19972,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",19983,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRow_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRow_Night_0_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",19972,], +["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",19983,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",19972,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",19972,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",19983,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",19983,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_2_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemInformativeView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemInformativeView_Night_0_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",19972,], +["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",19983,], ["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",19972,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",19972,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",19972,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",19972,], -["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",19972,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",19983,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",19983,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",19983,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",19983,], +["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",19983,], ["features.messages.impl.timeline.components_TimelineItemReactionsViewFew_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewFew_Night_0_en",0,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",19972,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",19972,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",19983,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",19983,], ["features.messages.impl.timeline.components_TimelineItemReactionsView_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsView_Night_0_en",0,], -["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",19972,], +["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",19983,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_0_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_0_en",0,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_1_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_1_en",0,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_2_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_2_en",0,], @@ -1047,8 +1059,8 @@ export const screenshots = [ ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_5_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_5_en",0,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_6_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_6_en",0,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_7_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_7_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",19972,], -["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",19972,], +["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",19983,], +["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",19983,], ["features.messages.impl.timeline.components_TimelineItemStateEventRow_Day_0_en","features.messages.impl.timeline.components_TimelineItemStateEventRow_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemStateView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemStateView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemStickerView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemStickerView_Night_0_en",0,], @@ -1060,7 +1072,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_4_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_5_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",19972,], +["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",19983,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_2_en",0,], @@ -1082,84 +1094,84 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemVoiceView_Day_9_en","features.messages.impl.timeline.components.event_TimelineItemVoiceView_Night_9_en",0,], ["features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Day_0_en","features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Night_0_en",0,], -["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",19972,], -["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",19972,], +["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",19983,], +["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",19983,], ["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",0,], -["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",19972,], -["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",19972,], -["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",19972,], -["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",19972,], -["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",19972,], -["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",19972,], -["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",19975,], -["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",19972,], +["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",19983,], +["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",19983,], +["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",19983,], +["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",19983,], +["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",19983,], +["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",19983,], +["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",19983,], +["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",19983,], ["features.messages.impl.timeline_TimelineView_Day_2_en","features.messages.impl.timeline_TimelineView_Night_2_en",0,], ["features.messages.impl.timeline_TimelineView_Day_3_en","features.messages.impl.timeline_TimelineView_Night_3_en",0,], -["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",19972,], +["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",19983,], ["features.messages.impl.timeline_TimelineView_Day_5_en","features.messages.impl.timeline_TimelineView_Night_5_en",0,], -["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",19972,], +["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",19983,], ["features.messages.impl.timeline_TimelineView_Day_7_en","features.messages.impl.timeline_TimelineView_Night_7_en",0,], -["features.messages.impl.timeline_TimelineView_Day_8_en","features.messages.impl.timeline_TimelineView_Night_8_en",19972,], +["features.messages.impl.timeline_TimelineView_Day_8_en","features.messages.impl.timeline_TimelineView_Night_8_en",19983,], ["features.messages.impl.timeline_TimelineView_Day_9_en","features.messages.impl.timeline_TimelineView_Night_9_en",0,], ["libraries.designsystem.theme.components_TopAppBar_App Bars_en","",0,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",19972,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",19972,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",19972,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",19972,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",19972,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",19972,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",19972,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",19972,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",19983,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",19983,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",19983,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",19983,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",19983,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",19983,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",19983,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",19983,], ["features.messages.impl.typing_TypingNotificationView_Day_0_en","features.messages.impl.typing_TypingNotificationView_Night_0_en",0,], -["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",19972,], -["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",19972,], -["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",19972,], -["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",19972,], -["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",19972,], -["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",19972,], +["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",19983,], +["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",19983,], +["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",19983,], +["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",19983,], +["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",19983,], +["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",19983,], ["features.messages.impl.typing_TypingNotificationView_Day_7_en","features.messages.impl.typing_TypingNotificationView_Night_7_en",0,], ["features.messages.impl.typing_TypingNotificationView_Day_8_en","features.messages.impl.typing_TypingNotificationView_Night_8_en",0,], ["libraries.designsystem.atomic.atoms_UnreadIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_UnreadIndicatorAtom_Night_0_en",0,], -["libraries.matrix.ui.components_UnresolvedUserRow_en","",19972,], +["libraries.matrix.ui.components_UnresolvedUserRow_en","",19983,], ["libraries.matrix.ui.components_UnsavedAvatar_Day_0_en","libraries.matrix.ui.components_UnsavedAvatar_Night_0_en",0,], ["libraries.designsystem.components.avatar_UserAvatarColors_Day_0_en","libraries.designsystem.components.avatar_UserAvatarColors_Night_0_en",0,], -["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",19972,], -["features.createroom.impl.components_UserListView_Day_0_en","features.createroom.impl.components_UserListView_Night_0_en",19972,], -["features.createroom.impl.components_UserListView_Day_1_en","features.createroom.impl.components_UserListView_Night_1_en",19972,], -["features.createroom.impl.components_UserListView_Day_2_en","features.createroom.impl.components_UserListView_Night_2_en",19972,], +["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",19983,], +["features.createroom.impl.components_UserListView_Day_0_en","features.createroom.impl.components_UserListView_Night_0_en",19983,], +["features.createroom.impl.components_UserListView_Day_1_en","features.createroom.impl.components_UserListView_Night_1_en",19983,], +["features.createroom.impl.components_UserListView_Day_2_en","features.createroom.impl.components_UserListView_Night_2_en",19983,], ["features.createroom.impl.components_UserListView_Day_3_en","features.createroom.impl.components_UserListView_Night_3_en",0,], ["features.createroom.impl.components_UserListView_Day_4_en","features.createroom.impl.components_UserListView_Night_4_en",0,], ["features.createroom.impl.components_UserListView_Day_5_en","features.createroom.impl.components_UserListView_Night_5_en",0,], ["features.createroom.impl.components_UserListView_Day_6_en","features.createroom.impl.components_UserListView_Night_6_en",0,], -["features.createroom.impl.components_UserListView_Day_7_en","features.createroom.impl.components_UserListView_Night_7_en",19972,], +["features.createroom.impl.components_UserListView_Day_7_en","features.createroom.impl.components_UserListView_Night_7_en",19983,], ["features.createroom.impl.components_UserListView_Day_8_en","features.createroom.impl.components_UserListView_Night_8_en",0,], -["features.createroom.impl.components_UserListView_Day_9_en","features.createroom.impl.components_UserListView_Night_9_en",19972,], +["features.createroom.impl.components_UserListView_Day_9_en","features.createroom.impl.components_UserListView_Night_9_en",19983,], ["features.preferences.impl.user_UserPreferences_Day_0_en","features.preferences.impl.user_UserPreferences_Night_0_en",0,], ["features.preferences.impl.user_UserPreferences_Day_1_en","features.preferences.impl.user_UserPreferences_Night_1_en",0,], ["features.preferences.impl.user_UserPreferences_Day_2_en","features.preferences.impl.user_UserPreferences_Night_2_en",0,], ["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",0,], -["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",19972,], -["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",19972,], -["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",19972,], -["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",19972,], -["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",19972,], -["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",19972,], -["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",19972,], -["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",19972,], -["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",19972,], -["features.verifysession.impl_VerifySelfSessionView_Day_0_en","features.verifysession.impl_VerifySelfSessionView_Night_0_en",19972,], -["features.verifysession.impl_VerifySelfSessionView_Day_10_en","features.verifysession.impl_VerifySelfSessionView_Night_10_en",19975,], +["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",19983,], +["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",19983,], +["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",19983,], +["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",19983,], +["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",19983,], +["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",19983,], +["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",19983,], +["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",19983,], +["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",19983,], +["features.verifysession.impl_VerifySelfSessionView_Day_0_en","features.verifysession.impl_VerifySelfSessionView_Night_0_en",19983,], +["features.verifysession.impl_VerifySelfSessionView_Day_10_en","features.verifysession.impl_VerifySelfSessionView_Night_10_en",19983,], ["features.verifysession.impl_VerifySelfSessionView_Day_11_en","features.verifysession.impl_VerifySelfSessionView_Night_11_en",0,], ["features.verifysession.impl_VerifySelfSessionView_Day_12_en","features.verifysession.impl_VerifySelfSessionView_Night_12_en",0,], -["features.verifysession.impl_VerifySelfSessionView_Day_1_en","features.verifysession.impl_VerifySelfSessionView_Night_1_en",19972,], -["features.verifysession.impl_VerifySelfSessionView_Day_2_en","features.verifysession.impl_VerifySelfSessionView_Night_2_en",19972,], -["features.verifysession.impl_VerifySelfSessionView_Day_3_en","features.verifysession.impl_VerifySelfSessionView_Night_3_en",19972,], -["features.verifysession.impl_VerifySelfSessionView_Day_4_en","features.verifysession.impl_VerifySelfSessionView_Night_4_en",19972,], -["features.verifysession.impl_VerifySelfSessionView_Day_5_en","features.verifysession.impl_VerifySelfSessionView_Night_5_en",19972,], -["features.verifysession.impl_VerifySelfSessionView_Day_6_en","features.verifysession.impl_VerifySelfSessionView_Night_6_en",19972,], -["features.verifysession.impl_VerifySelfSessionView_Day_7_en","features.verifysession.impl_VerifySelfSessionView_Night_7_en",19972,], -["features.verifysession.impl_VerifySelfSessionView_Day_8_en","features.verifysession.impl_VerifySelfSessionView_Night_8_en",19972,], -["features.verifysession.impl_VerifySelfSessionView_Day_9_en","features.verifysession.impl_VerifySelfSessionView_Night_9_en",19972,], +["features.verifysession.impl_VerifySelfSessionView_Day_1_en","features.verifysession.impl_VerifySelfSessionView_Night_1_en",19983,], +["features.verifysession.impl_VerifySelfSessionView_Day_2_en","features.verifysession.impl_VerifySelfSessionView_Night_2_en",19983,], +["features.verifysession.impl_VerifySelfSessionView_Day_3_en","features.verifysession.impl_VerifySelfSessionView_Night_3_en",19983,], +["features.verifysession.impl_VerifySelfSessionView_Day_4_en","features.verifysession.impl_VerifySelfSessionView_Night_4_en",19983,], +["features.verifysession.impl_VerifySelfSessionView_Day_5_en","features.verifysession.impl_VerifySelfSessionView_Night_5_en",19983,], +["features.verifysession.impl_VerifySelfSessionView_Day_6_en","features.verifysession.impl_VerifySelfSessionView_Night_6_en",19983,], +["features.verifysession.impl_VerifySelfSessionView_Day_7_en","features.verifysession.impl_VerifySelfSessionView_Night_7_en",19983,], +["features.verifysession.impl_VerifySelfSessionView_Day_8_en","features.verifysession.impl_VerifySelfSessionView_Night_8_en",19983,], +["features.verifysession.impl_VerifySelfSessionView_Day_9_en","features.verifysession.impl_VerifySelfSessionView_Night_9_en",19983,], ["libraries.designsystem.ruler_VerticalRuler_Day_0_en","libraries.designsystem.ruler_VerticalRuler_Night_0_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_0_en","features.viewfolder.impl.file_ViewFileView_Night_0_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_1_en","features.viewfolder.impl.file_ViewFileView_Night_1_en",0,], @@ -1173,12 +1185,7 @@ export const screenshots = [ ["libraries.textcomposer.components_VoiceMessageRecorderButton_Day_0_en","libraries.textcomposer.components_VoiceMessageRecorderButton_Night_0_en",0,], ["libraries.textcomposer.components_VoiceMessageRecording_Day_0_en","libraries.textcomposer.components_VoiceMessageRecording_Night_0_en",0,], ["libraries.textcomposer.components_VoiceMessage_Day_0_en","libraries.textcomposer.components_VoiceMessage_Night_0_en",0,], -["features.login.impl.screens.waitlistscreen_WaitListView_Day_0_en","features.login.impl.screens.waitlistscreen_WaitListView_Night_0_en",19972,], -["features.login.impl.screens.waitlistscreen_WaitListView_Day_1_en","features.login.impl.screens.waitlistscreen_WaitListView_Night_1_en",19972,], -["features.login.impl.screens.waitlistscreen_WaitListView_Day_2_en","features.login.impl.screens.waitlistscreen_WaitListView_Night_2_en",19972,], -["features.login.impl.screens.waitlistscreen_WaitListView_Day_3_en","features.login.impl.screens.waitlistscreen_WaitListView_Night_3_en",19972,], -["features.login.impl.screens.waitlistscreen_WaitListView_Day_4_en","features.login.impl.screens.waitlistscreen_WaitListView_Night_4_en",19972,], ["libraries.designsystem.components.media_WaveformPlaybackView_Day_0_en","libraries.designsystem.components.media_WaveformPlaybackView_Night_0_en",0,], -["features.ftue.impl.welcome_WelcomeView_Day_0_en","features.ftue.impl.welcome_WelcomeView_Night_0_en",19972,], +["features.ftue.impl.welcome_WelcomeView_Day_0_en","features.ftue.impl.welcome_WelcomeView_Night_0_en",19983,], ["libraries.designsystem.ruler_WithRulers_Day_0_en","libraries.designsystem.ruler_WithRulers_Night_0_en",0,], ]; diff --git a/services/apperror/impl/src/main/kotlin/io/element/android/services/apperror/impl/AppErrorView.kt b/services/apperror/impl/src/main/kotlin/io/element/android/services/apperror/impl/AppErrorView.kt index 80d3de2dfe..2bd46eeb00 100644 --- a/services/apperror/impl/src/main/kotlin/io/element/android/services/apperror/impl/AppErrorView.kt +++ b/services/apperror/impl/src/main/kotlin/io/element/android/services/apperror/impl/AppErrorView.kt @@ -36,7 +36,7 @@ private fun AppErrorViewContent( ErrorDialog( title = title, content = body, - onDismiss = onDismiss, + onSubmit = onDismiss, ) } diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_3_en.png new file mode 100644 index 0000000000..e625c84b24 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98f68bcb22f867add02028ec43cc9b609d8cd76ace450eec1f010899b3f29445 +size 24728 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_3_en.png new file mode 100644 index 0000000000..6d9414c34a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:48e881658f3627275743a5f5cf172f312d26ea0633fa84243da4b277b664f66b +size 22753 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en.png new file mode 100644 index 0000000000..f4a7f63530 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f2f3a79b578e2c4cb7f6c9cf48f2c94dd6889d57b82bd7454d9a0d7b76f58d8a +size 38689 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en.png new file mode 100644 index 0000000000..d880280de2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f45332a3b2a62939aff408aa9537f9b71c4bb85bbe26b43c61a6400441830483 +size 39482 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en.png new file mode 100644 index 0000000000..f30b395104 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a6818bb540210582166ff6484dfca0ec45e88def508c9c5e3a0eb838514b591 +size 37634 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en.png new file mode 100644 index 0000000000..5c49aa51c3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea7772eb8d6f825dc5d8a5823e8452e4e4aec1db0b460a189715b843d8f7ae7c +size 37021 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Day_0_en.png new file mode 100644 index 0000000000..d1ce609f52 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:89344bcdfe8634d9d2e0ea701c7357e1744d18284a5374209e1903a100b5de47 +size 13651 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Day_1_en.png new file mode 100644 index 0000000000..7cf778987e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d47d8ae4144ac6a63cee3359fe7b6e0068069fc006fa99ff874b40ab38c3ef82 +size 13693 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Day_2_en.png new file mode 100644 index 0000000000..352466596f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c36947469bd91cb58d7d404feb22fe3257a648195d68d5594ef96d21f6a38da +size 13496 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Day_3_en.png new file mode 100644 index 0000000000..9d943f498f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6738e2c9b06b5f229b3b3d1d807e18e8d04661b664e61f5645a853329f11c23c +size 15423 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Night_0_en.png new file mode 100644 index 0000000000..beab9a1b1b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff4e3e6a8845b8cf2c81160d556c82ab1aa48e4f1912a63313b751ea0f83be08 +size 13252 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Night_1_en.png new file mode 100644 index 0000000000..f998350fcf --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e19e9c62760cc4cb0a5f4e2dd814c32c118233efd5714ab11f68520deb83d470 +size 13279 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Night_2_en.png new file mode 100644 index 0000000000..27d44ecd6a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc1cbe1378c4cd1daf809d27d13f1221a443d683d9bfdcf3c0b2af1ada5bdf82 +size 12328 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Night_3_en.png new file mode 100644 index 0000000000..6740d1895e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.createaccount_CreateAccountView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d8542941be6e786b2c33a4615eada254287bf61ca34536a9ec3f672f5b7613f +size 13753 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_12_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_12_en.png new file mode 100644 index 0000000000..aad99c4b83 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_12_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:67210d5b8bba63c7c18013967fba0ffe92b1263733f66f26f864c6b52d73783a +size 46864 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_12_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_12_en.png new file mode 100644 index 0000000000..3ec416c537 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_12_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f3dea9d5bd54cd6342a6d243760e383e39db8998a0caa25fb3dfb0547789efbe +size 45864 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_0_en.png new file mode 100644 index 0000000000..1b6fb4bab8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96a867cb12498cbdc97957bee07855dfaa13602baddaf933aff2b666ef4c7650 +size 3642 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png new file mode 100644 index 0000000000..b10cbcbcac --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91368f180f0dc431efed5f4a628a700a87aa3571bd5b49747171b6cfbc6fd464 +size 56981 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png new file mode 100644 index 0000000000..5b7b58e615 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a6261c5a443005e4cb8f3e8f8f176c4e367feca6d6c1eb89ecc6b72aac119d3 +size 55135 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_0_en.png new file mode 100644 index 0000000000..d6fd8eeb70 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5bb36ccd718f3fec5b04f1bc812dc7718b5ea7fa4619c8b031466297a8d016fd +size 3659 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png new file mode 100644 index 0000000000..42a57b0ba4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d632744706c96f51c14171b74f34e1be8f97aaefcd9f8173e7b03b7b458430d +size 55532 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png new file mode 100644 index 0000000000..59542a02f9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:423d72e2dab1e87c9ee52b170c730e189cb642e22d218cf4b2c7a99783cdda5f +size 53333 diff --git a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_2_en.png index 67d47d34d2..09dcffa934 100644 --- a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78cd46cb6a689088dda0ea79d7efcee4bbb41e956dd308820c369daa3e11582d -size 309346 +oid sha256:137ed2c613128525a2ee749c3f488cf8c2900dda49778dae420d72364a1d98d1 +size 307793 diff --git a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_3_en.png index 17ed84c21e..e38bad3edb 100644 --- a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:59d2f5f0f9ffc4f3ceca83626121d35e22482ee7f0be0626be2c922b64762cd9 -size 303240 +oid sha256:9a19a446abbcd7c4317dd6283df8de2fdf1829e905f353d4dd79132bdf1c4845 +size 301997 diff --git a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_2_en.png index e15808ede5..d2dee86e6d 100644 --- a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:01d8ce27c1f526f003003a643408216bf106d882c6f0275caf9bea78b8e05b92 -size 385522 +oid sha256:db259afbc02e4696b36101aa6ae6829d0ab8d23ee6c662b2ff63838894466b4f +size 384093 diff --git a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_3_en.png index 1a58a5c612..fac9148917 100644 --- a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5ab54696b3e60080df81b17c02804c81687c6b260c76408ffad6d0079d4bf86 -size 366816 +oid sha256:5b83da6855e10e0765619fb6557843b0b657526928ca94f2a565144f5b261c8e +size 365396 diff --git a/tools/localazy/config.json b/tools/localazy/config.json index f736825725..f463b6d621 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -1,5 +1,12 @@ { "modules" : [ + { + "name" : ":appnav", + "includeRegex" : [ + "banner\\.migrate_to_native_sliding_sync\\.force_logout.title", + "banner\\.migrate_to_native_sliding_sync\\.action" + ] + }, { "name" : ":features:rageshake:impl", "includeRegex" : [ @@ -120,6 +127,7 @@ "screen_server_confirmation_.*", "screen_change_server_.*", "screen_change_account_provider_.*", + "screen_create_account_.*", "screen_account_provider_.*", "screen_waitlist_.*", "screen_qr_code_login_.*"