diff --git a/.github/workflows/maestro.yml b/.github/workflows/maestro.yml index 2aeafa9fba..8835e94594 100644 --- a/.github/workflows/maestro.yml +++ b/.github/workflows/maestro.yml @@ -79,7 +79,7 @@ jobs: uses: actions/download-artifact@v4 with: name: elementx-apk-maestro - - uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 + - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch' with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index d4b7accbaa..c224ad564b 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/CHANGES.md b/CHANGES.md index a274a14643..6452a0c1bb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,19 @@ +Changes in Element X v0.7.2 (2024-10-29) +======================================== + +## What's Changed +### 🙌 Improvements +* Add setting to compress image and video by @bmarty in https://github.com/element-hq/element-x-android/pull/3744 +### 🗣 Translations +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/3743 +### 🧱 Build +* Release script improvement by @bmarty in https://github.com/element-hq/element-x-android/pull/3741 +### Dependency upgrades +* Update dependency org.maplibre.gl:android-sdk to v11.5.2 by @renovate in https://github.com/element-hq/element-x-android/pull/3720 +* Update dependency io.sentry:sentry-android to v7.16.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3726 +* Update dependencyAnalysis to v2.3.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3740 +* Update dependency org.matrix.rustcomponents:sdk-android to v0.2.58 by @renovate in https://github.com/element-hq/element-x-android/pull/3749 + Changes in Element X v0.7.1 (2024-10-25) ======================================== diff --git a/README.md b/README.md index f838a0fe50..5ae9b0c2b4 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ # Element X Android -Element X Android is a [Matrix](https://matrix.org/) Android Client provided by [element.io](https://element.io/). This app is currently in a pre-alpha release stage with only basic functionalities. +Element X Android is a [Matrix](https://matrix.org/) Android Client provided by [element.io](https://element.io/). The application is a total rewrite of [Element-Android](https://github.com/element-hq/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 7+. The UI layer is written using [Jetpack Compose](https://developer.android.com/jetpack/compose), and the navigation is managed using [Appyx](https://github.com/bumble-tech/appyx). @@ -71,7 +71,7 @@ We're doing this as a way to share code between platforms and while we've seen p ## Status -This project is in work in progress. The app does not cover yet all functionalities we expect. The list of supported features can be found in [this issue](https://github.com/element-hq/element-x-android/issues/911). +This project is in an early rollout and migration phase. ## Contributing diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 74e8abcad8..52fb01f067 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -48,9 +48,9 @@ android { } else { "io.element.android.x" } - targetSdk = Versions.targetSdk - versionCode = Versions.versionCode - versionName = Versions.versionName + targetSdk = Versions.TARGET_SDK + versionCode = Versions.VERSION_CODE + versionName = Versions.VERSION_NAME // Keep abiFilter for the universalApk ndk { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a53541a1a6..8bbbb8adfa 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -10,12 +10,11 @@ - ( @@ -123,6 +129,12 @@ class LoggedInFlowNode @AssistedInject constructor( matrixClient.roomMembershipObserver(), ) + private val verificationListener = object : SessionVerificationServiceListener { + override fun onIncomingSessionRequest(sessionVerificationRequestDetails: SessionVerificationRequestDetails) { + backstack.singleTop(NavTarget.IncomingVerificationRequest(sessionVerificationRequestDetails)) + } + } + override fun onBuilt() { super.onBuilt() lifecycle.subscribe( @@ -131,6 +143,7 @@ class LoggedInFlowNode @AssistedInject constructor( // TODO We do not support Space yet, so directly navigate to main space appNavigationStateService.onNavigateToSpace(id, MAIN_SPACE) loggedInFlowProcessor.observeEvents(coroutineScope) + matrixClient.sessionVerificationService().setListener(verificationListener) ftueService.state .onEach { ftueState -> @@ -152,6 +165,7 @@ class LoggedInFlowNode @AssistedInject constructor( appNavigationStateService.onLeavingSpace(id) appNavigationStateService.onLeavingSession(id) loggedInFlowProcessor.stopObserving() + matrixClient.sessionVerificationService().setListener(null) } ) observeSyncStateAndNetworkStatus() @@ -232,6 +246,9 @@ class LoggedInFlowNode @AssistedInject constructor( @Parcelize data object LogoutForNativeSlidingSyncMigrationNeeded : NavTarget + + @Parcelize + data class IncomingVerificationRequest(val data: SessionVerificationRequestDetails) : NavTarget } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { @@ -260,7 +277,7 @@ class LoggedInFlowNode @AssistedInject constructor( } override fun onSetUpRecoveryClick() { - backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.SetUpRecovery)) + backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.Root)) } override fun onSessionConfirmRecoveryKeyClick() { @@ -432,6 +449,16 @@ class LoggedInFlowNode @AssistedInject constructor( .callback(callback) .build() } + is NavTarget.IncomingVerificationRequest -> { + incomingVerificationEntryPoint.nodeBuilder(this, buildContext) + .params(IncomingVerificationEntryPoint.Params(navTarget.data)) + .callback(object : IncomingVerificationEntryPoint.Callback { + override fun onDone() { + backstack.pop() + } + }) + .build() + } } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/di/MatrixClientsHolder.kt b/appnav/src/main/kotlin/io/element/android/appnav/di/MatrixClientsHolder.kt index 168cbe8314..8cff242cb2 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/di/MatrixClientsHolder.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/di/MatrixClientsHolder.kt @@ -27,10 +27,18 @@ private const val SAVE_INSTANCE_KEY = "io.element.android.x.di.MatrixClientsHold @SingleIn(AppScope::class) @ContributesBinding(AppScope::class) -class MatrixClientsHolder @Inject constructor(private val authenticationService: MatrixAuthenticationService) : MatrixClientProvider { +class MatrixClientsHolder @Inject constructor( + private val authenticationService: MatrixAuthenticationService, +) : MatrixClientProvider { private val sessionIdsToMatrixClient = ConcurrentHashMap() private val restoreMutex = Mutex() + init { + authenticationService.listenToNewMatrixClients { matrixClient -> + sessionIdsToMatrixClient[matrixClient.sessionId] = matrixClient + } + } + fun removeAll() { sessionIdsToMatrixClient.clear() } diff --git a/appnav/src/main/res/values-nl/translations.xml b/appnav/src/main/res/values-nl/translations.xml new file mode 100644 index 0000000000..e38f95863c --- /dev/null +++ b/appnav/src/main/res/values-nl/translations.xml @@ -0,0 +1,5 @@ + + + "Uitloggen & Upgraden" + "Je homeserver ondersteunt het oude protocol niet meer. Log uit en log opnieuw in om de app te blijven gebruiken." + diff --git a/appnav/src/test/kotlin/io/element/android/appnav/di/MatrixClientsHolderTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/di/MatrixClientsHolderTest.kt index 0058485aa5..390b095c40 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/di/MatrixClientsHolderTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/di/MatrixClientsHolderTest.kt @@ -81,4 +81,17 @@ class MatrixClientsHolderTest { matrixClientsHolder.restoreWithSavedState(savedStateMap) assertThat(matrixClientsHolder.getOrNull(A_SESSION_ID)).isEqualTo(fakeMatrixClient) } + + @Test + fun `test AuthenticationService listenToNewMatrixClients emits a Client value and we save it`() = runTest { + val fakeAuthenticationService = FakeMatrixAuthenticationService() + val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService) + assertThat(matrixClientsHolder.getOrNull(A_SESSION_ID)).isNull() + + fakeAuthenticationService.givenMatrixClient(FakeMatrixClient(sessionId = A_SESSION_ID)) + val loginSucceeded = fakeAuthenticationService.login("user", "pass") + + assertThat(loginSucceeded.isSuccess).isTrue() + assertThat(matrixClientsHolder.getOrNull(A_SESSION_ID)).isNotNull() + } } diff --git a/build.gradle.kts b/build.gradle.kts index a71b8a6312..56e29f87d0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -49,7 +49,7 @@ allprojects { config.from(files("$rootDir/tools/detekt/detekt.yml")) } dependencies { - detektPlugins("io.nlopez.compose.rules:detekt:0.4.16") + detektPlugins("io.nlopez.compose.rules:detekt:0.4.17") } // KtLint diff --git a/fastlane/metadata/android/en-US/changelogs/40007030.txt b/fastlane/metadata/android/en-US/changelogs/40007030.txt new file mode 100644 index 0000000000..120548b6e1 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40007030.txt @@ -0,0 +1,2 @@ +Main changes in this version: TODO. +Full changelog: https://github.com/element-hq/element-x-android/releases \ No newline at end of file diff --git a/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCall.kt b/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCall.kt new file mode 100644 index 0000000000..387d4a98ad --- /dev/null +++ b/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCall.kt @@ -0,0 +1,25 @@ +/* + * 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.call.api + +import io.element.android.libraries.matrix.api.core.RoomId + +/** + * Value for the local current call. + */ +sealed interface CurrentCall { + data object None : CurrentCall + + data class RoomCall( + val roomId: RoomId, + ) : CurrentCall + + data class ExternalUrl( + val url: String, + ) : CurrentCall +} diff --git a/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallService.kt b/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallService.kt new file mode 100644 index 0000000000..9cc61ab96d --- /dev/null +++ b/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallService.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.call.api + +import kotlinx.coroutines.flow.StateFlow + +interface CurrentCallService { + /** + * The current call state flow, which will be updated when the active call changes. + * This value reflect the local state of the call. It is not updated if the user answers + * a call from another session. + */ + val currentCall: StateFlow +} 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 ce9319d0cf..711b203f99 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 @@ -183,6 +183,7 @@ private fun WebView.setup( allowFileAccess = true domStorageEnabled = true mediaPlaybackRequiresUserGesture = false + @Suppress("DEPRECATION") databaseEnabled = true loadsImagesAutomatically = true userAgentString = userAgent diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt index a685ef7b9a..0495f8ec7d 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt @@ -35,6 +35,7 @@ import androidx.core.content.IntentCompat import androidx.core.util.Consumer import androidx.lifecycle.Lifecycle import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CallType.ExternalUrl import io.element.android.features.call.impl.DefaultElementCallEntryPoint import io.element.android.features.call.impl.di.CallBindings import io.element.android.features.call.impl.pip.PictureInPictureEvents @@ -44,11 +45,14 @@ import io.element.android.features.call.impl.pip.PipView import io.element.android.features.call.impl.services.CallForegroundService import io.element.android.features.call.impl.utils.CallIntentDataParser import io.element.android.libraries.architecture.bindings +import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.designsystem.theme.ElementThemeApp import io.element.android.libraries.preferences.api.store.AppPreferencesStore import timber.log.Timber import javax.inject.Inject +private val loggerTag = LoggerTag("ElementCallActivity") + class ElementCallActivity : AppCompatActivity(), CallScreenNavigator, @@ -132,7 +136,7 @@ class ElementCallActivity : DisposableEffect(Unit) { val listener = Runnable { if (requestPermissionCallback != null) { - Timber.w("Ignoring onUserLeaveHint event because user is asked to grant permissions") + Timber.tag(loggerTag.value).w("Ignoring onUserLeaveHint event because user is asked to grant permissions") } else { pipEventSink(PictureInPictureEvents.EnterPictureInPicture) } @@ -146,7 +150,7 @@ class ElementCallActivity : val onPictureInPictureModeChangedListener = Consumer { _: PictureInPictureModeChangedInfo -> pipEventSink(PictureInPictureEvents.OnPictureInPictureModeChanged(isInPictureInPictureMode)) if (!isInPictureInPictureMode && !lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { - Timber.d("Exiting PiP mode: Hangup the call") + Timber.tag(loggerTag.value).d("Exiting PiP mode: Hangup the call") eventSink?.invoke(CallScreenEvents.Hangup) } } @@ -185,23 +189,23 @@ class ElementCallActivity : private fun setCallType(intent: Intent?) { val callType = intent?.let { - IntentCompat.getParcelableExtra(it, DefaultElementCallEntryPoint.EXTRA_CALL_TYPE, CallType::class.java) + IntentCompat.getParcelableExtra(intent, DefaultElementCallEntryPoint.EXTRA_CALL_TYPE, CallType::class.java) + ?: intent.dataString?.let(::parseUrl)?.let(::ExternalUrl) } - val intentUrl = intent?.dataString?.let(::parseUrl) - when { - // Re-opened the activity but we have no url to load or a cached one, finish the activity - intent?.dataString == null && callType == null && webViewTarget.value == null -> finish() - callType != null -> { - webViewTarget.value = callType - presenter = presenterFactory.create(callType, this) - } - intentUrl != null -> { - val fallbackInputs = CallType.ExternalUrl(intentUrl) - webViewTarget.value = fallbackInputs - presenter = presenterFactory.create(fallbackInputs, this) - } - // Coming back from notification, do nothing - else -> return + val currentCallType = webViewTarget.value + if (currentCallType == null && callType == null) { + Timber.tag(loggerTag.value).d("Re-opened the activity but we have no url to load or a cached one, finish the activity") + finish() + } else if (currentCallType == null) { + Timber.tag(loggerTag.value).d("Set the call type and create the presenter") + webViewTarget.value = callType + presenter = presenterFactory.create(callType!!, this) + } else if (callType != currentCallType) { + Timber.tag(loggerTag.value).d("User starts another call, restart the Activity") + setIntent(intent) + recreate() + } else { + Timber.tag(loggerTag.value).d("Coming back from notification, do nothing") } } diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt index 45f1f2be63..d774a5133c 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt @@ -12,6 +12,7 @@ import androidx.core.app.NotificationManagerCompat import com.squareup.anvil.annotations.ContributesBinding import io.element.android.appconfig.ElementCallConfig import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CurrentCall import io.element.android.features.call.impl.notifications.CallNotificationData import io.element.android.features.call.impl.notifications.RingingCallNotificationCreator import io.element.android.libraries.di.AppScope @@ -82,6 +83,7 @@ class DefaultActiveCallManager @Inject constructor( private val ringingCallNotificationCreator: RingingCallNotificationCreator, private val notificationManagerCompat: NotificationManagerCompat, private val matrixClientProvider: MatrixClientProvider, + private val defaultCurrentCallService: DefaultCurrentCallService, ) : ActiveCallManager { private var timedOutCallJob: Job? = null @@ -89,6 +91,7 @@ class DefaultActiveCallManager @Inject constructor( init { observeRingingCall() + observeCurrentCall() } override fun registerIncomingCall(notificationData: CallNotificationData) { @@ -209,6 +212,28 @@ class DefaultActiveCallManager @Inject constructor( } .launchIn(coroutineScope) } + + private fun observeCurrentCall() { + activeCall + .onEach { value -> + if (value == null) { + defaultCurrentCallService.onCallEnded() + } else { + when (value.callState) { + is CallState.Ringing -> { + // Nothing to do + } + is CallState.InCall -> { + when (val callType = value.callType) { + is CallType.ExternalUrl -> defaultCurrentCallService.onCallStarted(CurrentCall.ExternalUrl(callType.url)) + is CallType.RoomCall -> defaultCurrentCallService.onCallStarted(CurrentCall.RoomCall(callType.roomId)) + } + } + } + } + } + .launchIn(coroutineScope) + } } /** diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCurrentCallService.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCurrentCallService.kt new file mode 100644 index 0000000000..a2a358483b --- /dev/null +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCurrentCallService.kt @@ -0,0 +1,30 @@ +/* + * 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.call.impl.utils + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.call.api.CurrentCall +import io.element.android.features.call.api.CurrentCallService +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.SingleIn +import kotlinx.coroutines.flow.MutableStateFlow +import javax.inject.Inject + +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) +class DefaultCurrentCallService @Inject constructor() : CurrentCallService { + override val currentCall = MutableStateFlow(CurrentCall.None) + + fun onCallStarted(call: CurrentCall) { + currentCall.value = call + } + + fun onCallEnded() { + currentCall.value = CurrentCall.None + } +} diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt index 45568f2d39..b9f6a524ae 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt @@ -15,6 +15,7 @@ import io.element.android.features.call.impl.notifications.RingingCallNotificati import io.element.android.features.call.impl.utils.ActiveCall import io.element.android.features.call.impl.utils.CallState import io.element.android.features.call.impl.utils.DefaultActiveCallManager +import io.element.android.features.call.impl.utils.DefaultCurrentCallService import io.element.android.features.call.test.aCallNotificationData import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -299,5 +300,6 @@ class DefaultActiveCallManagerTest { ), notificationManagerCompat = notificationManagerCompat, matrixClientProvider = matrixClientProvider, + defaultCurrentCallService = DefaultCurrentCallService(), ) } diff --git a/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallService.kt b/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallService.kt new file mode 100644 index 0000000000..b9efc70ece --- /dev/null +++ b/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallService.kt @@ -0,0 +1,16 @@ +/* + * 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.call.test + +import io.element.android.features.call.api.CurrentCall +import io.element.android.features.call.api.CurrentCallService +import kotlinx.coroutines.flow.MutableStateFlow + +class FakeCurrentCallService( + override val currentCall: MutableStateFlow = MutableStateFlow(CurrentCall.None), +) : CurrentCallService diff --git a/features/createroom/impl/build.gradle.kts b/features/createroom/impl/build.gradle.kts index bcd3799c92..6ddae68a72 100644 --- a/features/createroom/impl/build.gradle.kts +++ b/features/createroom/impl/build.gradle.kts @@ -40,6 +40,7 @@ dependencies { implementation(projects.libraries.usersearch.impl) implementation(projects.services.analytics.api) implementation(libs.coil.compose) + implementation(projects.libraries.featureflag.api) api(projects.features.createroom.api) testImplementation(libs.test.junit) @@ -56,6 +57,7 @@ dependencies { testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.usersearch.test) testImplementation(projects.features.createroom.test) + testImplementation(projects.libraries.featureflag.test) testImplementation(projects.tests.testutils) testImplementation(libs.androidx.compose.ui.test.junit) testReleaseImplementation(libs.androidx.compose.ui.test.manifest) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt index 731dc27d07..ab6e116598 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt @@ -8,7 +8,7 @@ package io.element.android.features.createroom.impl import android.net.Uri -import io.element.android.features.createroom.impl.configureroom.RoomPrivacy +import io.element.android.features.createroom.impl.configureroom.RoomVisibilityState import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -18,5 +18,7 @@ data class CreateRoomConfig( val topic: String? = null, val avatarUri: Uri? = null, val invites: ImmutableList = persistentListOf(), - val privacy: RoomPrivacy = RoomPrivacy.Private, -) + val roomVisibility: RoomVisibilityState = RoomVisibilityState.Private, +) { + val isValid = roomName.isNullOrEmpty().not() && roomVisibility.isValid() +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt index 5925ca7818..56ebe326dd 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt @@ -8,7 +8,12 @@ package io.element.android.features.createroom.impl import android.net.Uri -import io.element.android.features.createroom.impl.configureroom.RoomPrivacy +import io.element.android.features.createroom.impl.configureroom.RoomAccess +import io.element.android.features.createroom.impl.configureroom.RoomAccessItem +import io.element.android.features.createroom.impl.configureroom.RoomAddress +import io.element.android.features.createroom.impl.configureroom.RoomAddressErrorState +import io.element.android.features.createroom.impl.configureroom.RoomVisibilityItem +import io.element.android.features.createroom.impl.configureroom.RoomVisibilityState import io.element.android.features.createroom.impl.di.CreateRoomScope import io.element.android.features.createroom.impl.userlist.UserListDataStore import io.element.android.libraries.androidutils.file.safeDelete @@ -17,6 +22,7 @@ import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.getAndUpdate import java.io.File import javax.inject.Inject @@ -31,28 +37,89 @@ class CreateRoomDataStore @Inject constructor( field = value } - fun getCreateRoomConfig(): Flow = combine( + val createRoomConfigWithInvites: Flow = combine( selectedUserListDataStore.selectedUsers(), createRoomConfigFlow, ) { selectedUsers, config -> config.copy(invites = selectedUsers.toImmutableList()) } - fun setRoomName(roomName: String?) { - createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(roomName = roomName?.takeIf { it.isNotEmpty() })) + fun setRoomName(roomName: String) { + createRoomConfigFlow.getAndUpdate { config -> + /* + val newVisibility = when (config.roomVisibility) { + is RoomVisibilityState.Public -> { + val roomAddress = config.roomVisibility.roomAddress + if (roomAddress is RoomAddress.AutoFilled || roomName.isEmpty()) { + config.roomVisibility.copy( + roomAddress = RoomAddress.AutoFilled(roomName), + ) + } else { + config.roomVisibility + } + } + else -> config.roomVisibility + } + */ + config.copy( + roomName = roomName.takeIf { it.isNotEmpty() }, + ) + } } - fun setTopic(topic: String?) { - createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(topic = topic?.takeIf { it.isNotEmpty() })) + fun setTopic(topic: String) { + createRoomConfigFlow.getAndUpdate { config -> + config.copy(topic = topic.takeIf { it.isNotEmpty() }) + } } fun setAvatarUri(uri: Uri?, cached: Boolean = false) { cachedAvatarUri = uri.takeIf { cached } - createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(avatarUri = uri)) + createRoomConfigFlow.getAndUpdate { config -> + config.copy(avatarUri = uri) + } + } + + fun setRoomVisibility(visibility: RoomVisibilityItem) { + createRoomConfigFlow.getAndUpdate { config -> + config.copy( + roomVisibility = when (visibility) { + RoomVisibilityItem.Private -> RoomVisibilityState.Private + RoomVisibilityItem.Public -> RoomVisibilityState.Public( + roomAddress = RoomAddress.AutoFilled(config.roomName.orEmpty()), + roomAddressErrorState = RoomAddressErrorState.None, + roomAccess = RoomAccess.Anyone, + ) + } + ) + } } - fun setPrivacy(privacy: RoomPrivacy) { - createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(privacy = privacy)) + fun setRoomAddress(address: String) { + createRoomConfigFlow.getAndUpdate { config -> + config.copy( + roomVisibility = when (config.roomVisibility) { + is RoomVisibilityState.Public -> config.roomVisibility.copy(roomAddress = RoomAddress.Edited(address)) + else -> config.roomVisibility + } + ) + } + } + + fun setRoomAccess(access: RoomAccessItem) { + createRoomConfigFlow.getAndUpdate { config -> + config.copy( + roomVisibility = when (config.roomVisibility) { + is RoomVisibilityState.Public -> { + when (access) { + RoomAccessItem.Anyone -> config.roomVisibility.copy(roomAccess = RoomAccess.Anyone) + RoomAccessItem.AskToJoin -> config.roomVisibility.copy(roomAccess = RoomAccess.Knocking) + } + } + else -> config.roomVisibility + } + ) + } } fun clearCachedData() { diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt deleted file mode 100644 index 302f6134e6..0000000000 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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.createroom.impl.components - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.selection.selectable -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.unit.dp -import io.element.android.compound.theme.ElementTheme -import io.element.android.features.createroom.impl.configureroom.RoomPrivacyItem -import io.element.android.features.createroom.impl.configureroom.roomPrivacyItems -import io.element.android.libraries.designsystem.preview.ElementPreview -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.RadioButton -import io.element.android.libraries.designsystem.theme.components.Text - -@Composable -fun RoomPrivacyOption( - roomPrivacyItem: RoomPrivacyItem, - onOptionClick: (RoomPrivacyItem) -> Unit, - modifier: Modifier = Modifier, - isSelected: Boolean = false, -) { - Row( - modifier - .fillMaxWidth() - .selectable( - selected = isSelected, - onClick = { onOptionClick(roomPrivacyItem) }, - role = Role.RadioButton, - ) - .padding(8.dp), - ) { - Icon( - modifier = Modifier.padding(horizontal = 8.dp), - resourceId = roomPrivacyItem.icon, - contentDescription = null, - tint = MaterialTheme.colorScheme.secondary, - ) - - Column( - Modifier - .weight(1f) - .padding(horizontal = 8.dp) - ) { - Text( - text = roomPrivacyItem.title, - style = ElementTheme.typography.fontBodyLgRegular, - color = MaterialTheme.colorScheme.primary, - ) - Spacer(Modifier.size(3.dp)) - Text( - text = roomPrivacyItem.description, - style = ElementTheme.typography.fontBodySmRegular, - color = MaterialTheme.colorScheme.tertiary, - ) - } - - RadioButton( - modifier = Modifier - .align(Alignment.CenterVertically) - .size(48.dp), - selected = isSelected, - // null recommended for accessibility with screenreaders - onClick = null - ) - } -} - -@PreviewsDayNight -@Composable -internal fun RoomPrivacyOptionPreview() = ElementPreview { - val aRoomPrivacyItem = roomPrivacyItems().first() - Column { - RoomPrivacyOption( - roomPrivacyItem = aRoomPrivacyItem, - onOptionClick = {}, - isSelected = true, - ) - RoomPrivacyOption( - roomPrivacyItem = aRoomPrivacyItem, - onOptionClick = {}, - isSelected = false, - ) - } -} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt index d6f647fe61..26fee055ee 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt @@ -7,16 +7,17 @@ package io.element.android.features.createroom.impl.configureroom -import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.media.AvatarAction sealed interface ConfigureRoomEvents { data class RoomNameChanged(val name: String) : ConfigureRoomEvents data class TopicChanged(val topic: String) : ConfigureRoomEvents - data class RoomPrivacyChanged(val privacy: RoomPrivacy) : ConfigureRoomEvents - data class RemoveFromSelection(val matrixUser: MatrixUser) : ConfigureRoomEvents - data class CreateRoom(val config: CreateRoomConfig) : ConfigureRoomEvents + data class RoomVisibilityChanged(val visibilityItem: RoomVisibilityItem) : ConfigureRoomEvents + data class RoomAccessChanged(val roomAccess: RoomAccessItem) : ConfigureRoomEvents + data class RoomAddressChanged(val roomAddress: String) : ConfigureRoomEvents + data class RemoveUserFromSelection(val matrixUser: MatrixUser) : ConfigureRoomEvents + data object CreateRoom : ConfigureRoomEvents data class HandleAvatarAction(val action: AvatarAction) : ConfigureRoomEvents data object CancelCreateRoom : ConfigureRoomEvents } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index 09553d6160..bbefc987ef 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -24,6 +24,8 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters @@ -38,6 +40,7 @@ import io.element.android.services.analytics.api.AnalyticsService import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject class ConfigureRoomPresenter @Inject constructor( @@ -47,6 +50,7 @@ class ConfigureRoomPresenter @Inject constructor( private val mediaPreProcessor: MediaPreProcessor, private val analyticsService: AnalyticsService, permissionsPresenterFactory: PermissionsPresenter.Factory, + private val featureFlagService: FeatureFlagService, ) : Presenter { private val cameraPermissionPresenter: PermissionsPresenter = permissionsPresenterFactory.create(android.Manifest.permission.CAMERA) private var pendingPermissionRequest = false @@ -54,7 +58,9 @@ class ConfigureRoomPresenter @Inject constructor( @Composable override fun present(): ConfigureRoomState { val cameraPermissionState = cameraPermissionPresenter.present() - val createRoomConfig = dataStore.getCreateRoomConfig().collectAsState(CreateRoomConfig()) + val createRoomConfig = dataStore.createRoomConfigWithInvites.collectAsState(CreateRoomConfig()) + val homeserverName = remember { matrixClient.userIdServerName() } + val isKnockFeatureEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock).collectAsState(initial = false) val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker( onResult = { uri -> if (uri != null) dataStore.setAvatarUri(uri = uri, cached = true) }, @@ -92,9 +98,11 @@ class ConfigureRoomPresenter @Inject constructor( when (event) { is ConfigureRoomEvents.RoomNameChanged -> dataStore.setRoomName(event.name) is ConfigureRoomEvents.TopicChanged -> dataStore.setTopic(event.topic) - is ConfigureRoomEvents.RoomPrivacyChanged -> dataStore.setPrivacy(event.privacy) - is ConfigureRoomEvents.RemoveFromSelection -> dataStore.selectedUserListDataStore.removeUserFromSelection(event.matrixUser) - is ConfigureRoomEvents.CreateRoom -> createRoom(event.config) + is ConfigureRoomEvents.RoomVisibilityChanged -> dataStore.setRoomVisibility(event.visibilityItem) + is ConfigureRoomEvents.RemoveUserFromSelection -> dataStore.selectedUserListDataStore.removeUserFromSelection(event.matrixUser) + is ConfigureRoomEvents.RoomAccessChanged -> dataStore.setRoomAccess(event.roomAccess) + is ConfigureRoomEvents.RoomAddressChanged -> dataStore.setRoomAddress(event.roomAddress) + is ConfigureRoomEvents.CreateRoom -> createRoom(createRoomConfig.value) is ConfigureRoomEvents.HandleAvatarAction -> { when (event.action) { AvatarAction.ChoosePhoto -> galleryImagePicker.launch() @@ -113,10 +121,12 @@ class ConfigureRoomPresenter @Inject constructor( } return ConfigureRoomState( + isKnockFeatureEnabled = isKnockFeatureEnabled, config = createRoomConfig.value, avatarActions = avatarActions, createRoomAction = createRoomAction.value, cameraPermissionState = cameraPermissionState, + homeserverName = homeserverName, eventSink = ::handleEvents, ) } @@ -127,26 +137,50 @@ class ConfigureRoomPresenter @Inject constructor( ) = launch { suspend { val avatarUrl = config.avatarUri?.let { uploadAvatar(it) } - val params = CreateRoomParameters( - name = config.roomName, - topic = config.topic, - isEncrypted = config.privacy == RoomPrivacy.Private, - isDirect = false, - visibility = if (config.privacy == RoomPrivacy.Public) RoomVisibility.PUBLIC else RoomVisibility.PRIVATE, - preset = if (config.privacy == RoomPrivacy.Public) RoomPreset.PUBLIC_CHAT else RoomPreset.PRIVATE_CHAT, - invite = config.invites.map { it.userId }, - avatar = avatarUrl, - ) - matrixClient.createRoom(params).getOrThrow() - .also { + val params = if (config.roomVisibility is RoomVisibilityState.Public) { + CreateRoomParameters( + name = config.roomName, + topic = config.topic, + isEncrypted = false, + isDirect = false, + visibility = RoomVisibility.PUBLIC, + joinRuleOverride = config.roomVisibility.roomAccess.toJoinRule(), + preset = RoomPreset.PUBLIC_CHAT, + invite = config.invites.map { it.userId }, + avatar = avatarUrl, + canonicalAlias = config.roomVisibility.roomAddress() + ) + } else { + CreateRoomParameters( + name = config.roomName, + topic = config.topic, + isEncrypted = config.roomVisibility is RoomVisibilityState.Private, + isDirect = false, + visibility = RoomVisibility.PRIVATE, + preset = RoomPreset.PRIVATE_CHAT, + invite = config.invites.map { it.userId }, + avatar = avatarUrl, + ) + } + matrixClient.createRoom(params) + .onFailure { failure -> + Timber.e(failure, "Failed to create room") + } + .onSuccess { dataStore.clearCachedData() analyticsService.capture(CreatedRoom(isDM = false)) } + .getOrThrow() }.runCatchingUpdatingState(createRoomAction) } private suspend fun uploadAvatar(avatarUri: Uri): String { - val preprocessed = mediaPreProcessor.process(avatarUri, MimeTypes.Jpeg, compressIfPossible = false).getOrThrow() + val preprocessed = mediaPreProcessor.process( + uri = avatarUri, + mimeType = MimeTypes.Jpeg, + deleteOriginal = false, + compressIfPossible = false, + ).getOrThrow() val byteArray = preprocessed.file.readBytes() return matrixClient.uploadMedia(MimeTypes.Jpeg, byteArray, null).getOrThrow() } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt index 70a0a9b76d..8a2122c306 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt @@ -15,11 +15,11 @@ import io.element.android.libraries.permissions.api.PermissionsState import kotlinx.collections.immutable.ImmutableList data class ConfigureRoomState( + val isKnockFeatureEnabled: Boolean, val config: CreateRoomConfig, val avatarActions: ImmutableList, val createRoomAction: AsyncAction, val cameraPermissionState: PermissionsState, + val homeserverName: String, val eventSink: (ConfigureRoomEvents) -> Unit -) { - val isCreateButtonEnabled: Boolean = config.roomName.isNullOrEmpty().not() -} +) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt index ba49ffcbdd..80445fbff4 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt @@ -10,30 +10,59 @@ package io.element.android.features.createroom.impl.configureroom import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.ui.components.aMatrixUserList +import io.element.android.libraries.matrix.ui.media.AvatarAction +import io.element.android.libraries.permissions.api.PermissionsState import io.element.android.libraries.permissions.api.aPermissionsState -import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList open class ConfigureRoomStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aConfigureRoomState(), - aConfigureRoomState().copy( + aConfigureRoomState( + isKnockFeatureEnabled = false, config = CreateRoomConfig( roomName = "Room 101", topic = "Room topic for this room when the text goes onto multiple lines and is really long, there shouldn’t be more than 3 lines", invites = aMatrixUserList().toImmutableList(), - privacy = RoomPrivacy.Public, + roomVisibility = RoomVisibilityState.Public( + roomAddress = RoomAddress.AutoFilled("Room 101"), + roomAccess = RoomAccess.Knocking, + roomAddressErrorState = RoomAddressErrorState.None, + ), + ), + ), + aConfigureRoomState( + config = CreateRoomConfig( + roomName = "Room 101", + topic = "Room topic for this room when the text goes onto multiple lines and is really long, there shouldn’t be more than 3 lines", + invites = aMatrixUserList().toImmutableList(), + roomVisibility = RoomVisibilityState.Public( + roomAddress = RoomAddress.AutoFilled("Room 101"), + roomAccess = RoomAccess.Knocking, + roomAddressErrorState = RoomAddressErrorState.None, + ), ), ), ) } -fun aConfigureRoomState() = ConfigureRoomState( - config = CreateRoomConfig(), - avatarActions = persistentListOf(), - createRoomAction = AsyncAction.Uninitialized, - cameraPermissionState = aPermissionsState(showDialog = false), - eventSink = { }, +fun aConfigureRoomState( + config: CreateRoomConfig = CreateRoomConfig(), + isKnockFeatureEnabled: Boolean = true, + avatarActions: List = emptyList(), + createRoomAction: AsyncAction = AsyncAction.Uninitialized, + cameraPermissionState: PermissionsState = aPermissionsState(showDialog = false), + homeserverName: String = "matrix.org", + eventSink: (ConfigureRoomEvents) -> Unit = { }, +) = ConfigureRoomState( + config = config, + isKnockFeatureEnabled = isKnockFeatureEnabled, + avatarActions = avatarActions.toImmutableList(), + createRoomAction = createRoomAction, + cameraPermissionState = cameraPermissionState, + homeserverName = homeserverName, + eventSink = eventSink, ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index cecdf643fb..b0084f68f6 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -11,9 +11,11 @@ import android.net.Uri import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -21,6 +23,7 @@ import androidx.compose.foundation.selection.selectableGroup import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -33,18 +36,22 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.features.createroom.impl.R -import io.element.android.features.createroom.impl.components.RoomPrivacyOption +import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom +import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize import io.element.android.libraries.designsystem.components.LabelledTextField import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.modifiers.clearFocusOnTap import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.aliasScreenTitle +import io.element.android.libraries.designsystem.theme.components.ListItem 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.TextButton +import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet @@ -72,11 +79,11 @@ fun ConfigureRoomView( modifier = modifier.clearFocusOnTap(focusManager), topBar = { ConfigureRoomToolbar( - isNextActionEnabled = state.isCreateButtonEnabled, + isNextActionEnabled = state.config.isValid, onBackClick = onBackClick, onNextClick = { focusManager.clearFocus() - state.eventSink(ConfigureRoomEvents.CreateRoom(state.config)) + state.eventSink(ConfigureRoomEvents.CreateRoom) }, ) } @@ -103,23 +110,42 @@ fun ConfigureRoomView( ) if (state.config.invites.isNotEmpty()) { SelectedUsersRowList( - modifier = Modifier.padding(bottom = 16.dp), contentPadding = PaddingValues(horizontal = 24.dp), selectedUsers = state.config.invites, onUserRemove = { focusManager.clearFocus() - state.eventSink(ConfigureRoomEvents.RemoveFromSelection(it)) + state.eventSink(ConfigureRoomEvents.RemoveUserFromSelection(it)) }, ) } - RoomPrivacyOptions( - modifier = Modifier.padding(bottom = 40.dp), - selected = state.config.privacy, + RoomVisibilityOptions( + selected = when (state.config.roomVisibility) { + is RoomVisibilityState.Private -> RoomVisibilityItem.Private + is RoomVisibilityState.Public -> RoomVisibilityItem.Public + }, onOptionClick = { focusManager.clearFocus() - state.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(it.privacy)) + state.eventSink(ConfigureRoomEvents.RoomVisibilityChanged(it)) }, ) + if (state.config.roomVisibility is RoomVisibilityState.Public && state.isKnockFeatureEnabled) { + RoomAccessOptions( + selected = when (state.config.roomVisibility.roomAccess) { + RoomAccess.Anyone -> RoomAccessItem.Anyone + RoomAccess.Knocking -> RoomAccessItem.AskToJoin + }, + onOptionClick = { + focusManager.clearFocus() + state.eventSink(ConfigureRoomEvents.RoomAccessChanged(it)) + }, + ) + RoomAddressField( + modifier = Modifier.padding(horizontal = 16.dp), + address = state.config.roomVisibility.roomAddress, + homeserverName = state.homeserverName, + onAddressChange = { state.eventSink(ConfigureRoomEvents.RoomAddressChanged(it)) }, + ) + } } } @@ -139,7 +165,7 @@ fun ConfigureRoomView( }, onSuccess = { onCreateRoomSuccess(it) }, errorMessage = { stringResource(R.string.screen_create_room_error_creating_room) }, - onRetry = { state.eventSink(ConfigureRoomEvents.CreateRoom(state.config)) }, + onRetry = { state.eventSink(ConfigureRoomEvents.CreateRoom) }, onErrorDismiss = { state.eventSink(ConfigureRoomEvents.CancelCreateRoom) }, ) @@ -221,23 +247,123 @@ private fun RoomTopic( } @Composable -private fun RoomPrivacyOptions( - selected: RoomPrivacy?, - onOptionClick: (RoomPrivacyItem) -> Unit, +private fun ConfigureRoomOptions( + title: String, + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit, +) { + Column( + modifier = modifier.selectableGroup() + ) { + Text( + text = title, + style = ElementTheme.typography.fontBodyLgMedium, + color = ElementTheme.colors.textPrimary, + modifier = Modifier.padding(horizontal = 16.dp), + ) + content() + } +} + +@Composable +private fun RoomVisibilityOptions( + selected: RoomVisibilityItem, + onOptionClick: (RoomVisibilityItem) -> Unit, modifier: Modifier = Modifier, ) { - val items = roomPrivacyItems() - Column(modifier = modifier.selectableGroup()) { - items.forEach { item -> - RoomPrivacyOption( - roomPrivacyItem = item, - isSelected = selected == item.privacy, - onOptionClick = onOptionClick, + ConfigureRoomOptions( + title = stringResource(R.string.screen_create_room_room_visibility_section_title), + modifier = modifier, + ) { + RoomVisibilityItem.entries.forEach { item -> + val isSelected = item == selected + ListItem( + leadingContent = ListItemContent.Custom { + RoundedIconAtom( + size = RoundedIconAtomSize.Big, + resourceId = item.icon, + tint = if (isSelected) ElementTheme.colors.iconPrimary else ElementTheme.colors.iconSecondary, + ) + }, + headlineContent = { Text(text = stringResource(item.title)) }, + supportingContent = { Text(text = stringResource(item.description)) }, + trailingContent = ListItemContent.RadioButton(selected = isSelected), + onClick = { onOptionClick(item) }, ) } } } +@Composable +private fun RoomAccessOptions( + selected: RoomAccessItem, + onOptionClick: (RoomAccessItem) -> Unit, + modifier: Modifier = Modifier, +) { + ConfigureRoomOptions( + title = stringResource(R.string.screen_create_room_room_access_section_header), + modifier = modifier, + ) { + RoomAccessItem.entries.forEach { item -> + ListItem( + headlineContent = { Text(text = stringResource(item.title)) }, + supportingContent = { Text(text = stringResource(item.description)) }, + trailingContent = ListItemContent.RadioButton(selected = item == selected), + onClick = { onOptionClick(item) }, + ) + } + } +} + +@Composable +private fun RoomAddressField( + address: RoomAddress, + homeserverName: String, + onAddressChange: (String) -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + modifier = Modifier.padding(horizontal = 16.dp), + style = ElementTheme.typography.fontBodyMdRegular, + color = MaterialTheme.colorScheme.primary, + text = stringResource(R.string.screen_create_room_room_address_section_title), + ) + + TextField( + modifier = Modifier.fillMaxWidth(), + value = address.value, + leadingIcon = { + Text( + text = "#", + style = ElementTheme.typography.fontBodyLgMedium, + color = ElementTheme.colors.textSecondary, + ) + }, + trailingIcon = { + Text( + text = homeserverName, + style = ElementTheme.typography.fontBodyLgMedium, + color = ElementTheme.colors.textSecondary, + modifier = Modifier.padding(end = 16.dp) + ) + }, + supportingText = { + Text( + text = stringResource(R.string.screen_create_room_room_address_section_footer), + style = ElementTheme.typography.fontBodySmRegular, + color = ElementTheme.colors.textSecondary, + ) + }, + onValueChange = onAddressChange, + singleLine = true, + ) + } +} + @PreviewsDayNight @Composable internal fun ConfigureRoomViewPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = ElementPreview { diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccess.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccess.kt new file mode 100644 index 0000000000..fc99079c22 --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccess.kt @@ -0,0 +1,22 @@ +/* + * 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.createroom.impl.configureroom + +import io.element.android.libraries.matrix.api.createroom.JoinRuleOverride + +enum class RoomAccess { + Anyone, + Knocking +} + +fun RoomAccess.toJoinRule(): JoinRuleOverride { + return when (this) { + RoomAccess.Anyone -> JoinRuleOverride.None + RoomAccess.Knocking -> JoinRuleOverride.Knock + } +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccessItem.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccessItem.kt new file mode 100644 index 0000000000..ce1e249396 --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccessItem.kt @@ -0,0 +1,25 @@ +/* + * 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.createroom.impl.configureroom + +import androidx.annotation.StringRes +import io.element.android.features.createroom.impl.R + +enum class RoomAccessItem( + @StringRes val title: Int, + @StringRes val description: Int +) { + Anyone( + title = R.string.screen_create_room_room_access_section_anyone_option_title, + description = R.string.screen_create_room_room_access_section_anyone_option_description, + ), + AskToJoin( + title = R.string.screen_create_room_room_access_section_knocking_option_title, + description = R.string.screen_create_room_room_access_section_knocking_option_description, + ), +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddress.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddress.kt new file mode 100644 index 0000000000..5c10cfb7b5 --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddress.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.createroom.impl.configureroom + +sealed class RoomAddress(open val value: String) { + data class AutoFilled(override val value: String) : RoomAddress(value) + data class Edited(override val value: String) : RoomAddress(value) +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddressErrorState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddressErrorState.kt new file mode 100644 index 0000000000..f2cadfd6bb --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddressErrorState.kt @@ -0,0 +1,17 @@ +/* + * 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.createroom.impl.configureroom + +/** + * Represents the error state of a room address. + */ +sealed interface RoomAddressErrorState { + data object InvalidCharacters : RoomAddressErrorState + data object AlreadyExists : RoomAddressErrorState + data object None : RoomAddressErrorState +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacy.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacy.kt deleted file mode 100644 index d376b84681..0000000000 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacy.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * 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.createroom.impl.configureroom - -enum class RoomPrivacy { - Private, - Public, -} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt deleted file mode 100644 index a0b7e4cc05..0000000000 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.createroom.impl.configureroom - -import androidx.annotation.DrawableRes -import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource -import io.element.android.features.createroom.impl.R -import io.element.android.libraries.designsystem.icons.CompoundDrawables -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList - -data class RoomPrivacyItem( - val privacy: RoomPrivacy, - @DrawableRes val icon: Int, - val title: String, - val description: String, -) - -@Composable -fun roomPrivacyItems(): ImmutableList { - return RoomPrivacy.entries - .map { - when (it) { - RoomPrivacy.Private -> RoomPrivacyItem( - privacy = it, - icon = CompoundDrawables.ic_compound_lock_solid, - title = stringResource(R.string.screen_create_room_private_option_title), - description = stringResource(R.string.screen_create_room_private_option_description), - ) - RoomPrivacy.Public -> RoomPrivacyItem( - privacy = it, - icon = CompoundDrawables.ic_compound_public, - title = stringResource(R.string.screen_create_room_public_option_title), - description = stringResource(R.string.screen_create_room_public_option_description), - ) - } - } - .toImmutableList() -} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityItem.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityItem.kt new file mode 100644 index 0000000000..12909cdd5e --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityItem.kt @@ -0,0 +1,30 @@ +/* + * 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.createroom.impl.configureroom + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import io.element.android.features.createroom.impl.R +import io.element.android.libraries.designsystem.icons.CompoundDrawables + +enum class RoomVisibilityItem( + @DrawableRes val icon: Int, + @StringRes val title: Int, + @StringRes val description: Int +) { + Private( + icon = CompoundDrawables.ic_compound_lock, + title = R.string.screen_create_room_private_option_title, + description = R.string.screen_create_room_private_option_description, + ), + Public( + icon = CompoundDrawables.ic_compound_public, + title = R.string.screen_create_room_public_option_title, + description = R.string.screen_create_room_public_option_description, + ) +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityState.kt new file mode 100644 index 0000000000..cc5af7836e --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityState.kt @@ -0,0 +1,34 @@ +/* + * 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.createroom.impl.configureroom + +import java.util.Optional + +sealed interface RoomVisibilityState { + data object Private : RoomVisibilityState + + data class Public( + val roomAddress: RoomAddress, + val roomAddressErrorState: RoomAddressErrorState, + val roomAccess: RoomAccess, + ) : RoomVisibilityState + + fun roomAddress(): Optional { + return when (this) { + is Private -> Optional.empty() + is Public -> Optional.of(roomAddress.value) + } + } + + fun isValid(): Boolean { + return when (this) { + is Private -> true + is Public -> roomAddressErrorState is RoomAddressErrorState.None && roomAddress.value.isNotEmpty() + } + } +} diff --git a/features/createroom/impl/src/main/res/values-be/translations.xml b/features/createroom/impl/src/main/res/values-be/translations.xml index 2415fe6bb6..861fca4d05 100644 --- a/features/createroom/impl/src/main/res/values-be/translations.xml +++ b/features/createroom/impl/src/main/res/values-be/translations.xml @@ -7,6 +7,9 @@ "Прыватны пакой (толькі па запрашэнні)" "Паведамленні не зашыфраваны, і кожны можа іх прачытаць. Вы можаце ўключыць шыфраванне пазней." "Публічны пакой (для ўсіх)" + "Хто заўгодна" + "Доступ у пакой" + "Папрасіце далучыцца" "Назва пакоя" "Стварыце пакой" "Тэма (неабавязкова)" diff --git a/features/createroom/impl/src/main/res/values-cs/translations.xml b/features/createroom/impl/src/main/res/values-cs/translations.xml index 13ed58be07..697120201d 100644 --- a/features/createroom/impl/src/main/res/values-cs/translations.xml +++ b/features/createroom/impl/src/main/res/values-cs/translations.xml @@ -8,7 +8,15 @@ "Tuto místnost může najít kdokoli. To můžete kdykoli změnit v nastavení místnosti." "Veřejná místnost" + "Do této místnosti může vstoupit kdokoli" + "Kdokoliv" + "Přístup do místnosti" + "Kdokoli může požádat o vstup do místnosti, ale správce nebo moderátor bude muset žádost přijmout" + "Požádat o připojení" + "Aby byla tato místnost viditelná v adresáři veřejných místností, budete potřebovat adresu místnosti." + "Adresa místnosti" "Název místnosti" + "Viditelnost místnosti" "Vytvořit místnost" "Téma (nepovinné)" "Při pokusu o zahájení chatu došlo k chybě" diff --git a/features/createroom/impl/src/main/res/values-el/translations.xml b/features/createroom/impl/src/main/res/values-el/translations.xml index 82eda078f4..72e1d997db 100644 --- a/features/createroom/impl/src/main/res/values-el/translations.xml +++ b/features/createroom/impl/src/main/res/values-el/translations.xml @@ -8,6 +8,11 @@ "Ο καθένας μπορεί να βρει αυτό το δωμάτιο. Μπορείς να το αλλάξεις ανά πάσα στιγμή στις ρυθμίσεις δωματίου." "Δημόσιο δωμάτιο" + "Οποιοσδήποτε μπορεί να συμμετάσχει σε αυτό το δωμάτιο" + "Οποιοσδήποτε" + "Πρόσβαση Δωματίου" + "Οποιοσδήποτε μπορεί να ζητήσει να συμμετάσχει στο δωμάτιο, αλλά ένας διαχειριστής ή συντονιστής θα πρέπει να αποδεχθεί το αίτημα" + "Αίτημα συμμετοχής" "Όνομα δωματίου" "Δημιούργησε ένα δωμάτιο" "Θέμα (προαιρετικό)" diff --git a/features/createroom/impl/src/main/res/values-et/translations.xml b/features/createroom/impl/src/main/res/values-et/translations.xml index 043c228dea..67db887f2c 100644 --- a/features/createroom/impl/src/main/res/values-et/translations.xml +++ b/features/createroom/impl/src/main/res/values-et/translations.xml @@ -8,7 +8,15 @@ "Kõik saavad seda jututuba leida. Sa võid seda jututoa seadistustest alati muuta." "Avalik jututuba" + "Kõik võivad selle jututoaga liituda" + "Kõik" + "Ligipääs jututoale" + "Kõik võivad paluda selle jututoaga liitumist, kuid peakasutaja või moderaator peavad selle kinnitama" + "Küsi võimalust liitumiseks" + "Selleks, et see jututuba oleks nähtav jututubade avalikus kataloogis, sa vajad jututoa aadressi." + "Jututoa aadress" "Jututoa nimi" + "Jututoa nähtavus" "Loo jututuba" "Teema (kui soovid lisada)" "Vestluse alustamisel tekkis viga" diff --git a/features/createroom/impl/src/main/res/values-fr/translations.xml b/features/createroom/impl/src/main/res/values-fr/translations.xml index 51d335571b..af40d1f9fe 100644 --- a/features/createroom/impl/src/main/res/values-fr/translations.xml +++ b/features/createroom/impl/src/main/res/values-fr/translations.xml @@ -3,11 +3,20 @@ "Nouveau salon" "Inviter des amis" "Une erreur s’est produite lors de la création du salon" - "Les messages dans ce salon sont chiffrés. Le chiffrement ne pourra pas être désactivé par la suite." - "Salon privé (sur invitation seulement)" - "Les messages ne sont pas chiffrés et n’importe qui peut les lire. Vous pouvez activer le chiffrement ultérieurement." - "Salon public (tout le monde)" + "Seules les personnes invitées peuvent accéder à ce salon. Tous les messages sont chiffrés de bout en bout." + "Salon privé" + "N’importe qui peut trouver ce salon. +Vous pouvez modifier cela à tout moment dans les paramètres du salon." + "Salon public" + "Tout le monde peut rejoindre ce salon" + "Tout le monde" + "Accès au salon" + "Tout le monde peut demander à rejoindre le salon, mais un administrateur ou un modérateur devra accepter la demande" + "Demander à rejoindre" + "Pour que ce salon soit visible dans le répertoire des salons publics, vous aurez besoin d’une adresse de salon." + "Adresse du salon" "Nom du salon" + "Visibilité du salon" "Créer un salon" "Sujet (facultatif)" "Une erreur s’est produite lors de la tentative de création de la discussion" diff --git a/features/createroom/impl/src/main/res/values-hu/translations.xml b/features/createroom/impl/src/main/res/values-hu/translations.xml index e00de669a6..ee935c8c60 100644 --- a/features/createroom/impl/src/main/res/values-hu/translations.xml +++ b/features/createroom/impl/src/main/res/values-hu/translations.xml @@ -3,11 +3,20 @@ "Új szoba" "Ismerősök meghívása" "Hiba történt a szoba létrehozásakor" - "A szobában lévő üzenetek titkosítottak. A titkosítást utólag nem lehet kikapcsolni." - "Privát szoba (csak meghívással)" - "Az üzenetek nincsenek titkosítva, és bárki elolvashatja őket. A titkosítást később is engedélyezheti." - "Nyilvános szoba (bárki)" + "Csak a meghívottak léphetnek be ebbe a szobába. Az összes üzenet végpontok közti titkosítással van védve." + "Privát szoba" + "Bárki megtalálhatja ezt a szobát. +Ezt bármikor módosíthatja a szobabeállításokban." + "Nyilvános szoba" + "Bárki csatlakozhat ehhez a szobához" + "Bárki" + "Szobahozzáférés" + "Bárki kérheti, hogy csatlakozzon a szobához, de egy adminisztrátornak vagy moderátornak el kell fogadnia a kérést" + "Csatlakozás kérése" + "Ahhoz, hogy ez a szoba látható legyen a nyilvános szobák címtárában, meg kell adnia a szoba címét." + "Szoba címe" "Szoba neve" + "Szoba láthatósága" "Szoba létrehozása" "Téma (nem kötelező)" "Hiba történt a csevegés indításakor" diff --git a/features/createroom/impl/src/main/res/values-in/translations.xml b/features/createroom/impl/src/main/res/values-in/translations.xml index a9f795983e..b1c7aeef1c 100644 --- a/features/createroom/impl/src/main/res/values-in/translations.xml +++ b/features/createroom/impl/src/main/res/values-in/translations.xml @@ -8,7 +8,15 @@ "Siapa pun dapat mencari ruangan ini. Anda dapat mengubah ini kapan pun dalam pengaturan ruangan." "Ruangan publik" + "Siapa pun dapat bergabung dengan ruangan ini" + "Siapa pun" + "Akses Ruangan" + "Siapa pun dapat meminta untuk bergabung dengan ruangan tetapi administrator atau moderator harus menerima permintaan tersebut" + "Minta untuk bergabung" + "Supaya ruangan ini terlihat di direktori ruangan publik, Anda memerlukan alamat ruangan." + "Alamat ruangan" "Nama ruangan" + "Keterlihatan ruangan" "Buat ruangan" "Topik (opsional)" "Terjadi kesalahan saat mencoba memulai obrolan" diff --git a/features/createroom/impl/src/main/res/values-ka/translations.xml b/features/createroom/impl/src/main/res/values-ka/translations.xml index 45a6c79d33..45e87809b2 100644 --- a/features/createroom/impl/src/main/res/values-ka/translations.xml +++ b/features/createroom/impl/src/main/res/values-ka/translations.xml @@ -4,9 +4,10 @@ "ხალხის მოწვევა" "ოთახის შექმნისას შეცდომა მოხდა" "ამ ოთახში შეტყობინებები დაშიფრულია. შემდგომ დაშიფვრის გამორთვა შეუძლებელია." - "კერძო ოთახი (მხოლოდ მოწვევა)" - "შეტყობინებები არ არის დაშიფრული და ყველას შეუძლია მათი წაკითხვა. შეგიძლიათ ჩართოთ დაშიფვრა მოგვიანებით." - "საჯარო ოთახი (ნებისმიერი)" + "კერძო ოთახი" + "ყველას ამ ოთახის მოძებნა შეუძლია. +თქვენ ნებისმიერ დროს შეგიძლიათ ამის შეცვლა ოთახის პარამეტრებში." + "საჯარო ოთახი" "ოთახის სახელი" "ოთახის შექმნა" "თემა (სურვილისამებრ)" diff --git a/features/createroom/impl/src/main/res/values-nl/translations.xml b/features/createroom/impl/src/main/res/values-nl/translations.xml index 2cdb3202a5..c1f0fd92f5 100644 --- a/features/createroom/impl/src/main/res/values-nl/translations.xml +++ b/features/createroom/impl/src/main/res/values-nl/translations.xml @@ -4,9 +4,15 @@ "Mensen uitnodigen" "Er is een fout opgetreden bij het aanmaken van de kamer" "Berichten in deze kamer zijn versleuteld. Versleuteling kan achteraf niet worden uitgeschakeld." - "Privé kamer (alleen op uitnodiging)" - "Berichten zijn niet versleuteld en iedereen kan ze lezen. Je kunt versleuteling later inschakelen." - "Openbare kamer (iedereen)" + "Privé kamer" + "Iedereen kan deze kamer vinden. +Je kunt dit op elk gewenst moment wijzigen in de kamerinstellingen." + "Openbare kamer" + "Iedereen kan toetreden tot deze kamer" + "Iedereen" + "Toegang tot de kamer" + "Iedereen kan vragen om toe te treden tot de kamer, maar een beheerder of moderator moet het verzoek accepteren" + "Vraag om toe te treden" "Naam van de kamer" "Creëer een kamer" "Onderwerp (optioneel)" diff --git a/features/createroom/impl/src/main/res/values-pl/translations.xml b/features/createroom/impl/src/main/res/values-pl/translations.xml index e0b19a0c49..7902fb4c3c 100644 --- a/features/createroom/impl/src/main/res/values-pl/translations.xml +++ b/features/createroom/impl/src/main/res/values-pl/translations.xml @@ -3,11 +3,20 @@ "Nowy pokój" "Zaproś znajomych" "Wystąpił błąd w trakcie tworzenia pokoju" - "Wiadomości w tym pokoju są szyfrowane. Szyfrowania nie można później wyłączyć." - "Pokój prywatny (tylko zaproszenie)" - "Wiadomości nie są szyfrowane i każdy może je odczytać. Możesz aktywować szyfrowanie później." - "Pokój publiczny (wszyscy)" + "Tylko zaproszone osoby mogą dołączyć do tego pokoju. Wszystkie wiadomości są szyfrowane end-to-end." + "Pokój prywatny" + "Każdy może znaleźć ten pokój. +Możesz to zmienić w ustawieniach pokoju." + "Pokój publiczny" + "Każdy może dołączyć do tego pokoju" + "Wszyscy" + "Dostęp do pokoju" + "Każdy może poprosić o dołączenie do pokoju, ale administrator lub moderator będzie musiał zatwierdzić prośbę" + "Poproś o dołączenie" + "Aby ten pokój był widoczny w katalogu pomieszczeń publicznych, będziesz potrzebował adres pokoju." + "Adres pokoju" "Nazwa pokoju" + "Widoczność pomieszczenia" "Utwórz pokój" "Temat (opcjonalnie)" "Wystąpił błąd podczas próby rozpoczęcia czatu" diff --git a/features/createroom/impl/src/main/res/values-pt/translations.xml b/features/createroom/impl/src/main/res/values-pt/translations.xml index 398be62190..60ee753b13 100644 --- a/features/createroom/impl/src/main/res/values-pt/translations.xml +++ b/features/createroom/impl/src/main/res/values-pt/translations.xml @@ -3,11 +3,20 @@ "Nova sala" "Convidar pessoas" "Ocorreu um erro ao criar a sala" - "As mensagens serão cifradas. Uma vez ativada, não é possível desativar a cifragem." - "Sala privada (entrada apenas por convite)" - "As mensagens não serão cifradas e qualquer um as poderá ler. É possível ativar a cifragem posteriormente." - "Sala pública (entrada livre)" + "Apenas as pessoas convidadas podem aceder a esta sala. Todas as mensagens são encriptadas ponta a ponta." + "Sala privada" + "Qualquer um pode encontrar esta sala. +Pode alterar esta opção nas definições da sala." + "Sala pública" + "Qualquer pessoa pode entrar nesta sala" + "Qualquer pessoa" + "Acesso à sala" + "Qualquer pessoa pode pedir para entrar na sala, mas um administrador ou um moderador terá de aceitar o pedido" + "Pedir para participar" + "Para que esta sala seja visível no diretório público de salas, precisas de um endereço de sala." + "Endereço da sala" "Nome da sala" + "Visibilidade da sala" "Criar uma sala" "Descrição (opcional)" "Ocorreu um erro ao tentar iniciar uma conversa" diff --git a/features/createroom/impl/src/main/res/values-ru/translations.xml b/features/createroom/impl/src/main/res/values-ru/translations.xml index 39df1f0ef5..b21bcb68ac 100644 --- a/features/createroom/impl/src/main/res/values-ru/translations.xml +++ b/features/createroom/impl/src/main/res/values-ru/translations.xml @@ -8,7 +8,15 @@ "Любой желающий может найти эту комнату. Вы можете изменить это в любое время в настройках комнаты." "Общедоступная комната" + "Любой желающий может присоединиться к этой комнате" + "Любой" + "Доступ в комнату" + "Любой желающий может подать заявку на присоединение к комнате, но администратор или модератор должен будет принять запрос." + "Попросить присоединиться" + "Чтобы эта комната была видна в каталоге общедоступных, вам необходим ее адрес" + "Адрес комнаты" "Название комнаты" + "Видимость комнаты" "Создать комнату" "Тема (необязательно)" "Произошла ошибка при запуске чата" diff --git a/features/createroom/impl/src/main/res/values-sk/translations.xml b/features/createroom/impl/src/main/res/values-sk/translations.xml index 12a94590c8..d2d742239f 100644 --- a/features/createroom/impl/src/main/res/values-sk/translations.xml +++ b/features/createroom/impl/src/main/res/values-sk/translations.xml @@ -8,7 +8,15 @@ "Túto miestnosť môže nájsť ktokoľvek. Môžete to kedykoľvek zmeniť v nastaveniach miestnosti." "Verejná miestnosť" + "Do tejto miestnosti sa môže pripojiť ktokoľvek" + "Ktokoľvek" + "Prístup do miestnosti" + "Ktokoľvek môže požiadať o pripojenie sa k miestnosti, ale administrátor alebo moderátor bude musieť žiadosť schváliť" + "Požiadať o pripojenie" + "Aby bola táto miestnosť viditeľná v adresári verejných miestností, budete potrebovať adresu miestnosti." + "Adresa miestnosti" "Názov miestnosti" + "Viditeľnosť miestnosti" "Vytvoriť miestnosť" "Téma (voliteľné)" "Pri pokuse o spustenie konverzácie sa vyskytla chyba" diff --git a/features/createroom/impl/src/main/res/values/localazy.xml b/features/createroom/impl/src/main/res/values/localazy.xml index e5256d928b..6ed5510ce0 100644 --- a/features/createroom/impl/src/main/res/values/localazy.xml +++ b/features/createroom/impl/src/main/res/values/localazy.xml @@ -8,7 +8,15 @@ "Anyone can find this room. You can change this anytime in room settings." "Public room" + "Anyone can join this room" + "Anyone" + "Room Access" + "Anyone can ask to join the room but an administrator or a moderator will have to accept the request" + "Ask to join" + "In order for this room to be visible in the public room directory, you will need a room address." + "Room address" "Room name" + "Room visibility" "Create a room" "Topic (optional)" "An error occurred when trying to start a chat" diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTest.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTest.kt index 9294f95b78..fefb09c241 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTest.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTest.kt @@ -8,15 +8,16 @@ package io.element.android.features.createroom.impl.configureroom import android.net.Uri -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test +import app.cash.turbine.TurbineTestContext import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.CreatedRoom import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.features.createroom.impl.userlist.UserListDataStore import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.featureflag.test.FakeFeatureFlagService +import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.A_MESSAGE @@ -25,13 +26,18 @@ import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.media.AvatarAction +import io.element.android.libraries.mediapickers.api.PickerProvider import io.element.android.libraries.mediapickers.test.FakePickerProvider +import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.mediaupload.api.MediaUploadInfo import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor +import io.element.android.libraries.permissions.api.PermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory +import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.test import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic @@ -56,33 +62,8 @@ class ConfigureRoomPresenterTest { @get:Rule val warmUpRule = WarmUpRule() - private lateinit var presenter: ConfigureRoomPresenter - private lateinit var userListDataStore: UserListDataStore - private lateinit var createRoomDataStore: CreateRoomDataStore - private lateinit var fakeMatrixClient: FakeMatrixClient - private lateinit var fakePickerProvider: FakePickerProvider - private lateinit var fakeMediaPreProcessor: FakeMediaPreProcessor - private lateinit var fakeAnalyticsService: FakeAnalyticsService - private lateinit var fakePermissionsPresenter: FakePermissionsPresenter - @Before fun setup() { - fakeMatrixClient = FakeMatrixClient() - userListDataStore = UserListDataStore() - createRoomDataStore = CreateRoomDataStore(userListDataStore) - fakePickerProvider = FakePickerProvider() - fakeMediaPreProcessor = FakeMediaPreProcessor() - fakeAnalyticsService = FakeAnalyticsService() - fakePermissionsPresenter = FakePermissionsPresenter() - presenter = ConfigureRoomPresenter( - dataStore = createRoomDataStore, - matrixClient = fakeMatrixClient, - mediaPickerProvider = fakePickerProvider, - mediaPreProcessor = fakeMediaPreProcessor, - analyticsService = fakeAnalyticsService, - permissionsPresenterFactory = FakePermissionsPresenterFactory(fakePermissionsPresenter), - ) - mockkStatic(File::readBytes) every { any().readBytes() } returns byteArrayOf() } @@ -94,50 +75,56 @@ class ConfigureRoomPresenterTest { @Test fun `present - initial state`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val presenter = createConfigureRoomPresenter() + presenter.test { + val initialState = initialState() assertThat(initialState.config).isEqualTo(CreateRoomConfig()) assertThat(initialState.config.roomName).isNull() assertThat(initialState.config.topic).isNull() assertThat(initialState.config.invites).isEmpty() assertThat(initialState.config.avatarUri).isNull() - assertThat(initialState.config.privacy).isEqualTo(RoomPrivacy.Private) + assertThat(initialState.config.roomVisibility).isEqualTo(RoomVisibilityState.Private) + assertThat(initialState.createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) + assertThat(initialState.homeserverName).isEqualTo("matrix.org") } } @Test fun `present - create room button is enabled only if the required fields are completed`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val presenter = createConfigureRoomPresenter() + presenter.test { + val initialState = initialState() var config = initialState.config - assertThat(initialState.isCreateButtonEnabled).isFalse() + assertThat(initialState.config.isValid).isFalse() // Room name not empty initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME)) var newState: ConfigureRoomState = awaitItem() config = config.copy(roomName = A_ROOM_NAME) assertThat(newState.config).isEqualTo(config) - assertThat(newState.isCreateButtonEnabled).isTrue() + assertThat(newState.config.isValid).isTrue() // Clear room name newState.eventSink(ConfigureRoomEvents.RoomNameChanged("")) newState = awaitItem() config = config.copy(roomName = null) assertThat(newState.config).isEqualTo(config) - assertThat(newState.isCreateButtonEnabled).isFalse() + assertThat(newState.config.isValid).isFalse() } } @Test fun `present - state is updated when fields are changed`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val userListDataStore = UserListDataStore() + val pickerProvider = FakePickerProvider() + val permissionsPresenter = FakePermissionsPresenter() + val presenter = createConfigureRoomPresenter( + createRoomDataStore = CreateRoomDataStore(userListDataStore), + pickerProvider = pickerProvider, + permissionsPresenter = permissionsPresenter, + ) + presenter.test { + val initialState = initialState() var expectedConfig = CreateRoomConfig() assertThat(initialState.config).isEqualTo(expectedConfig) @@ -165,22 +152,22 @@ class ConfigureRoomPresenterTest { // Room avatar // Pick avatar - fakePickerProvider.givenResult(null) + pickerProvider.givenResult(null) // From gallery val uriFromGallery = Uri.parse(AN_URI_FROM_GALLERY) - fakePickerProvider.givenResult(uriFromGallery) + pickerProvider.givenResult(uriFromGallery) newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) newState = awaitItem() expectedConfig = expectedConfig.copy(avatarUri = uriFromGallery) assertThat(newState.config).isEqualTo(expectedConfig) // From camera val uriFromCamera = Uri.parse(AN_URI_FROM_CAMERA) - fakePickerProvider.givenResult(uriFromCamera) + pickerProvider.givenResult(uriFromCamera) assertThat(newState.cameraPermissionState.permissionGranted).isFalse() newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto)) newState = awaitItem() assertThat(newState.cameraPermissionState.showDialog).isTrue() - fakePermissionsPresenter.setPermissionGranted() + permissionsPresenter.setPermissionGranted() newState = awaitItem() assertThat(newState.cameraPermissionState.permissionGranted).isTrue() newState = awaitItem() @@ -188,7 +175,7 @@ class ConfigureRoomPresenterTest { assertThat(newState.config).isEqualTo(expectedConfig) // Do it again, no permission is requested val uriFromCamera2 = Uri.parse(AN_URI_FROM_CAMERA_2) - fakePickerProvider.givenResult(uriFromCamera2) + pickerProvider.givenResult(uriFromCamera2) newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto)) newState = awaitItem() expectedConfig = expectedConfig.copy(avatarUri = uriFromCamera2) @@ -200,13 +187,19 @@ class ConfigureRoomPresenterTest { assertThat(newState.config).isEqualTo(expectedConfig) // Room privacy - newState.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(RoomPrivacy.Public)) + newState.eventSink(ConfigureRoomEvents.RoomVisibilityChanged(RoomVisibilityItem.Public)) newState = awaitItem() - expectedConfig = expectedConfig.copy(privacy = RoomPrivacy.Public) + expectedConfig = expectedConfig.copy( + roomVisibility = RoomVisibilityState.Public( + roomAddress = RoomAddress.AutoFilled(expectedConfig.roomName ?: ""), + roomAddressErrorState = RoomAddressErrorState.None, + roomAccess = RoomAccess.Anyone, + ) + ) assertThat(newState.config).isEqualTo(expectedConfig) // Remove user - newState.eventSink(ConfigureRoomEvents.RemoveFromSelection(selectedUser1)) + newState.eventSink(ConfigureRoomEvents.RemoveUserFromSelection(selectedUser1)) newState = awaitItem() expectedConfig = expectedConfig.copy(invites = expectedConfig.invites.minus(selectedUser1).toImmutableList()) assertThat(newState.config).isEqualTo(expectedConfig) @@ -215,15 +208,17 @@ class ConfigureRoomPresenterTest { @Test fun `present - trigger create room action`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val matrixClient = createMatrixClient() + val presenter = createConfigureRoomPresenter( + matrixClient = matrixClient + ) + presenter.test { + val initialState = initialState() val createRoomResult = Result.success(RoomId("!createRoomResult:domain")) - fakeMatrixClient.givenCreateRoomResult(createRoomResult) + matrixClient.givenCreateRoomResult(createRoomResult) - initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + initialState.eventSink(ConfigureRoomEvents.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterCreateRoom = awaitItem() assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Success::class.java) @@ -233,18 +228,22 @@ class ConfigureRoomPresenterTest { @Test fun `present - record analytics when creating room`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val matrixClient = createMatrixClient() + val analyticsService = FakeAnalyticsService() + val presenter = createConfigureRoomPresenter( + matrixClient = matrixClient, + analyticsService = analyticsService + ) + presenter.test { + val initialState = initialState() val createRoomResult = Result.success(RoomId("!createRoomResult:domain")) - fakeMatrixClient.givenCreateRoomResult(createRoomResult) + matrixClient.givenCreateRoomResult(createRoomResult) - initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + initialState.eventSink(ConfigureRoomEvents.CreateRoom) skipItems(2) - val analyticsEvent = fakeAnalyticsService.capturedEvents.filterIsInstance().firstOrNull() + val analyticsEvent = analyticsService.capturedEvents.filterIsInstance().firstOrNull() assertThat(analyticsEvent).isNotNull() assertThat(analyticsEvent?.isDM).isFalse() } @@ -252,23 +251,31 @@ class ConfigureRoomPresenterTest { @Test fun `present - trigger create room with upload error and retry`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - skipItems(1) + val matrixClient = createMatrixClient() + val analyticsService = FakeAnalyticsService() + val mediaPreProcessor = FakeMediaPreProcessor() + val createRoomDataStore = CreateRoomDataStore(UserListDataStore()) + val presenter = createConfigureRoomPresenter( + createRoomDataStore = createRoomDataStore, + mediaPreProcessor = mediaPreProcessor, + matrixClient = matrixClient, + analyticsService = analyticsService + ) + presenter.test { + val initialState = initialState() createRoomDataStore.setAvatarUri(Uri.parse(AN_URI_FROM_GALLERY)) - fakeMediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(mockk(), mockk(), mockk()))) - fakeMatrixClient.givenUploadMediaResult(Result.failure(A_THROWABLE)) + skipItems(1) + mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(mockk(), mockk(), mockk()))) + matrixClient.givenUploadMediaResult(Result.failure(A_THROWABLE)) - val initialState = awaitItem() - initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + initialState.eventSink(ConfigureRoomEvents.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterCreateRoom = awaitItem() assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java) - assertThat(fakeAnalyticsService.capturedEvents.filterIsInstance()).isEmpty() + assertThat(analyticsService.capturedEvents.filterIsInstance()).isEmpty() - fakeMatrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL)) - stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + matrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL)) + stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Success::class.java) @@ -277,23 +284,25 @@ class ConfigureRoomPresenterTest { @Test fun `present - trigger retry and cancel actions`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val fakeMatrixClient = createMatrixClient() + val presenter = createConfigureRoomPresenter( + matrixClient = fakeMatrixClient + ) + presenter.test { + val initialState = initialState() val createRoomResult = Result.failure(A_THROWABLE) fakeMatrixClient.givenCreateRoomResult(createRoomResult) // Create - initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + initialState.eventSink(ConfigureRoomEvents.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterCreateRoom = awaitItem() assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java) assertThat((stateAfterCreateRoom.createRoomAction as? AsyncAction.Failure)?.error).isEqualTo(createRoomResult.exceptionOrNull()) // Retry - stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterRetry = awaitItem() @@ -305,4 +314,33 @@ class ConfigureRoomPresenterTest { assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) } } + + private suspend fun TurbineTestContext.initialState(): ConfigureRoomState { + skipItems(1) + return awaitItem() + } + + private fun createMatrixClient() = FakeMatrixClient( + userIdServerNameLambda = { "matrix.org" }, + ) + + private fun createConfigureRoomPresenter( + createRoomDataStore: CreateRoomDataStore = CreateRoomDataStore(UserListDataStore()), + matrixClient: MatrixClient = createMatrixClient(), + pickerProvider: PickerProvider = FakePickerProvider(), + mediaPreProcessor: MediaPreProcessor = FakeMediaPreProcessor(), + analyticsService: AnalyticsService = FakeAnalyticsService(), + permissionsPresenter: PermissionsPresenter = FakePermissionsPresenter(), + isKnockFeatureEnabled: Boolean = true, + ) = ConfigureRoomPresenter( + dataStore = createRoomDataStore, + matrixClient = matrixClient, + mediaPickerProvider = pickerProvider, + mediaPreProcessor = mediaPreProcessor, + analyticsService = analyticsService, + permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter), + featureFlagService = FakeFeatureFlagService( + mapOf(FeatureFlags.Knock.key to isKnockFeatureEnabled) + ) + ) } diff --git a/features/deactivation/impl/src/main/res/values-nl/translations.xml b/features/deactivation/impl/src/main/res/values-nl/translations.xml new file mode 100644 index 0000000000..5f7cf78847 --- /dev/null +++ b/features/deactivation/impl/src/main/res/values-nl/translations.xml @@ -0,0 +1,14 @@ + + + "Bevestig dat je je account wilt sluiten. Deze actie kan niet ongedaan worden gemaakt." + "Verwijder al mijn berichten" + "Waarschuwing: Toekomstige gebruikers kunnen onvolledige gesprekken te zien krijgen." + "Je account sluiten is %1$s, het zal:" + "onomkeerbaar" + "Je account %1$s (je kunt niet opnieuw inloggen en je ID kan niet opnieuw worden gebruikt)" + "permanent uitschakelen" + "Je verwijderen uit alle chatrooms." + "Je accountgegevens verwijderen van onze identiteitsserver." + "Je berichten zijn nog steeds zichtbaar voor geregistreerde gebruikers, maar niet beschikbaar voor nieuwe of niet-geregistreerde gebruikers als je ervoor kiest ze te verwijderen." + "Account sluiten" + diff --git a/features/ftue/impl/build.gradle.kts b/features/ftue/impl/build.gradle.kts index a4a0ef8f71..1937a4b0fc 100644 --- a/features/ftue/impl/build.gradle.kts +++ b/features/ftue/impl/build.gradle.kts @@ -45,6 +45,7 @@ dependencies { testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) testImplementation(projects.services.analytics.test) + testImplementation(projects.services.analytics.noop) testImplementation(projects.libraries.permissions.impl) testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.preferences.test) diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt index 16473e62f5..f84a0d2b87 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt @@ -8,7 +8,10 @@ package io.element.android.features.ftue.impl import android.os.Parcelable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.lifecycle.lifecycleScope import com.bumble.appyx.core.lifecycle.subscribe @@ -31,12 +34,14 @@ import io.element.android.features.lockscreen.api.LockScreenEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.SessionScope import io.element.android.services.analytics.api.AnalyticsService import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -80,14 +85,17 @@ class FtueFlowNode @AssistedInject constructor( super.onBuilt() lifecycle.subscribe(onCreate = { - lifecycleScope.launch { moveToNextStep() } + moveToNextStepIfNeeded() }) analyticsService.didAskUserConsent() .distinctUntilChanged() - .onEach { - lifecycleScope.launch { moveToNextStep() } - } + .onEach { moveToNextStepIfNeeded() } + .launchIn(lifecycleScope) + + ftueState.isVerificationStatusKnown + .filter { it } + .onEach { moveToNextStepIfNeeded() } .launchIn(lifecycleScope) } @@ -99,7 +107,7 @@ class FtueFlowNode @AssistedInject constructor( NavTarget.SessionVerification -> { val callback = object : FtueSessionVerificationFlowNode.Callback { override fun onDone() { - lifecycleScope.launch { moveToNextStep() } + moveToNextStepIfNeeded() } } createNode(buildContext, listOf(callback)) @@ -107,7 +115,7 @@ class FtueFlowNode @AssistedInject constructor( NavTarget.NotificationsOptIn -> { val callback = object : NotificationsOptInNode.Callback { override fun onNotificationsOptInFinished() { - lifecycleScope.launch { moveToNextStep() } + moveToNextStepIfNeeded() } } createNode(buildContext, listOf(callback)) @@ -118,7 +126,7 @@ class FtueFlowNode @AssistedInject constructor( NavTarget.LockScreenSetup -> { val callback = object : LockScreenEntryPoint.Callback { override fun onSetupDone() { - lifecycleScope.launch { moveToNextStep() } + moveToNextStepIfNeeded() } } lockScreenEntryPoint.nodeBuilder(this, buildContext, LockScreenEntryPoint.Target.Setup) @@ -128,8 +136,11 @@ class FtueFlowNode @AssistedInject constructor( } } - private fun moveToNextStep() = lifecycleScope.launch { + private fun moveToNextStepIfNeeded() = lifecycleScope.launch { when (ftueState.getNextStep()) { + FtueStep.WaitingForInitialState -> { + backstack.newRoot(NavTarget.Placeholder) + } FtueStep.SessionVerification -> { backstack.newRoot(NavTarget.SessionVerification) } @@ -155,7 +166,14 @@ class FtueFlowNode @AssistedInject constructor( class PlaceholderNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, - ) : Node(buildContext, plugins = plugins) + ) : Node(buildContext, plugins = plugins) { + @Composable + override fun View(modifier: Modifier) { + Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + CircularProgressIndicator() + } + } + } } private class NoOpBackstackHandlerStrategy : BaseBackPressHandlerStrategy() { diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/state/DefaultFtueService.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/state/DefaultFtueService.kt index fd4f5bb8b1..924259b9d2 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/state/DefaultFtueService.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/state/DefaultFtueService.kt @@ -15,6 +15,7 @@ import io.element.android.features.ftue.api.state.FtueService import io.element.android.features.ftue.api.state.FtueState import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus @@ -23,21 +24,17 @@ import io.element.android.libraries.preferences.api.store.SessionPreferencesStor import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.timeout -import kotlinx.coroutines.runBlocking -import timber.log.Timber import javax.inject.Inject -import kotlin.time.Duration.Companion.seconds @ContributesBinding(SessionScope::class) +@SingleIn(SessionScope::class) class DefaultFtueService @Inject constructor( private val sdkVersionProvider: BuildVersionSdkIntProvider, @SessionCoroutineScope sessionCoroutineScope: CoroutineScope, @@ -49,6 +46,14 @@ class DefaultFtueService @Inject constructor( ) : FtueService { override val state = MutableStateFlow(FtueState.Unknown) + /** + * This flow emits true when the FTUE flow is ready to be displayed. + * In this case, the FTUE flow is ready when the session verification status is known. + */ + val isVerificationStatusKnown = sessionVerificationService.sessionVerifiedStatus + .map { it != SessionVerifiedStatus.Unknown } + .distinctUntilChanged() + override suspend fun reset() { analyticsService.reset() if (sdkVersionProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) { @@ -69,7 +74,12 @@ class DefaultFtueService @Inject constructor( suspend fun getNextStep(currentStep: FtueStep? = null): FtueStep? = when (currentStep) { - null -> if (isSessionNotVerified()) { + null -> if (!isSessionVerificationStateReady()) { + FtueStep.WaitingForInitialState + } else { + getNextStep(FtueStep.WaitingForInitialState) + } + FtueStep.WaitingForInitialState -> if (isSessionNotVerified()) { FtueStep.SessionVerification } else { getNextStep(FtueStep.SessionVerification) @@ -89,34 +99,18 @@ class DefaultFtueService @Inject constructor( } else { getNextStep(FtueStep.AnalyticsOptIn) } - FtueStep.AnalyticsOptIn -> { - updateState() - null - } + FtueStep.AnalyticsOptIn -> null } - private suspend fun isAnyStepIncomplete(): Boolean { - return listOf Boolean>( - { isSessionNotVerified() }, - { shouldAskNotificationPermissions() }, - { needsAnalyticsOptIn() }, - { shouldDisplayLockscreenSetup() }, - ).any { it() } + private fun isSessionVerificationStateReady(): Boolean { + return sessionVerificationService.sessionVerifiedStatus.value != SessionVerifiedStatus.Unknown } - @OptIn(FlowPreview::class) private suspend fun isSessionNotVerified(): Boolean { - // Wait for the first known (or ready) verification status - val readyVerifiedSessionStatus = sessionVerificationService.sessionVerifiedStatus - .filter { it != SessionVerifiedStatus.Unknown } - // This is not ideal, but there are some very rare cases when reading the flow seems to get stuck - .timeout(5.seconds) - .catch { - Timber.e(it, "Failed to get session verification status, assume it's not verified") - emit(SessionVerifiedStatus.NotVerified) - } - .first() - return readyVerifiedSessionStatus == SessionVerifiedStatus.NotVerified && !canSkipVerification() + // Wait until the session verification status is known + isVerificationStatusKnown.filter { it }.first() + + return sessionVerificationService.sessionVerifiedStatus.value == SessionVerifiedStatus.NotVerified && !canSkipVerification() } private suspend fun canSkipVerification(): Boolean { @@ -130,7 +124,7 @@ class DefaultFtueService @Inject constructor( private suspend fun shouldAskNotificationPermissions(): Boolean { return if (sdkVersionProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) { val permission = Manifest.permission.POST_NOTIFICATIONS - val isPermissionDenied = runBlocking { permissionStateProvider.isPermissionDenied(permission).first() } + val isPermissionDenied = permissionStateProvider.isPermissionDenied(permission).first() val isPermissionGranted = permissionStateProvider.isPermissionGranted(permission) !isPermissionGranted && !isPermissionDenied } else { @@ -144,14 +138,17 @@ class DefaultFtueService @Inject constructor( @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) internal suspend fun updateState() { + val nextStep = getNextStep() state.value = when { - isAnyStepIncomplete() -> FtueState.Incomplete - else -> FtueState.Complete + // Final state, there aren't any more next steps + nextStep == null -> FtueState.Complete + else -> FtueState.Incomplete } } } sealed interface FtueStep { + data object WaitingForInitialState : FtueStep data object SessionVerification : FtueStep data object NotificationsOptIn : FtueStep data object AnalyticsOptIn : FtueStep diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueServiceTest.kt b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueServiceTest.kt index 10189c3c67..ca0222398c 100644 --- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueServiceTest.kt +++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueServiceTest.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.permissions.impl.FakePermissionStateProvider import io.element.android.libraries.preferences.api.store.SessionPreferencesStore import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore import io.element.android.services.analytics.api.AnalyticsService +import io.element.android.services.analytics.noop.NoopAnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider import io.element.android.tests.testutils.lambda.lambdaRecorder @@ -35,7 +36,7 @@ class DefaultFtueServiceTest { @Test fun `given any check being false and session verification state being loaded, FtueState is Incomplete`() = runTest { val sessionVerificationService = FakeSessionVerificationService().apply { - givenVerifiedStatus(SessionVerifiedStatus.Unknown) + emitVerifiedStatus(SessionVerifiedStatus.Unknown) } val service = createDefaultFtueService( sessionVerificationService = sessionVerificationService, @@ -46,7 +47,7 @@ class DefaultFtueServiceTest { assertThat(awaitItem()).isEqualTo(FtueState.Unknown) // Verification state is known, we should display the flow if any check is false - sessionVerificationService.givenVerifiedStatus(SessionVerifiedStatus.NotVerified) + sessionVerificationService.emitVerifiedStatus(SessionVerifiedStatus.NotVerified) assertThat(awaitItem()).isEqualTo(FtueState.Incomplete) } } @@ -64,7 +65,7 @@ class DefaultFtueServiceTest { lockScreenService = lockScreenService, ) - sessionVerificationService.givenVerifiedStatus(SessionVerifiedStatus.Verified) + sessionVerificationService.emitVerifiedStatus(SessionVerifiedStatus.Verified) analyticsService.setDidAskUserConsent() permissionStateProvider.setPermissionGranted() lockScreenService.setIsPinSetup(true) @@ -73,10 +74,31 @@ class DefaultFtueServiceTest { assertThat(service.state.value).isEqualTo(FtueState.Complete) } + @Test + fun `given all checks being true with no analytics, FtueState is Complete`() = runTest { + val analyticsService = NoopAnalyticsService() + val sessionVerificationService = FakeSessionVerificationService() + val permissionStateProvider = FakePermissionStateProvider(permissionGranted = true) + val lockScreenService = FakeLockScreenService() + val service = createDefaultFtueService( + sessionVerificationService = sessionVerificationService, + analyticsService = analyticsService, + permissionStateProvider = permissionStateProvider, + lockScreenService = lockScreenService, + ) + + sessionVerificationService.emitVerifiedStatus(SessionVerifiedStatus.Verified) + permissionStateProvider.setPermissionGranted() + lockScreenService.setIsPinSetup(true) + service.updateState() + + assertThat(service.state.value).isEqualTo(FtueState.Complete) + } + @Test fun `traverse flow`() = runTest { val sessionVerificationService = FakeSessionVerificationService().apply { - givenVerifiedStatus(SessionVerifiedStatus.NotVerified) + emitVerifiedStatus(SessionVerifiedStatus.NotVerified) } val analyticsService = FakeAnalyticsService() val permissionStateProvider = FakePermissionStateProvider(permissionGranted = false) @@ -91,7 +113,7 @@ class DefaultFtueServiceTest { // Session verification steps.add(service.getNextStep(steps.lastOrNull())) - sessionVerificationService.givenVerifiedStatus(SessionVerifiedStatus.NotVerified) + sessionVerificationService.emitVerifiedStatus(SessionVerifiedStatus.NotVerified) // Notifications opt in steps.add(service.getNextStep(steps.lastOrNull())) @@ -132,7 +154,7 @@ class DefaultFtueServiceTest { ) // Skip first 3 steps - sessionVerificationService.givenVerifiedStatus(SessionVerifiedStatus.Verified) + sessionVerificationService.emitVerifiedStatus(SessionVerifiedStatus.Verified) permissionStateProvider.setPermissionGranted() lockScreenService.setIsPinSetup(true) @@ -155,7 +177,7 @@ class DefaultFtueServiceTest { lockScreenService = lockScreenService, ) - sessionVerificationService.givenVerifiedStatus(SessionVerifiedStatus.Verified) + sessionVerificationService.emitVerifiedStatus(SessionVerifiedStatus.Verified) lockScreenService.setIsPinSetup(true) assertThat(service.getNextStep()).isEqualTo(FtueStep.AnalyticsOptIn) diff --git a/features/joinroom/impl/src/main/res/values-fr/translations.xml b/features/joinroom/impl/src/main/res/values-fr/translations.xml index 9dc20cdab0..5e89edb1fa 100644 --- a/features/joinroom/impl/src/main/res/values-fr/translations.xml +++ b/features/joinroom/impl/src/main/res/values-fr/translations.xml @@ -1,6 +1,9 @@ "Annuler la demande" + "Oui, annuler" + "Êtes-vous sûr de vouloir annuler votre demande d’accès à ce salon?" + "Annuler la demande d’adhésion" "Rejoindre" "Demander à joindre" "Message (facultatif)" diff --git a/features/joinroom/impl/src/main/res/values-nl/translations.xml b/features/joinroom/impl/src/main/res/values-nl/translations.xml index d778ef8640..c64b8917d3 100644 --- a/features/joinroom/impl/src/main/res/values-nl/translations.xml +++ b/features/joinroom/impl/src/main/res/values-nl/translations.xml @@ -2,6 +2,8 @@ "Toetreden tot de kamer" "Klop om deel te nemen" + "Bericht (optioneel)" + "Verzoek om toe te treden verzonden" "%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." diff --git a/features/joinroom/impl/src/main/res/values-pl/translations.xml b/features/joinroom/impl/src/main/res/values-pl/translations.xml index 585169fa80..758d8d5615 100644 --- a/features/joinroom/impl/src/main/res/values-pl/translations.xml +++ b/features/joinroom/impl/src/main/res/values-pl/translations.xml @@ -1,7 +1,14 @@ + "Anuluj prośbę" + "Tak, anuluj" + "Czy na pewno chcesz anulować prośbę o dołączenie do tego pokoju?" + "Anuluj prośbę o dołączenie" "Dołącz do pokoju" - "Zapukaj, by dołączyć" + "Wyślij prośbę o dołączenie" + "Wiadomość (opcjonalne)" + "Otrzymasz zaproszenie dołączenia do pokoju, jeśli prośba zostanie zaakceptowana." + "Wysłano prośbę o dołączenie" "%1$s jeszcze nie obsługuje przestrzeni. Uzyskaj dostęp do przestrzeni w wersji web." "Przestrzenie nie są jeszcze obsługiwane" "Kliknij przycisk poniżej, aby powiadomić administratora pokoju. Po zatwierdzeniu będziesz mógł dołączyć do rozmowy." diff --git a/features/joinroom/impl/src/main/res/values-pt/translations.xml b/features/joinroom/impl/src/main/res/values-pt/translations.xml index 81e2c4bd1c..93eeaf3bca 100644 --- a/features/joinroom/impl/src/main/res/values-pt/translations.xml +++ b/features/joinroom/impl/src/main/res/values-pt/translations.xml @@ -1,6 +1,9 @@ "Cancelar pedido" + "Sim, cancelar" + "Tens a certeza de que queres cancelar o teu pedido de entrada nesta sala?" + "Cancela o pedido de adesão" "Entrar na sala" "Bater à porta" "Mensagem (opcional)" 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 024871d5b3..d278d3a15a 100644 --- a/features/login/impl/src/main/res/values-hu/translations.xml +++ b/features/login/impl/src/main/res/values-hu/translations.xml @@ -40,15 +40,15 @@ "A kapcsolat nem biztonságos" "A rendszer kérni fogja, hogy adja meg az alábbi két számjegyet az eszközén." "Adja meg az alábbi számot a másik eszközén" - "Jelentkezzen be a másik eszközére, majd próbálja újra, vagy használjon egy másik eszközt, amelyre már bejelentkezett." + "Jelentkezzen be másik eszközére, majd próbálkozzon újra, vagy használjon egy másik, már bejelentkezett eszközt." "Más eszköz nincs bejelentkezve" - "A bejelentkezés megszakadt a másik eszközön." + "A bejelentkezést megszakították a másik eszközön." "Bejelentkezési kérés törölve" - "A bejelentkezés el lett utasítva a másik eszközön." + "A bejelentkezést elutasították a másik eszközön." "A bejelentkezés elutasítva" "A bejelentkezés lejárt. Próbálja újra." "A bejelentkezés nem fejeződött be időben" - "A másik eszköz nem támogatja a %s QR-kóddal történő bejelentkezést. + "A másik eszköz nem támogatja QR-kóddal történő bejelentkezést az %sbe. Próbáljon meg kézileg bejelentkezni, vagy olvassa be a QR-kódot egy másik eszközzel." "A QR-kód nem támogatott" 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 749406da04..0ddc32c212 100644 --- a/features/login/impl/src/main/res/values-in/translations.xml +++ b/features/login/impl/src/main/res/values-in/translations.xml @@ -21,6 +21,7 @@ "Anda hanya dapat terhubung ke server yang ada yang mendukung sinkronisasi geser. Admin homeserver Anda perlu mengaturnya. %1$s" "Apa alamat server Anda?" "Pilih server Anda" + "Buat akun" "Akun ini telah dinonaktifkan." "Nama pengguna dan/atau kata sandi salah" "Ini bukan pengenal pengguna yang valid. Format yang diharapkan: \'@pengguna:homeserver.org\'" @@ -59,6 +60,7 @@ Coba masuk secara manual, atau pindai kode QR dengan perangkat lain." "Pilih %1$s" "“Tautkan perangkat baru”" "Pindai kode QR dengan perangkat ini" + "Hanya tersedia jika penyedia akun Anda mendukungnya." "Buka %1$s di perangkat lain untuk mendapatkan kode QR" "Gunakan kode QR yang ditampilkan di perangkat lain." "Coba lagi" 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 aa4622ad28..4097408d55 100644 --- a/features/login/impl/src/main/res/values-nl/translations.xml +++ b/features/login/impl/src/main/res/values-nl/translations.xml @@ -21,7 +21,8 @@ "Je kunt alleen verbinding maken met een bestaande server die sliding sync ondersteunt. De beheerder van de homeserver moet dit configureren. %1$s" "Wat is het adres van je server?" "Selecteer je server" - "Dit account is gedeactiveerd." + "Account aanmaken" + "Dit account is gesloten." "Onjuiste gebruikersnaam en/of wachtwoord" "Dit is geen geldige gebruikers-ID. Verwacht formaat: \'@user:homeserver.org\'" "Deze server is geconfigureerd om verversingstokens te gebruiken. Deze worden niet ondersteund bij inloggen met een wachtwoord." @@ -59,6 +60,7 @@ Probeer handmatig in te loggen, of scan de QR code met een ander apparaat.""Selecteer %1$s" "“Nieuw apparaat koppelen”" "Scan de QR-code met dit apparaat" + "Alleen beschikbaar als je accountprovider dit ondersteunt." "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" 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 93a9f82c1b..fc0ad2789c 100644 --- a/features/login/impl/src/main/res/values-pl/translations.xml +++ b/features/login/impl/src/main/res/values-pl/translations.xml @@ -60,6 +60,7 @@ Spróbuj zalogować się ręcznie lub zeskanuj kod QR na innym urządzeniu.""Wybierz %1$s" "“Powiąż nowe urządzenie”" "Zeskanuj kod QR za pomocą tego urządzenia" + "Dostępne tylko wtedy, gdy Twój dostawca konta obsługuje tę funkcję." "Otwórz %1$s na innym urządzeniu, aby uzyskać kod QR" "Użyj kodu QR widocznego na drugim urządzeniu." "Spróbuj ponownie" diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index 892c69ea2c..824e8c1692 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { implementation(projects.features.call.api) implementation(projects.features.location.api) implementation(projects.features.poll.api) + implementation(projects.features.roomcall.api) implementation(projects.libraries.androidutils) implementation(projects.libraries.core) implementation(projects.libraries.architecture) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index b215fc49fd..7840c307c7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -50,6 +50,7 @@ import io.element.android.features.messages.impl.timeline.protection.TimelinePro import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus +import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.androidutils.clipboard.ClipboardHelper import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter @@ -75,7 +76,6 @@ import io.element.android.libraries.matrix.api.room.powerlevels.canSendMessage import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.ui.messages.reply.map import io.element.android.libraries.matrix.ui.model.getAvatarData -import io.element.android.libraries.matrix.ui.room.canCall import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.api.AnalyticsService @@ -98,6 +98,7 @@ class MessagesPresenter @AssistedInject constructor( private val reactionSummaryPresenter: Presenter, private val readReceiptBottomSheetPresenter: Presenter, private val pinnedMessagesBannerPresenter: Presenter, + private val roomCallStatePresenter: Presenter, private val networkMonitor: NetworkMonitor, private val snackbarDispatcher: SnackbarDispatcher, private val dispatchers: CoroutineDispatchers, @@ -133,6 +134,7 @@ class MessagesPresenter @AssistedInject constructor( val reactionSummaryState = reactionSummaryPresenter.present() val readReceiptBottomSheetState = readReceiptBottomSheetPresenter.present() val pinnedMessagesBannerState = pinnedMessagesBannerPresenter.present() + val roomCallState = roomCallStatePresenter.present() val syncUpdateFlow = room.syncUpdateFlow.collectAsState() @@ -152,8 +154,6 @@ class MessagesPresenter @AssistedInject constructor( mutableStateOf(false) } - val canJoinCall by room.canCall(updateKey = syncUpdateFlow.value) - LaunchedEffect(Unit) { // Remove the unread flag on entering but don't send read receipts // as those will be handled by the timeline. @@ -204,12 +204,6 @@ class MessagesPresenter @AssistedInject constructor( } } - val callState = when { - !canJoinCall -> RoomCallState.DISABLED - roomInfo?.hasRoomCall == true -> RoomCallState.ONGOING - else -> RoomCallState.ENABLED - } - return MessagesState( roomId = room.roomId, roomName = roomName, @@ -232,7 +226,7 @@ class MessagesPresenter @AssistedInject constructor( enableTextFormatting = MessageComposerConfig.ENABLE_RICH_TEXT_EDITING, enableVoiceMessages = enableVoiceMessages, appName = buildMeta.applicationName, - callState = callState, + roomCallState = roomCallState, pinnedMessagesBannerState = pinnedMessagesBannerState, eventSink = { handleEvents(it) } ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt index 2dc43030a4..a643784f1d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt @@ -18,6 +18,7 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState +import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage @@ -46,14 +47,8 @@ data class MessagesState( val showReinvitePrompt: Boolean, val enableTextFormatting: Boolean, val enableVoiceMessages: Boolean, - val callState: RoomCallState, + val roomCallState: RoomCallState, val appName: String, val pinnedMessagesBannerState: PinnedMessagesBannerState, val eventSink: (MessagesEvents) -> Unit ) - -enum class RoomCallState { - ENABLED, - ONGOING, - DISABLED -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index c8a8ee6f1f..e8dc5329f4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -33,13 +33,15 @@ import io.element.android.features.messages.impl.timeline.protection.aTimelinePr import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessageComposerState import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessagePreviewState +import io.element.android.features.roomcall.api.RoomCallState +import io.element.android.features.roomcall.api.aStandByCallState +import io.element.android.features.roomcall.api.anOngoingCallState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.textcomposer.aRichTextEditorState import io.element.android.libraries.textcomposer.model.MessageComposerMode -import io.element.android.libraries.textcomposer.model.TextEditorState +import io.element.android.libraries.textcomposer.model.aTextEditorStateRich import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentSetOf @@ -71,7 +73,7 @@ open class MessagesStateProvider : PreviewParameterProvider { ), ), aMessagesState( - callState = RoomCallState.ONGOING, + roomCallState = anOngoingCallState(), ), aMessagesState( enableVoiceMessages = true, @@ -81,7 +83,7 @@ open class MessagesStateProvider : PreviewParameterProvider { ), ), aMessagesState( - callState = RoomCallState.DISABLED, + roomCallState = aStandByCallState(canStartCall = false), ), aMessagesState( pinnedMessagesBannerState = aLoadedPinnedMessagesBannerState( @@ -97,7 +99,7 @@ fun aMessagesState( roomAvatar: AsyncData = AsyncData.Success(AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom)), userEventPermissions: UserEventPermissions = aUserEventPermissions(), composerState: MessageComposerState = aMessageComposerState( - textEditorState = TextEditorState.Rich(aRichTextEditorState(initialText = "Hello", initialFocus = true)), + textEditorState = aTextEditorStateRich(initialText = "Hello", initialFocus = true), isFullScreen = false, mode = MessageComposerMode.Normal, ), @@ -116,7 +118,7 @@ fun aMessagesState( hasNetworkConnection: Boolean = true, showReinvitePrompt: Boolean = false, enableVoiceMessages: Boolean = true, - callState: RoomCallState = RoomCallState.ENABLED, + roomCallState: RoomCallState = aStandByCallState(), pinnedMessagesBannerState: PinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(), eventSink: (MessagesEvents) -> Unit = {}, ) = MessagesState( @@ -140,7 +142,7 @@ fun aMessagesState( showReinvitePrompt = showReinvitePrompt, enableTextFormatting = true, enableVoiceMessages = enableVoiceMessages, - callState = callState, + roomCallState = roomCallState, appName = "Element", pinnedMessagesBannerState = pinnedMessagesBannerState, eventSink = eventSink, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index bd8ae98b2f..e5d73fbed6 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 @@ -52,7 +52,6 @@ import androidx.compose.ui.text.style.TextOverflow 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.actionlist.ActionListEvents import io.element.android.features.messages.impl.actionlist.ActionListView import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction @@ -69,7 +68,7 @@ import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBan import io.element.android.features.messages.impl.timeline.FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.TimelineView -import io.element.android.features.messages.impl.timeline.components.JoinCallMenuItem +import io.element.android.features.messages.impl.timeline.components.CallMenuItem import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionBottomSheet import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents @@ -81,6 +80,7 @@ import io.element.android.features.messages.impl.voicemessages.composer.VoiceMes import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessagePermissionRationaleDialog import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageSendingFailedDialog import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView +import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.androidutils.ui.hideKeyboard import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule import io.element.android.libraries.designsystem.components.ProgressDialog @@ -93,8 +93,6 @@ import io.element.android.libraries.designsystem.components.dialogs.Confirmation import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.BottomSheetDragHandle -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.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar @@ -190,7 +188,7 @@ fun MessagesView( roomName = state.roomName.dataOrNull(), roomAvatar = state.roomAvatar.dataOrNull(), heroes = state.heroes, - callState = state.callState, + roomCallState = state.roomCallState, onBackClick = { // Since the textfield is now based on an Android view, this is no longer done automatically. // We need to hide the keyboard when navigating out of this screen. @@ -479,7 +477,7 @@ private fun MessagesViewTopBar( roomName: String?, roomAvatar: AvatarData?, heroes: ImmutableList, - callState: RoomCallState, + roomCallState: RoomCallState, onRoomDetailsClick: () -> Unit, onJoinCallClick: () -> Unit, onBackClick: () -> Unit, @@ -509,9 +507,8 @@ private fun MessagesViewTopBar( }, actions = { CallMenuItem( - isCallOngoing = callState == RoomCallState.ONGOING, - onClick = onJoinCallClick, - enabled = callState != RoomCallState.DISABLED + roomCallState = roomCallState, + onJoinCallClick = onJoinCallClick, ) Spacer(Modifier.width(8.dp)) }, @@ -519,24 +516,6 @@ private fun MessagesViewTopBar( ) } -@Composable -private fun CallMenuItem( - isCallOngoing: Boolean, - enabled: Boolean = true, - onClick: () -> Unit, -) { - if (isCallOngoing) { - JoinCallMenuItem(onJoinCallClick = onClick) - } else { - IconButton(onClick = onClick, enabled = enabled) { - Icon( - imageVector = CompoundIcons.VideoCallSolid(), - contentDescription = stringResource(CommonStrings.a11y_start_call), - ) - } - } -} - @Composable private fun RoomAvatarAndNameRow( roomName: String, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvents.kt index 28cc95eaf0..742e78e8e0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvents.kt @@ -12,5 +12,6 @@ import androidx.compose.runtime.Immutable @Immutable sealed interface AttachmentsPreviewEvents { data object SendAttachment : AttachmentsPreviewEvents + data object Cancel : AttachmentsPreviewEvents data object ClearSendState : AttachmentsPreviewEvents } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt index a435821197..2417d8346e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt @@ -31,7 +31,14 @@ class AttachmentsPreviewNode @AssistedInject constructor( private val inputs: Inputs = inputs() - private val presenter = presenterFactory.create(inputs.attachment) + private val onDoneListener = OnDoneListener { + navigateUp() + } + + private val presenter = presenterFactory.create( + attachment = inputs.attachment, + onDoneListener = onDoneListener, + ) @Composable override fun View(modifier: Modifier) { @@ -39,7 +46,6 @@ class AttachmentsPreviewNode @AssistedInject constructor( val state = presenter.present() AttachmentsPreviewView( state = state, - onDismiss = this::navigateUp, modifier = modifier ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt index 42468d66cf..bc3e121070 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt @@ -9,16 +9,22 @@ package io.element.android.features.messages.impl.attachments.preview import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.element.android.features.messages.impl.attachments.Attachment +import io.element.android.libraries.androidutils.file.TemporaryUriDeleter import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.core.ProgressCallback +import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.libraries.mediaupload.api.MediaSender +import io.element.android.libraries.textcomposer.model.TextEditorState +import io.element.android.libraries.textcomposer.model.rememberMarkdownTextEditorState import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -29,11 +35,17 @@ import kotlin.coroutines.coroutineContext class AttachmentsPreviewPresenter @AssistedInject constructor( @Assisted private val attachment: Attachment, + @Assisted private val onDoneListener: OnDoneListener, private val mediaSender: MediaSender, + private val permalinkBuilder: PermalinkBuilder, + private val temporaryUriDeleter: TemporaryUriDeleter, ) : Presenter { @AssistedFactory interface Factory { - fun create(attachment: Attachment): AttachmentsPreviewPresenter + fun create( + attachment: Attachment, + onDoneListener: OnDoneListener, + ): AttachmentsPreviewPresenter } @Composable @@ -44,11 +56,27 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( mutableStateOf(SendActionState.Idle) } + val markdownTextEditorState = rememberMarkdownTextEditorState(initialText = null, initialFocus = false) + val textEditorState by rememberUpdatedState( + TextEditorState.Markdown(markdownTextEditorState) + ) + val ongoingSendAttachmentJob = remember { mutableStateOf(null) } fun handleEvents(attachmentsPreviewEvents: AttachmentsPreviewEvents) { when (attachmentsPreviewEvents) { - AttachmentsPreviewEvents.SendAttachment -> ongoingSendAttachmentJob.value = coroutineScope.sendAttachment(attachment, sendActionState) + is AttachmentsPreviewEvents.SendAttachment -> { + val caption = markdownTextEditorState.getMessageMarkdown(permalinkBuilder) + .takeIf { it.isNotEmpty() } + ongoingSendAttachmentJob.value = coroutineScope.sendAttachment( + attachment = attachment, + caption = caption, + sendActionState = sendActionState, + ) + } + AttachmentsPreviewEvents.Cancel -> { + coroutineScope.cancel(attachment) + } AttachmentsPreviewEvents.ClearSendState -> { ongoingSendAttachmentJob.value?.let { it.cancel() @@ -62,26 +90,42 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( return AttachmentsPreviewState( attachment = attachment, sendActionState = sendActionState.value, + textEditorState = textEditorState, eventSink = ::handleEvents ) } private fun CoroutineScope.sendAttachment( attachment: Attachment, + caption: String?, sendActionState: MutableState, ) = launch { when (attachment) { is Attachment.Media -> { sendMedia( mediaAttachment = attachment, + caption = caption, sendActionState = sendActionState, ) } } } + private fun CoroutineScope.cancel( + attachment: Attachment, + ) = launch { + // Delete the temporary file + when (attachment) { + is Attachment.Media -> { + temporaryUriDeleter.delete(attachment.localMedia.uri) + } + } + onDoneListener() + } + private suspend fun sendMedia( mediaAttachment: Attachment.Media, + caption: String?, sendActionState: MutableState, ) = runCatching { val context = coroutineContext @@ -96,11 +140,12 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( mediaSender.sendMedia( uri = mediaAttachment.localMedia.uri, mimeType = mediaAttachment.localMedia.info.mimeType, + caption = caption, progressCallback = progressCallback ).getOrThrow() }.fold( onSuccess = { - sendActionState.value = SendActionState.Done + onDoneListener() }, onFailure = { error -> Timber.e(error, "Failed to send attachment") diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt index fc446d60a8..5ffe9364ff 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt @@ -9,12 +9,21 @@ package io.element.android.features.messages.impl.attachments.preview import androidx.compose.runtime.Immutable import io.element.android.features.messages.impl.attachments.Attachment +import io.element.android.libraries.core.bool.orFalse +import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage +import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo +import io.element.android.libraries.textcomposer.model.TextEditorState data class AttachmentsPreviewState( val attachment: Attachment, val sendActionState: SendActionState, + val textEditorState: TextEditorState, val eventSink: (AttachmentsPreviewEvents) -> Unit -) +) { + val allowCaption: Boolean = (attachment as? Attachment.Media)?.localMedia?.info?.mimeType?.let { + it.isMimeTypeImage() || it.isMimeTypeVideo() + }.orFalse() +} @Immutable sealed interface SendActionState { @@ -27,5 +36,4 @@ sealed interface SendActionState { } data class Failure(val error: Throwable) : SendActionState - data object Done : SendActionState } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt index 718f9cfbe4..78f3ffc81a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt @@ -12,13 +12,19 @@ import androidx.core.net.toUri import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.mediaviewer.api.local.MediaInfo +import io.element.android.libraries.mediaviewer.api.local.aVideoMediaInfo import io.element.android.libraries.mediaviewer.api.local.anApkMediaInfo +import io.element.android.libraries.mediaviewer.api.local.anAudioMediaInfo import io.element.android.libraries.mediaviewer.api.local.anImageMediaInfo +import io.element.android.libraries.textcomposer.model.TextEditorState +import io.element.android.libraries.textcomposer.model.aTextEditorStateMarkdown open class AttachmentsPreviewStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anAttachmentsPreviewState(), + anAttachmentsPreviewState(mediaInfo = aVideoMediaInfo()), + anAttachmentsPreviewState(mediaInfo = anAudioMediaInfo()), anAttachmentsPreviewState(mediaInfo = anApkMediaInfo()), anAttachmentsPreviewState(sendActionState = SendActionState.Sending.Uploading(0.5f)), anAttachmentsPreviewState(sendActionState = SendActionState.Failure(RuntimeException("error"))), @@ -27,11 +33,13 @@ open class AttachmentsPreviewStateProvider : PreviewParameterProvider Unit, modifier: Modifier = Modifier, ) { fun postSendAttachment() { state.eventSink(AttachmentsPreviewEvents.SendAttachment) } + fun postCancel() { + state.eventSink(AttachmentsPreviewEvents.Cancel) + } + fun postClearSendState() { state.eventSink(AttachmentsPreviewEvents.ClearSendState) } - if (state.sendActionState is SendActionState.Done) { - val latestOnDismiss by rememberUpdatedState(onDismiss) - LaunchedEffect(state.sendActionState) { - latestOnDismiss() - } + BackHandler(enabled = state.sendActionState !is SendActionState.Sending) { + postCancel() } - Scaffold(modifier) { + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + navigationIcon = { + BackButton( + imageVector = CompoundIcons.Close(), + onClick = ::postCancel, + ) + }, + title = {}, + ) + } + ) { AttachmentPreviewContent( - attachment = state.attachment, + state = state, onSendClick = ::postSendAttachment, - onDismiss = onDismiss ) } AttachmentSendStateView( @@ -106,21 +123,19 @@ private fun AttachmentSendStateView( @Composable private fun AttachmentPreviewContent( - attachment: Attachment, + state: AttachmentsPreviewState, onSendClick: () -> Unit, - onDismiss: () -> Unit, ) { Box( modifier = Modifier .fillMaxSize() .navigationBarsPadding(), - contentAlignment = Alignment.BottomCenter ) { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { - when (attachment) { + when (val attachment = state.attachment) { is Attachment.Media -> { val localMediaViewState = rememberLocalMediaViewState( zoomableState = rememberZoomableState( @@ -137,27 +152,46 @@ private fun AttachmentPreviewContent( } } AttachmentsPreviewBottomActions( - onCancelClick = onDismiss, + state = state, onSendClick = onSendClick, modifier = Modifier .fillMaxWidth() - .background(Color.Black.copy(alpha = 0.7f)) - .padding(horizontal = 24.dp) - .defaultMinSize(minHeight = 80.dp) + .background(ElementTheme.colors.bgCanvasDefault) + .height(IntrinsicSize.Min) + .align(Alignment.BottomCenter) + .imePadding(), ) } } @Composable private fun AttachmentsPreviewBottomActions( - onCancelClick: () -> Unit, + state: AttachmentsPreviewState, onSendClick: () -> Unit, modifier: Modifier = Modifier ) { - ButtonRowMolecule(modifier = modifier) { - TextButton(stringResource(id = CommonStrings.action_cancel), onClick = onCancelClick) - TextButton(stringResource(id = CommonStrings.action_send), onClick = onSendClick) - } + TextComposer( + modifier = modifier, + state = state.textEditorState, + voiceMessageState = VoiceMessageState.Idle, + composerMode = MessageComposerMode.Attachment(state.allowCaption), + onRequestFocus = {}, + onSendMessage = onSendClick, + showTextFormatting = false, + onResetComposerMode = {}, + onAddAttachment = {}, + onDismissTextFormatting = {}, + enableVoiceMessages = false, + onVoiceRecorderEvent = {}, + onVoicePlayerEvent = {}, + onSendVoiceMessage = {}, + onDeleteVoiceMessage = {}, + onReceiveSuggestion = {}, + resolveMentionDisplay = { _, _ -> TextDisplay.Plain }, + onError = {}, + onTyping = {}, + onSelectRichContent = {}, + ) } // Only preview in dark, dark theme is forced on the Node. @@ -166,6 +200,5 @@ private fun AttachmentsPreviewBottomActions( internal fun AttachmentsPreviewViewPreview(@PreviewParameter(AttachmentsPreviewStateProvider::class) state: AttachmentsPreviewState) = ElementPreviewDark { AttachmentsPreviewView( state = state, - onDismiss = {}, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/OnDoneListener.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/OnDoneListener.kt new file mode 100644 index 0000000000..2e53ab2c50 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/OnDoneListener.kt @@ -0,0 +1,12 @@ +/* + * 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.attachments.preview + +fun interface OnDoneListener { + operator fun invoke() +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenter.kt index 16bc39fac8..3b56fc9993 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenter.kt @@ -15,8 +15,6 @@ import androidx.compose.runtime.rememberCoroutineScope import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.featureflag.api.FeatureFlagService -import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.room.MatrixRoom @@ -41,7 +39,6 @@ import javax.inject.Inject class IdentityChangeStatePresenter @Inject constructor( private val room: MatrixRoom, private val encryptionService: EncryptionService, - private val featureFlagService: FeatureFlagService, ) : Presenter { @Composable override fun present(): IdentityChangeState { @@ -64,11 +61,7 @@ class IdentityChangeStatePresenter @Inject constructor( @OptIn(ExperimentalCoroutinesApi::class) private fun ProduceStateScope>.observeRoomMemberIdentityStateChange() { - featureFlagService.isFeatureEnabledFlow(FeatureFlags.IdentityPinningViolationNotifications) - .filter { it } - .flatMapLatest { - room.syncUpdateFlow - } + room.syncUpdateFlow .filter { // Room cannot become unencrypted, so we can just apply a filter here. room.isEncrypted diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt index 1b57c52d23..c34f072c6d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt @@ -14,8 +14,7 @@ import io.element.android.features.messages.impl.aMessagesState import io.element.android.features.messages.impl.messagecomposer.aMessageComposerState import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.textcomposer.model.MarkdownTextEditorState -import io.element.android.libraries.textcomposer.model.TextEditorState +import io.element.android.libraries.textcomposer.model.aTextEditorStateMarkdown @PreviewsDayNight @Composable @@ -25,11 +24,9 @@ internal fun MessagesViewWithIdentityChangePreview( MessagesView( state = aMessagesState( composerState = aMessageComposerState( - textEditorState = TextEditorState.Markdown( - state = MarkdownTextEditorState( - initialText = "", - initialFocus = false, - ) + textEditorState = aTextEditorStateMarkdown( + initialText = "", + initialFocus = false, ) ), identityChangeState = identityChangeState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index cce78d601e..6101f48c1f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -436,6 +436,7 @@ class MessageComposerPresenter @Inject constructor( // Reset composer right away resetComposer(markdownTextEditorState, richTextEditorState, fromEdit = capturedMode is MessageComposerMode.Edit) when (capturedMode) { + is MessageComposerMode.Attachment, is MessageComposerMode.Normal -> room.sendMessage( body = message.markdown, htmlBody = message.html, @@ -605,6 +606,7 @@ class MessageComposerPresenter @Inject constructor( ): ComposerDraft? { val message = currentComposerMessage(markdownTextEditorState, richTextEditorState, withMentions = false) val draftType = when (val mode = messageComposerContext.composerMode) { + is MessageComposerMode.Attachment, is MessageComposerMode.Normal -> ComposerDraftType.NewMessage is MessageComposerMode.Edit -> { mode.eventOrTransactionId.eventId?.let { eventId -> ComposerDraftType.Edit(eventId) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt index 2730872072..a36102bc7d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt @@ -8,10 +8,10 @@ package io.element.android.features.messages.impl.messagecomposer import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.libraries.textcomposer.aRichTextEditorState import io.element.android.libraries.textcomposer.mentions.ResolvedSuggestion import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.textcomposer.model.TextEditorState +import io.element.android.libraries.textcomposer.model.aTextEditorStateRich import io.element.android.wysiwyg.display.TextDisplay import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -24,7 +24,7 @@ open class MessageComposerStateProvider : PreviewParameterProvider, private val typingNotificationPresenter: Presenter, + private val roomCallStatePresenter: Presenter, ) : Presenter { @AssistedFactory interface Factory { @@ -229,14 +230,15 @@ class TimelinePresenter @AssistedInject constructor( } val typingNotificationState = typingNotificationPresenter.present() - val timelineRoomInfo by remember(typingNotificationState) { + val roomCallState = roomCallStatePresenter.present() + val timelineRoomInfo by remember(typingNotificationState, roomCallState) { derivedStateOf { TimelineRoomInfo( name = room.displayName, isDm = room.isDm, userHasPermissionToSendMessage = userHasPermissionToSendMessage, userHasPermissionToSendReaction = userHasPermissionToSendReaction, - isCallOngoing = roomInfo?.hasRoomCall.orFalse(), + roomCallState = roomCallState, pinnedEventIds = roomInfo?.pinnedEventIds.orEmpty(), typingNotificationState = typingNotificationState, ) 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 bfb357b579..aad5b7e354 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 @@ -12,6 +12,7 @@ import io.element.android.features.messages.impl.crypto.sendfailure.resolve.Reso import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.typing.TypingNotificationState +import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield @@ -73,7 +74,7 @@ data class TimelineRoomInfo( val name: String?, val userHasPermissionToSendMessage: Boolean, val userHasPermissionToSendReaction: Boolean, - val isCallOngoing: Boolean, + val roomCallState: RoomCallState, val pinnedEventIds: List, val typingNotificationState: TypingNotificationState, ) 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 6c790d7b1d..4d7677560e 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 @@ -23,6 +23,7 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI import io.element.android.features.messages.impl.timeline.model.virtual.aTimelineItemDaySeparatorModel import io.element.android.features.messages.impl.typing.TypingNotificationState import io.element.android.features.messages.impl.typing.aTypingNotificationState +import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.matrix.api.core.EventId @@ -249,7 +250,7 @@ internal fun aTimelineRoomInfo( name = name, userHasPermissionToSendMessage = userHasPermissionToSendMessage, userHasPermissionToSendReaction = true, - isCallOngoing = false, + roomCallState = aStandByCallState(), pinnedEventIds = pinnedEventIds, typingNotificationState = typingNotificationState, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/CallMenuItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/CallMenuItem.kt new file mode 100644 index 0000000000..2284ffa50c --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/CallMenuItem.kt @@ -0,0 +1,120 @@ +/* + * 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.timeline.components + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.runtime.Composable +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.roomcall.api.RoomCallState +import io.element.android.features.roomcall.api.RoomCallStateProvider +import io.element.android.libraries.designsystem.preview.ElementPreview +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.IconButton +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.ui.strings.CommonStrings + +@Composable +internal fun CallMenuItem( + roomCallState: RoomCallState, + onJoinCallClick: () -> Unit, + modifier: Modifier = Modifier, +) { + when (roomCallState) { + is RoomCallState.StandBy -> { + StandByCallMenuItem( + roomCallState = roomCallState, + onJoinCallClick = onJoinCallClick, + modifier = modifier, + ) + } + is RoomCallState.OnGoing -> { + OnGoingCallMenuItem( + roomCallState = roomCallState, + onJoinCallClick = onJoinCallClick, + modifier = modifier, + ) + } + } +} + +@Composable +private fun StandByCallMenuItem( + roomCallState: RoomCallState.StandBy, + onJoinCallClick: () -> Unit, + modifier: Modifier = Modifier, +) { + IconButton( + modifier = modifier, + onClick = onJoinCallClick, + enabled = roomCallState.canStartCall, + ) { + Icon( + imageVector = CompoundIcons.VideoCallSolid(), + contentDescription = stringResource(CommonStrings.a11y_start_call), + ) + } +} + +@Composable +private fun OnGoingCallMenuItem( + roomCallState: RoomCallState.OnGoing, + onJoinCallClick: () -> Unit, + modifier: Modifier = Modifier, +) { + if (!roomCallState.isUserLocallyInTheCall) { + Button( + onClick = onJoinCallClick, + colors = ButtonDefaults.buttonColors( + contentColor = ElementTheme.colors.bgCanvasDefault, + containerColor = ElementTheme.colors.iconAccentTertiary + ), + contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp), + modifier = modifier.heightIn(min = 36.dp), + enabled = roomCallState.canJoinCall, + ) { + Icon( + modifier = Modifier.size(20.dp), + imageVector = CompoundIcons.VideoCallSolid(), + contentDescription = null + ) + Spacer(Modifier.width(8.dp)) + Text( + text = stringResource(CommonStrings.action_join), + style = ElementTheme.typography.fontBodyMdMedium + ) + Spacer(Modifier.width(8.dp)) + } + } else { + // Else user is already in the call, hide the button. + Box(modifier) + } +} + +@PreviewsDayNight +@Composable +internal fun CallMenuItemPreview( + @PreviewParameter(RoomCallStateProvider::class) roomCallState: RoomCallState +) = ElementPreview { + CallMenuItem( + roomCallState = roomCallState, + onJoinCallClick = {} + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ContentPadding.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ContentPadding.kt new file mode 100644 index 0000000000..5e7bdf5623 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ContentPadding.kt @@ -0,0 +1,14 @@ +/* + * 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.timeline.components + +enum class ContentPadding { + Textual, + Media, + CaptionedMedia +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/JoinCallMenuItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/JoinCallMenuItem.kt deleted file mode 100644 index 80611ccd7d..0000000000 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/JoinCallMenuItem.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.timeline.components - -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -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.libraries.designsystem.theme.components.Icon -import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.ui.strings.CommonStrings - -@Composable -internal fun JoinCallMenuItem( - onJoinCallClick: () -> Unit, -) { - Button( - onClick = onJoinCallClick, - colors = ButtonDefaults.buttonColors( - contentColor = ElementTheme.colors.bgCanvasDefault, - containerColor = ElementTheme.colors.iconAccentTertiary - ), - contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp), - modifier = Modifier.heightIn(min = 36.dp), - ) { - Icon( - modifier = Modifier.size(20.dp), - imageVector = CompoundIcons.VideoCallSolid(), - contentDescription = null - ) - Spacer(Modifier.width(8.dp)) - Text( - text = stringResource(CommonStrings.action_join), - style = ElementTheme.typography.fontBodyMdMedium - ) - Spacer(Modifier.width(8.dp)) - } -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt index d64430e087..ca499336f9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt @@ -31,6 +31,8 @@ import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.timeline.aTimelineItemEvent 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.roomcall.api.RoomCallState +import io.element.android.features.roomcall.api.RoomCallStateProvider import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -41,7 +43,7 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable internal fun TimelineItemCallNotifyView( event: TimelineItem.Event, - isCallOngoing: Boolean, + roomCallState: RoomCallState, onLongClick: (TimelineItem.Event) -> Unit, onJoinCallClick: () -> Unit, modifier: Modifier = Modifier @@ -82,8 +84,11 @@ internal fun TimelineItemCallNotifyView( ) } } - if (isCallOngoing) { - JoinCallMenuItem(onJoinCallClick) + if (roomCallState is RoomCallState.OnGoing) { + CallMenuItem( + roomCallState = roomCallState, + onJoinCallClick = onJoinCallClick, + ) } else { Text( text = event.sentTime, @@ -101,18 +106,14 @@ internal fun TimelineItemCallNotifyView( internal fun TimelineItemCallNotifyViewPreview() { ElementPreview { Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) { - TimelineItemCallNotifyView( - event = aTimelineItemEvent(content = TimelineItemCallNotifyContent()), - isCallOngoing = true, - onLongClick = {}, - onJoinCallClick = {}, - ) - TimelineItemCallNotifyView( - event = aTimelineItemEvent(content = TimelineItemCallNotifyContent()), - isCallOngoing = false, - onLongClick = {}, - onJoinCallClick = {}, - ) + RoomCallStateProvider().values.forEach { roomCallState -> + TimelineItemCallNotifyView( + event = aTimelineItemEvent(content = TimelineItemCallNotifyContent()), + roomCallState = roomCallState, + onLongClick = {}, + onJoinCallClick = {}, + ) + } } } } 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 d52cc9a360..2f0165cd7b 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 @@ -325,7 +325,12 @@ private fun TimelineItemEventRowContent( MessageEventBubble( modifier = Modifier .constrainAs(message) { - top.linkTo(sender.bottom, margin = NEGATIVE_MARGIN_FOR_BUBBLE) + val topMargin = if (bubbleState.cutTopStart) { + NEGATIVE_MARGIN_FOR_BUBBLE + } else { + 0.dp + } + top.linkTo(sender.bottom, margin = topMargin) if (event.isMine) { end.linkTo(parent.end, margin = 16.dp) } else { @@ -522,32 +527,33 @@ private fun MessageEventBubbleContent( fun CommonLayout( timestampPosition: TimestampPosition, showThreadDecoration: Boolean, + paddingBehaviour: ContentPadding, inReplyToDetails: InReplyToDetails?, modifier: Modifier = Modifier, canShrinkContent: Boolean = false, ) { - val timestampLayoutModifier: Modifier - val contentModifier: Modifier - when { - inReplyToDetails != null -> { - if (timestampPosition == TimestampPosition.Overlay) { - timestampLayoutModifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp) - contentModifier = Modifier.clip(RoundedCornerShape(12.dp)) + val timestampLayoutModifier = + if (inReplyToDetails != null && timestampPosition == TimestampPosition.Overlay) { + Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp) + } else { + Modifier + } + + val topPadding = if (inReplyToDetails != null) 0.dp else 8.dp + val contentModifier = when (paddingBehaviour) { + ContentPadding.Textual -> + Modifier.padding(start = 12.dp, end = 12.dp, top = topPadding, bottom = 8.dp) + ContentPadding.Media -> { + if (inReplyToDetails == null) { + Modifier } else { - contentModifier = Modifier.padding(start = 12.dp, end = 12.dp, top = 0.dp, bottom = 8.dp) - timestampLayoutModifier = Modifier + Modifier.clip(RoundedCornerShape(10.dp)) } } - timestampPosition != TimestampPosition.Overlay -> { - timestampLayoutModifier = Modifier - contentModifier = Modifier - .padding(start = 12.dp, end = 12.dp, top = 8.dp, bottom = 8.dp) - } - else -> { - timestampLayoutModifier = Modifier - contentModifier = Modifier - } + ContentPadding.CaptionedMedia -> + Modifier.padding(start = 8.dp, end = 8.dp, top = topPadding, bottom = 8.dp) } + val threadDecoration = @Composable { if (showThreadDecoration) { ThreadDecoration(modifier = Modifier.padding(top = 8.dp, start = 12.dp, end = 12.dp)) @@ -601,9 +607,17 @@ private fun MessageEventBubbleContent( is TimelineItemPollContent -> TimestampPosition.Below else -> TimestampPosition.Default } + val paddingBehaviour = when (event.content) { + is TimelineItemImageContent -> if (event.content.showCaption) ContentPadding.CaptionedMedia else ContentPadding.Media + is TimelineItemVideoContent -> if (event.content.showCaption) ContentPadding.CaptionedMedia else ContentPadding.Media + is TimelineItemStickerContent, + is TimelineItemLocationContent -> ContentPadding.Media + else -> ContentPadding.Textual + } CommonLayout( showThreadDecoration = event.isThreaded, timestampPosition = timestampPosition, + paddingBehaviour = paddingBehaviour, inReplyToDetails = event.inReplyTo, canShrinkContent = event.content is TimelineItemVoiceContent, modifier = bubbleModifier.semantics(mergeDescendants = true) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt index c429e9cc83..fcdd8610d9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt @@ -50,7 +50,9 @@ internal fun TimelineItemEventRowWithReplyContentToPreview( isMine = it, timelineItemReactions = aTimelineItemReactions(count = 0), content = aTimelineItemImageContent( - aspectRatio = 2.5f + aspectRatio = 2.5f, + filename = "image.jpg", + caption = "A reply with an image.", ), inReplyTo = inReplyToDetails, displayNameAmbiguous = displayNameAmbiguous, 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 c7c1cb5350..13c247645b 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 @@ -105,7 +105,7 @@ internal fun TimelineItemRow( TimelineItemCallNotifyView( modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp), event = timelineItem, - isCallOngoing = timelineRoomInfo.isCallOngoing, + roomCallState = timelineRoomInfo.roomCallState, onLongClick = onLongClick, onJoinCallClick = onJoinCallClick, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt index fcc24b791e..63bbac7f6f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt @@ -51,7 +51,6 @@ import io.element.android.libraries.designsystem.components.blurhash.blurHashBac import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat -import io.element.android.libraries.matrix.ui.media.MediaRequestData import io.element.android.libraries.textcomposer.ElementRichTextEditorStyle import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.wysiwyg.compose.EditorStyledText @@ -69,9 +68,7 @@ fun TimelineItemImageView( modifier = modifier.semantics { contentDescription = description }, ) { val containerModifier = if (content.showCaption) { - Modifier - .padding(top = 6.dp) - .clip(RoundedCornerShape(6.dp)) + Modifier.clip(RoundedCornerShape(10.dp)) } else { Modifier } @@ -88,13 +85,7 @@ fun TimelineItemImageView( modifier = Modifier .fillMaxWidth() .then(if (isLoaded) Modifier.background(Color.White) else Modifier), - model = MediaRequestData( - source = content.preferredMediaSource, - kind = MediaRequestData.Kind.File( - fileName = content.filename, - mimeType = content.mimeType, - ), - ), + model = content.thumbnailMediaRequestData, contentScale = ContentScale.Fit, alignment = Alignment.Center, contentDescription = description, @@ -119,6 +110,7 @@ fun TimelineItemImageView( val aspectRatio = content.aspectRatio ?: DEFAULT_ASPECT_RATIO EditorStyledText( modifier = Modifier + .padding(horizontal = 4.dp) // This is (12.dp - 8.dp) contentPadding from CommonLayout .widthIn(min = MIN_HEIGHT_IN_DP.dp * aspectRatio, max = MAX_HEIGHT_IN_DP.dp * aspectRatio), text = caption, style = ElementRichTextEditorStyle.textStyle(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt index e743338ccf..64e6d00d71 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt @@ -57,6 +57,8 @@ import io.element.android.libraries.designsystem.modifiers.roundedBackground import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat +import io.element.android.libraries.matrix.ui.media.MAX_THUMBNAIL_HEIGHT +import io.element.android.libraries.matrix.ui.media.MAX_THUMBNAIL_WIDTH import io.element.android.libraries.matrix.ui.media.MediaRequestData import io.element.android.libraries.textcomposer.ElementRichTextEditorStyle import io.element.android.libraries.ui.strings.CommonStrings @@ -70,14 +72,14 @@ fun TimelineItemVideoView( onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit, modifier: Modifier = Modifier, ) { - val description = stringResource(CommonStrings.common_image) + val description = stringResource(CommonStrings.common_video) Column( modifier = modifier.semantics { contentDescription = description } ) { val containerModifier = if (content.showCaption) { Modifier - .padding(top = 6.dp) - .clip(RoundedCornerShape(6.dp)) + .padding(top = 6.dp) + .clip(RoundedCornerShape(6.dp)) } else { Modifier } @@ -93,13 +95,13 @@ fun TimelineItemVideoView( var isLoaded by remember { mutableStateOf(false) } AsyncImage( modifier = Modifier - .fillMaxWidth() - .then(if (isLoaded) Modifier.background(Color.White) else Modifier), + .fillMaxWidth() + .then(if (isLoaded) Modifier.background(Color.White) else Modifier), model = MediaRequestData( source = content.thumbnailSource, - kind = MediaRequestData.Kind.File( - fileName = content.filename, - mimeType = content.mimeType + kind = MediaRequestData.Kind.Thumbnail( + width = content.thumbnailWidth?.toLong() ?: MAX_THUMBNAIL_WIDTH, + height = content.thumbnailHeight?.toLong() ?: MAX_THUMBNAIL_HEIGHT, ) ), contentScale = ContentScale.Fit, @@ -137,6 +139,7 @@ fun TimelineItemVideoView( val aspectRatio = content.aspectRatio ?: DEFAULT_ASPECT_RATIO EditorStyledText( modifier = Modifier + .padding(horizontal = 4.dp) // This is (12.dp - 8.dp) contentPadding from CommonLayout .widthIn(min = MIN_HEIGHT_IN_DP.dp * aspectRatio, max = MAX_HEIGHT_IN_DP.dp * aspectRatio), text = caption, style = ElementRichTextEditorStyle.textStyle(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index 3eb0c66594..001d40b254 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -93,6 +93,8 @@ class TimelineItemContentMessageFactory @Inject constructor( blurhash = messageType.info?.blurhash, width = messageType.info?.width?.toInt(), height = messageType.info?.height?.toInt(), + thumbnailWidth = messageType.info?.thumbnailInfo?.width?.toInt(), + thumbnailHeight = messageType.info?.thumbnailInfo?.height?.toInt(), aspectRatio = aspectRatio, formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0), fileExtension = fileExtensionExtractor.extractFromName(messageType.filename) @@ -146,6 +148,8 @@ class TimelineItemContentMessageFactory @Inject constructor( mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, width = messageType.info?.width?.toInt(), height = messageType.info?.height?.toInt(), + thumbnailWidth = messageType.info?.thumbnailInfo?.width?.toInt(), + thumbnailHeight = messageType.info?.thumbnailInfo?.height?.toInt(), duration = messageType.info?.duration ?: Duration.ZERO, blurHash = messageType.info?.blurhash, aspectRatio = aspectRatio, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionProvider.kt index 990c9e34e5..10e4898725 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionProvider.kt @@ -12,6 +12,7 @@ import io.element.android.libraries.matrix.api.core.UserId import kotlinx.collections.immutable.toImmutableList import java.text.DateFormat import java.util.Date +import java.util.TimeZone open class AggregatedReactionProvider : PreviewParameterProvider { override val values: Sequence @@ -29,7 +30,9 @@ fun anAggregatedReaction( count: Int = 1, isHighlighted: Boolean = false, ): AggregatedReaction { - val timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT, java.util.Locale.US) + val timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT, java.util.Locale.US).apply { + timeZone = TimeZone.getTimeZone("UTC") + } val date = Date(1_689_061_264L) val senders = buildList { repeat(count) { index -> diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt index efc2d4a100..e6e4bffb9b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt @@ -7,9 +7,12 @@ package io.element.android.features.messages.impl.timeline.model.event -import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAnimatedImage import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody +import io.element.android.libraries.matrix.ui.media.MAX_THUMBNAIL_HEIGHT +import io.element.android.libraries.matrix.ui.media.MAX_THUMBNAIL_WIDTH +import io.element.android.libraries.matrix.ui.media.MediaRequestData data class TimelineItemImageContent( override val filename: String, @@ -23,15 +26,31 @@ data class TimelineItemImageContent( val blurhash: String?, val width: Int?, val height: Int?, + val thumbnailWidth: Int?, + val thumbnailHeight: Int?, val aspectRatio: Float? ) : TimelineItemEventContentWithAttachment { override val type: String = "TimelineItemImageContent" val showCaption = caption != null - val preferredMediaSource = if (mimeType == MimeTypes.Gif) { - mediaSource - } else { - thumbnailSource ?: mediaSource + val thumbnailMediaRequestData: MediaRequestData by lazy { + if (mimeType.isMimeTypeAnimatedImage()) { + MediaRequestData( + source = mediaSource, + kind = MediaRequestData.Kind.File( + fileName = filename, + mimeType = mimeType + ) + ) + } else { + MediaRequestData( + source = thumbnailSource ?: mediaSource, + kind = MediaRequestData.Kind.Thumbnail( + width = thumbnailWidth?.toLong() ?: MAX_THUMBNAIL_WIDTH, + height = thumbnailHeight?.toLong() ?: MAX_THUMBNAIL_HEIGHT + ), + ) + } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt index 8c645ab901..60edb0e6d7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt @@ -37,6 +37,8 @@ fun aTimelineItemImageContent( blurhash = blurhash, width = null, height = 300, + thumbnailWidth = null, + thumbnailHeight = 150, aspectRatio = aspectRatio, formattedFileSize = "4MB", fileExtension = "jpg" diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContent.kt index 3b2e6c1f21..5c0e601708 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContent.kt @@ -22,6 +22,8 @@ data class TimelineItemVideoContent( val blurHash: String?, val height: Int?, val width: Int?, + val thumbnailWidth: Int?, + val thumbnailHeight: Int?, val mimeType: String, val formattedFileSize: String, val fileExtension: String, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentProvider.kt index 39d104b2af..b9390b4e52 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentProvider.kt @@ -35,8 +35,10 @@ fun aTimelineItemVideoContent( aspectRatio = aspectRatio, duration = 100.milliseconds, videoSource = MediaSource(""), - height = 300, width = 150, + height = 300, + thumbnailWidth = 150, + thumbnailHeight = 300, mimeType = MimeTypes.Mp4, formattedFileSize = "14MB", fileExtension = "mp4" diff --git a/features/messages/impl/src/main/res/values-nl/translations.xml b/features/messages/impl/src/main/res/values-nl/translations.xml index e7464c63bd..3cfd37be19 100644 --- a/features/messages/impl/src/main/res/values-nl/translations.xml +++ b/features/messages/impl/src/main/res/values-nl/translations.xml @@ -34,7 +34,7 @@ "Toon minder" "Bericht gekopieerd" "Je hebt geen toestemming om berichten in deze kamer te plaatsen" - "Minder tonen" + "Toon minder" "Meer tonen" "Nieuw" 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 7664379207..9ca5c0b98e 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 @@ -37,6 +37,7 @@ import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvi import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.features.poll.api.actions.EndPollAction import io.element.android.features.poll.test.actions.FakeEndPollAction +import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter @@ -69,9 +70,9 @@ import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.matrix.test.room.aRoomMember import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails -import io.element.android.libraries.textcomposer.model.MarkdownTextEditorState import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.textcomposer.model.TextEditorState +import io.element.android.libraries.textcomposer.model.aTextEditorStateMarkdown import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.WarmUpRule @@ -139,27 +140,6 @@ class MessagesPresenterTest { } } - @Test - fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest { - val room = FakeMatrixRoom( - canUserJoinCallResult = { Result.success(false) }, - canUserSendMessageResult = { _, _ -> Result.success(true) }, - canRedactOwnResult = { Result.success(true) }, - canRedactOtherResult = { Result.success(true) }, - typingNoticeResult = { Result.success(Unit) }, - canUserPinUnpinResult = { Result.success(true) }, - ).apply { - givenRoomInfo(aRoomInfo(hasRoomCall = true)) - } - val presenter = createMessagesPresenter(matrixRoom = room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = consumeItemsUntilTimeout().last() - assertThat(initialState.callState).isEqualTo(RoomCallState.DISABLED) - } - } - @Test fun `present - handle toggling a reaction`() = runTest { val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true) @@ -344,6 +324,8 @@ class MessagesPresenterTest { blurhash = null, width = 20, height = 20, + thumbnailWidth = null, + thumbnailHeight = null, aspectRatio = 1.0f, fileExtension = "jpg", formattedFileSize = "4MB" @@ -384,6 +366,8 @@ class MessagesPresenterTest { blurHash = null, width = 20, height = 20, + thumbnailWidth = 20, + thumbnailHeight = 20, aspectRatio = 1.0f, fileExtension = "mp4", formattedFileSize = "50MB" @@ -1005,7 +989,7 @@ class MessagesPresenterTest { messageComposerPresenter: Presenter = Presenter { aMessageComposerState( // Use TextEditorState.Markdown, so that we can request focus manually. - textEditorState = TextEditorState.Markdown(MarkdownTextEditorState(initialText = "", initialFocus = false)) + textEditorState = aTextEditorStateMarkdown(initialText = "", initialFocus = false) ) }, actionListEventSink: (ActionListEvents) -> Unit = {}, @@ -1030,6 +1014,7 @@ class MessagesPresenterTest { readReceiptBottomSheetPresenter = { aReadReceiptBottomSheetState() }, identityChangeStatePresenter = { anIdentityChangeState() }, pinnedMessagesBannerPresenter = { aLoadedPinnedMessagesBannerState() }, + roomCallStatePresenter = { aStandByCallState() }, networkMonitor = FakeNetworkMonitor(), snackbarDispatcher = SnackbarDispatcher(), navigator = navigator, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt index 5d59ce5464..ac753f6cdb 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt @@ -16,11 +16,19 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewEvents import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewPresenter +import io.element.android.features.messages.impl.attachments.preview.OnDoneListener import io.element.android.features.messages.impl.attachments.preview.SendActionState import io.element.android.features.messages.impl.fixtures.aMediaAttachment +import io.element.android.libraries.androidutils.file.TemporaryUriDeleter import io.element.android.libraries.matrix.api.core.ProgressCallback +import io.element.android.libraries.matrix.api.media.FileInfo +import io.element.android.libraries.matrix.api.media.ImageInfo +import io.element.android.libraries.matrix.api.media.VideoInfo +import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.test.A_CAPTION import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler +import io.element.android.libraries.matrix.test.permalink.FakePermalinkBuilder import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.mediaupload.api.MediaSender @@ -29,23 +37,30 @@ import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.fake.FakeTemporaryUriDeleter +import io.element.android.tests.testutils.lambda.any import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import java.io.File +@RunWith(RobolectricTestRunner::class) class AttachmentsPreviewPresenterTest { @get:Rule val warmUpRule = WarmUpRule() - private val mediaPreProcessor = FakeMediaPreProcessor() private val mockMediaUrl: Uri = mockk("localMediaUri") @Test fun `present - send media success scenario`() = runTest { - val sendMediaResult = lambdaRecorder> { + val sendFileResult = lambdaRecorder> { _, _, _ -> Result.success(FakeMediaUploadHandler()) } val room = FakeMatrixRoom( @@ -54,9 +69,13 @@ class AttachmentsPreviewPresenterTest { Pair(5, 10), Pair(10, 10) ), - sendMediaResult = sendMediaResult, + sendFileResult = sendFileResult, + ) + val onDoneListener = lambdaRecorder { } + val presenter = createAttachmentsPreviewPresenter( + room = room, + onDoneListener = { onDoneListener() }, ) - val presenter = createAttachmentsPreviewPresenter(room = room) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -67,20 +86,117 @@ class AttachmentsPreviewPresenterTest { assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(0f)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(0.5f)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(1f)) - val successState = awaitItem() - assertThat(successState.sendActionState).isEqualTo(SendActionState.Done) - sendMediaResult.assertions().isCalledOnce() + advanceUntilIdle() + sendFileResult.assertions().isCalledOnce() + onDoneListener.assertions().isCalledOnce() + } + } + + @Test + fun `present - cancel scenario`() = runTest { + val onDoneListener = lambdaRecorder { } + val deleteCallback = lambdaRecorder {} + val presenter = createAttachmentsPreviewPresenter( + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + onDoneListener = { onDoneListener() }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) + initialState.eventSink(AttachmentsPreviewEvents.Cancel) + deleteCallback.assertions().isCalledOnce() + onDoneListener.assertions().isCalledOnce() + } + } + + @Test + fun `present - send image with caption success scenario`() = runTest { + val sendImageResult = + lambdaRecorder> { _, _, _, _, _, _ -> + Result.success(FakeMediaUploadHandler()) + } + val mediaPreProcessor = FakeMediaPreProcessor().apply { + givenImageResult() + } + val room = FakeMatrixRoom( + sendImageResult = sendImageResult, + ) + val onDoneListener = lambdaRecorder { } + val presenter = createAttachmentsPreviewPresenter( + room = room, + mediaPreProcessor = mediaPreProcessor, + onDoneListener = { onDoneListener() }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) + initialState.textEditorState.setMarkdown(A_CAPTION) + initialState.eventSink(AttachmentsPreviewEvents.SendAttachment) + assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing) + advanceUntilIdle() + sendImageResult.assertions().isCalledOnce().with( + any(), + any(), + any(), + value(A_CAPTION), + any(), + any(), + ) + onDoneListener.assertions().isCalledOnce() + } + } + + @Test + fun `present - send video with caption success scenario`() = runTest { + val sendVideoResult = + lambdaRecorder> { _, _, _, _, _, _ -> + Result.success(FakeMediaUploadHandler()) + } + val mediaPreProcessor = FakeMediaPreProcessor().apply { + givenVideoResult() + } + val room = FakeMatrixRoom( + sendVideoResult = sendVideoResult, + ) + val onDoneListener = lambdaRecorder { } + val presenter = createAttachmentsPreviewPresenter( + room = room, + mediaPreProcessor = mediaPreProcessor, + onDoneListener = { onDoneListener() }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) + initialState.textEditorState.setMarkdown(A_CAPTION) + initialState.eventSink(AttachmentsPreviewEvents.SendAttachment) + assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing) + advanceUntilIdle() + sendVideoResult.assertions().isCalledOnce().with( + any(), + any(), + any(), + value(A_CAPTION), + any(), + any(), + ) + onDoneListener.assertions().isCalledOnce() } } @Test fun `present - send media failure scenario`() = runTest { val failure = MediaPreProcessor.Failure(null) - val sendMediaResult = lambdaRecorder> { + val sendFileResult = lambdaRecorder> { _, _, _ -> Result.failure(failure) } val room = FakeMatrixRoom( - sendMediaResult = sendMediaResult, + sendFileResult = sendFileResult, ) val presenter = createAttachmentsPreviewPresenter(room = room) moleculeFlow(RecompositionMode.Immediate) { @@ -93,7 +209,7 @@ class AttachmentsPreviewPresenterTest { assertThat(loadingState.sendActionState).isEqualTo(SendActionState.Sending.Processing) val failureState = awaitItem() assertThat(failureState.sendActionState).isEqualTo(SendActionState.Failure(failure)) - sendMediaResult.assertions().isCalledOnce() + sendFileResult.assertions().isCalledOnce() failureState.eventSink(AttachmentsPreviewEvents.ClearSendState) val clearedState = awaitItem() assertThat(clearedState.sendActionState).isEqualTo(SendActionState.Idle) @@ -119,11 +235,18 @@ class AttachmentsPreviewPresenterTest { localMedia: LocalMedia = aLocalMedia( uri = mockMediaUrl, ), - room: MatrixRoom = FakeMatrixRoom() + room: MatrixRoom = FakeMatrixRoom(), + permalinkBuilder: PermalinkBuilder = FakePermalinkBuilder(), + mediaPreProcessor: MediaPreProcessor = FakeMediaPreProcessor(), + temporaryUriDeleter: TemporaryUriDeleter = FakeTemporaryUriDeleter(), + onDoneListener: OnDoneListener = OnDoneListener {}, ): AttachmentsPreviewPresenter { return AttachmentsPreviewPresenter( attachment = aMediaAttachment(localMedia), - mediaSender = MediaSender(mediaPreProcessor, room, InMemorySessionPreferencesStore()) + onDoneListener = onDoneListener, + mediaSender = MediaSender(mediaPreProcessor, room, InMemorySessionPreferencesStore()), + permalinkBuilder = permalinkBuilder, + temporaryUriDeleter = temporaryUriDeleter, ) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenterTest.kt index 92774f4feb..f87c727379 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenterTest.kt @@ -9,9 +9,6 @@ package io.element.android.features.messages.impl.crypto.identity import com.google.common.truth.Truth.assertThat import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.featureflag.api.FeatureFlagService -import io.element.android.libraries.featureflag.api.FeatureFlags -import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.identity.IdentityState @@ -68,43 +65,6 @@ class IdentityChangeStatePresenterTest { } } - @Test - fun `present - when the room emits identity change, but the feature is disabled, the presenter does not emit new state`() = runTest { - val room = FakeMatrixRoom( - isEncrypted = true, - ) - val featureFlagService = FakeFeatureFlagService( - initialState = mapOf( - FeatureFlags.IdentityPinningViolationNotifications.key to false, - ) - ) - val presenter = createIdentityChangeStatePresenter( - room = room, - featureFlagService = featureFlagService, - ) - presenter.test { - val initialState = awaitItem() - assertThat(initialState.roomMemberIdentityStateChanges).isEmpty() - room.emitIdentityStateChanges( - listOf( - IdentityStateChange( - userId = A_USER_ID_2, - identityState = IdentityState.PinViolation, - ), - ) - ) - // No item emitted. - expectNoEvents() - // Enable the feature - featureFlagService.setFeatureEnabled(FeatureFlags.IdentityPinningViolationNotifications, true) - val finalItem = awaitItem() - assertThat(finalItem.roomMemberIdentityStateChanges).hasSize(1) - val value = finalItem.roomMemberIdentityStateChanges.first() - assertThat(value.identityRoomMember.userId).isEqualTo(A_USER_ID_2) - assertThat(value.identityState).isEqualTo(IdentityState.PinViolation) - } - } - @Test fun `present - when the clear room emits identity change, the presenter does not emit new state`() = runTest { val room = FakeMatrixRoom(isEncrypted = false) @@ -188,16 +148,10 @@ class IdentityChangeStatePresenterTest { private fun createIdentityChangeStatePresenter( room: MatrixRoom = FakeMatrixRoom(), encryptionService: EncryptionService = FakeEncryptionService(), - featureFlagService: FeatureFlagService = FakeFeatureFlagService( - initialState = mapOf( - FeatureFlags.IdentityPinningViolationNotifications.key to true, - ) - ), ): IdentityChangeStatePresenter { return IdentityChangeStatePresenter( room = room, encryptionService = encryptionService, - featureFlagService = featureFlagService, ) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt index b029cd724f..2e68c9b199 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt @@ -32,6 +32,7 @@ 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.ProgressCallback import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.media.FileInfo import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder @@ -684,7 +685,7 @@ class MessageComposerPresenterTest { @Test fun `present - Pick file from storage`() = runTest { - val sendMediaResult = lambdaRecorder { _: ProgressCallback? -> + val sendFileResult = lambdaRecorder> { _, _, _ -> Result.success(FakeMediaUploadHandler()) } val room = FakeMatrixRoom( @@ -693,7 +694,7 @@ class MessageComposerPresenterTest { Pair(5, 10), Pair(10, 10) ), - sendMediaResult = sendMediaResult, + sendFileResult = sendFileResult, typingNoticeResult = { Result.success(Unit) } ) val presenter = createPresenter(this, room = room) @@ -710,7 +711,7 @@ class MessageComposerPresenterTest { assertThat(awaitItem().attachmentsState).isEqualTo(AttachmentsState.Sending.Uploading(1f)) val sentState = awaitItem() assertThat(sentState.attachmentsState).isEqualTo(AttachmentsState.None) - sendMediaResult.assertions().isCalledOnce() + sendFileResult.assertions().isCalledOnce() } } @@ -852,8 +853,11 @@ class MessageComposerPresenterTest { @Test fun `present - Uploading media failure can be recovered from`() = runTest { + val sendFileResult = lambdaRecorder> { _, _, _ -> + Result.failure(Exception()) + } val room = FakeMatrixRoom( - sendMediaResult = { Result.failure(Exception()) }, + sendFileResult = sendFileResult, typingNoticeResult = { Result.success(Unit) } ) val presenter = createPresenter(this, room = room) 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 74df8ee46c..d153dd5743 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 @@ -27,6 +27,7 @@ import io.element.android.features.poll.api.actions.EndPollAction import io.element.android.features.poll.api.actions.SendPollResponseAction import io.element.android.features.poll.test.actions.FakeEndPollAction import io.element.android.features.poll.test.actions.FakeSendPollResponseAction +import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState @@ -685,5 +686,6 @@ internal fun TestScope.createTimelinePresenter( timelineController = TimelineController(room), resolveVerifiedUserSendFailurePresenter = { aResolveVerifiedUserSendFailureState() }, typingNotificationPresenter = { aTypingNotificationState() }, + roomCallStatePresenter = { aStandByCallState() }, ) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt index 337ca9ab7e..e9343bf21d 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt @@ -246,6 +246,8 @@ class TimelineItemContentMessageFactoryTest { width = null, mimeType = MimeTypes.OctetStream, formattedFileSize = "0 Bytes", + thumbnailWidth = null, + thumbnailHeight = null, fileExtension = "", ) assertThat(result).isEqualTo(expected) @@ -294,6 +296,8 @@ class TimelineItemContentMessageFactoryTest { width = 300, mimeType = MimeTypes.Mp4, formattedFileSize = "555 Bytes", + thumbnailWidth = 5, + thumbnailHeight = 10, fileExtension = "mp4", ) assertThat(result).isEqualTo(expected) @@ -458,6 +462,8 @@ class TimelineItemContentMessageFactoryTest { blurhash = null, width = null, height = null, + thumbnailWidth = null, + thumbnailHeight = null, aspectRatio = null ) assertThat(result).isEqualTo(expected) @@ -531,6 +537,8 @@ class TimelineItemContentMessageFactoryTest { blurhash = A_BLUR_HASH, width = 5, height = 10, + thumbnailWidth = 5, + thumbnailHeight = 10, aspectRatio = 0.5f, ) assertThat(result).isEqualTo(expected) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt index 0e0009e139..a7cbaaffd2 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt @@ -21,6 +21,7 @@ import io.element.android.features.messages.impl.messagecomposer.aReplyMode import io.element.android.features.messages.impl.voicemessages.VoiceMessageException import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.libraries.matrix.api.core.ProgressCallback +import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer @@ -46,6 +47,7 @@ import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test +import java.io.File import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds @@ -57,9 +59,12 @@ class VoiceMessageComposerPresenterTest { recordingDuration = RECORDING_DURATION ) private val analyticsService = FakeAnalyticsService() - private val sendMediaResult = lambdaRecorder> { Result.success(FakeMediaUploadHandler()) } + private val sendVoiceMessageResult = + lambdaRecorder, ProgressCallback?, Result> { _, _, _, _ -> + Result.success(FakeMediaUploadHandler()) + } private val matrixRoom = FakeMatrixRoom( - sendMediaResult = sendMediaResult + sendVoiceMessageResult = sendVoiceMessageResult ) private val mediaPreProcessor = FakeMediaPreProcessor().apply { givenAudioResult() } private val mediaSender = MediaSender(mediaPreProcessor, matrixRoom, InMemorySessionPreferencesStore()) @@ -292,7 +297,7 @@ class VoiceMessageComposerPresenterTest { val finalState = awaitItem() assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle) - sendMediaResult.assertions().isCalledOnce() + sendVoiceMessageResult.assertions().isCalledOnce() voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1) testPauseAndDestroy(finalState) @@ -343,7 +348,7 @@ class VoiceMessageComposerPresenterTest { val finalState = awaitItem() assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle) - sendMediaResult.assertions().isCalledOnce() + sendVoiceMessageResult.assertions().isCalledOnce() voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1) testPauseAndDestroy(finalState) @@ -366,7 +371,7 @@ class VoiceMessageComposerPresenterTest { val finalState = awaitItem() assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle) - sendMediaResult.assertions().isCalledOnce() + sendVoiceMessageResult.assertions().isCalledOnce() voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1) testPauseAndDestroy(finalState) @@ -390,7 +395,7 @@ class VoiceMessageComposerPresenterTest { val finalState = awaitItem() assertThat(finalState.voiceMessageState).isEqualTo(aPreviewState(isSending = true)) - sendMediaResult.assertions().isNeverCalled() + sendVoiceMessageResult.assertions().isNeverCalled() assertThat(analyticsService.trackedErrors).hasSize(0) voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 0) @@ -415,13 +420,13 @@ class VoiceMessageComposerPresenterTest { ensureAllEventsConsumed() assertThat(previewState.voiceMessageState).isEqualTo(aPreviewState()) - sendMediaResult.assertions().isNeverCalled() + sendVoiceMessageResult.assertions().isNeverCalled() mediaPreProcessor.givenAudioResult() previewState.eventSink(VoiceMessageComposerEvents.SendVoiceMessage) val finalState = awaitItem() assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle) - sendMediaResult.assertions().isCalledOnce() + sendVoiceMessageResult.assertions().isCalledOnce() voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1) testPauseAndDestroy(finalState) @@ -458,7 +463,7 @@ class VoiceMessageComposerPresenterTest { assertThat(showSendFailureDialog).isFalse() } - sendMediaResult.assertions().isNeverCalled() + sendVoiceMessageResult.assertions().isNeverCalled() testPauseAndDestroy(finalState) } } @@ -474,7 +479,7 @@ class VoiceMessageComposerPresenterTest { initialState.eventSink(VoiceMessageComposerEvents.SendVoiceMessage) assertThat(initialState.voiceMessageState).isEqualTo(VoiceMessageState.Idle) - sendMediaResult.assertions().isNeverCalled() + sendVoiceMessageResult.assertions().isNeverCalled() assertThat(analyticsService.trackedErrors).hasSize(1) voiceRecorder.assertCalls(started = 0) @@ -493,7 +498,7 @@ class VoiceMessageComposerPresenterTest { val initialState = awaitItem() initialState.eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) - sendMediaResult.assertions().isNeverCalled() + sendVoiceMessageResult.assertions().isNeverCalled() assertThat(analyticsService.trackedErrors).containsExactly( VoiceMessageException.PermissionMissing(message = "Expected permission to record but none", cause = exception) ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt index ee54325ce1..296beaaa7a 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt @@ -37,7 +37,7 @@ class AdvancedSettingsPresenter @Inject constructor( .collectAsState(initial = true) val doesCompressMedia by sessionPreferencesStore .doesCompressMedia() - .collectAsState(initial = false) + .collectAsState(initial = true) val theme by remember { appPreferencesStore.getThemeFlow().mapToTheme() } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt index 10e59bf334..0c758852db 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt @@ -138,8 +138,8 @@ private fun ColumnScope.ManageAppSection( } if (state.showSecureBackup) { ListItem( - headlineContent = { Text(stringResource(id = CommonStrings.common_chat_backup)) }, - leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.KeySolid())), + headlineContent = { Text(stringResource(id = CommonStrings.common_encryption)) }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Key())), trailingContent = ListItemContent.Badge.takeIf { state.showSecureBackupBadge }, onClick = onSecureBackupClick, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt index 14d0bd7dcd..0e5a05be56 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt @@ -22,6 +22,7 @@ import androidx.core.net.toUri import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import io.element.android.libraries.androidutils.file.TemporaryUriDeleter import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState @@ -43,6 +44,7 @@ class EditUserProfilePresenter @AssistedInject constructor( private val matrixClient: MatrixClient, private val mediaPickerProvider: PickerProvider, private val mediaPreProcessor: MediaPreProcessor, + private val temporaryUriDeleter: TemporaryUriDeleter, permissionsPresenterFactory: PermissionsPresenter.Factory, ) : Presenter { private val cameraPermissionPresenter: PermissionsPresenter = permissionsPresenterFactory.create(android.Manifest.permission.CAMERA) @@ -59,10 +61,20 @@ class EditUserProfilePresenter @AssistedInject constructor( var userAvatarUri by rememberSaveable { mutableStateOf(matrixUser.avatarUrl?.let { Uri.parse(it) }) } var userDisplayName by rememberSaveable { mutableStateOf(matrixUser.displayName) } val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker( - onResult = { uri -> if (uri != null) userAvatarUri = uri } + onResult = { uri -> + if (uri != null) { + temporaryUriDeleter.delete(userAvatarUri) + userAvatarUri = uri + } + } ) val galleryImagePicker = mediaPickerProvider.registerGalleryImagePicker( - onResult = { uri -> if (uri != null) userAvatarUri = uri } + onResult = { uri -> + if (uri != null) { + temporaryUriDeleter.delete(userAvatarUri) + userAvatarUri = uri + } + } ) val avatarActions by remember(userAvatarUri) { @@ -96,7 +108,10 @@ class EditUserProfilePresenter @AssistedInject constructor( pendingPermissionRequest = true cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions) } - AvatarAction.Remove -> userAvatarUri = null + AvatarAction.Remove -> { + temporaryUriDeleter.delete(userAvatarUri) + userAvatarUri = null + } } } @@ -155,7 +170,12 @@ class EditUserProfilePresenter @AssistedInject constructor( private suspend fun updateAvatar(avatarUri: Uri?): Result { return runCatching { if (avatarUri != null) { - val preprocessed = mediaPreProcessor.process(avatarUri, MimeTypes.Jpeg, compressIfPossible = false).getOrThrow() + val preprocessed = mediaPreProcessor.process( + uri = avatarUri, + mimeType = MimeTypes.Jpeg, + deleteOriginal = false, + compressIfPossible = false, + ).getOrThrow() matrixClient.uploadAvatar(MimeTypes.Jpeg, preprocessed.file.readBytes()).getOrThrow() } else { matrixClient.removeAvatar().getOrThrow() diff --git a/features/preferences/impl/src/main/res/values-cs/translations.xml b/features/preferences/impl/src/main/res/values-cs/translations.xml index b49f9cfc95..bc0e36ed9e 100644 --- a/features/preferences/impl/src/main/res/values-cs/translations.xml +++ b/features/preferences/impl/src/main/res/values-cs/translations.xml @@ -8,6 +8,8 @@ "Vlastní URL pro Element Call" "Nastavte vlastní URL pro Element Call." "Neplatné URL, ujistěte se, že jste uvedli protokol (http/https) a správnou adresu." + "Optimalizovat pro nahrávání" + "Média" "Poskytovatel push oznámení" "Vypněte editor formátovaného textu pro ruční zadání Markdown." "Potvrzení o přečtení" diff --git a/features/preferences/impl/src/main/res/values-et/translations.xml b/features/preferences/impl/src/main/res/values-et/translations.xml index f301fa46d5..bf7c028dd3 100644 --- a/features/preferences/impl/src/main/res/values-et/translations.xml +++ b/features/preferences/impl/src/main/res/values-et/translations.xml @@ -8,8 +8,8 @@ "Element Calli kohandatud teenuseaadress" "Seadista kohandatud teenuseaadress Element Calli jaoks." "Vigane url. Palun vaata, et url algaks protokolliga (http/https) ning aadress ise oleks ka õige." - "Optimeeri üleslaadimiseks" - "Meedia" + "Sellega laadid fotosid ja videoid kiiremini üles ning vähendad andmemahtu" + "Optimeeri meedia kvaliteeti" "Tõuketeavituste pakkuja" "Kui soovid Markdown-vormingut käsitsi lisada, siis lülita vormindatud teksti toimeti välja." "Lugemisteatised" diff --git a/features/preferences/impl/src/main/res/values-fr/translations.xml b/features/preferences/impl/src/main/res/values-fr/translations.xml index 4fa09c3b6f..82d28bd954 100644 --- a/features/preferences/impl/src/main/res/values-fr/translations.xml +++ b/features/preferences/impl/src/main/res/values-fr/translations.xml @@ -8,6 +8,8 @@ "URL de base pour Element Call personnalisée" "Configurer une URL de base pour Element Call." "URL invalide, assurez-vous d’inclure le protocol (http/https) et l’adresse correcte." + "Optimisé pour le téléchargement" + "Media" "Fournisseur de Push" "Désactivez l’éditeur de texte enrichi pour saisir manuellement du Markdown." "Accusés de lecture" diff --git a/features/preferences/impl/src/main/res/values-hu/translations.xml b/features/preferences/impl/src/main/res/values-hu/translations.xml index 92c1df9871..cb01dd66f3 100644 --- a/features/preferences/impl/src/main/res/values-hu/translations.xml +++ b/features/preferences/impl/src/main/res/values-hu/translations.xml @@ -1,14 +1,16 @@ - "Annak érdekében, hogy soha ne maradjon le egyetlen fontos hívásról sem, módosítsa a beállításokat, hogy engedélyezze a teljes képernyős értesítéseket, amikor a telefon zárolva van." - "Növelje a hívásélményét" + "Hogy sose maradjon le egyetlen fontos hívásról sem, a beállításokban engedélyezze a teljes képernyős értesítéseket, amikor a telefon zárolva van." + "Fokozza a hívásélményét" "Válassza ki az értesítések fogadási módját" "Fejlesztői mód" "Engedélyezze, hogy elérje a fejlesztőknek szánt funkciókat." "Egyéni Element Call alapwebcím" "Egyéni alapwebcím beállítása az Element Callhoz." "Érvénytelen webcím, győződjön meg arról, hogy szerepel-e benne a protokoll (http/https), és hogy helyes-e a cím." - "Leküldéses értesítési szolgáltató" + "Töltse fel gyorsabban a fényképeket és videókat, valamint csökkentse az adatforgalmat" + "Média minőségének optimalizálása" + "Leküldéses értesítések szolgáltatója" "A formázott szöveges szerkesztő letiltása, hogy kézzel írhasson Markdownt." "Olvasási visszaigazolások" "Ha ki van kapcsolva, az olvasási visszaigazolások nem lesznek elküldve senkinek. A többi felhasználó olvasási visszaigazolását továbbra is meg fogja kapni." diff --git a/features/preferences/impl/src/main/res/values-in/translations.xml b/features/preferences/impl/src/main/res/values-in/translations.xml index f6bb5b1968..7b9bc51a82 100644 --- a/features/preferences/impl/src/main/res/values-in/translations.xml +++ b/features/preferences/impl/src/main/res/values-in/translations.xml @@ -8,6 +8,8 @@ "URL dasar Element Call khusus" "Tetapkan URL dasar khusus untuk Element Call." "URL tidak valid, pastikan Anda menyertakan protokol (http/https) dan alamat yang benar." + "Unggah foto dan video lebih cepat dan kurangi penggunaan data" + "Optimalkan kualitas media" "Penyedia notifikasi dorongan" "Nonaktifkan penyunting teks kaya untuk mengetik Markdown secara manual." "Laporan dibaca" diff --git a/features/preferences/impl/src/main/res/values-pl/translations.xml b/features/preferences/impl/src/main/res/values-pl/translations.xml index 6bc66cd63c..1416a9bd45 100644 --- a/features/preferences/impl/src/main/res/values-pl/translations.xml +++ b/features/preferences/impl/src/main/res/values-pl/translations.xml @@ -54,5 +54,5 @@ Niektóre ustawienia mogą ulec zmianie, jeśli kontynuujesz." "Powiadomienia systemowe wyłączone" "Powiadomienia" "Rozwiązywanie problemów" - "Powiadomienia rozwiązywania problemów" + "Rozwiązywanie problemów powiadomień" diff --git a/features/preferences/impl/src/main/res/values-pt/translations.xml b/features/preferences/impl/src/main/res/values-pt/translations.xml index 5502334026..766e27f469 100644 --- a/features/preferences/impl/src/main/res/values-pt/translations.xml +++ b/features/preferences/impl/src/main/res/values-pt/translations.xml @@ -8,6 +8,8 @@ "URL base para Element Call personalizado" "Define um URL base para a Element Call." "URL inválido, certifica-te de que incluis o protocolo (http/https) e o endereço correto." + "Carrega fotos e vídeos mais rapidamente e reduz a utilização de dados" + "Otimiza a qualidade da mídia" "Fornecedor de envio" "Desativa o editor de texto rico para poderes escrever Markdown manualmente." "Recibos de leitura" diff --git a/features/preferences/impl/src/main/res/values-ru/translations.xml b/features/preferences/impl/src/main/res/values-ru/translations.xml index bbe04958c5..c015011c89 100644 --- a/features/preferences/impl/src/main/res/values-ru/translations.xml +++ b/features/preferences/impl/src/main/res/values-ru/translations.xml @@ -8,8 +8,8 @@ "Базовый URL сервера звонков Element" "Задайте свой сервер Element Call." "Адрес указан неверно, удостоверьтесь, что вы указали протокол (http/https) и правильный адрес." - "Оптимизировать для загрузки" - "Медиа" + "Загружайте фотографии и видео быстрее и сокращайте потребление трафика" + "Оптимизировать качество мультимедиа" "Поставщик push-уведомлений" "Отключить редактор форматированного текста и включить Markdown." "Уведомления о прочтении" diff --git a/features/preferences/impl/src/main/res/values/localazy.xml b/features/preferences/impl/src/main/res/values/localazy.xml index d7b0f6dda2..d2a03f922f 100644 --- a/features/preferences/impl/src/main/res/values/localazy.xml +++ b/features/preferences/impl/src/main/res/values/localazy.xml @@ -8,8 +8,8 @@ "Custom Element Call base URL" "Set a custom base URL for Element Call." "Invalid URL, please make sure you include the protocol (http/https) and the correct address." - "Optimize for upload" - "Media" + "Upload photos and videos faster and reduce data usage" + "Optimise media quality" "Push notification provider" "Disable the rich text editor to type Markdown manually." "Read receipts" diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt index 1634918296..843df71afb 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt @@ -34,7 +34,7 @@ class AdvancedSettingsPresenterTest { assertThat(initialState.isDeveloperModeEnabled).isFalse() assertThat(initialState.showChangeThemeDialog).isFalse() assertThat(initialState.isSharePresenceEnabled).isTrue() - assertThat(initialState.doesCompressMedia).isFalse() + assertThat(initialState.doesCompressMedia).isTrue() assertThat(initialState.theme).isEqualTo(Theme.System) } } @@ -76,11 +76,11 @@ class AdvancedSettingsPresenterTest { presenter.present() }.test { val initialState = awaitLastSequentialItem() - assertThat(initialState.doesCompressMedia).isFalse() - initialState.eventSink.invoke(AdvancedSettingsEvents.SetCompressMedia(true)) - assertThat(awaitItem().doesCompressMedia).isTrue() + assertThat(initialState.doesCompressMedia).isTrue() initialState.eventSink.invoke(AdvancedSettingsEvents.SetCompressMedia(false)) assertThat(awaitItem().doesCompressMedia).isFalse() + initialState.eventSink.invoke(AdvancedSettingsEvents.SetCompressMedia(true)) + assertThat(awaitItem().doesCompressMedia).isTrue() } } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt index 5fae451e0f..0f39ab491c 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt @@ -12,6 +12,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.libraries.androidutils.file.TemporaryUriDeleter import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.user.MatrixUser @@ -29,6 +30,9 @@ import io.element.android.libraries.permissions.test.FakePermissionsPresenterFac import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.consumeItemsUntilPredicate import io.element.android.tests.testutils.consumeItemsUntilTimeout +import io.element.android.tests.testutils.fake.FakeTemporaryUriDeleter +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic @@ -73,12 +77,14 @@ class EditUserProfilePresenterTest { matrixClient: MatrixClient = FakeMatrixClient(), matrixUser: MatrixUser = aMatrixUser(), permissionsPresenter: PermissionsPresenter = FakePermissionsPresenter(), + temporaryUriDeleter: TemporaryUriDeleter = FakeTemporaryUriDeleter(), ): EditUserProfilePresenter { return EditUserProfilePresenter( matrixClient = matrixClient, matrixUser = matrixUser, mediaPickerProvider = fakePickerProvider, mediaPreProcessor = fakeMediaPreProcessor, + temporaryUriDeleter = temporaryUriDeleter, permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter), ) } @@ -107,7 +113,12 @@ class EditUserProfilePresenterTest { @Test fun `present - updates state in response to changes`() = runTest { val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) - val presenter = createEditUserProfilePresenter(matrixUser = user) + val presenter = createEditUserProfilePresenter( + matrixUser = user, + temporaryUriDeleter = FakeTemporaryUriDeleter( + deleteLambda = { assertThat(it).isEqualTo(userAvatarUri) } + ), + ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -136,7 +147,12 @@ class EditUserProfilePresenterTest { fun `present - obtains avatar uris from gallery`() = runTest { val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) fakePickerProvider.givenResult(anotherAvatarUri) - val presenter = createEditUserProfilePresenter(matrixUser = user) + val presenter = createEditUserProfilePresenter( + matrixUser = user, + temporaryUriDeleter = FakeTemporaryUriDeleter( + deleteLambda = { assertThat(it).isEqualTo(userAvatarUri) } + ), + ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -154,9 +170,13 @@ class EditUserProfilePresenterTest { val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) fakePickerProvider.givenResult(anotherAvatarUri) val fakePermissionsPresenter = FakePermissionsPresenter() + val deleteCallback = lambdaRecorder {} val presenter = createEditUserProfilePresenter( matrixUser = user, permissionsPresenter = fakePermissionsPresenter, + temporaryUriDeleter = FakeTemporaryUriDeleter( + deleteLambda = deleteCallback, + ), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -177,6 +197,10 @@ class EditUserProfilePresenterTest { stateWithNewAvatar.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.TakePhoto)) val stateWithNewAvatar2 = awaitItem() assertThat(stateWithNewAvatar2.userAvatarUrl).isEqualTo(userAvatarUri) + deleteCallback.assertions().isCalledExactly(2).withSequence( + listOf(value(userAvatarUri)), + listOf(value(anotherAvatarUri)), + ) } } @@ -184,7 +208,13 @@ class EditUserProfilePresenterTest { fun `present - updates save button state`() = runTest { val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) fakePickerProvider.givenResult(userAvatarUri) - val presenter = createEditUserProfilePresenter(matrixUser = user) + val deleteCallback = lambdaRecorder {} + val presenter = createEditUserProfilePresenter( + matrixUser = user, + temporaryUriDeleter = FakeTemporaryUriDeleter( + deleteLambda = deleteCallback + ), + ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -210,6 +240,10 @@ class EditUserProfilePresenterTest { awaitItem().apply { assertThat(saveButtonEnabled).isFalse() } + deleteCallback.assertions().isCalledExactly(2).withSequence( + listOf(value(userAvatarUri)), + listOf(value(null)), + ) } } @@ -217,7 +251,13 @@ class EditUserProfilePresenterTest { fun `present - updates save button state when initial values are null`() = runTest { val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = null) fakePickerProvider.givenResult(userAvatarUri) - val presenter = createEditUserProfilePresenter(matrixUser = user) + val deleteCallback = lambdaRecorder {} + val presenter = createEditUserProfilePresenter( + matrixUser = user, + temporaryUriDeleter = FakeTemporaryUriDeleter( + deleteLambda = deleteCallback + ), + ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -243,6 +283,10 @@ class EditUserProfilePresenterTest { awaitItem().apply { assertThat(saveButtonEnabled).isFalse() } + deleteCallback.assertions().isCalledExactly(2).withSequence( + listOf(value(null)), + listOf(value(userAvatarUri)), + ) } } @@ -252,7 +296,10 @@ class EditUserProfilePresenterTest { val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) val presenter = createEditUserProfilePresenter( matrixClient = matrixClient, - matrixUser = user + matrixUser = user, + temporaryUriDeleter = FakeTemporaryUriDeleter( + deleteLambda = { assertThat(it).isEqualTo(userAvatarUri) } + ), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -318,7 +365,10 @@ class EditUserProfilePresenterTest { givenPickerReturnsFile() val presenter = createEditUserProfilePresenter( matrixClient = matrixClient, - matrixUser = user + matrixUser = user, + temporaryUriDeleter = FakeTemporaryUriDeleter( + deleteLambda = { assertThat(it).isEqualTo(userAvatarUri) } + ), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -337,7 +387,10 @@ class EditUserProfilePresenterTest { val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) val presenter = createEditUserProfilePresenter( matrixClient = matrixClient, - matrixUser = user + matrixUser = user, + temporaryUriDeleter = FakeTemporaryUriDeleter( + deleteLambda = { assertThat(it).isEqualTo(userAvatarUri) } + ), ) fakePickerProvider.givenResult(anotherAvatarUri) fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no"))) @@ -403,7 +456,13 @@ class EditUserProfilePresenterTest { } private suspend fun saveAndAssertFailure(matrixUser: MatrixUser, matrixClient: MatrixClient, event: EditUserProfileEvents) { - val presenter = createEditUserProfilePresenter(matrixUser = matrixUser, matrixClient = matrixClient) + val presenter = createEditUserProfilePresenter( + matrixUser = matrixUser, + matrixClient = matrixClient, + temporaryUriDeleter = FakeTemporaryUriDeleter( + deleteLambda = { assertThat(it).isEqualTo(userAvatarUri) } + ), + ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt index 806837de7a..b7c6a07f89 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt @@ -17,7 +17,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import io.element.android.features.rageshake.api.crash.CrashDataStore -import io.element.android.features.rageshake.api.logs.LogFilesRemover import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.features.rageshake.api.reporter.BugReporterListener import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder @@ -31,7 +30,6 @@ class BugReportPresenter @Inject constructor( private val bugReporter: BugReporter, private val crashDataStore: CrashDataStore, private val screenshotHolder: ScreenshotHolder, - private val logFilesRemover: LogFilesRemover, private val appCoroutineScope: CoroutineScope, ) : Presenter { private class BugReporterUploadListener( @@ -143,6 +141,5 @@ class BugReportPresenter @Inject constructor( private fun CoroutineScope.resetAll() = launch { screenshotHolder.reset() crashDataStore.reset() - logFilesRemover.perform() } } diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt index 283d6296d2..15512b21e4 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt @@ -12,18 +12,15 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.rageshake.api.crash.CrashDataStore -import io.element.android.features.rageshake.api.logs.LogFilesRemover import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder import io.element.android.features.rageshake.test.crash.A_CRASH_DATA import io.element.android.features.rageshake.test.crash.FakeCrashDataStore -import io.element.android.features.rageshake.test.logs.FakeLogFilesRemover import io.element.android.features.rageshake.test.screenshot.A_SCREENSHOT_URI import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.test.A_FAILURE_REASON import io.element.android.tests.testutils.WarmUpRule -import io.element.android.tests.testutils.lambda.LambdaOneParamRecorder import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -111,11 +108,9 @@ class BugReportPresenterTest { @Test fun `present - reset all`() = runTest { - val logFilesRemover = FakeLogFilesRemover() val presenter = createPresenter( crashDataStore = FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true), screenshotHolder = FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI), - logFilesRemover = logFilesRemover, ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -127,7 +122,6 @@ class BugReportPresenterTest { initialState.eventSink.invoke(BugReportEvents.ResetAll) val resetState = awaitItem() assertThat(resetState.hasCrashLogs).isFalse() - logFilesRemover.performLambda.assertions().isCalledOnce() // TODO Make it live assertThat(resetState.screenshotUri).isNull() } } @@ -236,12 +230,10 @@ class BugReportPresenterTest { bugReporter: BugReporter = FakeBugReporter(), crashDataStore: CrashDataStore = FakeCrashDataStore(), screenshotHolder: ScreenshotHolder = FakeScreenshotHolder(), - logFilesRemover: LogFilesRemover = FakeLogFilesRemover(LambdaOneParamRecorder(ensureNeverCalled = true) { }), ) = BugReportPresenter( bugReporter = bugReporter, crashDataStore = crashDataStore, screenshotHolder = screenshotHolder, - logFilesRemover = logFilesRemover, appCoroutineScope = this, ) } diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt index abb6682a66..ffe98680dd 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import kotlin.jvm.optionals.getOrElse class RoomAliasResolverPresenter @AssistedInject constructor( @Assisted private val roomAlias: RoomAlias, @@ -57,7 +58,9 @@ class RoomAliasResolverPresenter @AssistedInject constructor( private fun CoroutineScope.resolveAlias(resolveState: MutableState>) = launch { suspend { - matrixClient.resolveRoomAlias(roomAlias).getOrThrow() + matrixClient.resolveRoomAlias(roomAlias) + .getOrThrow() + .getOrElse { error("Failed to resolve room alias $roomAlias") } }.runCatchingUpdatingState(resolveState) } } diff --git a/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenterTest.kt b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenterTest.kt index e651cf41e8..9894c2b342 100644 --- a/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenterTest.kt +++ b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenterTest.kt @@ -24,6 +24,7 @@ import io.element.android.tests.testutils.WarmUpRule import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test +import java.util.Optional class RoomAliasResolverPresenterTest { @get:Rule @@ -42,7 +43,7 @@ class RoomAliasResolverPresenterTest { @Test fun `present - resolve alias to roomId`() = runTest { - val result = aResolvedRoomAlias() + val result = Optional.of(aResolvedRoomAlias()) val client = FakeMatrixClient( resolveRoomAliasResult = { Result.success(result) } ) @@ -54,7 +55,7 @@ class RoomAliasResolverPresenterTest { assertThat(awaitItem().resolveState.isLoading()).isTrue() val resultState = awaitItem() assertThat(resultState.roomAlias).isEqualTo(A_ROOM_ALIAS) - assertThat(resultState.resolveState.dataOrNull()).isEqualTo(result) + assertThat(resultState.resolveState.dataOrNull()).isEqualTo(result.get()) } } diff --git a/features/roomcall/api/build.gradle.kts b/features/roomcall/api/build.gradle.kts new file mode 100644 index 0000000000..12a2117b16 --- /dev/null +++ b/features/roomcall/api/build.gradle.kts @@ -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. + */ + +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.roomcall.api" +} + +dependencies { + implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) + implementation(libs.androidx.compose.ui.tooling.preview) +} diff --git a/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallState.kt b/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallState.kt new file mode 100644 index 0000000000..e47a623914 --- /dev/null +++ b/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallState.kt @@ -0,0 +1,30 @@ +/* + * 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.roomcall.api + +import androidx.compose.runtime.Immutable +import io.element.android.features.roomcall.api.RoomCallState.OnGoing +import io.element.android.features.roomcall.api.RoomCallState.StandBy + +@Immutable +sealed interface RoomCallState { + data class StandBy( + val canStartCall: Boolean, + ) : RoomCallState + + data class OnGoing( + val canJoinCall: Boolean, + val isUserInTheCall: Boolean, + val isUserLocallyInTheCall: Boolean, + ) : RoomCallState +} + +fun RoomCallState.hasPermissionToJoin() = when (this) { + is StandBy -> canStartCall + is OnGoing -> canJoinCall +} diff --git a/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStateProvider.kt b/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStateProvider.kt new file mode 100644 index 0000000000..6351c25479 --- /dev/null +++ b/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStateProvider.kt @@ -0,0 +1,36 @@ +/* + * 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.roomcall.api + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider + +open class RoomCallStateProvider : PreviewParameterProvider { + override val values: Sequence = sequenceOf( + aStandByCallState(), + aStandByCallState(canStartCall = false), + anOngoingCallState(), + anOngoingCallState(canJoinCall = false), + anOngoingCallState(canJoinCall = true, isUserInTheCall = true), + ) +} + +fun anOngoingCallState( + canJoinCall: Boolean = true, + isUserInTheCall: Boolean = false, + isUserLocallyInTheCall: Boolean = isUserInTheCall, +) = RoomCallState.OnGoing( + canJoinCall = canJoinCall, + isUserInTheCall = isUserInTheCall, + isUserLocallyInTheCall = isUserLocallyInTheCall, +) + +fun aStandByCallState( + canStartCall: Boolean = true, +) = RoomCallState.StandBy( + canStartCall = canStartCall, +) diff --git a/features/roomcall/impl/build.gradle.kts b/features/roomcall/impl/build.gradle.kts new file mode 100644 index 0000000000..6ac4ff934e --- /dev/null +++ b/features/roomcall/impl/build.gradle.kts @@ -0,0 +1,38 @@ +import extension.setupAnvil + +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +plugins { + id("io.element.android-compose-library") +} + +android { + namespace = "io.element.android.features.roomcall.impl" +} + +setupAnvil() + +dependencies { + api(projects.features.roomcall.api) + implementation(libs.kotlinx.collections.immutable) + implementation(projects.features.call.api) + implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrixui) + + testImplementation(libs.test.junit) + testImplementation(libs.coroutines.test) + testImplementation(libs.molecule.runtime) + testImplementation(libs.test.truth) + testImplementation(libs.test.turbine) + testImplementation(projects.libraries.matrix.test) + testImplementation(projects.features.call.test) + testImplementation(projects.tests.testutils) + testImplementation(libs.androidx.compose.ui.test.junit) + testReleaseImplementation(libs.androidx.compose.ui.test.manifest) +} diff --git a/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt b/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt new file mode 100644 index 0000000000..57ac545c19 --- /dev/null +++ b/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt @@ -0,0 +1,53 @@ +/* + * 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.roomcall.impl + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import io.element.android.features.call.api.CurrentCall +import io.element.android.features.call.api.CurrentCallService +import io.element.android.features.roomcall.api.RoomCallState +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.ui.room.canCall +import javax.inject.Inject + +class RoomCallStatePresenter @Inject constructor( + private val room: MatrixRoom, + private val currentCallService: CurrentCallService, +) : Presenter { + @Composable + override fun present(): RoomCallState { + val roomInfo by room.roomInfoFlow.collectAsState(null) + val syncUpdateFlow = room.syncUpdateFlow.collectAsState() + val canJoinCall by room.canCall(updateKey = syncUpdateFlow.value) + val isUserInTheCall by remember { + derivedStateOf { + room.sessionId in roomInfo?.activeRoomCallParticipants.orEmpty() + } + } + val currentCall by currentCallService.currentCall.collectAsState() + val isUserLocallyInTheCall by remember { + derivedStateOf { + (currentCall as? CurrentCall.RoomCall)?.roomId == room.roomId + } + } + val callState = when { + roomInfo?.hasRoomCall == true -> RoomCallState.OnGoing( + canJoinCall = canJoinCall, + isUserInTheCall = isUserInTheCall, + isUserLocallyInTheCall = isUserLocallyInTheCall, + ) + else -> RoomCallState.StandBy(canStartCall = canJoinCall) + } + return callState + } +} diff --git a/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/di/RoomCallModule.kt b/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/di/RoomCallModule.kt new file mode 100644 index 0000000000..34c8d2448f --- /dev/null +++ b/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/di/RoomCallModule.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.roomcall.impl.di + +import com.squareup.anvil.annotations.ContributesTo +import dagger.Binds +import dagger.Module +import io.element.android.features.roomcall.api.RoomCallState +import io.element.android.features.roomcall.impl.RoomCallStatePresenter +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.di.RoomScope + +@ContributesTo(RoomScope::class) +@Module +interface RoomCallModule { + @Binds + fun bindRoomCallStatePresenter(presenter: RoomCallStatePresenter): Presenter +} diff --git a/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt b/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt new file mode 100644 index 0000000000..d29bff08ff --- /dev/null +++ b/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt @@ -0,0 +1,201 @@ +/* + * 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.roomcall.impl + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.call.api.CurrentCall +import io.element.android.features.call.api.CurrentCallService +import io.element.android.features.call.test.FakeCurrentCallService +import io.element.android.features.roomcall.api.RoomCallState +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomInfo +import io.element.android.tests.testutils.test +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class RoomCallStatePresenterTest { + @Test + fun `present - initial state`() = runTest { + val room = FakeMatrixRoom( + canUserJoinCallResult = { Result.success(false) }, + ) + val presenter = createRoomCallStatePresenter(matrixRoom = room) + presenter.test { + val initialState = awaitItem() + assertThat(initialState).isEqualTo( + RoomCallState.StandBy( + canStartCall = false, + ) + ) + } + } + + @Test + fun `present - initial state - user can join call`() = runTest { + val room = FakeMatrixRoom( + canUserJoinCallResult = { Result.success(true) }, + ) + val presenter = createRoomCallStatePresenter(matrixRoom = room) + presenter.test { + skipItems(1) + val initialState = awaitItem() + assertThat(initialState).isEqualTo( + RoomCallState.StandBy( + canStartCall = true, + ) + ) + } + } + + @Test + fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest { + val room = FakeMatrixRoom( + canUserJoinCallResult = { Result.success(false) }, + ).apply { + givenRoomInfo(aRoomInfo(hasRoomCall = true)) + } + val presenter = createRoomCallStatePresenter(matrixRoom = room) + presenter.test { + skipItems(1) + assertThat(awaitItem()).isEqualTo( + RoomCallState.OnGoing( + canJoinCall = false, + isUserInTheCall = false, + isUserLocallyInTheCall = false, + ) + ) + } + } + + @Test + fun `present - user has joined the call on another session`() = runTest { + val room = FakeMatrixRoom( + canUserJoinCallResult = { Result.success(true) }, + ).apply { + givenRoomInfo( + aRoomInfo( + hasRoomCall = true, + activeRoomCallParticipants = listOf(sessionId), + ) + ) + } + val presenter = createRoomCallStatePresenter(matrixRoom = room) + presenter.test { + skipItems(1) + assertThat(awaitItem()).isEqualTo( + RoomCallState.OnGoing( + canJoinCall = true, + isUserInTheCall = true, + isUserLocallyInTheCall = false, + ) + ) + } + } + + @Test + fun `present - user has joined the call locally`() = runTest { + val room = FakeMatrixRoom( + canUserJoinCallResult = { Result.success(true) }, + ).apply { + givenRoomInfo( + aRoomInfo( + hasRoomCall = true, + activeRoomCallParticipants = listOf(sessionId), + ) + ) + } + val presenter = createRoomCallStatePresenter( + matrixRoom = room, + currentCallService = FakeCurrentCallService(MutableStateFlow(CurrentCall.RoomCall(room.roomId))), + ) + presenter.test { + skipItems(1) + assertThat(awaitItem()).isEqualTo( + RoomCallState.OnGoing( + canJoinCall = true, + isUserInTheCall = true, + isUserLocallyInTheCall = true, + ) + ) + } + } + + @Test + fun `present - user leaves the call`() = runTest { + val room = FakeMatrixRoom( + canUserJoinCallResult = { Result.success(true) }, + ).apply { + givenRoomInfo( + aRoomInfo( + hasRoomCall = true, + activeRoomCallParticipants = listOf(sessionId), + ) + ) + } + val currentCall = MutableStateFlow(CurrentCall.RoomCall(room.roomId)) + val currentCallService = FakeCurrentCallService(currentCall = currentCall) + val presenter = createRoomCallStatePresenter( + matrixRoom = room, + currentCallService = currentCallService + ) + presenter.test { + skipItems(1) + assertThat(awaitItem()).isEqualTo( + RoomCallState.OnGoing( + canJoinCall = true, + isUserInTheCall = true, + isUserLocallyInTheCall = true, + ) + ) + currentCall.value = CurrentCall.None + assertThat(awaitItem()).isEqualTo( + RoomCallState.OnGoing( + canJoinCall = true, + isUserInTheCall = true, + isUserLocallyInTheCall = false, + ) + ) + room.givenRoomInfo( + aRoomInfo( + hasRoomCall = true, + activeRoomCallParticipants = emptyList(), + ) + ) + assertThat(awaitItem()).isEqualTo( + RoomCallState.OnGoing( + canJoinCall = true, + isUserInTheCall = false, + isUserLocallyInTheCall = false, + ) + ) + room.givenRoomInfo( + aRoomInfo( + hasRoomCall = false, + activeRoomCallParticipants = emptyList(), + ) + ) + assertThat(awaitItem()).isEqualTo( + RoomCallState.StandBy( + canStartCall = true, + ) + ) + } + } + + private fun createRoomCallStatePresenter( + matrixRoom: MatrixRoom, + currentCallService: CurrentCallService = FakeCurrentCallService(), + ): RoomCallStatePresenter { + return RoomCallStatePresenter( + room = matrixRoom, + currentCallService = currentCallService, + ) + } +} diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index 42f27963f3..231161e583 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -49,6 +49,7 @@ dependencies { implementation(projects.services.analytics.compose) implementation(projects.features.poll.api) implementation(projects.features.messages.api) + implementation(projects.features.roomcall.api) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt index eccc8dd9d2..586790b618 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt @@ -21,6 +21,7 @@ import im.vector.app.features.analytics.plan.Interaction import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.messages.api.pinned.IsPinnedMessagesFeatureEnabled +import io.element.android.features.roomcall.api.RoomCallState import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.bool.orFalse @@ -37,7 +38,6 @@ import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.powerlevels.canInvite import io.element.android.libraries.matrix.api.room.powerlevels.canSendState import io.element.android.libraries.matrix.api.room.roomNotificationSettings -import io.element.android.libraries.matrix.ui.room.canCall import io.element.android.libraries.matrix.ui.room.getCurrentRoomMember import io.element.android.libraries.matrix.ui.room.getDirectRoomMember import io.element.android.libraries.matrix.ui.room.isOwnUserAdmin @@ -57,6 +57,7 @@ class RoomDetailsPresenter @Inject constructor( private val notificationSettingsService: NotificationSettingsService, private val roomMembersDetailsPresenterFactory: RoomMemberDetailsPresenter.Factory, private val leaveRoomPresenter: Presenter, + private val roomCallStatePresenter: Presenter, private val dispatchers: CoroutineDispatchers, private val analyticsService: AnalyticsService, private val isPinnedMessagesFeatureEnabled: IsPinnedMessagesFeatureEnabled, @@ -87,18 +88,16 @@ class RoomDetailsPresenter @Inject constructor( } } - val syncUpdateTimestamp by room.syncUpdateFlow.collectAsState() - val membersState by room.membersStateFlow.collectAsState() val canInvite by getCanInvite(membersState) val canEditName by getCanSendState(membersState, StateEventType.ROOM_NAME) val canEditAvatar by getCanSendState(membersState, StateEventType.ROOM_AVATAR) val canEditTopic by getCanSendState(membersState, StateEventType.ROOM_TOPIC) - val canJoinCall by room.canCall(updateKey = syncUpdateTimestamp) val dmMember by room.getDirectRoomMember(membersState) val currentMember by room.getCurrentRoomMember(membersState) val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember) val roomType by getRoomType(dmMember, currentMember) + val roomCallState = roomCallStatePresenter.present() val topicState = remember(canEditTopic, roomTopic, roomType) { val topic = roomTopic @@ -143,7 +142,7 @@ class RoomDetailsPresenter @Inject constructor( canInvite = canInvite, canEdit = (canEditAvatar || canEditName || canEditTopic) && roomType == RoomDetailsType.Room, canShowNotificationSettings = canShowNotificationSettings.value, - canCall = canJoinCall, + roomCallState = roomCallState, roomType = roomType, roomMemberDetailsState = roomMemberDetailsState, leaveRoomState = leaveRoomState, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt index 2208748563..d43b0a813a 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt @@ -9,6 +9,7 @@ package io.element.android.features.roomdetails.impl import androidx.compose.runtime.Immutable import io.element.android.features.leaveroom.api.LeaveRoomState +import io.element.android.features.roomcall.api.RoomCallState import io.element.android.features.userprofile.api.UserProfileState import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId @@ -31,7 +32,7 @@ data class RoomDetailsState( val canEdit: Boolean, val canInvite: Boolean, val canShowNotificationSettings: Boolean, - val canCall: Boolean, + val roomCallState: RoomCallState, val leaveRoomState: LeaveRoomState, val roomNotificationSettings: RoomNotificationSettings?, val isFavorite: Boolean, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt index 7e71d2b39f..49b9f73cb5 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt @@ -10,6 +10,8 @@ package io.element.android.features.roomdetails.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.leaveroom.api.aLeaveRoomState +import io.element.android.features.roomcall.api.RoomCallState +import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.shared.aUserProfileState @@ -42,7 +44,7 @@ open class RoomDetailsStateProvider : PreviewParameterProvider // Also test the roomNotificationSettings ALL_MESSAGES in the same screenshot. Icon 'Mute' should be displayed roomNotificationSettings = aRoomNotificationSettings(mode = RoomNotificationMode.ALL_MESSAGES, isDefault = true) ), - aRoomDetailsState(canCall = false, canInvite = false), + aRoomDetailsState(roomCallState = aStandByCallState(false), canInvite = false), aRoomDetailsState(isPublic = false), aRoomDetailsState(heroes = aMatrixUserList()), aRoomDetailsState(pinnedMessagesCount = 3), @@ -89,7 +91,7 @@ fun aRoomDetailsState( canInvite: Boolean = false, canEdit: Boolean = false, canShowNotificationSettings: Boolean = true, - canCall: Boolean = true, + roomCallState: RoomCallState = aStandByCallState(), roomType: RoomDetailsType = RoomDetailsType.Room, roomMemberDetailsState: UserProfileState? = null, leaveRoomState: LeaveRoomState = aLeaveRoomState(), @@ -112,7 +114,7 @@ fun aRoomDetailsState( canInvite = canInvite, canEdit = canEdit, canShowNotificationSettings = canShowNotificationSettings, - canCall = canCall, + roomCallState = roomCallState, roomType = roomType, roomMemberDetailsState = roomMemberDetailsState, leaveRoomState = leaveRoomState, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index 163a2c5fd5..7e89c3ef07 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -42,6 +42,7 @@ import im.vector.app.features.analytics.plan.Interaction import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.leaveroom.api.LeaveRoomView +import io.element.android.features.roomcall.api.hasPermissionToJoin import io.element.android.features.userprofile.shared.blockuser.BlockUserDialogs import io.element.android.features.userprofile.shared.blockuser.BlockUserSection import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage @@ -299,7 +300,8 @@ private fun MainActionsSection( ) } } - if (state.canCall) { + if (state.roomCallState.hasPermissionToJoin()) { + // TODO Improve the view depending on all the cases here? MainActionButton( title = stringResource(CommonStrings.action_call), imageVector = CompoundIcons.VideoCall(), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt index 8ad7eed8f4..df220f0d50 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.core.net.toUri +import io.element.android.libraries.androidutils.file.TemporaryUriDeleter import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState @@ -45,6 +46,7 @@ class RoomDetailsEditPresenter @Inject constructor( private val room: MatrixRoom, private val mediaPickerProvider: PickerProvider, private val mediaPreProcessor: MediaPreProcessor, + private val temporaryUriDeleter: TemporaryUriDeleter, permissionsPresenterFactory: PermissionsPresenter.Factory, ) : Presenter { private val cameraPermissionPresenter = permissionsPresenterFactory.create(android.Manifest.permission.CAMERA) @@ -59,6 +61,7 @@ class RoomDetailsEditPresenter @Inject constructor( var roomAvatarUriEdited by rememberSaveable { mutableStateOf(null) } LaunchedEffect(roomAvatarUri) { // Every time the roomAvatar change (from sync), we can set the new avatar. + temporaryUriDeleter.delete(roomAvatarUriEdited) roomAvatarUriEdited = roomAvatarUri } @@ -98,10 +101,20 @@ class RoomDetailsEditPresenter @Inject constructor( } val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker( - onResult = { uri -> if (uri != null) roomAvatarUriEdited = uri } + onResult = { uri -> + if (uri != null) { + temporaryUriDeleter.delete(roomAvatarUriEdited) + roomAvatarUriEdited = uri + } + } ) val galleryImagePicker = mediaPickerProvider.registerGalleryImagePicker( - onResult = { uri -> if (uri != null) roomAvatarUriEdited = uri } + onResult = { uri -> + if (uri != null) { + temporaryUriDeleter.delete(roomAvatarUriEdited) + roomAvatarUriEdited = uri + } + } ) LaunchedEffect(cameraPermissionState.permissionGranted) { @@ -143,7 +156,10 @@ class RoomDetailsEditPresenter @Inject constructor( pendingPermissionRequest = true cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions) } - AvatarAction.Remove -> roomAvatarUriEdited = null + AvatarAction.Remove -> { + temporaryUriDeleter.delete(roomAvatarUriEdited) + roomAvatarUriEdited = null + } } } @@ -202,7 +218,12 @@ class RoomDetailsEditPresenter @Inject constructor( private suspend fun updateAvatar(avatarUri: Uri?): Result { return runCatching { if (avatarUri != null) { - val preprocessed = mediaPreProcessor.process(avatarUri, MimeTypes.Jpeg, compressIfPossible = false).getOrThrow() + val preprocessed = mediaPreProcessor.process( + uri = avatarUri, + mimeType = MimeTypes.Jpeg, + deleteOriginal = false, + compressIfPossible = false, + ).getOrThrow() room.updateAvatar(MimeTypes.Jpeg, preprocessed.file.readBytes()).getOrThrow() } else { room.removeAvatar().getOrThrow() diff --git a/features/roomdetails/impl/src/main/res/values-hu/translations.xml b/features/roomdetails/impl/src/main/res/values-hu/translations.xml index bacbf66536..8f992b1c7d 100644 --- a/features/roomdetails/impl/src/main/res/values-hu/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-hu/translations.xml @@ -35,8 +35,8 @@ "Téma hozzáadása" "Már tag" "Már meghívták" - "Titkosítva" - "Nincs titkosítva" + "Titkosított" + "Nem titkosított" "Nyilvános szoba" "Szoba szerkesztése" "Ismeretlen hiba történt, és az információkat nem lehetett megváltoztatni." diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt index b1352cd7b6..b1edabbbff 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt @@ -8,15 +8,14 @@ package io.element.android.features.roomdetails.edit import android.net.Uri -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow import app.cash.turbine.ReceiveTurbine -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.roomdetails.aMatrixRoom import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditEvents import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditPresenter +import io.element.android.libraries.androidutils.file.TemporaryUriDeleter import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.test.AN_AVATAR_URL @@ -30,9 +29,11 @@ import io.element.android.libraries.permissions.api.PermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.fake.FakeTemporaryUriDeleter 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 io.element.android.tests.testutils.test import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic @@ -45,6 +46,7 @@ import org.junit.Rule import org.junit.Test import java.io.File +@Suppress("LargeClass") @ExperimentalCoroutinesApi class RoomDetailsEditPresenterTest { @get:Rule @@ -76,12 +78,14 @@ class RoomDetailsEditPresenterTest { private fun createRoomDetailsEditPresenter( room: MatrixRoom, permissionsPresenter: PermissionsPresenter = FakePermissionsPresenter(), + temporaryUriDeleter: TemporaryUriDeleter = FakeTemporaryUriDeleter(), ): RoomDetailsEditPresenter { return RoomDetailsEditPresenter( room = room, mediaPickerProvider = fakePickerProvider, mediaPreProcessor = fakeMediaPreProcessor, permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter), + temporaryUriDeleter = temporaryUriDeleter, ) } @@ -94,10 +98,12 @@ class RoomDetailsEditPresenterTest { emitRoomInfo = true, canSendStateResult = { _, _ -> Result.success(true) } ) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { val initialState = awaitFirstItem() assertThat(initialState.roomId).isEqualTo(room.roomId) assertThat(initialState.roomRawName).isEqualTo(A_ROOM_RAW_NAME) @@ -126,10 +132,12 @@ class RoomDetailsEditPresenterTest { } }, ) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { // Initially false val initialState = awaitItem() assertThat(initialState.canChangeName).isFalse() @@ -140,6 +148,7 @@ class RoomDetailsEditPresenterTest { assertThat(settledState.canChangeName).isTrue() assertThat(settledState.canChangeAvatar).isFalse() assertThat(settledState.canChangeTopic).isFalse() + deleteCallback.assertions().isCalledOnce().with(value(null)) } } @@ -156,10 +165,12 @@ class RoomDetailsEditPresenterTest { } } ) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { // Initially false val initialState = awaitItem() assertThat(initialState.canChangeName).isFalse() @@ -186,10 +197,12 @@ class RoomDetailsEditPresenterTest { } } ) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { // Initially false val initialState = awaitItem() assertThat(initialState.canChangeName).isFalse() @@ -212,10 +225,12 @@ class RoomDetailsEditPresenterTest { emitRoomInfo = true, canSendStateResult = { _, _ -> Result.success(true) } ) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { val initialState = awaitFirstItem() assertThat(initialState.roomTopic).isEqualTo("My topic") assertThat(initialState.roomRawName).isEqualTo("Name") @@ -257,10 +272,12 @@ class RoomDetailsEditPresenterTest { canSendStateResult = { _, _ -> Result.success(true) } ) fakePickerProvider.givenResult(anotherAvatarUri) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { val initialState = awaitFirstItem() assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri) initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) @@ -281,13 +298,13 @@ class RoomDetailsEditPresenterTest { ) fakePickerProvider.givenResult(anotherAvatarUri) val fakePermissionsPresenter = FakePermissionsPresenter() + val deleteCallback = lambdaRecorder {} val presenter = createRoomDetailsEditPresenter( room = room, permissionsPresenter = fakePermissionsPresenter, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitFirstItem() assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri) assertThat(initialState.cameraPermissionState.permissionGranted).isFalse() @@ -304,6 +321,12 @@ class RoomDetailsEditPresenterTest { stateWithNewAvatar.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.TakePhoto)) val stateWithNewAvatar2 = awaitItem() assertThat(stateWithNewAvatar2.roomAvatarUrl).isEqualTo(roomAvatarUri) + deleteCallback.assertions().isCalledExactly(4).withSequence( + listOf(value(null)), + listOf(value(null)), + listOf(value(roomAvatarUri)), + listOf(value(anotherAvatarUri)), + ) } } @@ -317,10 +340,12 @@ class RoomDetailsEditPresenterTest { canSendStateResult = { _, _ -> Result.success(true) } ) fakePickerProvider.givenResult(roomAvatarUri) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { val initialState = awaitFirstItem() assertThat(initialState.saveButtonEnabled).isFalse() // Once a change is made, the save button is enabled @@ -366,10 +391,12 @@ class RoomDetailsEditPresenterTest { canSendStateResult = { _, _ -> Result.success(true) } ) fakePickerProvider.givenResult(roomAvatarUri) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { val initialState = awaitFirstItem() assertThat(initialState.saveButtonEnabled).isFalse() // Once a change is made, the save button is enabled @@ -420,10 +447,12 @@ class RoomDetailsEditPresenterTest { removeAvatarResult = removeAvatarResult, canSendStateResult = { _, _ -> Result.success(true) } ) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { val initialState = awaitFirstItem() initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("New name")) initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("New topic")) @@ -444,10 +473,12 @@ class RoomDetailsEditPresenterTest { avatarUrl = AN_AVATAR_URL, canSendStateResult = { _, _ -> Result.success(true) } ) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { val initialState = awaitItem() initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName(" Name ")) initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(" My topic ")) @@ -464,14 +495,17 @@ class RoomDetailsEditPresenterTest { avatarUrl = AN_AVATAR_URL, canSendStateResult = { _, _ -> Result.success(true) } ) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { val initialState = awaitItem() initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("")) initialState.eventSink(RoomDetailsEditEvents.Save) cancelAndIgnoreRemainingEvents() + deleteCallback.assertions().isCalledOnce().with(value(null)) } } @@ -483,14 +517,17 @@ class RoomDetailsEditPresenterTest { avatarUrl = AN_AVATAR_URL, canSendStateResult = { _, _ -> Result.success(true) } ) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { val initialState = awaitItem() initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("")) initialState.eventSink(RoomDetailsEditEvents.Save) cancelAndIgnoreRemainingEvents() + deleteCallback.assertions().isCalledOnce().with(value(null)) } } @@ -505,15 +542,21 @@ class RoomDetailsEditPresenterTest { canSendStateResult = { _, _ -> Result.success(true) } ) givenPickerReturnsFile() - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { val initialState = awaitItem() initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) initialState.eventSink(RoomDetailsEditEvents.Save) skipItems(4) - updateAvatarResult.assertions().isCalledOnce().with(value("image/jpeg"), value(fakeFileContents)) + updateAvatarResult.assertions().isCalledOnce().with(value(MimeTypes.Jpeg), value(fakeFileContents)) + deleteCallback.assertions().isCalledExactly(2).withSequence( + listOf(value(null)), + listOf(value(null)), + ) } } @@ -527,10 +570,12 @@ class RoomDetailsEditPresenterTest { ) fakePickerProvider.givenResult(anotherAvatarUri) fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no"))) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { val initialState = awaitItem() initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) initialState.eventSink(RoomDetailsEditEvents.Save) @@ -575,7 +620,7 @@ class RoomDetailsEditPresenterTest { removeAvatarResult = { Result.failure(Throwable("!")) }, canSendStateResult = { _, _ -> Result.success(true) } ) - saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) + saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove), deleteCallbackNumberOfInvocation = 3) } @Test @@ -589,7 +634,7 @@ class RoomDetailsEditPresenterTest { updateAvatarResult = { _, _ -> Result.failure(Throwable("!")) }, canSendStateResult = { _, _ -> Result.success(true) } ) - saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) + saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto), deleteCallbackNumberOfInvocation = 3) } @Test @@ -602,10 +647,12 @@ class RoomDetailsEditPresenterTest { setTopicResult = { Result.failure(Throwable("!")) }, canSendStateResult = { _, _ -> Result.success(true) } ) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { val initialState = awaitItem() initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("foo")) initialState.eventSink(RoomDetailsEditEvents.Save) @@ -616,17 +663,24 @@ class RoomDetailsEditPresenterTest { } } - private suspend fun saveAndAssertFailure(room: MatrixRoom, event: RoomDetailsEditEvents) { - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + private suspend fun saveAndAssertFailure( + room: MatrixRoom, + event: RoomDetailsEditEvents, + deleteCallbackNumberOfInvocation: Int = 2, + ) { + val deleteCallback = lambdaRecorder {} + val presenter = createRoomDetailsEditPresenter( + room = room, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + presenter.test { val initialState = awaitFirstItem() initialState.eventSink(event) initialState.eventSink(RoomDetailsEditEvents.Save) skipItems(1) assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java) + deleteCallback.assertions().isCalledExactly(deleteCallbackNumberOfInvocation) } } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt index ea83f89181..b41090b661 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt @@ -17,6 +17,7 @@ import im.vector.app.features.analytics.plan.Interaction import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.leaveroom.api.aLeaveRoomState +import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.features.roomdetails.aMatrixRoom import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter @@ -98,6 +99,7 @@ class RoomDetailsPresenterTest { notificationSettingsService = matrixClient.notificationSettingsService(), roomMembersDetailsPresenterFactory = roomMemberDetailsPresenterFactory, leaveRoomPresenter = { leaveRoomState }, + roomCallStatePresenter = { aStandByCallState() }, dispatchers = dispatchers, isPinnedMessagesFeatureEnabled = { isPinnedMessagesFeatureEnabled }, analyticsService = analyticsService, diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt index 2f3b0cff75..a5b62ff1e3 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt @@ -28,6 +28,8 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import javax.inject.Inject +private const val SEARCH_BATCH_SIZE = 20 + class RoomDirectoryPresenter @Inject constructor( private val dispatchers: CoroutineDispatchers, private val roomDirectoryService: RoomDirectoryService, @@ -51,7 +53,7 @@ class RoomDirectoryPresenter @Inject constructor( loadingMore = false // debounce search query delay(300) - roomDirectoryList.filter(searchQuery, 20) + roomDirectoryList.filter(filter = searchQuery, batchSize = SEARCH_BATCH_SIZE, viaServerName = null) } LaunchedEffect(loadingMore) { if (loadingMore) { diff --git a/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenterTest.kt b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenterTest.kt index 10dda624bd..20a709bce7 100644 --- a/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenterTest.kt +++ b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenterTest.kt @@ -81,7 +81,7 @@ import org.junit.Test @Test fun `present - emit search event`() = runTest { - val filterLambda = lambdaRecorder { _: String?, _: Int -> + val filterLambda = lambdaRecorder { _: String?, _: Int, _: String? -> Result.success(Unit) } val roomDirectoryList = FakeRoomDirectoryList(filterLambda = filterLambda) @@ -99,7 +99,7 @@ import org.junit.Test } assert(filterLambda) .isCalledOnce() - .with(value("test"), any()) + .with(value("test"), any(), value(null)) } @Test diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/SetUpRecoveryKeyBanner.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/SetUpRecoveryKeyBanner.kt index 75fd6bda9b..971edaf540 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/SetUpRecoveryKeyBanner.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/SetUpRecoveryKeyBanner.kt @@ -25,6 +25,7 @@ internal fun SetUpRecoveryKeyBanner( modifier = modifier, title = stringResource(R.string.banner_set_up_recovery_title), content = stringResource(R.string.banner_set_up_recovery_content), + actionText = stringResource(R.string.banner_set_up_recovery_submit), onSubmitClick = onContinueClick, onDismissClick = onDismissClick, ) diff --git a/features/roomlist/impl/src/main/res/values-be/translations.xml b/features/roomlist/impl/src/main/res/values-be/translations.xml index b5abcfc70e..a6201922ff 100644 --- a/features/roomlist/impl/src/main/res/values-be/translations.xml +++ b/features/roomlist/impl/src/main/res/values-be/translations.xml @@ -5,6 +5,7 @@ "Ваш хатні сервер больш не падтрымлівае стары пратакол. Калі ласка, выйдзіце і ўвайдзіце зноў, каб працягнуць выкарыстанне праграмы." "Даступна абнаўленне" "Стварыце новы ключ аднаўлення, які можна выкарыстоўваць для аднаўлення зашыфраванай гісторыі паведамленняў у выпадку страты доступу да вашых прылад." + "Наладзьце аднаўленне" "Наладзіць аднаўленне" "Ваша рэзервовая копія чата зараз не сінхранізавана. Вам трэба пацвердзіць ключ аднаўлення, каб захаваць доступ да рэзервовай копіі чата." "Увядзіце ключ аднаўлення" 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 18fe226b8f..c26aeabc68 100644 --- a/features/roomlist/impl/src/main/res/values-cs/translations.xml +++ b/features/roomlist/impl/src/main/res/values-cs/translations.xml @@ -5,6 +5,7 @@ "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" "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í." "Potvrďte 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 ccb20fc3e8..8671c381b8 100644 --- a/features/roomlist/impl/src/main/res/values-de/translations.xml +++ b/features/roomlist/impl/src/main/res/values-de/translations.xml @@ -5,6 +5,7 @@ "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" "Erstelle einen neuen Wiederherstellungsschlüssel, mit dem du deinen verschlüsselten Nachrichtenverlauf wiederherstellen kannst, wenn du dich an einem neuen Gerät anmeldest." + "Wiederherstellung einrichten" "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." 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 9723550412..151d995f4f 100644 --- a/features/roomlist/impl/src/main/res/values-el/translations.xml +++ b/features/roomlist/impl/src/main/res/values-el/translations.xml @@ -5,6 +5,7 @@ "Ο οικιακός διακομιστής σου δεν υποστηρίζει πλέον το παλιό πρωτόκολλο. Αποσυνδέσου και συνδέσου ξανά για να συνεχίσεις να χρησιμοποιείς την εφαρμογή." "Διαθέσιμη αναβάθμιση" "Δημιούργησε ένα νέο κλειδί ανάκτησης που μπορεί να χρησιμοποιηθεί για την επαναφορά του ιστορικού των κρυπτογραφημένων μηνυμάτων σου σε περίπτωση που χάσεις την πρόσβαση στις συσκευές σου." + "Ρύθμιση ανάκτησης" "Ρύθμιση ανάκτησης" "Το αντίγραφο ασφαλείας της συνομιλίας σου δεν είναι συγχρονισμένο αυτήν τη στιγμή. Πρέπει να εισαγάγεις το κλειδί ανάκτησης για να διατηρήσεις την πρόσβαση στο αντίγραφο ασφαλείας της συνομιλίας σου." "Εισήγαγε το κλειδί ανάκτησης" diff --git a/features/roomlist/impl/src/main/res/values-es/translations.xml b/features/roomlist/impl/src/main/res/values-es/translations.xml index 0c3e47822d..25f8e0ba21 100644 --- a/features/roomlist/impl/src/main/res/values-es/translations.xml +++ b/features/roomlist/impl/src/main/res/values-es/translations.xml @@ -1,5 +1,6 @@ + "Configurar la recuperación" "La copia de seguridad del chat no está sincronizada en este momento. Debes confirmar tu clave de recuperación para mantener el acceso a la copia de seguridad del chat." "Confirma tu clave de recuperación" "¿Estás seguro de que quieres rechazar la invitación a unirte a %1$s?" 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 214c7f2493..2eb1f63832 100644 --- a/features/roomlist/impl/src/main/res/values-et/translations.xml +++ b/features/roomlist/impl/src/main/res/values-et/translations.xml @@ -5,9 +5,12 @@ "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 andmete taastamine" "Seadista taastamine" - "Sinu vestluste varukoopia pole hetkel sünkroonis. Säilitamaks ligipääsu vestluse varukoopiale palun sisesta oma taastevõti." - "Sisesta oma taastevõti" + "Säilitamaks ligipääsu vestluste ja krüptovõtmete varukoopiale, palun sisesta kinnituseks oma taastevõti." + "Sisesta oma taastevõti" + "Kas unustasid oma taastevõtme?" + "Sinu võtmehoidla pole sünkroonis" "Selleks, et sul ainsamgi tähtis kõne ei jääks märkamata, siis palun muuda oma nutiseadme seadistusi nii, et lukustusvaates oleksid täisekraani mõõtu teavitused." "Sinu tõhusad telefonikõned" "Kas sa oled kindel, et soovid keelduda liitumiskutsest: %1$s?" diff --git a/features/roomlist/impl/src/main/res/values-fa/translations.xml b/features/roomlist/impl/src/main/res/values-fa/translations.xml index be6024f012..f99eb289a6 100644 --- a/features/roomlist/impl/src/main/res/values-fa/translations.xml +++ b/features/roomlist/impl/src/main/res/values-fa/translations.xml @@ -2,6 +2,7 @@ "خروج و ارتقا" "ارتقا موجود است" + "برپایی بازیابی" "برپایی بازیابی" "ورود کلید بازیابیتان" "بهبود تجریهٔ تماستان" 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 1fe6c26004..91b0d9621d 100644 --- a/features/roomlist/impl/src/main/res/values-fr/translations.xml +++ b/features/roomlist/impl/src/main/res/values-fr/translations.xml @@ -5,6 +5,7 @@ "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 sauvegarde" "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" 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 a5aded8b5b..1505f13367 100644 --- a/features/roomlist/impl/src/main/res/values-hu/translations.xml +++ b/features/roomlist/impl/src/main/res/values-hu/translations.xml @@ -5,11 +5,14 @@ "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" "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." - "Helyreállítási kulcs megerősítése" - "Annak érdekében, hogy soha ne maradjon le egyetlen fontos hívásról sem, módosítsa a beállításokat, hogy engedélyezze a teljes képernyős értesítéseket, amikor a telefon zárolva van." - "Növelje a hívásélményét" + "Erősítse meg a helyreállítási kulcsát, hogy továbbra is hozzáférjen a kulcstárolójához és az üzenetelőzményekhez." + "Adja meg a helyreállítási kulcsot" + "Elfelejtette a helyreállítási kulcsot?" + "A kulcstároló nincs szinkronizálva" + "Hogy sose maradjon le egyetlen fontos hívásról sem, a beállításokban engedélyezze a teljes képernyős értesítéseket, amikor a telefon zárolva van." + "Fokozza a hívásélményét" "Biztos, hogy elutasítja a meghívást, hogy csatlakozzon ehhez: %1$s?" "Meghívás elutasítása" "Biztos, hogy elutasítja ezt a privát csevegést vele: %1$s?" diff --git a/features/roomlist/impl/src/main/res/values-in/translations.xml b/features/roomlist/impl/src/main/res/values-in/translations.xml index d284e83091..e3c3bb4800 100644 --- a/features/roomlist/impl/src/main/res/values-in/translations.xml +++ b/features/roomlist/impl/src/main/res/values-in/translations.xml @@ -5,9 +5,12 @@ "Homeserver Anda tidak lagi mendukung protokol lama. Silakan keluar dan masuk kembali untuk terus menggunakan aplikasi." "Peningkatan tersedia" "Buat kunci pemulihan baru yang dapat digunakan untuk memulihkan riwayat pesan terenkripsi Anda jika Anda kehilangan akses ke perangkat Anda." + "Siapkan pemulihan" "Siapkan pemulihan" - "Cadangan percakapan Anda saat ini tidak tersinkron. Anda perlu mengonfirmasi kunci pemulihan Anda untuk tetap memiliki akses ke cadangan percakapan Anda." - "Konfirmasi kunci pemulihan Anda" + "Konfirmasikan kunci pemulihan Anda untuk mempertahankan akses ke penyimpanan kunci dan riwayat pesan Anda." + "Masukkan kunci pemulihan Anda" + "Lupa kunci pemulihan Anda?" + "Penyimpanan kunci Anda tidak sinkron" "Untuk memastikan Anda tidak melewatkan panggilan penting, silakan ubah pengaturan Anda untuk memperbolehkan notifikasi layar penuh ketika ponsel Anda terkunci." "Tingkatkan pengalaman panggilan Anda" "Apakah Anda yakin ingin menolak undangan untuk bergabung ke %1$s?" @@ -16,6 +19,7 @@ "Tolak obrolan" "Tidak ada undangan" "%1$s (%2$s) mengundang Anda" + "Permintaan untuk bergabung dikirim" "Ini adalah proses satu kali, terima kasih telah menunggu." "Menyiapkan akun Anda." "Buat percakapan atau ruangan baru" diff --git a/features/roomlist/impl/src/main/res/values-it/translations.xml b/features/roomlist/impl/src/main/res/values-it/translations.xml index 16fffb77c4..af305d4b7d 100644 --- a/features/roomlist/impl/src/main/res/values-it/translations.xml +++ b/features/roomlist/impl/src/main/res/values-it/translations.xml @@ -5,6 +5,7 @@ "Il tuo homeserver non supporta più il vecchio protocollo. Esci e rientra per continuare a usare l\'app." "Aggiornamento disponibile" "Genera una nuova chiave di recupero che può essere usata per ripristinare la cronologia dei messaggi crittografati nel caso in cui tu perda l\'accesso ai tuoi dispositivi." + "Configura il recupero" "Configura il ripristino" "Il backup della chat non è attualmente sincronizzato. Devi confermare la chiave di recupero per mantenere l\'accesso al backup della chat." "Inserisci la chiave di recupero" diff --git a/features/roomlist/impl/src/main/res/values-ka/translations.xml b/features/roomlist/impl/src/main/res/values-ka/translations.xml index dac714cd0a..0086aeb6e4 100644 --- a/features/roomlist/impl/src/main/res/values-ka/translations.xml +++ b/features/roomlist/impl/src/main/res/values-ka/translations.xml @@ -1,5 +1,6 @@ + "აღდგენის დაყენება" "თქვენი ჩეთების სარეზერვო ასლი ამჟამად არ არის სინქრონიზებული. თქვენ უნდა შეიყვანოთ თქვენი აღდგენის გასაღები, რათა შეინარჩუნოთ წვდომა ჩეთების სარეზერვო ასლზე." "შეიყვანეთ აღდგენის გასაღები" "დარწმუნებული ხართ, რომ გსურთ, უარი თქვათ მოწვევაზე %1$s-ში?" 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 129c2579ee..8b3c48f88b 100644 --- a/features/roomlist/impl/src/main/res/values-nl/translations.xml +++ b/features/roomlist/impl/src/main/res/values-nl/translations.xml @@ -1,5 +1,10 @@ + "Uitloggen & Upgraden" + "Je server ondersteunt nu een nieuw, sneller protocol. Log uit en log opnieuw in om nu te upgraden. Als je dit nu doet, voorkom je dat je geforceerd uitlogt wordt wanneer het oude protocol later wordt verwijderd." + "Je homeserver ondersteunt het oude protocol niet meer. Log uit en log opnieuw in om de app te blijven gebruiken." + "Upgrade beschikbaar" + "Herstelmogelijkheid instellen" "Je chatback-up is momenteel niet gesynchroniseerd. Je moet je herstelsleutel invoeren om toegang te behouden tot je chatback-up." "Voer je herstelsleutel in" "Verbeter je gesprekservaring" @@ -9,6 +14,7 @@ "Chat weigeren" "Geen uitnodigingen" "%1$s (%2$s) heeft je uitgenodigd" + "Verzoek om toe te treden verzonden" "Dit is een eenmalig proces, bedankt voor het wachten." "Je account instellen." "Begin een nieuw gesprek of maak een nieuwe kamer" diff --git a/features/roomlist/impl/src/main/res/values-pl/translations.xml b/features/roomlist/impl/src/main/res/values-pl/translations.xml index d3c87eb81f..9323ddb96f 100644 --- a/features/roomlist/impl/src/main/res/values-pl/translations.xml +++ b/features/roomlist/impl/src/main/res/values-pl/translations.xml @@ -5,6 +5,7 @@ "Twój serwer domowy już nie wspiera starego protokołu. Zaloguj się ponownie, aby kontynuować korzystanie z aplikacji." "Dostępna aktualizacja" "Wygeneruj nowy klucz przywracania, którego można użyć do przywrócenia historii wiadomości szyfrowanych w przypadku utraty dostępu do swoich urządzeń." + "Skonfiguruj przywracanie" "Skonfiguruj przywracanie" "Twoja kopia zapasowa czatu jest obecnie niezsynchronizowana. Aby zachować dostęp do kopii zapasowej czatu, musisz potwierdzić klucz odzyskiwania." "Wprowadź swój klucz przywracania" @@ -16,6 +17,7 @@ "Odrzuć czat" "Brak zaproszeń" "%1$s (%2$s) zaprosił Cię" + "Wysłano prośbę o dołączenie" "Jest to jednorazowy proces, dziękujemy za czekanie." "Konfigurowanie Twojego konta." "Utwórz nową rozmowę lub pokój" diff --git a/features/roomlist/impl/src/main/res/values-pt-rBR/translations.xml b/features/roomlist/impl/src/main/res/values-pt-rBR/translations.xml index 5f896a6427..6b1efae98c 100644 --- a/features/roomlist/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/roomlist/impl/src/main/res/values-pt-rBR/translations.xml @@ -1,5 +1,6 @@ + "Configurar a recuperação" "Insira sua chave de recuperação" "Tem certeza de que deseja recusar o convite para ingressar em %1$s?" "Recusar convite" diff --git a/features/roomlist/impl/src/main/res/values-pt/translations.xml b/features/roomlist/impl/src/main/res/values-pt/translations.xml index d38b92f076..e0ddffa056 100644 --- a/features/roomlist/impl/src/main/res/values-pt/translations.xml +++ b/features/roomlist/impl/src/main/res/values-pt/translations.xml @@ -5,9 +5,12 @@ "Seu homeserver não suporta mais o protocolo antigo. Termine sessão e volte a iniciar sessão para continuar a utilizar a aplicação." "Atualização disponível" "Gere uma nova chave de recuperação que pode ser usada para restaurar seu histórico de mensagens criptografadas caso você perca o acesso aos seus dispositivos." + "Configurar recuperação" "Configurar a recuperação" - "A tua cópia de segurança das conversas está atualmente dessincronizada. Tens de inserir a tua chave de recuperação para manteres o acesso à cópia." - "Insere a tua chave de recuperação" + "Confirma a tua chave de recuperação para manteres o acesso ao teu armazenamento de chaves e ao histórico de mensagens." + "Introduz a tua chave de recuperação" + "Esqueceste-te da tua chave de recuperação?" + "O teu armazenamento de chaves não está sincronizado" "Para garantir que nunca perdes uma chamada importante, altera as configurações para permitir notificações em ecrã inteiro quando o telemóvel está bloqueado." "Melhora a tua experiência de chamada" "Tens a certeza que queres rejeitar o convite para entra em %1$s?" diff --git a/features/roomlist/impl/src/main/res/values-ro/translations.xml b/features/roomlist/impl/src/main/res/values-ro/translations.xml index 3e219883ad..93ff52b7e9 100644 --- a/features/roomlist/impl/src/main/res/values-ro/translations.xml +++ b/features/roomlist/impl/src/main/res/values-ro/translations.xml @@ -1,5 +1,6 @@ + "Configurați recuperarea" "Backup-ul pentru chat nu este sincronizat în prezent. Trebuie să confirmați cheia de recuperare pentru a menține accesul la backup." "Confirmați cheia de recuperare" "Pentru a vă asigura că nu pierdeți niciodată un apel important, vă rugăm să modificați setările pentru a permite notificări fullscreen atunci când telefonul este blocat." 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 2b0bd5ea40..9c2e902afc 100644 --- a/features/roomlist/impl/src/main/res/values-ru/translations.xml +++ b/features/roomlist/impl/src/main/res/values-ru/translations.xml @@ -5,9 +5,12 @@ "Ваш домашний сервер больше не поддерживает старый протокол. Пожалуйста, выйдите и войдите в свою учётную запись снова, чтобы продолжить использование приложения." "Доступно обновление" "Создайте новый ключ восстановления, который можно использовать для восстановления зашифрованной истории сообщений в случае потери доступа к своим устройствам." - "Настроить восстановление" - "В настоящее время резервная копия ваших чатов не синхронизирована. Вам потребуется ввести свой ключ восстановления, чтобы сохранить доступ к резервной копии чатов." - "Введите ключ восстановления" + "Настроить восстановление" + "Для защиты вашего аккаунта рекомендуется настроить восстановление" + "Подтвердите ключ восстановления, чтобы сохранить доступ к хранилищу ключей и истории сообщений." + "Введите ключ восстановления" + "Забыли ключ восстановления?" + "Хранилище ключей не синхронизировано" "Чтобы больше не пропускать важные звонки, разрешите приложению показывать полноэкранные уведомления на заблокированном экране телефона." "Улучшите качество звонков" "Вы уверены, что хотите отклонить приглашение в %1$s?" 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 4646ff3896..4c5079a51b 100644 --- a/features/roomlist/impl/src/main/res/values-sk/translations.xml +++ b/features/roomlist/impl/src/main/res/values-sk/translations.xml @@ -5,6 +5,7 @@ "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" "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." "Potvrďte svoj kľúč na obnovenie" diff --git a/features/roomlist/impl/src/main/res/values-sv/translations.xml b/features/roomlist/impl/src/main/res/values-sv/translations.xml index 2471c26a91..e614c42764 100644 --- a/features/roomlist/impl/src/main/res/values-sv/translations.xml +++ b/features/roomlist/impl/src/main/res/values-sv/translations.xml @@ -3,6 +3,7 @@ "Din server stöder nu ett nytt, snabbare protokoll. Logga ut och logga in igen för att uppgradera nu. Om du gör detta nu hjälper du dig att undvika en tvingad utloggning när det gamla protokollet tas bort senare." "Uppgradering tillgänglig" "Skapa en ny återställningsnyckel som kan användas för att återställa din krypterade meddelandehistorik om du förlorar åtkomst till dina enheter." + "Ställ in återställning" "Ställ in återställning" "Din chattsäkerhetskopia är för närvarande inte synkroniserad. Du måste ange din återställningsnyckel för att behålla åtkomsten till din chattsäkerhetskopia." "Ange din återställningsnyckel" diff --git a/features/roomlist/impl/src/main/res/values-uk/translations.xml b/features/roomlist/impl/src/main/res/values-uk/translations.xml index 8b33aee7e4..91564da550 100644 --- a/features/roomlist/impl/src/main/res/values-uk/translations.xml +++ b/features/roomlist/impl/src/main/res/values-uk/translations.xml @@ -1,5 +1,6 @@ + "Налаштувати відновлення" "Ваша резервна копія чату наразі не синхронізована. Вам потрібно підтвердити ключ відновлення, щоб зберегти доступ до резервної копії чату." "Підтвердіть ключ відновлення" "Щоб ніколи не пропустити важливий дзвінок, змініть налаштування, щоб увімкнути повноекранні сповіщення, коли телефон заблоковано." diff --git a/features/roomlist/impl/src/main/res/values-uz/translations.xml b/features/roomlist/impl/src/main/res/values-uz/translations.xml index 7ca2ae798a..0fb8858304 100644 --- a/features/roomlist/impl/src/main/res/values-uz/translations.xml +++ b/features/roomlist/impl/src/main/res/values-uz/translations.xml @@ -1,5 +1,6 @@ + "Qayta tiklashni sozlang" "Haqiqatan ham qo\'shilish taklifini rad qilmoqchimisiz%1$s ?" "Taklifni rad etish" "Haqiqatan ham bu shaxsiy chatni rad qilmoqchimisiz%1$s ?" diff --git a/features/roomlist/impl/src/main/res/values-zh/translations.xml b/features/roomlist/impl/src/main/res/values-zh/translations.xml index 3e49dc899a..f578323790 100644 --- a/features/roomlist/impl/src/main/res/values-zh/translations.xml +++ b/features/roomlist/impl/src/main/res/values-zh/translations.xml @@ -5,6 +5,7 @@ "您的服务器不再支持旧协议。请登出并重新登录以继续使用此应用。" "有可用升级" "生成新的恢复密钥,该密钥可用于在您无法访问设备时恢复加密的消息历史记录。" + "设置恢复" "设置恢复" "聊天备份目前不同步,需要输入恢复密钥才能访问聊天备份。" "输入恢复密钥" diff --git a/features/roomlist/impl/src/main/res/values/localazy.xml b/features/roomlist/impl/src/main/res/values/localazy.xml index 79f9b493e6..e10d8da908 100644 --- a/features/roomlist/impl/src/main/res/values/localazy.xml +++ b/features/roomlist/impl/src/main/res/values/localazy.xml @@ -4,10 +4,13 @@ "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" - "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup." - "Enter your recovery key" + "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices." + "Set up recovery" + "Set up recovery to protect your account" + "Confirm your recovery key to maintain access to your key storage and message history." + "Enter your recovery key" + "Forgot your recovery key?" + "Your key storage is out of sync" "To ensure you never miss an important call, please change your settings to allow full-screen notifications when your phone is locked." "Enhance your call experience" "Are you sure you want to decline the invitation to join %1$s?" diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt index cb7ef2852a..69e9a7d401 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt @@ -136,7 +136,7 @@ class RoomListPresenterTest { }.test { val initialState = awaitItem() assertThat(initialState.showAvatarIndicator).isTrue() - sessionVerificationService.givenNeedsSessionVerification(false) + sessionVerificationService.emitNeedsSessionVerification(false) encryptionService.emitBackupState(BackupState.ENABLED) val finalState = awaitItem() assertThat(finalState.showAvatarIndicator).isFalse() @@ -231,7 +231,7 @@ class RoomListPresenterTest { roomListService = roomListService, encryptionService = encryptionService, sessionVerificationService = FakeSessionVerificationService().apply { - givenNeedsSessionVerification(false) + emitNeedsSessionVerification(false) }, syncService = FakeSyncService(MutableStateFlow(SyncState.Running)), ) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt index 63327d0d49..66821ada35 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt @@ -121,12 +121,9 @@ class RoomListViewTest { ), onSetUpRecoveryClick = callback, ) - // Remove automatic initial events eventsRecorder.clear() - - rule.clickOn(CommonStrings.action_continue) - + rule.clickOn(R.string.banner_set_up_recovery_submit) eventsRecorder.assertEmpty() } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt index d3388cb37e..5d9aeec21e 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt @@ -22,7 +22,6 @@ import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.securebackup.api.SecureBackupEntryPoint import io.element.android.features.securebackup.impl.disable.SecureBackupDisableNode -import io.element.android.features.securebackup.impl.enable.SecureBackupEnableNode import io.element.android.features.securebackup.impl.enter.SecureBackupEnterRecoveryKeyNode import io.element.android.features.securebackup.impl.reset.ResetIdentityFlowNode import io.element.android.features.securebackup.impl.root.SecureBackupRootNode @@ -63,9 +62,6 @@ class SecureBackupFlowNode @AssistedInject constructor( @Parcelize data object Disable : NavTarget - @Parcelize - data object Enable : NavTarget - @Parcelize data object EnterRecoveryKey : NavTarget @@ -91,10 +87,6 @@ class SecureBackupFlowNode @AssistedInject constructor( backstack.push(NavTarget.Disable) } - override fun onEnableClick() { - backstack.push(NavTarget.Enable) - } - override fun onConfirmRecoveryKeyClick() { backstack.push(NavTarget.EnterRecoveryKey) } @@ -116,9 +108,6 @@ class SecureBackupFlowNode @AssistedInject constructor( NavTarget.Disable -> { createNode(buildContext) } - NavTarget.Enable -> { - createNode(buildContext) - } NavTarget.EnterRecoveryKey -> { val callback = object : SecureBackupEnterRecoveryKeyNode.Callback { override fun onEnterRecoveryKeySuccess() { diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt index 4e661e0a13..fc721d23ca 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt @@ -37,11 +37,7 @@ class SecureBackupDisablePresenter @Inject constructor( val coroutineScope = rememberCoroutineScope() fun handleEvents(event: SecureBackupDisableEvents) { when (event) { - is SecureBackupDisableEvents.DisableBackup -> if (disableAction.value.isConfirming()) { - coroutineScope.disableBackup(disableAction) - } else { - disableAction.value = AsyncAction.ConfirmingNoParams - } + is SecureBackupDisableEvents.DisableBackup -> coroutineScope.disableBackup(disableAction) SecureBackupDisableEvents.DismissDialogs -> { disableAction.value = AsyncAction.Uninitialized } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt index 1e603ae20d..8d32dba4c2 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt @@ -7,6 +7,7 @@ package io.element.android.features.securebackup.impl.disable +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope @@ -25,7 +26,6 @@ import io.element.android.features.securebackup.impl.R import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.components.async.AsyncActionView -import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button @@ -44,7 +44,7 @@ fun SecureBackupDisableView( onBackClick = onBackClick, title = stringResource(id = R.string.screen_key_backup_disable_title), subTitle = stringResource(id = R.string.screen_key_backup_disable_description), - iconStyle = BigIcon.Style.Default(CompoundIcons.KeyOffSolid()), + iconStyle = BigIcon.Style.AlertSolid, buttons = { Buttons(state = state) }, ) { Content(state = state) @@ -52,12 +52,6 @@ fun SecureBackupDisableView( AsyncActionView( async = state.disableAction, - confirmationDialog = { - SecureBackupDisableConfirmationDialog( - onConfirm = { state.eventSink.invoke(SecureBackupDisableEvents.DisableBackup) }, - onDismiss = { state.eventSink.invoke(SecureBackupDisableEvents.DismissDialogs) }, - ) - }, progressDialog = {}, errorMessage = { it.message ?: it.toString() }, onErrorDismiss = { state.eventSink.invoke(SecureBackupDisableEvents.DismissDialogs) }, @@ -65,18 +59,6 @@ fun SecureBackupDisableView( ) } -@Composable -private fun SecureBackupDisableConfirmationDialog(onConfirm: () -> Unit, onDismiss: () -> Unit) { - ConfirmationDialog( - title = stringResource(id = R.string.screen_key_backup_disable_confirmation_title), - content = stringResource(id = R.string.screen_key_backup_disable_confirmation_description), - submitText = stringResource(id = R.string.screen_key_backup_disable_confirmation_action_turn_off), - destructiveSubmit = true, - onSubmitClick = onConfirm, - onDismiss = onDismiss, - ) -} - @Composable private fun ColumnScope.Buttons( state: SecureBackupDisableState, @@ -105,15 +87,20 @@ private fun Content(state: SecureBackupDisableState) { @Composable private fun SecureBackupDisableItem(text: String) { - Row(modifier = Modifier.fillMaxWidth()) { + Row( + modifier = Modifier + .fillMaxWidth() + .background(color = ElementTheme.colors.bgActionSecondaryHovered) + .padding(horizontal = 16.dp, vertical = 12.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { Icon( imageVector = CompoundIcons.Close(), contentDescription = null, tint = ElementTheme.colors.iconCriticalPrimary, - modifier = Modifier.size(20.dp) + modifier = Modifier.size(24.dp) ) Text( - modifier = Modifier.padding(start = 8.dp, end = 4.dp), text = text, color = ElementTheme.colors.textSecondary, style = ElementTheme.typography.fontBodyMdRegular, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableEvents.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableEvents.kt deleted file mode 100644 index 57e43268d1..0000000000 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableEvents.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * 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.securebackup.impl.enable - -sealed interface SecureBackupEnableEvents { - data object EnableBackup : SecureBackupEnableEvents - data object DismissDialog : SecureBackupEnableEvents -} diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableNode.kt deleted file mode 100644 index 1ae7044edc..0000000000 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableNode.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.securebackup.impl.enable - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -import io.element.android.anvilannotations.ContributesNode -import io.element.android.libraries.di.SessionScope - -@ContributesNode(SessionScope::class) -class SecureBackupEnableNode @AssistedInject constructor( - @Assisted buildContext: BuildContext, - @Assisted plugins: List, - private val presenter: SecureBackupEnablePresenter, -) : Node(buildContext, plugins = plugins) { - @Composable - override fun View(modifier: Modifier) { - val state = presenter.present() - SecureBackupEnableView( - state = state, - modifier = modifier, - onSuccess = ::navigateUp, - onBackClick = ::navigateUp, - ) - } -} diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnablePresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnablePresenter.kt deleted file mode 100644 index ae2ee57c9c..0000000000 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnablePresenter.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.securebackup.impl.enable - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import io.element.android.features.securebackup.impl.loggerTagDisable -import io.element.android.libraries.architecture.AsyncAction -import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.architecture.runCatchingUpdatingState -import io.element.android.libraries.matrix.api.encryption.EncryptionService -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch -import timber.log.Timber -import javax.inject.Inject - -class SecureBackupEnablePresenter @Inject constructor( - private val encryptionService: EncryptionService, -) : Presenter { - @Composable - override fun present(): SecureBackupEnableState { - val enableAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } - val coroutineScope = rememberCoroutineScope() - fun handleEvents(event: SecureBackupEnableEvents) { - when (event) { - is SecureBackupEnableEvents.EnableBackup -> - coroutineScope.enableBackup(enableAction) - SecureBackupEnableEvents.DismissDialog -> { - enableAction.value = AsyncAction.Uninitialized - } - } - } - - return SecureBackupEnableState( - enableAction = enableAction.value, - eventSink = ::handleEvents - ) - } - - private fun CoroutineScope.enableBackup(action: MutableState>) = launch { - suspend { - Timber.tag(loggerTagDisable.value).d("Calling encryptionService.enableBackups()") - encryptionService.enableBackups().getOrThrow() - }.runCatchingUpdatingState(action) - } -} diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableState.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableState.kt deleted file mode 100644 index 058ba49cb6..0000000000 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableState.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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.securebackup.impl.enable - -import io.element.android.libraries.architecture.AsyncAction - -data class SecureBackupEnableState( - val enableAction: AsyncAction, - val eventSink: (SecureBackupEnableEvents) -> Unit -) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableStateProvider.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableStateProvider.kt deleted file mode 100644 index 482f11f1fd..0000000000 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableStateProvider.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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.securebackup.impl.enable - -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.libraries.architecture.AsyncAction - -open class SecureBackupEnableStateProvider : PreviewParameterProvider { - override val values: Sequence - get() = sequenceOf( - aSecureBackupEnableState(), - aSecureBackupEnableState(enableAction = AsyncAction.Loading), - aSecureBackupEnableState(enableAction = AsyncAction.Failure(Exception("Failed to enable"))), - // Add other states here - ) -} - -fun aSecureBackupEnableState( - enableAction: AsyncAction = AsyncAction.Uninitialized, -) = SecureBackupEnableState( - enableAction = enableAction, - eventSink = {} -) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt deleted file mode 100644 index f14e361c46..0000000000 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.securebackup.impl.enable - -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.PreviewParameter -import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.features.securebackup.impl.R -import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage -import io.element.android.libraries.designsystem.components.BigIcon -import io.element.android.libraries.designsystem.components.async.AsyncActionView -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 - -@Composable -fun SecureBackupEnableView( - state: SecureBackupEnableState, - onSuccess: () -> Unit, - onBackClick: () -> Unit, - modifier: Modifier = Modifier, -) { - FlowStepPage( - modifier = modifier, - onBackClick = onBackClick, - title = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable), - iconStyle = BigIcon.Style.Default(CompoundIcons.KeySolid()), - buttons = { Buttons(state = state) } - ) - AsyncActionView( - async = state.enableAction, - progressDialog = { }, - onSuccess = { onSuccess() }, - onErrorDismiss = { state.eventSink.invoke(SecureBackupEnableEvents.DismissDialog) } - ) -} - -@Composable -private fun ColumnScope.Buttons( - state: SecureBackupEnableState, -) { - Button( - text = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable), - showProgress = state.enableAction.isLoading(), - modifier = Modifier.fillMaxWidth(), - onClick = { state.eventSink.invoke(SecureBackupEnableEvents.EnableBackup) } - ) -} - -@PreviewsDayNight -@Composable -internal fun SecureBackupEnableViewPreview( - @PreviewParameter(SecureBackupEnableStateProvider::class) state: SecureBackupEnableState -) = ElementPreview { - SecureBackupEnableView( - state = state, - onSuccess = {}, - onBackClick = {}, - ) -} diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootEvents.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootEvents.kt index 89de592805..f80e3c638b 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootEvents.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootEvents.kt @@ -9,4 +9,7 @@ package io.element.android.features.securebackup.impl.root sealed interface SecureBackupRootEvents { data object RetryKeyBackupState : SecureBackupRootEvents + data object EnableKeyStorage : SecureBackupRootEvents + data object DisplayKeyStorageDisabledError : SecureBackupRootEvents + data object DismissDialog : SecureBackupRootEvents } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt index 113c569d39..a0eace77e6 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt @@ -34,7 +34,6 @@ class SecureBackupRootNode @AssistedInject constructor( fun onSetupClick() fun onChangeClick() fun onDisableClick() - fun onEnableClick() fun onConfirmRecoveryKeyClick() } @@ -50,10 +49,6 @@ class SecureBackupRootNode @AssistedInject constructor( plugins().forEach { it.onDisableClick() } } - private fun onEnableClick() { - plugins().forEach { it.onEnableClick() } - } - private fun onConfirmRecoveryKeyClick() { plugins().forEach { it.onConfirmRecoveryKeyClick() } } @@ -71,7 +66,6 @@ class SecureBackupRootNode @AssistedInject constructor( onBackClick = ::navigateUp, onSetupClick = ::onSetupClick, onChangeClick = ::onChangeClick, - onEnableClick = ::onEnableClick, onDisableClick = ::onDisableClick, onConfirmRecoveryKeyClick = ::onConfirmRecoveryKeyClick, onLearnMoreClick = { onLearnMoreClick(uriHandler) }, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt index 075c9980a1..655be4f7a2 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt @@ -15,7 +15,10 @@ 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 io.element.android.features.securebackup.impl.loggerTagDisable import io.element.android.features.securebackup.impl.loggerTagRoot +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState @@ -41,7 +44,8 @@ class SecureBackupRootPresenter @Inject constructor( val backupState by encryptionService.backupStateStateFlow.collectAsState() val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState() - + val enableAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } + var displayKeyStorageDisabledError by remember { mutableStateOf(false) } Timber.tag(loggerTagRoot.value).d("backupState: $backupState") Timber.tag(loggerTagRoot.value).d("recoveryState: $recoveryState") @@ -56,14 +60,22 @@ class SecureBackupRootPresenter @Inject constructor( fun handleEvents(event: SecureBackupRootEvents) { when (event) { SecureBackupRootEvents.RetryKeyBackupState -> localCoroutineScope.getKeyBackupStatus(doesBackupExistOnServerAction) + SecureBackupRootEvents.EnableKeyStorage -> localCoroutineScope.enableBackup(enableAction) + SecureBackupRootEvents.DismissDialog -> { + enableAction.value = AsyncAction.Uninitialized + displayKeyStorageDisabledError = false + } + SecureBackupRootEvents.DisplayKeyStorageDisabledError -> displayKeyStorageDisabledError = true } } return SecureBackupRootState( + enableAction = enableAction.value, backupState = backupState, doesBackupExistOnServer = doesBackupExistOnServerAction.value, recoveryState = recoveryState, appName = buildMeta.applicationName, + displayKeyStorageDisabledError = displayKeyStorageDisabledError, snackbarMessage = snackbarMessage, eventSink = ::handleEvents, ) @@ -74,4 +86,11 @@ class SecureBackupRootPresenter @Inject constructor( encryptionService.doesBackupExistOnServer().getOrThrow() }.runCatchingUpdatingState(action) } + + private fun CoroutineScope.enableBackup(action: MutableState>) = launch { + suspend { + Timber.tag(loggerTagDisable.value).d("Calling encryptionService.enableBackups()") + encryptionService.enableBackups().getOrThrow() + }.runCatchingUpdatingState(action) + } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt index 944706e6cc..5da4f0c5f3 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt @@ -7,16 +7,31 @@ package io.element.android.features.securebackup.impl.root +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.RecoveryState data class SecureBackupRootState( + val enableAction: AsyncAction, val backupState: BackupState, val doesBackupExistOnServer: AsyncData, val recoveryState: RecoveryState, val appName: String, + val displayKeyStorageDisabledError: Boolean, val snackbarMessage: SnackbarMessage?, val eventSink: (SecureBackupRootEvents) -> Unit, -) +) { + val isKeyStorageEnabled: Boolean + get() = when (backupState) { + BackupState.UNKNOWN -> doesBackupExistOnServer.dataOrNull() == true + BackupState.CREATING, + BackupState.ENABLING, + BackupState.RESUMING, + BackupState.DOWNLOADING, + BackupState.ENABLED -> true + BackupState.WAITING_FOR_SYNC, + BackupState.DISABLING -> false + } +} diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStateProvider.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStateProvider.kt index c07c166975..003fab5f3f 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStateProvider.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStateProvider.kt @@ -8,6 +8,7 @@ package io.element.android.features.securebackup.impl.root import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.encryption.BackupState @@ -22,28 +23,47 @@ open class SecureBackupRootStateProvider : PreviewParameterProvider = AsyncAction.Uninitialized, backupState: BackupState = BackupState.UNKNOWN, doesBackupExistOnServer: AsyncData = AsyncData.Uninitialized, recoveryState: RecoveryState = RecoveryState.UNKNOWN, + displayKeyStorageDisabledError: Boolean = false, snackbarMessage: SnackbarMessage? = null, ) = SecureBackupRootState( + enableAction = enableAction, backupState = backupState, doesBackupExistOnServer = doesBackupExistOnServer, recoveryState = recoveryState, appName = "Element", + displayKeyStorageDisabledError = displayKeyStorageDisabledError, snackbarMessage = snackbarMessage, eventSink = {}, ) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt index 47e3068869..2a4cfbbe6e 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt @@ -7,28 +7,27 @@ package io.element.android.features.securebackup.impl.root -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.progressSemantics import androidx.compose.runtime.Composable 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.features.securebackup.impl.R import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.designsystem.components.async.AsyncLoading +import io.element.android.libraries.designsystem.components.async.AsyncActionView +import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.components.list.ListItemContent -import io.element.android.libraries.designsystem.components.preferences.PreferenceDivider import io.element.android.libraries.designsystem.components.preferences.PreferencePage -import io.element.android.libraries.designsystem.components.preferences.PreferenceText import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.buildAnnotatedStringWithStyledPart import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator +import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState import io.element.android.libraries.matrix.api.encryption.BackupState @@ -41,7 +40,6 @@ fun SecureBackupRootView( onBackClick: () -> Unit, onSetupClick: () -> Unit, onChangeClick: () -> Unit, - onEnableClick: () -> Unit, onDisableClick: () -> Unit, onConfirmRecoveryKeyClick: () -> Unit, onLearnMoreClick: () -> Unit, @@ -52,122 +50,186 @@ fun SecureBackupRootView( PreferencePage( modifier = modifier, onBackClick = onBackClick, - title = stringResource(id = CommonStrings.common_chat_backup), + title = stringResource(id = CommonStrings.common_encryption), snackbarHost = { SnackbarHost(snackbarHostState) }, ) { - val text = buildAnnotatedStringWithStyledPart( - fullTextRes = R.string.screen_chat_backup_key_backup_description, - coloredTextRes = CommonStrings.action_learn_more, - color = ElementTheme.colors.textPrimary, - underline = false, - bold = true, - ) - PreferenceText( - title = stringResource(id = R.string.screen_chat_backup_key_backup_title), - subtitleAnnotated = text, + ListItem( + headlineContent = { + Text( + text = stringResource(id = R.string.screen_chat_backup_key_backup_title), + ) + }, + supportingContent = { + Text( + text = buildAnnotatedStringWithStyledPart( + fullTextRes = R.string.screen_chat_backup_key_backup_description, + coloredTextRes = CommonStrings.action_learn_more, + color = ElementTheme.colors.textPrimary, + underline = false, + bold = true, + ), + ) + }, onClick = onLearnMoreClick, ) - // Disable / Enable backup - when (state.backupState) { - BackupState.WAITING_FOR_SYNC -> Unit - BackupState.UNKNOWN -> { - when (state.doesBackupExistOnServer) { - is AsyncData.Success -> when (state.doesBackupExistOnServer.data) { - true -> { - PreferenceText( - title = stringResource(id = R.string.screen_chat_backup_key_backup_action_disable), - tintColor = ElementTheme.colors.textCriticalPrimary, - onClick = onDisableClick, - ) + // Disable / Enable key storage + ListItem( + headlineContent = { + Text( + text = stringResource(id = R.string.screen_chat_backup_key_storage_toggle_title), + ) + }, + trailingContent = when (state.backupState) { + BackupState.WAITING_FOR_SYNC, + BackupState.DISABLING -> ListItemContent.Custom { LoadingView() } + BackupState.UNKNOWN -> { + when (state.doesBackupExistOnServer) { + is AsyncData.Success -> { + ListItemContent.Switch(checked = state.doesBackupExistOnServer.data) } - false -> { - PreferenceText( - title = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable), - onClick = onEnableClick, + is AsyncData.Loading, + AsyncData.Uninitialized -> ListItemContent.Custom { LoadingView() } + is AsyncData.Failure -> ListItemContent.Custom { + Text( + text = stringResource(id = CommonStrings.action_retry) ) } } - is AsyncData.Loading, - AsyncData.Uninitialized -> { - ListItem(headlineContent = { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.Center, - ) { - CircularProgressIndicator() - } - }) - } - is AsyncData.Failure -> { - ListItem( - headlineContent = { - Text( - text = stringResource(id = CommonStrings.error_unknown), - ) - }, - trailingContent = ListItemContent.Custom { - TextButton( - text = stringResource( - id = CommonStrings.action_retry - ), - onClick = { state.eventSink.invoke(SecureBackupRootEvents.RetryKeyBackupState) } - ) + } + BackupState.CREATING, + BackupState.ENABLING, + BackupState.RESUMING, + BackupState.ENABLED, + BackupState.DOWNLOADING -> ListItemContent.Switch(checked = true) + }, + onClick = { + when (state.backupState) { + BackupState.WAITING_FOR_SYNC, + BackupState.DISABLING -> Unit + BackupState.UNKNOWN -> { + when (state.doesBackupExistOnServer) { + is AsyncData.Success -> { + if (state.doesBackupExistOnServer.data) { + onDisableClick() + } else { + state.eventSink.invoke(SecureBackupRootEvents.EnableKeyStorage) + } } - ) - - PreferenceText( - title = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable), - onClick = onEnableClick, - ) + is AsyncData.Loading, + AsyncData.Uninitialized -> Unit + is AsyncData.Failure -> state.eventSink.invoke(SecureBackupRootEvents.RetryKeyBackupState) + } } + BackupState.CREATING, + BackupState.ENABLING, + BackupState.RESUMING, + BackupState.ENABLED, + BackupState.DOWNLOADING -> onDisableClick() } - } - BackupState.CREATING, - BackupState.ENABLING, - BackupState.RESUMING, - BackupState.ENABLED, - BackupState.DOWNLOADING -> { - PreferenceText( - title = stringResource(id = R.string.screen_chat_backup_key_backup_action_disable), - tintColor = ElementTheme.colors.textCriticalPrimary, - onClick = onDisableClick, - ) - } - BackupState.DISABLING -> { - AsyncLoading() - } - } - - PreferenceDivider() - + }, + ) + HorizontalDivider() // Setup recovery when (state.recoveryState) { RecoveryState.UNKNOWN, RecoveryState.WAITING_FOR_SYNC -> Unit RecoveryState.DISABLED -> { - PreferenceText( - title = stringResource(id = R.string.screen_chat_backup_recovery_action_setup), - subtitle = stringResource(id = R.string.screen_chat_backup_recovery_action_setup_description, state.appName), - onClick = onSetupClick, - showEndBadge = true, + ListItem( + headlineContent = { + Text( + text = stringResource(id = R.string.screen_chat_backup_recovery_action_setup), + ) + }, + supportingContent = { + Text( + text = stringResource(id = R.string.screen_chat_backup_recovery_action_setup_description, state.appName), + ) + }, + trailingContent = ListItemContent.Badge, + enabled = state.isKeyStorageEnabled, + alwaysClickable = true, + onClick = { + if (state.isKeyStorageEnabled) { + onSetupClick() + } else { + state.eventSink.invoke(SecureBackupRootEvents.DisplayKeyStorageDisabledError) + } + }, ) } RecoveryState.ENABLED -> { - PreferenceText( - title = stringResource(id = R.string.screen_chat_backup_recovery_action_change), - onClick = onChangeClick, + ListItem( + headlineContent = { + Text( + text = stringResource(id = R.string.screen_chat_backup_recovery_action_change), + ) + }, + supportingContent = { + Text( + text = stringResource(id = R.string.screen_chat_backup_recovery_action_change_description), + ) + }, + enabled = state.isKeyStorageEnabled, + alwaysClickable = true, + onClick = { + if (state.isKeyStorageEnabled) { + onChangeClick() + } else { + state.eventSink.invoke(SecureBackupRootEvents.DisplayKeyStorageDisabledError) + } + }, ) } RecoveryState.INCOMPLETE -> - PreferenceText( - title = stringResource(id = R.string.screen_chat_backup_recovery_action_confirm), - subtitle = stringResource(id = R.string.screen_chat_backup_recovery_action_confirm_description), - showEndBadge = true, - onClick = onConfirmRecoveryKeyClick, + ListItem( + headlineContent = { + Text( + text = stringResource(id = R.string.screen_chat_backup_recovery_action_confirm), + ) + }, + supportingContent = { + Text( + text = stringResource(id = R.string.screen_chat_backup_recovery_action_confirm_description), + ) + }, + trailingContent = ListItemContent.Badge, + enabled = state.isKeyStorageEnabled, + alwaysClickable = true, + onClick = { + if (state.isKeyStorageEnabled) { + onConfirmRecoveryKeyClick() + } else { + state.eventSink.invoke(SecureBackupRootEvents.DisplayKeyStorageDisabledError) + } + }, ) } } + + AsyncActionView( + async = state.enableAction, + progressDialog = { }, + onSuccess = { }, + onErrorDismiss = { state.eventSink.invoke(SecureBackupRootEvents.DismissDialog) } + ) + if (state.displayKeyStorageDisabledError) { + ErrorDialog( + title = null, + content = stringResource(id = R.string.screen_chat_backup_key_storage_disabled_error), + onSubmit = { state.eventSink.invoke(SecureBackupRootEvents.DismissDialog) }, + ) + } +} + +@Composable +private fun LoadingView() { + CircularProgressIndicator( + modifier = Modifier + .progressSemantics() + .size(24.dp), + strokeWidth = 2.dp + ) } @PreviewsDayNight @@ -180,7 +242,6 @@ internal fun SecureBackupRootViewPreview( onBackClick = {}, onSetupClick = {}, onChangeClick = {}, - onEnableClick = {}, onDisableClick = {}, onConfirmRecoveryKeyClick = {}, onLearnMoreClick = {}, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt index 06fa7b2c46..7ebf2d0219 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt @@ -91,14 +91,14 @@ private fun RecoveryKeyStaticContent( ) { Row( modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(14.dp)) - .background( - color = ElementTheme.colors.bgSubtleSecondary, - shape = RoundedCornerShape(14.dp) - ) - .clickableIfNotNull(onClick) - .padding(horizontal = 16.dp, vertical = 16.dp), + .fillMaxWidth() + .clip(RoundedCornerShape(14.dp)) + .background( + color = ElementTheme.colors.bgSubtleSecondary, + shape = RoundedCornerShape(14.dp) + ) + .clickableIfNotNull(onClick) + .padding(horizontal = 16.dp, vertical = 16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { if (state.formattedRecoveryKey != null) { @@ -116,15 +116,15 @@ private fun RecoveryKeyStaticContent( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, modifier = Modifier - .fillMaxWidth() - .padding(vertical = 11.dp) + .fillMaxWidth() + .padding(vertical = 11.dp) ) { if (state.inProgress) { CircularProgressIndicator( modifier = Modifier - .progressSemantics() - .padding(end = 8.dp) - .size(16.dp), + .progressSemantics() + .padding(end = 8.dp) + .size(16.dp), color = ElementTheme.colors.textPrimary, strokeWidth = 1.5.dp, ) @@ -161,12 +161,12 @@ private fun RecoveryKeyFormContent( } OutlinedTextField( modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.recoveryKey) - .autofill( - autofillTypes = listOf(AutofillType.Password), - onFill = { onChange(it) }, - ), + .fillMaxWidth() + .testTag(TestTags.recoveryKey) + .autofill( + autofillTypes = listOf(AutofillType.Password), + onFill = { onChange(it) }, + ), minLines = 2, value = state.formattedRecoveryKey.orEmpty(), onValueChange = onChange, @@ -189,30 +189,18 @@ private fun RecoveryKeyFooter(state: RecoveryKeyViewState) { RecoveryKeyUserStory.Setup, RecoveryKeyUserStory.Change -> { if (state.formattedRecoveryKey == null) { - Row( - verticalAlignment = Alignment.CenterVertically, - ) { - Icon( - imageVector = CompoundIcons.InfoSolid(), - contentDescription = null, - tint = ElementTheme.colors.iconSecondary, - modifier = Modifier - .padding(start = 16.dp) - .size(20.dp), - ) - Text( - text = stringResource( - id = if (state.recoveryKeyUserStory == RecoveryKeyUserStory.Change) { - R.string.screen_recovery_key_change_generate_key_description - } else { - R.string.screen_recovery_key_setup_generate_key_description - } - ), - color = ElementTheme.colors.textSecondary, - modifier = Modifier.padding(start = 8.dp), - style = ElementTheme.typography.fontBodySmRegular, - ) - } + Text( + text = stringResource( + id = if (state.recoveryKeyUserStory == RecoveryKeyUserStory.Change) { + R.string.screen_recovery_key_change_generate_key_description + } else { + R.string.screen_recovery_key_setup_generate_key_description + } + ), + color = ElementTheme.colors.textSecondary, + modifier = Modifier.padding(start = 16.dp), + style = ElementTheme.typography.fontBodySmRegular, + ) } else { Text( text = stringResource(id = R.string.screen_recovery_key_save_key_description), 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 8af42b8da5..3a57f36ded 100644 --- a/features/securebackup/impl/src/main/res/values-be/translations.xml +++ b/features/securebackup/impl/src/main/res/values-be/translations.xml @@ -42,7 +42,6 @@ "Увесці…" "Страцілі ключ аднаўлення?" "Ключ аднаўлення пацверджаны" - "Увядзіце ключ аднаўлення" "Ключ аднаўлення скапіраваны" "Стварэнне…" "Захаваць ключ аднаўлення" diff --git a/features/securebackup/impl/src/main/res/values-bg/translations.xml b/features/securebackup/impl/src/main/res/values-bg/translations.xml index 37973150c5..7dfb012c34 100644 --- a/features/securebackup/impl/src/main/res/values-bg/translations.xml +++ b/features/securebackup/impl/src/main/res/values-bg/translations.xml @@ -15,7 +15,6 @@ "Въведете 48-символния код." "Въведете…" "Ключът за възстановяване е потвърден" - "Потвърдете ключа си за възстановяване" "Копиран ключ за възстановяване" "Запазване на ключа за възстановяване" "Въведете…" 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 0ab538d4d0..30e9f5128d 100644 --- a/features/securebackup/impl/src/main/res/values-cs/translations.xml +++ b/features/securebackup/impl/src/main/res/values-cs/translations.xml @@ -4,6 +4,7 @@ "Zapnout zálohování" "Bezpečně uložte svou kryptografickou identitu a klíče zpráv na serveru. To vám umožní zobrazit historii zpráv na všech nových zařízeních. %1$s." "Úložiště klíčů" + "Pro nastavení obnovení musí být zapnuto úložiště klíčů." "Nahrát klíče z tohoto zařízení" "Povolit ukládání klíčů" "Změnit klíč pro obnovení" @@ -45,7 +46,6 @@ "Zadejte…" "Ztratili jste klíč pro obnovení?" "Klíč pro obnovení potvrzen" - "Potvrďte klíč pro obnovení" "Klíč pro obnovení zkopírován" "Generování…" "Uložit klíč pro obnovení" 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 5a184d4d14..7201aaba3a 100644 --- a/features/securebackup/impl/src/main/res/values-de/translations.xml +++ b/features/securebackup/impl/src/main/res/values-de/translations.xml @@ -57,7 +57,6 @@ "Eingeben…" "Hast du deinen Wiederherstellungschlüssel vergessen?" "Wiederherstellungsschlüssel bestätigt" - "Wiederherstellungsschlüssel bestätigen." "Wiederherstellungsschlüssel kopiert" "Generieren…" "Wiederherstellungsschlüssel speichern" 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 21c9009ebe..add44a56f7 100644 --- a/features/securebackup/impl/src/main/res/values-el/translations.xml +++ b/features/securebackup/impl/src/main/res/values-el/translations.xml @@ -42,7 +42,6 @@ "Εισαγωγή…" "Έχασες το κλειδί ανάκτησης;" "Επιβεβαιώθηκε το κλειδί ανάκτησης" - "Εισήγαγε το κλειδί ανάκτησης" "Αντιγράφηκε το κλειδί ανάκτησης" "Δημιουργία…" "Αποθήκευση κλειδιού ανάκτησης" diff --git a/features/securebackup/impl/src/main/res/values-es/translations.xml b/features/securebackup/impl/src/main/res/values-es/translations.xml index 4c37eba8fb..64a98d6f94 100644 --- a/features/securebackup/impl/src/main/res/values-es/translations.xml +++ b/features/securebackup/impl/src/main/res/values-es/translations.xml @@ -7,7 +7,7 @@ "Cambiar la clave de recuperación" "Introduzca la clave de recuperación" "La copia de seguridad de tus chats no está sincronizada ahora mismo." - "Configurar la clave de recuperación" + "Configurar la recuperación" "Accede a tus mensajes cifrados si pierdes todos tus dispositivos o cierras sesión de %1$s en cualquier lugar." "Desactivar" "Perderás tus mensajes cifrados si cierras sesión en todos los dispositivos." @@ -27,7 +27,6 @@ "Introduce el código de 48 caracteres." "Ingresar…" "Clave de recuperación confirmada" - "Confirma tu clave de recuperación" "Clave de recuperación copiada" "Generando…" "Guardar clave de recuperación" 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 f67d204a54..9a7b09259b 100644 --- a/features/securebackup/impl/src/main/res/values-et/translations.xml +++ b/features/securebackup/impl/src/main/res/values-et/translations.xml @@ -4,13 +4,14 @@ "Lülita võtmete varundamine sisse" "Salvesta oma krüptoidentiteet ja sõnumite krüptovõtmed turvaliselt serveris. See tagab, et sinu sõnumite ajalugu on alati loetav, ka kõikides uutes seadmetes. %1$s." "Krüptovõtmete varundus" + "Taastamise seadistamiseks peab võtmehoidla olema sisselülitatud." "Laadi siin seadmes leiduvad võtmed üles" "Luba krüptovõtmete salvestamine" "Muuda taastevõtit" "Kui sa oled kaotanud ligipääsu kõikidele oma olemasolevatele seadmetele, siis sa saad taastevõtme abil taastada ligipääsu oma krüptoidentiteedile ja sõnumite ajaloole." "Sisesta taastevõti" - "Sinu vestluste krüptograafia varukoopia pole hetkel enam sünkroonis." - "Seadista krüptovõtmete varundus" + "Sinu krüptovõtmete varundus pole hetkel enam sünkroonis." + "Seadista andmete taastamine" "Säilita ligipääs oma krüptitud sõnumitele ka siis, kui sa kaotad kõik oma seadmed ja/või logid kõikjal välja rakendusest %1$s." "Ava %1$s töölauaga seadmes" "Logi uuesti sisse oma kasutajakontole" @@ -39,7 +40,7 @@ "Kas muudame taastevõtme?" "Loo uus taastevõti" "Palun vaata, et keegi teine ei näeks seda ekraanivaadet!" - "Kinnitamaks ligipääsu sinu vestluse varukoopiale, palun proovi uuesti" + "Kinnitamaks ligipääsu sinu krüptovõtmete varundusele, palun proovi uuesti" "Vigane taastevõti" "Kui sul on turvavõti või turvafraas, siis need toimivad ka." "Sisesta…" diff --git a/features/securebackup/impl/src/main/res/values-fa/translations.xml b/features/securebackup/impl/src/main/res/values-fa/translations.xml index b891d5e510..a0d71400c8 100644 --- a/features/securebackup/impl/src/main/res/values-fa/translations.xml +++ b/features/securebackup/impl/src/main/res/values-fa/translations.xml @@ -35,7 +35,6 @@ "ورود…" "گم کردن کلید بازیابیتان؟" "کلید بازیابی تأیید شد" - "ورود کلید بازیابیتان" "کلید بازیابی رونوشت شد" "تولید کردن…" "ذخیرهٔ کلید بازیابی" 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 7f39b91093..43c3fdbc28 100644 --- a/features/securebackup/impl/src/main/res/values-fr/translations.xml +++ b/features/securebackup/impl/src/main/res/values-fr/translations.xml @@ -2,12 +2,15 @@ "Désactiver la sauvegarde" "Activer la sauvegarde" - "La sauvegarde assure que vous ne perdiez pas l’historique des discussions. %1$s." - "Sauvegarde" + "Stockez votre identité cryptographique et vos clés de message en toute sécurité sur le serveur. Cela vous permettra de consulter l’historique de vos messages sur tous les nouveaux appareils. %1$s." + "Stockage des clés" + "Télécharger les clés depuis cet appareil" + "Autoriser le stockage des clés" "Changer la clé de récupération" + "Récupérez votre identité cryptographique et l’historique de vos messages à l’aide d’une clé de récupération si vous avez perdu tous vos appareils existants." "Utiliser la clé de récupération" "La sauvegarde des discussions est désynchronisée." - "Configurer la récupération" + "Configurer la sauvegarde" "Accédez à vos messages chiffrés si vous perdez tous vos appareils ou que vous êtes déconnectés de %1$s partout." "Ouvrez %1$s sur un ordinateur" "Connectez-vous à nouveau à votre compte" @@ -31,7 +34,7 @@ "Êtes-vous certain de vouloir désactiver la sauvegarde?" "Obtenez une nouvelle clé de récupération dans le cas où vous avez oublié l’ancienne. Après le changement, l’ancienne clé ne sera plus utilisable." "Générer une nouvelle clé" - "Assurez-vous de conserver la clé dans un endroit sûr" + "Ne partagez cela avec personne !" "Clé de récupération modifée" "Changer la clé de récupération?" "Créer une nouvelle clé de récupération" @@ -42,18 +45,17 @@ "Saisissez la clé ici…" "Clé de récupération perdue?" "Clé de récupération confirmée" - "Confirmer votre clé de récupération" "Clé de récupération copiée" "Génération…" "Enregistrer la clé" - "Recopier votre clé de récupération dans un endroit sécurisé ou enregistrer la dans un manager de mot de passe." + "Recopier cette clé de récupération dans un endroit sûr, comme un gestionnaire de mots de passe, une note chiffrée ou un coffre-fort physique." "Taper pour copier la clé" "Sauvegarder la clé" "La clé ne pourra plus être affichée après cette étape." "Avez-vous sauvegardé votre clé de récupération?" "Votre sauvegarde est protégée par votre clé de récupération. Si vous avez besoin d’une nouvelle clé après la configuration, vous pourrez en créer une nouvelle en cliquant sur \"Changer la clé de récupération\"." "Générer la clé de récupération" - "Assurez-vous de conserver la clé dans un endroit sûr" + "Ne partagez cela avec personne !" "Sauvegarde mise en place avec succès" "Configurer la sauvegarde" "Oui, réinitialisez maintenant" 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 eb0cc7fd27..f655234864 100644 --- a/features/securebackup/impl/src/main/res/values-hu/translations.xml +++ b/features/securebackup/impl/src/main/res/values-hu/translations.xml @@ -2,11 +2,15 @@ "Biztonsági mentés kikapcsolása" "Biztonsági mentés bekapcsolása" - "A biztonsági mentés biztosítja, hogy ne veszítse el az üzenetelőzményeit. %1$s." - "Biztonsági mentés" + "Tárolja kriptográfiai személyazonosságát és üzenetkulcsait biztonságosan a kiszolgálón. Ez lehetővé teszi, hogy bármilyen új eszközön megtekinthesse üzenetelőzményeit. %1$s." + "Kulcstároló" + "A helyreállítás beállításához be kell kapcsolni a kulcstárolást." + "Kulcsok feltöltése erről az eszközről" + "Kulcstárolás engedélyezése" "Helyreállítási kulcs módosítása" + "Ha az összes meglévő eszközét elvesztette, akkor egy helyreállítási kulccsal visszaszerezheti a kriptográfiai személyazonosságát és az üzenetelőzményeit." "Adja meg a helyreállítási kulcsot" - "A csevegéselőzményei nincsenek szinkronban." + "A kulcstároló jelenleg nincs szinkronizálva." "Helyreállítás beállítása" "Szerezzen hozzáférést a titkosított üzeneteihez, ha elvesztette az összes eszközét, vagy ha mindenütt kijelentkezett az %1$sből." "Nyissa meg az %1$set egy asztali eszközön" @@ -31,29 +35,29 @@ "Biztos, hogy kikapcsolja a biztonsági mentéseket?" "Szerezzen új helyreállítási kulcsot, ha elvesztette a meglévőt. A helyreállítása kulcsa módosítása után a régi már nem fog működni." "Új helyreállítási kulcs előállítása" - "Gondoskodjon arról, hogy biztonságos helyen tárolja a helyreállítási kulcsát" + "Ezt ne ossza meg senkivel!" "Helyreállítási kulcs lecserélve" "Módosítja a helyreállítási kulcsot?" "Új helyreállítási kulcs létrehozása" "Győződjön meg arról, hogy senki sem látja ezt a képernyőt!" - "Próbálja meg újra megerősíteni a csevegés biztonsági mentéséhez való hozzáférését." + "Próbálja újra megerősíteni a kulcstárolóhoz való hozzáférést." "Helytelen helyreállítási kulcs" "Ha van biztonsági kulcsa vagy biztonsági jelmondata, akkor ez is fog működni." "Megadás…" "Elvesztette a helyreállítási kulcsát?" "Helyreállítási kulcs megerősítve" - "Helyreállítási kulcs megerősítése" + "Adja meg a helyreállítási kulcsot" "Helyreállítási kulcs másolva" "Előállítás…" "Helyreállítási kulcs mentése" - "Írja le a helyreállítási kulcsát valami biztonságos helyre, vagy mentse egy jelszókezelőbe." + "Írja le a helyreállítási kulcsát valami biztonságos helyre, például mentse egy jelszókezelőbe, egy titkosított jegyzetbe vagy egy fizikai széfbe." "Koppintson a helyreállítási kulcs másolásához" "Mentse el a helyreállítási kulcsát" "Ezután a lépés után nem fog tudni hozzáférni az új helyreállítási kulcsához." "Mentette a helyreállítási kulcsát?" "A csevegései biztonsági mentését a helyreállítási kulcsa védi. Ha új helyreállítási kulcsra van szüksége a beállítás után, akkor a „Helyreállítási kulcs módosítása” választásával újból létrehozhat egyet." "Helyreállítási kulcs előállítása" - "Gondoskodjon arról, hogy biztonságos helyen tárolja a helyreállítási kulcsát" + "Ezt ne ossza meg senkivel!" "A helyreállítás beállítása sikeres" "Helyreállítás beállítása" "Igen, visszaállítás most" 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 9c1e59800e..bf88293c2b 100644 --- a/features/securebackup/impl/src/main/res/values-in/translations.xml +++ b/features/securebackup/impl/src/main/res/values-in/translations.xml @@ -4,9 +4,13 @@ "Nyalakan pencadangan" "Simpan identitas kriptografi Anda dan kunci-kunci pesan secara aman di server. Ini akan memungkinkan Anda untuk melihat riwayat pesan Anda di perangkat yang baru. %1$s." "Penyimpanan kunci" + "Penyimpanan kunci harus diaktifkan untuk menyiapkan pemulihan." + "Unggah kunci dari perangkat ini" + "Izinkan penyimpanan kunci" "Ubah kunci pemulihan" + "Pulihkan identitas kriptografi dan riwayat pesan Anda dengan kunci pemulihan jika Anda kehilangan semua perangkat yang ada." "Masukkan kunci pemulihan" - "Pencadangan percakapan Anda saat ini tidak tersinkron." + "Penyimpanan kunci Anda saat ini tidak sinkron." "Siapkan pemulihan" "Dapatkan akses ke pesan terenkripsi Anda jika Anda kehilangan semua perangkat Anda atau keluar dari %1$s di mana pun." "Buka %1$s di perangkat desktop" @@ -36,13 +40,13 @@ "Ubah kunci pemulihan?" "Buat kunci pemulihan baru" "Pastikan tidak ada yang bisa melihat layar ini!" - "Silakan coba lagi untuk mengonfirmasi akses ke cadangan percakapan Anda." + "Silakan coba lagi untuk mengonfirmasi akses ke penyimpanan kunci Anda." "Kunci pemulihan salah" "Jika Anda memiliki kunci keamanan atau frasa keamanan, ini juga bisa digunakan." "Masukkan…" "Kehilangan kunci pemulihan Anda?" "Kunci pemulihan dikonfirmasi" - "Konfirmasi kunci pemulihan Anda" + "Masukkan kunci pemulihan Anda" "Kunci pemulihan disalin" "Membuat…" "Simpan kunci pemulihan" 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 f8d55ad3ea..d6b629ea93 100644 --- a/features/securebackup/impl/src/main/res/values-it/translations.xml +++ b/features/securebackup/impl/src/main/res/values-it/translations.xml @@ -42,7 +42,6 @@ "Inserisci…" "Hai perso la chiave di recupero?" "Chiave di recupero confermata" - "Inserisci la chiave di recupero" "Chiave di recupero copiata" "Generazione…" "Salva la chiave di recupero" diff --git a/features/securebackup/impl/src/main/res/values-ka/translations.xml b/features/securebackup/impl/src/main/res/values-ka/translations.xml index 160d1b06e0..61e4c51c1e 100644 --- a/features/securebackup/impl/src/main/res/values-ka/translations.xml +++ b/features/securebackup/impl/src/main/res/values-ka/translations.xml @@ -3,7 +3,7 @@ "სარეზერვო ასლის გამორთვა" "სარეზერვო ასლის ჩართვა" "სარეზერვო ასლი უზრუნველყოფს იმას, რომ თქვენ შეტყობინებების ისტორიას არ დაკარგავთ. %1$s" - "სარეზერვო ასლი" + "გასაღების საცავი" "აღდგენის გასაღების შეცვლა" "თქვენი ჩატის სარეზერვო ასლი ამჟამად არ არის სინქრონიზებული." "აღდგენის დაყენება" @@ -26,7 +26,6 @@ "თუ თქვენ გაქვთ უსაფრთხოების გასაღები ან უსაფრთხოების ფრაზა, ეს ასევე იმუშავებს." "შეყვანა" "აღდგენის გასაღები დადასტურებულია" - "შეიყვანეთ აღდგენის გასაღები" "დაკოპირებულია აღდგენის გასაღები" "გენერირება…" "აღდგენის გასაღების შენახვა" 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 a12c0492af..30f8362ed6 100644 --- a/features/securebackup/impl/src/main/res/values-nl/translations.xml +++ b/features/securebackup/impl/src/main/res/values-nl/translations.xml @@ -2,7 +2,7 @@ "Back-up uitschakelen" "Back-up inschakelen" - "Een back-up maken zorgt ervoor dat je je berichtgeschiedenis niet verliest. %1$s." + "Sla je cryptografische identiteit en berichtsleutels veilig op de server op. Zo kun je je berichtgeschiedenis bekijken op nieuwe apparaten. %1$s." "Back-up" "Herstelsleutel wijzigen" "Voer herstelsleutel in" @@ -16,6 +16,12 @@ "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" + "Doorgaan met opnieuw instellen" + "Je accountgegevens, contacten, voorkeuren en chatlijst worden bewaard" + "Je verliest alle berichtgeschiedenis die alleen op de server is opgeslagen" + "Je moet al je bestaande apparaten en contacten opnieuw verifiëren" + "Stel je identiteit alleen opnieuw in als je geen toegang hebt tot een ander aangemeld apparaat en je je herstelsleutel kwijt bent." + "Kun je dit niet bevestigen? Je zult je identiteit opnieuw moeten instellen." "Uitschakelen" "Je verliest je versleutelde berichten als je bent uitgelogd op alle apparaten." "Weet je zeker dat je de back-up wilt uitschakelen?" @@ -25,7 +31,7 @@ "Weet je zeker dat je de back-up wilt uitschakelen?" "Maak een nieuwe herstelsleutel aan als je je bestaande kwijt bent. Nadat je je herstelsleutel hebt gewijzigd, werkt je oude herstelsleutel niet meer." "Genereer een nieuwe herstelsleutel" - "Zorg ervoor dat je je herstelsleutel op een veilige plek kunt bewaren" + "Deel dit met niemand!" "Herstelsleutel gewijzigd" "Herstelsleutel wijzigen?" "Maak een nieuwe herstelsleutel" @@ -36,19 +42,24 @@ "Voer in…" "Herstelsleutel kwijt?" "Herstelsleutel bevestigd" - "Voer je herstelsleutel in" "Herstelsleutel gekopieerd" "Genereren…" "Herstelsleutel opslaan" - "Noteer je herstelsleutel op een veilige plek of bewaar deze in een wachtwoordmanager." + "Bewaar je herstelsleutel op een veilige plek, zoals in een wachtwoordbeheerder, een versleutelde notitie of in een fysieke kluis." "Tik om de herstelsleutel te kopiëren" "Sla je herstelsleutel op" "Na deze stap kun je je nieuwe herstelsleutel niet meer inzien." "Heb je je herstelsleutel opgeslagen?" "Je chatback-up wordt beschermd door een herstelsleutel. Als je na de installatie een nieuwe herstelsleutel nodig hebt, kun je deze opnieuw aanmaken door \'Herstelsleutel wijzigen\' te selecteren." "Genereer je herstelsleutel" - "Zorg ervoor dat je je herstelsleutel op een veilige plek kunt bewaren" + "Deel dit met niemand!" "Herstelmogelijkheid succesvol ingesteld" "Herstelmogelijkheid instellen" + "Ja, nu opnieuw instellen" + "Dit proces is onomkeerbaar." + "Weet je zeker dat je je identiteit opnieuw wilt instellen?" + "Er is een onbekende fout opgetreden. Controleer of het wachtwoord van je account juist is en probeer het opnieuw." "Voer in…" + "Bevestig dat je je identiteit opnieuw wilt instellen." + "Voer het wachtwoord van je account in om verder te gaan" 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 9ca1e9a44a..82ee0c6790 100644 --- a/features/securebackup/impl/src/main/res/values-pl/translations.xml +++ b/features/securebackup/impl/src/main/res/values-pl/translations.xml @@ -2,9 +2,12 @@ "Wyłącz backup" "Włącz backup" - "Backup zapewnia, że nie stracisz swojej historii wiadomości. %1$s" - "Backup" + "Bezpiecznie przechowuj swoją tożsamość kryptograficzną i klucze wiadomości na serwerze. Umożliwi to przeglądanie historii wiadomości na każdym nowym urządzeniu. %1$s" + "Magazyn kluczy" + "Prześlij klucze z tego urządzenia" + "Zezwól na magazynowanie kluczy" "Zmień klucz przywracania" + "Odzyskaj swoją tożsamość kryptograficzną i historię wiadomości za pomocą klucza przywracania, jeśli utraciłeś dostęp do wszystkich swoich urządzeń." "Wprowadź klucz przywracania" "Backup czatu jest niezsynchronizowany." "Skonfiguruj przywracanie" @@ -31,7 +34,7 @@ "Czy na pewno chcesz wyłączyć backup?" "Uzyskaj nowy klucz przywracania, jeśli straciłeś dostęp do obecnego. Po zmianie klucza przywracania stary nie będzie już działał." "Generuj nowy klucz przywracania" - "Upewnij się, że klucz przywracania możesz przechowywać w bezpiecznym miejscu" + "Nie udostępniaj tego nikomu!" "Zmieniono klucz przywracania" "Zmienić klucz przywracania?" "Utwórz nowy klucz przywracania" @@ -42,18 +45,17 @@ "Wprowadź…" "Zgubiłeś swój kod przywracania?" "Potwierdzono klucz przywracania" - "Wprowadź swój klucz przywracania" "Skopiowano klucz przywracania" "Generuję…" "Zapisz klucz przywracania" - "Zapisz klucz przywracania w bezpiecznym miejscu lub zapisz go w menedżerze haseł." + "Zapisz klucz przywracania w bezpiecznym miejscu, np. w menedżerze haseł, notatce szyfrowanej lub sejfie." "Stuknij, by skopiować klucz przywracania" "Zapisz klucz przywracania" "Po tym kroku nie będziesz mieć dostępu do nowego klucza przywracania." "Czy zapisałeś swój klucz przywracania?" "Backup czatu jest chroniony przez klucz przywracania. Jeśli potrzebujesz utworzyć nowy klucz, możesz to zrobić wybierając `Zmień klucz przywracania`." "Wygeneruj klucz przywracania" - "Upewnij się, że klucz przywracania możesz przechowywać w bezpiecznym miejscu" + "Nie udostępniaj tego nikomu!" "Skonfigurowano przywracanie pomyślnie" "Skonfiguruj przywracanie" "Tak, zresetuj teraz" diff --git a/features/securebackup/impl/src/main/res/values-pt-rBR/translations.xml b/features/securebackup/impl/src/main/res/values-pt-rBR/translations.xml index e22133b881..f353a80d7c 100644 --- a/features/securebackup/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/securebackup/impl/src/main/res/values-pt-rBR/translations.xml @@ -26,7 +26,6 @@ "Se você tiver uma chave de segurança ou frase de segurança, isso também funcionará." "Inserir…" "Chave de recuperação confirmada" - "Insira sua chave de recuperação" "Chave de recuperação copiada" "Gerando…" "Salvar chave de recuperação" 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 b0709c64a2..a6a58acda0 100644 --- a/features/securebackup/impl/src/main/res/values-pt/translations.xml +++ b/features/securebackup/impl/src/main/res/values-pt/translations.xml @@ -2,11 +2,15 @@ "Desativar a cópia de segurança" "Ativar a cópia de segurança" - "A cópia de segurança garante que não perdes o teu histórico de mensagens. %1$s." - "Cópia de segurança" + "Guarda a tua identidade criptográfica e as chaves de mensagens de forma segura no servidor. Isto permitir-te-á ver o teu histórico de mensagens em qualquer dispositivo novo. %1$s." + "Armazenamento de chaves" + "O armazenamento de chaves deve ser ativado para configurar a recuperação." + "Carrega chaves a partir deste dispositivo" + "Permite o armazenamento de chaves" "Alterar chave de recuperação" + "Recupera a tua identidade criptográfica e o histórico de mensagens com uma chave de recuperação, caso tenhas perdido todos os teus dispositivos existentes." "Insere a chave de recuperação" - "A tua cópia de segurança das conversas está atualmente dessincronizada." + "O teu armazenamento de chaves está atualmente dessincronizado." "Configurar recuperação" "Obtém acesso às tuas mensagens cifradas mesmo se perderes todos os teus dispositivos ou se terminares todas as tuas sessões %1$s." "Abre a %1$s num computador" @@ -31,29 +35,29 @@ "Tens a certeza que queres desativar a cópia de segurança?" "Obtém uma nova chave de recuperação se tiveres perdido a atual. Depois de a alterares, a antiga deixará de funcionar." "Gerar uma nova chave de recuperação" - "Certifica-te de que podes guardar a tua chave de recuperação num local seguro" + "Não partilhes isto com ninguém!" "Chave de recuperação alterada" "Alterar a chave de recuperação?" "Criar nova chave de recuperação" "Certifica-te de que ninguém consegue ver esta página!" - "Por favor, tenta novamente para confirmar o acesso à tua cópia de segurança das conversas." + "Tenta novamente para confirmar o acesso ao teu armazenamento de chaves." "Chave de recuperação incorreta" "Também funciona se tiveres uma chave ou frase de segurança." "Inserir…" "Perdeste a tua chave?" "Chave de recuperação confirmada" - "Insere a tua chave de recuperação" + "Introduz a tua chave de recuperação" "Chave de recuperação copiada" "A gerar…" "Guardar chave" - "Anota a tua chave de recuperação num local seguro ou guarda-a num gestor de senhas." + "Anota esta chave de recuperação num local seguro, como um gestor de palavras-passe, uma nota encriptada ou um cofre físico." "Toca para copiar a chave de recuperação" "Guarda a tua chave de recuperação" "Não poderás aceder à tua nova chave de recuperação após este passo." "Guardaste a tua chave de recuperação?" "A tua cópia de segurança das conversas está protegida por uma chave de recuperação. Se precisares de uma nova chave após a configuração, podes recriá-la selecionando \"Alterar chave de recuperação\"." "Gerar a tua chave de recuperação" - "Certifica-te de que podes guardar a tua chave de recuperação num local seguro" + "Não partilhes isto com ninguém!" "Recuperação configurada com sucesso" "Configurar recuperação" "Sim, repor agora" 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 5e62e1e722..9045e713fd 100644 --- a/features/securebackup/impl/src/main/res/values-ro/translations.xml +++ b/features/securebackup/impl/src/main/res/values-ro/translations.xml @@ -36,7 +36,6 @@ "Introduceți…" "Ați pierdut cheia de recuperare?" "Cheia de recuperare confirmată" - "Confirmați cheia de recuperare" "Cheia de recuperare copiată" "Se generează…" "Salvați cheia de recuperare" 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 f69fa99951..9b209c5ec6 100644 --- a/features/securebackup/impl/src/main/res/values-ru/translations.xml +++ b/features/securebackup/impl/src/main/res/values-ru/translations.xml @@ -1,9 +1,10 @@ - "Отключить резервное копирование" + "Удалить хранилище ключей" "Включить резервное копирование" "Сохраните вашу криптографическую идентификацию и ключи сообщений в безопасности на сервере. Это позволит вам просматривать историю сообщений на любых новых устройствах.%1$s ." "Хранилище ключей" + "Для настройки восстановления необходимо включить хранилище ключей." "Загрузить ключи с этого устройства" "Разрешить хранение ключей" "Изменить ключ восстановления" @@ -28,10 +29,10 @@ "Выключить" "Вы потеряете зашифрованные сообщения, если выйдете из всех устройств." "Вы действительно хотите отключить резервное копирование?" - "Отключение резервного копирования удалит текущую резервную копию ключа шифрования и отключит другие функции безопасности. В этом случае вы выполните следующие действия:" + "Удаление хранилища ключей приведёт к удалению вашей криптографической идентификации и ключей сообщений с сервера, а также отключению следующих функций безопасности:" "Нет зашифрованной истории сообщений на новых устройствах" "Вы потеряете доступ к зашифрованным сообщениям, если выйдете из %1$s везде" - "Вы действительно хотите отключить резервное копирование?" + "Вы уверены, что хотите отключить хранение ключей и удалить их?" "Получите новый ключ восстановления, если вы потеряли существующий. После смены ключа восстановления старый ключ больше не будет работать." "Создать новый ключ восстановления" "Не сообщайте эту информацию никому!" @@ -54,7 +55,7 @@ "Сохраните ключ восстановления" "После этого шага вы не сможете получить доступ к новому ключу восстановления." "Вы сохранили ключ восстановления?" - "Резервная копия чата защищена ключом восстановления. Если после настройки вам понадобится новый ключ восстановления, вы можете создать его заново, выбрав «Изменить ключ восстановления»." + "Ваше хранилище ключей защищено ключом восстановления. Если после настройки вам понадобится новый ключ восстановления, вы можете его пересоздать, выбрав «Изменить ключ восстановления»." "Создайте ключ восстановления" "Не сообщайте эту информацию никому!" "Настройка восстановления выполнена успешно" 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 1d04001c76..0d75ce1bf6 100644 --- a/features/securebackup/impl/src/main/res/values-sk/translations.xml +++ b/features/securebackup/impl/src/main/res/values-sk/translations.xml @@ -10,7 +10,7 @@ "Obnovte svoju kryptografickú totožnosť a históriu správ pomocou kľúča na obnovenie, ak ste stratili všetky svoje existujúce zariadenia." "Zadajte kľúč na obnovenie" "Vaša záloha konverzácie nie je momentálne synchronizovaná." - "Nastaviť obnovovanie" + "Nastaviť obnovenie" "Získajte prístup k vašim šifrovaným správam aj keď stratíte všetky svoje zariadenia alebo sa odhlásite zo všetkých %1$s zariadení." "Otvoriť %1$s v stolnom počítači" "Znova sa prihláste do svojho účtu" @@ -45,7 +45,6 @@ "Zadať…" "Stratili ste kľúč na obnovenie?" "Kľúč na obnovu potvrdený" - "Potvrďte svoj kľúč na obnovenie" "Skopírovaný kľúč na obnovenie" "Generovanie…" "Uložiť kľúč na obnovenie" 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 5d1bbeea79..4bd702a339 100644 --- a/features/securebackup/impl/src/main/res/values-sv/translations.xml +++ b/features/securebackup/impl/src/main/res/values-sv/translations.xml @@ -42,7 +42,6 @@ "Ange …" "Blivit av med din återställningsnyckel?" "Återställningsnyckel bekräftad" - "Ange din återställningsnyckel" "Kopierade återställningsnyckel" "Genererar …" "Spara återställningsnyckeln" 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 8d06098c6f..a1ce5e529f 100644 --- a/features/securebackup/impl/src/main/res/values-uk/translations.xml +++ b/features/securebackup/impl/src/main/res/values-uk/translations.xml @@ -41,7 +41,6 @@ "Ввести…" "Загубили ключ відновлення?" "Ключ відновлення підтверджено" - "Підтвердіть ключ відновлення" "Скопійовано ключ відновлення" "Створення…" "Зберегти ключ відновлення" diff --git a/features/securebackup/impl/src/main/res/values-zh-rTW/translations.xml b/features/securebackup/impl/src/main/res/values-zh-rTW/translations.xml index 8dcc1bbcc9..188e3b22bc 100644 --- a/features/securebackup/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/securebackup/impl/src/main/res/values-zh-rTW/translations.xml @@ -6,6 +6,5 @@ "備份" "變更復原金鑰" "關閉" - "輸入您的復原金鑰" "點擊以複製復原金鑰" 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 8f6ef15eb0..74ff0f4414 100644 --- a/features/securebackup/impl/src/main/res/values-zh/translations.xml +++ b/features/securebackup/impl/src/main/res/values-zh/translations.xml @@ -7,7 +7,7 @@ "更改恢复密钥" "输入恢复密钥" "您的聊天备份当前不同步。" - "设置恢复密钥" + "设置恢复" "在丢失或从 %1$s 登出所有设备的情况下访问加密消息。" "在桌面设备中打开 %1$s" "再次登录您的账户" @@ -42,7 +42,6 @@ "输入……" "丢失了恢复密钥?" "恢复密钥已确认" - "输入恢复密钥" "恢复密钥已复制" "正在生成……" "保存恢复密钥" diff --git a/features/securebackup/impl/src/main/res/values/localazy.xml b/features/securebackup/impl/src/main/res/values/localazy.xml index 3fb2c3b027..0113c90596 100644 --- a/features/securebackup/impl/src/main/res/values/localazy.xml +++ b/features/securebackup/impl/src/main/res/values/localazy.xml @@ -1,15 +1,16 @@ - "Turn off backup" + "Delete key storage" "Turn on backup" "Store your cryptographic identity and message keys securely on the server. This will allow you to view your message history on any new devices. %1$s." "Key storage" + "Key storage must be turned on to set up recovery." "Upload keys from this device" "Allow key storage" "Change recovery key" "Recover your cryptographic identity and message history with a recovery key if you’ve lost all your existing devices." "Enter recovery key" - "Your chat backup is currently out of sync." + "Your key storage is currently out of sync." "Set up recovery" "Get access to your encrypted messages if you lose all your devices or are signed out of %1$s everywhere." "Open %1$s in a desktop device" @@ -28,10 +29,10 @@ "Turn off" "You will lose your encrypted messages if you are signed out of all devices." "Are you sure you want to turn off backup?" - "Turning off backup will remove your current encryption key backup and turn off other security features. In this case, you will:" - "Not have encrypted message history on new devices" - "Lose access to your encrypted messages if you are signed out of %1$s everywhere" - "Are you sure you want to turn off backup?" + "Deleting key storage will remove your cryptographic identity and message keys from the server and turn off the following security features:" + "You will not have encrypted message history on new devices" + "You will lose access to your encrypted messages if you are signed out of %1$s everywhere" + "Are you sure you want to turn off key storage and delete it?" "Get a new recovery key if you\'ve lost your existing one. After changing your recovery key, your old one will no longer work." "Generate a new recovery key" "Do not share this with anyone!" @@ -39,7 +40,7 @@ "Change recovery key?" "Create new recovery key" "Make sure nobody can see this screen!" - "Please try again to confirm access to your chat backup." + "Please try again to confirm access to your key storage." "Incorrect recovery key" "If you have a security key or security phrase, this will work too." "Enter…" @@ -54,7 +55,7 @@ "Save your recovery key somewhere safe" "You will not be able to access your new recovery key after this step." "Have you saved your recovery key?" - "Your chat backup is protected by a recovery key. If you need a new recovery key after setup you can recreate by selecting ‘Change recovery key’." + "Your key storage is protected by a recovery key. If you need a new recovery key after setup, you can recreate it by selecting ‘Change recovery key’." "Generate your recovery key" "Do not share this with anyone!" "Recovery setup successful" diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenterTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenterTest.kt index b0d6399c04..073a2de11c 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenterTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenterTest.kt @@ -38,22 +38,6 @@ class SecureBackupDisablePresenterTest { } } - @Test - fun `present - user delete backup and cancel`() = runTest { - val presenter = createSecureBackupDisablePresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() - initialState.eventSink(SecureBackupDisableEvents.DisableBackup) - val state = awaitItem() - assertThat(state.disableAction).isEqualTo(AsyncAction.ConfirmingNoParams) - initialState.eventSink(SecureBackupDisableEvents.DismissDialogs) - val finalState = awaitItem() - assertThat(finalState.disableAction).isEqualTo(AsyncAction.Uninitialized) - } - } - @Test fun `present - user delete backup success`() = runTest { val presenter = createSecureBackupDisablePresenter() @@ -63,9 +47,6 @@ class SecureBackupDisablePresenterTest { val initialState = awaitItem() assertThat(initialState.disableAction).isEqualTo(AsyncAction.Uninitialized) initialState.eventSink(SecureBackupDisableEvents.DisableBackup) - val state = awaitItem() - assertThat(state.disableAction).isEqualTo(AsyncAction.ConfirmingNoParams) - initialState.eventSink(SecureBackupDisableEvents.DisableBackup) val loadingState = awaitItem() assertThat(loadingState.disableAction).isInstanceOf(AsyncAction.Loading::class.java) val finalState = awaitItem() @@ -87,9 +68,6 @@ class SecureBackupDisablePresenterTest { val initialState = awaitItem() assertThat(initialState.disableAction).isEqualTo(AsyncAction.Uninitialized) initialState.eventSink(SecureBackupDisableEvents.DisableBackup) - val state = awaitItem() - assertThat(state.disableAction).isEqualTo(AsyncAction.ConfirmingNoParams) - initialState.eventSink(SecureBackupDisableEvents.DisableBackup) val loadingState = awaitItem() assertThat(loadingState.disableAction).isInstanceOf(AsyncAction.Loading::class.java) val errorState = awaitItem() diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnablePresenterTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnablePresenterTest.kt deleted file mode 100644 index 46d210ad93..0000000000 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnablePresenterTest.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.securebackup.impl.enable - -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test -import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.architecture.AsyncAction -import io.element.android.libraries.matrix.api.encryption.EncryptionService -import io.element.android.libraries.matrix.test.AN_EXCEPTION -import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService -import io.element.android.tests.testutils.WarmUpRule -import kotlinx.coroutines.test.runTest -import org.junit.Rule -import org.junit.Test - -class SecureBackupEnablePresenterTest { - @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.enableAction).isEqualTo(AsyncAction.Uninitialized) - } - } - - @Test - fun `present - user enable backup`() = runTest { - val presenter = createPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() - initialState.eventSink(SecureBackupEnableEvents.EnableBackup) - val loadingState = awaitItem() - assertThat(loadingState.enableAction).isInstanceOf(AsyncAction.Loading::class.java) - val finalState = awaitItem() - assertThat(finalState.enableAction).isEqualTo(AsyncAction.Success(Unit)) - } - } - - @Test - fun `present - user enable backup with error`() = runTest { - val encryptionService = FakeEncryptionService() - encryptionService.givenEnableBackupsFailure(AN_EXCEPTION) - val presenter = createPresenter(encryptionService = encryptionService) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() - initialState.eventSink(SecureBackupEnableEvents.EnableBackup) - val loadingState = awaitItem() - assertThat(loadingState.enableAction).isInstanceOf(AsyncAction.Loading::class.java) - val errorState = awaitItem() - assertThat(errorState.enableAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) - errorState.eventSink(SecureBackupEnableEvents.DismissDialog) - val finalState = awaitItem() - assertThat(finalState.enableAction).isEqualTo(AsyncAction.Uninitialized) - } - } - - private fun createPresenter( - encryptionService: EncryptionService = FakeEncryptionService(), - ) = SecureBackupEnablePresenter( - encryptionService = encryptionService, - ) -} diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.kt index 6d276ded53..5dc715547b 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.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.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.matrix.api.encryption.BackupState @@ -38,6 +39,8 @@ class SecureBackupRootPresenterTest { val initialState = awaitItem() assertThat(initialState.backupState).isEqualTo(BackupState.UNKNOWN) assertThat(initialState.doesBackupExistOnServer.dataOrNull()).isTrue() + assertThat(initialState.enableAction).isEqualTo(AsyncAction.Uninitialized) + assertThat(initialState.displayKeyStorageDisabledError).isFalse() assertThat(initialState.recoveryState).isEqualTo(RecoveryState.UNKNOWN) assertThat(initialState.appName).isEqualTo("Element") assertThat(initialState.snackbarMessage).isNull() @@ -70,6 +73,35 @@ class SecureBackupRootPresenterTest { } } + @Test + fun `present - setting up encryption when key storage is disabled should emit a state to render a dialog`() = runTest { + val presenter = createSecureBackupRootPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(2) + val initialState = awaitItem() + initialState.eventSink(SecureBackupRootEvents.DisplayKeyStorageDisabledError) + assertThat(awaitItem().displayKeyStorageDisabledError).isTrue() + initialState.eventSink(SecureBackupRootEvents.DismissDialog) + assertThat(awaitItem().displayKeyStorageDisabledError).isFalse() + } + } + + @Test + fun `present - enable key storage invoke the expected API`() = runTest { + val presenter = createSecureBackupRootPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(2) + val initialState = awaitItem() + initialState.eventSink(SecureBackupRootEvents.EnableKeyStorage) + assertThat(awaitItem().enableAction.isLoading()).isTrue() + assertThat(awaitItem().enableAction.isSuccess()).isTrue() + } + } + private fun createSecureBackupRootPresenter( encryptionService: EncryptionService = FakeEncryptionService(), appName: String = "Element", diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareService.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareService.kt index de53096076..f4ed61dba8 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareService.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareService.kt @@ -46,7 +46,7 @@ class DefaultShareService @Inject constructor( PackageManager.GET_ACTIVITIES or PackageManager.MATCH_DISABLED_COMPONENTS ) .activities - .firstOrNull { it.name.endsWith(".ShareActivity") } + ?.firstOrNull { it.name.endsWith(".ShareActivity") } ?.let { shareActivityInfo -> ComponentName( shareActivityInfo.packageName, diff --git a/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/SharePresenterTest.kt b/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/SharePresenterTest.kt index a834baf2bc..10d42716a9 100644 --- a/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/SharePresenterTest.kt +++ b/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/SharePresenterTest.kt @@ -16,6 +16,8 @@ import com.google.common.truth.Truth.assertThat import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.ProgressCallback +import io.element.android.libraries.matrix.api.media.FileInfo import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.FakeMatrixClient @@ -25,12 +27,14 @@ import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.lambda.lambdaRecorder import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner +import java.io.File @RunWith(RobolectricTestRunner::class) class SharePresenterTest { @@ -112,8 +116,11 @@ class SharePresenterTest { @Test fun `present - send media ok`() = runTest { + val sendFileResult = lambdaRecorder> { _, _, _ -> + Result.success(FakeMediaUploadHandler()) + } val matrixRoom = FakeMatrixRoom( - sendMediaResult = { Result.success(FakeMediaUploadHandler()) }, + sendFileResult = sendFileResult, ) val matrixClient = FakeMatrixClient().apply { givenGetRoomResult(A_ROOM_ID, matrixRoom) @@ -141,6 +148,7 @@ class SharePresenterTest { val success = awaitItem() assertThat(success.shareAction.isSuccess()).isTrue() assertThat(success.shareAction).isEqualTo(AsyncAction.Success(listOf(A_ROOM_ID))) + sendFileResult.assertions().isCalledOnce() } } diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt index c87b443d4a..2dac6b7d98 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt @@ -106,7 +106,7 @@ fun UserProfileView( private fun VerifyUserSection(state: UserProfileState) { if (state.isVerified.dataOrNull() == false) { ListItem( - headlineContent = { Text(stringResource(R.string.screen_room_member_details_verify_button_title, state.userName ?: state.userId)) }, + headlineContent = { Text(stringResource(CommonStrings.common_verify_identity)) }, supportingContent = { Text(stringResource(R.string.screen_room_member_details_verify_button_subtitle)) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())), enabled = false, diff --git a/features/userprofile/shared/src/main/res/values-pl/translations.xml b/features/userprofile/shared/src/main/res/values-pl/translations.xml index ebac7599dc..58a067e2e4 100644 --- a/features/userprofile/shared/src/main/res/values-pl/translations.xml +++ b/features/userprofile/shared/src/main/res/values-pl/translations.xml @@ -13,5 +13,7 @@ "Odblokuj" "Będziesz mógł ponownie zobaczyć wszystkie wiadomości od tego użytkownika." "Odblokuj użytkownika" + "Użyj aplikacji internetowej, aby zweryfikować tego użytkownika." + "Zweryfikuj %1$s" "Wystąpił błąd podczas próby rozpoczęcia czatu" diff --git a/features/verifysession/api/build.gradle.kts b/features/verifysession/api/build.gradle.kts index 37914dc0ba..748051b623 100644 --- a/features/verifysession/api/build.gradle.kts +++ b/features/verifysession/api/build.gradle.kts @@ -15,4 +15,5 @@ android { dependencies { implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) } diff --git a/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/IncomingVerificationEntryPoint.kt b/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/IncomingVerificationEntryPoint.kt new file mode 100644 index 0000000000..908816ac00 --- /dev/null +++ b/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/IncomingVerificationEntryPoint.kt @@ -0,0 +1,33 @@ +/* + * 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.verifysession.api + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import io.element.android.libraries.architecture.FeatureEntryPoint +import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails + +interface IncomingVerificationEntryPoint : FeatureEntryPoint { + data class Params( + val sessionVerificationRequestDetails: SessionVerificationRequestDetails, + ) : NodeInputs + + fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + + interface NodeBuilder { + fun callback(callback: Callback): NodeBuilder + fun params(params: Params): NodeBuilder + fun build(): Node + } + + interface Callback : Plugin { + fun onDone() + } +} diff --git a/features/verifysession/impl/build.gradle.kts b/features/verifysession/impl/build.gradle.kts index 8f5f6cae24..85d3b463ce 100644 --- a/features/verifysession/impl/build.gradle.kts +++ b/features/verifysession/impl/build.gradle.kts @@ -27,6 +27,7 @@ dependencies { implementation(projects.libraries.androidutils) implementation(projects.libraries.core) implementation(projects.libraries.architecture) + implementation(projects.libraries.dateformatter.api) implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) @@ -43,6 +44,7 @@ dependencies { testImplementation(libs.test.truth) testImplementation(libs.test.turbine) testImplementation(projects.features.logout.test) + testImplementation(projects.libraries.dateformatter.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.preferences.test) testImplementation(projects.tests.testutils) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt deleted file mode 100644 index b5762b6d5f..0000000000 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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.verifysession.impl - -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep -import io.element.android.libraries.architecture.AsyncAction -import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.matrix.api.verification.SessionVerificationData -import io.element.android.libraries.matrix.api.verification.VerificationEmoji - -open class VerifySelfSessionStateProvider : PreviewParameterProvider { - override val values: Sequence - get() = sequenceOf( - aVerifySelfSessionState(displaySkipButton = true), - aVerifySelfSessionState( - verificationFlowStep = VerificationStep.AwaitingOtherDeviceResponse - ), - aVerifySelfSessionState( - verificationFlowStep = VerificationStep.Verifying(aEmojisSessionVerificationData(), AsyncData.Uninitialized) - ), - aVerifySelfSessionState( - verificationFlowStep = VerificationStep.Verifying(aEmojisSessionVerificationData(), AsyncData.Loading()) - ), - aVerifySelfSessionState( - verificationFlowStep = VerificationStep.Canceled - ), - aVerifySelfSessionState( - verificationFlowStep = VerificationStep.Ready - ), - aVerifySelfSessionState( - verificationFlowStep = VerificationStep.Verifying(aDecimalsSessionVerificationData(), AsyncData.Uninitialized) - ), - aVerifySelfSessionState( - verificationFlowStep = VerificationStep.Initial(canEnterRecoveryKey = true) - ), - aVerifySelfSessionState( - verificationFlowStep = VerificationStep.Initial(canEnterRecoveryKey = true, isLastDevice = true) - ), - aVerifySelfSessionState( - verificationFlowStep = VerificationStep.Completed, - displaySkipButton = true, - ), - aVerifySelfSessionState( - signOutAction = AsyncAction.Loading, - displaySkipButton = true, - ), - aVerifySelfSessionState( - verificationFlowStep = VerificationStep.Loading - ), - aVerifySelfSessionState( - verificationFlowStep = VerificationStep.Skipped - ), - // Add other state here - ) -} - -internal fun aEmojisSessionVerificationData( - emojiList: List = aVerificationEmojiList(), -): SessionVerificationData { - return SessionVerificationData.Emojis(emojiList) -} - -private fun aDecimalsSessionVerificationData( - decimals: List = listOf(123, 456, 789), -): SessionVerificationData { - return SessionVerificationData.Decimals(decimals) -} - -internal fun aVerifySelfSessionState( - verificationFlowStep: VerificationStep = VerificationStep.Initial(canEnterRecoveryKey = false), - signOutAction: AsyncAction = AsyncAction.Uninitialized, - displaySkipButton: Boolean = false, - eventSink: (VerifySelfSessionViewEvents) -> Unit = {}, -) = VerifySelfSessionState( - verificationFlowStep = verificationFlowStep, - displaySkipButton = displaySkipButton, - eventSink = eventSink, - signOutAction = signOutAction, -) - -private fun aVerificationEmojiList() = listOf( - VerificationEmoji(number = 27, emoji = "🍕", description = "Pizza"), - VerificationEmoji(number = 54, emoji = "🚀", description = "Rocket"), - VerificationEmoji(number = 54, emoji = "🚀", description = "Rocket"), - VerificationEmoji(number = 42, emoji = "📕", description = "Book"), - VerificationEmoji(number = 48, emoji = "🔨", description = "Hammer"), - VerificationEmoji(number = 48, emoji = "🔨", description = "Hammer"), - VerificationEmoji(number = 63, emoji = "📌", description = "Pin"), -) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/DefaultIncomingVerificationEntryPoint.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/DefaultIncomingVerificationEntryPoint.kt new file mode 100644 index 0000000000..24ea086720 --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/DefaultIncomingVerificationEntryPoint.kt @@ -0,0 +1,40 @@ +/* + * 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.verifysession.impl.incoming + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint +import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.di.AppScope +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class DefaultIncomingVerificationEntryPoint @Inject constructor() : IncomingVerificationEntryPoint { + override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): IncomingVerificationEntryPoint.NodeBuilder { + val plugins = ArrayList() + + return object : IncomingVerificationEntryPoint.NodeBuilder { + override fun callback(callback: IncomingVerificationEntryPoint.Callback): IncomingVerificationEntryPoint.NodeBuilder { + plugins += callback + return this + } + + override fun params(params: IncomingVerificationEntryPoint.Params): IncomingVerificationEntryPoint.NodeBuilder { + plugins += params + return this + } + + override fun build(): Node { + return parentNode.createNode(buildContext, plugins) + } + } + } +} diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationNavigator.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationNavigator.kt new file mode 100644 index 0000000000..9ae5a09dd8 --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationNavigator.kt @@ -0,0 +1,12 @@ +/* + * 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.verifysession.impl.incoming + +fun interface IncomingVerificationNavigator { + fun onFinish() +} diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationNode.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationNode.kt new file mode 100644 index 0000000000..14df3ef8d9 --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationNode.kt @@ -0,0 +1,47 @@ +/* + * 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.verifysession.impl.incoming + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.core.plugin.plugins +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.element.android.anvilannotations.ContributesNode +import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint +import io.element.android.libraries.architecture.inputs +import io.element.android.libraries.di.SessionScope + +@ContributesNode(SessionScope::class) +class IncomingVerificationNode @AssistedInject constructor( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + presenterFactory: IncomingVerificationPresenter.Factory, +) : Node(buildContext, plugins = plugins), + IncomingVerificationNavigator { + private val presenter = presenterFactory.create( + sessionVerificationRequestDetails = inputs().sessionVerificationRequestDetails, + navigator = this, + ) + + override fun onFinish() { + plugins().forEach { it.onDone() } + } + + @Composable + override fun View(modifier: Modifier) { + val state = presenter.present() + IncomingVerificationView( + state = state, + modifier = modifier, + ) + } +} diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenter.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenter.kt new file mode 100644 index 0000000000..ebd897d84c --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenter.kt @@ -0,0 +1,189 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package io.element.android.features.verifysession.impl.incoming + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import com.freeletics.flowredux.compose.rememberStateAndDispatch +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import io.element.android.features.verifysession.impl.incoming.IncomingVerificationState.Step +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter +import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails +import io.element.android.libraries.matrix.api.verification.SessionVerificationService +import io.element.android.libraries.matrix.api.verification.VerificationFlowState +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import io.element.android.features.verifysession.impl.incoming.IncomingVerificationStateMachine.Event as StateMachineEvent +import io.element.android.features.verifysession.impl.incoming.IncomingVerificationStateMachine.State as StateMachineState + +class IncomingVerificationPresenter @AssistedInject constructor( + @Assisted private val sessionVerificationRequestDetails: SessionVerificationRequestDetails, + @Assisted private val navigator: IncomingVerificationNavigator, + private val sessionVerificationService: SessionVerificationService, + private val stateMachine: IncomingVerificationStateMachine, + private val dateFormatter: LastMessageTimestampFormatter, +) : Presenter { + @AssistedFactory + interface Factory { + fun create( + sessionVerificationRequestDetails: SessionVerificationRequestDetails, + navigator: IncomingVerificationNavigator, + ): IncomingVerificationPresenter + } + + @Composable + override fun present(): IncomingVerificationState { + LaunchedEffect(Unit) { + // Force reset, just in case the service was left in a broken state + sessionVerificationService.reset( + cancelAnyPendingVerificationAttempt = false + ) + // Acknowledge the request right now + sessionVerificationService.acknowledgeVerificationRequest(sessionVerificationRequestDetails) + } + val stateAndDispatch = stateMachine.rememberStateAndDispatch() + val formattedSignInTime = remember { + dateFormatter.format(sessionVerificationRequestDetails.firstSeenTimestamp) + } + val step by remember { + derivedStateOf { + stateAndDispatch.state.value.toVerificationStep( + sessionVerificationRequestDetails = sessionVerificationRequestDetails, + formattedSignInTime = formattedSignInTime, + ) + } + } + + LaunchedEffect(stateAndDispatch.state.value) { + if ((stateAndDispatch.state.value as? IncomingVerificationStateMachine.State.Initial)?.isCancelled == true) { + // The verification was canceled before it was started, maybe because another session accepted it + navigator.onFinish() + } + } + + // Start this after observing state machine + LaunchedEffect(Unit) { + observeVerificationService() + } + + fun handleEvents(event: IncomingVerificationViewEvents) { + Timber.d("Verification user action: ${event::class.simpleName}") + when (event) { + IncomingVerificationViewEvents.StartVerification -> + stateAndDispatch.dispatchAction(StateMachineEvent.AcceptIncomingRequest) + IncomingVerificationViewEvents.IgnoreVerification -> + navigator.onFinish() + IncomingVerificationViewEvents.ConfirmVerification -> + stateAndDispatch.dispatchAction(StateMachineEvent.AcceptChallenge) + IncomingVerificationViewEvents.DeclineVerification -> + stateAndDispatch.dispatchAction(StateMachineEvent.DeclineChallenge) + IncomingVerificationViewEvents.GoBack -> { + when (val verificationStep = step) { + is Step.Initial -> if (verificationStep.isWaiting) { + stateAndDispatch.dispatchAction(StateMachineEvent.Cancel) + } else { + navigator.onFinish() + } + is Step.Verifying -> if (verificationStep.isWaiting) { + // What do we do in this case? + } else { + stateAndDispatch.dispatchAction(StateMachineEvent.DeclineChallenge) + } + Step.Canceled, + Step.Completed, + Step.Failure -> navigator.onFinish() + } + } + } + } + + return IncomingVerificationState( + step = step, + eventSink = ::handleEvents, + ) + } + + private fun StateMachineState?.toVerificationStep( + sessionVerificationRequestDetails: SessionVerificationRequestDetails, + formattedSignInTime: String, + ): Step = + when (val machineState = this) { + is StateMachineState.Initial, + IncomingVerificationStateMachine.State.AcceptingIncomingVerification, + IncomingVerificationStateMachine.State.RejectingIncomingVerification, + null -> { + Step.Initial( + deviceDisplayName = sessionVerificationRequestDetails.displayName ?: sessionVerificationRequestDetails.deviceId.value, + deviceId = sessionVerificationRequestDetails.deviceId, + formattedSignInTime = formattedSignInTime, + isWaiting = machineState == IncomingVerificationStateMachine.State.AcceptingIncomingVerification || + machineState == IncomingVerificationStateMachine.State.RejectingIncomingVerification, + ) + } + is IncomingVerificationStateMachine.State.ChallengeReceived -> + Step.Verifying( + data = machineState.data, + isWaiting = false, + ) + IncomingVerificationStateMachine.State.Completed -> Step.Completed + IncomingVerificationStateMachine.State.Canceling, + IncomingVerificationStateMachine.State.Failure -> Step.Failure + is IncomingVerificationStateMachine.State.AcceptingChallenge -> + Step.Verifying( + data = machineState.data, + isWaiting = true, + ) + is IncomingVerificationStateMachine.State.RejectingChallenge -> + Step.Verifying( + data = machineState.data, + isWaiting = true, + ) + IncomingVerificationStateMachine.State.Canceled -> Step.Canceled + } + + private fun CoroutineScope.observeVerificationService() { + sessionVerificationService.verificationFlowState + .onEach { Timber.d("Verification flow state: ${it::class.simpleName}") } + .onEach { verificationAttemptState -> + when (verificationAttemptState) { + VerificationFlowState.Initial, + VerificationFlowState.DidAcceptVerificationRequest, + VerificationFlowState.DidStartSasVerification -> Unit + is VerificationFlowState.DidReceiveVerificationData -> { + stateMachine.dispatch(IncomingVerificationStateMachine.Event.DidReceiveChallenge(verificationAttemptState.data)) + } + VerificationFlowState.DidFinish -> { + stateMachine.dispatch(IncomingVerificationStateMachine.Event.DidAcceptChallenge) + } + VerificationFlowState.DidCancel -> { + // Can happen when: + // - the remote party cancel the verification (before it is started) + // - another session has accepted the incoming verification request + // - the user reject the challenge from this application (I think this is an error). In this case, the state + // machine will ignore this event and change state to Failure. + stateMachine.dispatch(IncomingVerificationStateMachine.Event.DidCancel) + } + VerificationFlowState.DidFail -> { + stateMachine.dispatch(IncomingVerificationStateMachine.Event.DidFail) + } + } + } + .launchIn(this) + } +} diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationState.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationState.kt new file mode 100644 index 0000000000..4fcb86dfb8 --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationState.kt @@ -0,0 +1,38 @@ +/* + * 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.verifysession.impl.incoming + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable +import io.element.android.libraries.matrix.api.core.DeviceId +import io.element.android.libraries.matrix.api.verification.SessionVerificationData + +@Immutable +data class IncomingVerificationState( + val step: Step, + val eventSink: (IncomingVerificationViewEvents) -> Unit, +) { + @Stable + sealed interface Step { + data class Initial( + val deviceDisplayName: String, + val deviceId: DeviceId, + val formattedSignInTime: String, + val isWaiting: Boolean, + ) : Step + + data class Verifying( + val data: SessionVerificationData, + val isWaiting: Boolean, + ) : Step + + data object Canceled : Step + data object Completed : Step + data object Failure : Step + } +} diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateMachine.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateMachine.kt new file mode 100644 index 0000000000..b8c3276af4 --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateMachine.kt @@ -0,0 +1,158 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package io.element.android.features.verifysession.impl.incoming + +import com.freeletics.flowredux.dsl.FlowReduxStateMachine +import io.element.android.features.verifysession.impl.util.andLogStateChange +import io.element.android.features.verifysession.impl.util.logReceivedEvents +import io.element.android.libraries.matrix.api.verification.SessionVerificationData +import io.element.android.libraries.matrix.api.verification.SessionVerificationService +import kotlinx.coroutines.ExperimentalCoroutinesApi +import javax.inject.Inject +import com.freeletics.flowredux.dsl.State as MachineState + +class IncomingVerificationStateMachine @Inject constructor( + private val sessionVerificationService: SessionVerificationService, +) : FlowReduxStateMachine( + initialState = State.Initial(isCancelled = false) +) { + init { + spec { + inState { + on { _: Event.AcceptIncomingRequest, state -> + state.override { State.AcceptingIncomingVerification.andLogStateChange() } + } + } + inState { + onEnterEffect { + sessionVerificationService.acceptVerificationRequest() + } + on { event: Event.DidReceiveChallenge, state -> + state.override { State.ChallengeReceived(event.data).andLogStateChange() } + } + } + inState { + on { _: Event.AcceptChallenge, state -> + state.override { State.AcceptingChallenge(state.snapshot.data).andLogStateChange() } + } + on { _: Event.DeclineChallenge, state -> + state.override { State.RejectingChallenge(state.snapshot.data).andLogStateChange() } + } + } + inState { + onEnterEffect { _ -> + sessionVerificationService.approveVerification() + } + on { _: Event.DidAcceptChallenge, state -> + state.override { State.Completed.andLogStateChange() } + } + } + inState { + onEnterEffect { _ -> + sessionVerificationService.declineVerification() + } + } + inState { + onEnterEffect { + sessionVerificationService.cancelVerification() + } + } + inState { + logReceivedEvents() + on { _: Event.Cancel, state: MachineState -> + when (state.snapshot) { + State.Completed, State.Canceled -> state.noChange() + else -> { + sessionVerificationService.cancelVerification() + state.override { State.Canceled.andLogStateChange() } + } + } + } + on { _: Event.DidCancel, state: MachineState -> + when (state.snapshot) { + is State.RejectingChallenge -> { + state.override { State.Failure.andLogStateChange() } + } + is State.Initial -> state.mutate { State.Initial(isCancelled = true).andLogStateChange() } + State.AcceptingIncomingVerification, + State.RejectingIncomingVerification, + is State.ChallengeReceived, + is State.AcceptingChallenge, + State.Canceling -> state.override { State.Canceled.andLogStateChange() } + State.Canceled, + State.Completed, + State.Failure -> state.noChange() + } + } + on { _: Event.DidFail, state: MachineState -> + state.override { State.Failure.andLogStateChange() } + } + } + } + } + + sealed interface State { + /** The initial state, before verification started. */ + data class Initial(val isCancelled: Boolean) : State + + /** User is accepting the incoming verification. */ + data object AcceptingIncomingVerification : State + + /** User is rejecting the incoming verification. */ + data object RejectingIncomingVerification : State + + /** Verification accepted and emojis received. */ + data class ChallengeReceived(val data: SessionVerificationData) : State + + /** Accepting the verification challenge. */ + data class AcceptingChallenge(val data: SessionVerificationData) : State + + /** Rejecting the verification challenge. */ + data class RejectingChallenge(val data: SessionVerificationData) : State + + /** The verification is being canceled. */ + data object Canceling : State + + /** The verification has been canceled, remotely or locally. */ + data object Canceled : State + + /** Verification successful. */ + data object Completed : State + + /** Verification failure. */ + data object Failure : State + } + + sealed interface Event { + /** User accepts the incoming request. */ + data object AcceptIncomingRequest : Event + + /** Has received data. */ + data class DidReceiveChallenge(val data: SessionVerificationData) : Event + + /** Emojis match. */ + data object AcceptChallenge : Event + + /** Emojis do not match. */ + data object DeclineChallenge : Event + + /** Remote accepted challenge. */ + data object DidAcceptChallenge : Event + + /** Request cancellation. */ + data object Cancel : Event + + /** Verification cancelled. */ + data object DidCancel : Event + + /** Request failed. */ + data object DidFail : Event + } +} diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateProvider.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateProvider.kt new file mode 100644 index 0000000000..0b43dcdd3c --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateProvider.kt @@ -0,0 +1,46 @@ +/* + * 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.verifysession.impl.incoming + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.verifysession.impl.incoming.IncomingVerificationState.Step +import io.element.android.features.verifysession.impl.ui.aDecimalsSessionVerificationData +import io.element.android.features.verifysession.impl.ui.aEmojisSessionVerificationData +import io.element.android.libraries.matrix.api.core.DeviceId + +open class IncomingVerificationStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + anIncomingVerificationState(), + anIncomingVerificationState(step = aStepInitial(isWaiting = true)), + anIncomingVerificationState(step = Step.Verifying(data = aEmojisSessionVerificationData(), isWaiting = false)), + anIncomingVerificationState(step = Step.Verifying(data = aEmojisSessionVerificationData(), isWaiting = true)), + anIncomingVerificationState(step = Step.Verifying(data = aDecimalsSessionVerificationData(), isWaiting = false)), + anIncomingVerificationState(step = Step.Completed), + anIncomingVerificationState(step = Step.Failure), + anIncomingVerificationState(step = Step.Canceled), + // Add other state here + ) +} + +internal fun aStepInitial( + isWaiting: Boolean = false, +) = Step.Initial( + deviceDisplayName = "Element X Android", + deviceId = DeviceId("ILAKNDNASDLK"), + formattedSignInTime = "12:34", + isWaiting = isWaiting, +) + +internal fun anIncomingVerificationState( + step: Step = aStepInitial(), + eventSink: (IncomingVerificationViewEvents) -> Unit = {}, +) = IncomingVerificationState( + step = step, + eventSink = eventSink, +) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt new file mode 100644 index 0000000000..3d6adf28cd --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt @@ -0,0 +1,232 @@ +/* + * 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.verifysession.impl.incoming + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +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.res.stringResource +import androidx.compose.ui.text.style.TextAlign +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.verifysession.impl.R +import io.element.android.features.verifysession.impl.incoming.IncomingVerificationState.Step +import io.element.android.features.verifysession.impl.incoming.ui.SessionDetailsView +import io.element.android.features.verifysession.impl.ui.VerificationBottomMenu +import io.element.android.features.verifysession.impl.ui.VerificationContentVerifying +import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage +import io.element.android.libraries.designsystem.components.BigIcon +import io.element.android.libraries.designsystem.components.PageTitle +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.InvisibleButton +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.TextButton +import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.matrix.api.verification.SessionVerificationData +import io.element.android.libraries.ui.strings.CommonStrings + +/** + * [Figma](https://www.figma.com/design/pDlJZGBsri47FNTXMnEdXB/Compound-Android-Templates?node-id=819-7324). + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun IncomingVerificationView( + state: IncomingVerificationState, + modifier: Modifier = Modifier, +) { + val step = state.step + + BackHandler { + state.eventSink(IncomingVerificationViewEvents.GoBack) + } + HeaderFooterPage( + modifier = modifier, + topBar = { + TopAppBar( + title = {}, + ) + }, + header = { + IncomingVerificationHeader(step = step) + }, + footer = { + IncomingVerificationBottomMenu( + state = state, + ) + } + ) { + IncomingVerificationContent( + step = step, + ) + } +} + +@Composable +private fun IncomingVerificationHeader(step: Step) { + val iconStyle = when (step) { + Step.Canceled -> BigIcon.Style.AlertSolid + is Step.Initial -> BigIcon.Style.Default(CompoundIcons.LockSolid()) + is Step.Verifying -> BigIcon.Style.Default(CompoundIcons.Reaction()) + Step.Completed -> BigIcon.Style.SuccessSolid + Step.Failure -> BigIcon.Style.AlertSolid + } + val titleTextId = when (step) { + Step.Canceled -> R.string.screen_session_verification_request_failure_title + is Step.Initial -> R.string.screen_session_verification_request_title + is Step.Verifying -> when (step.data) { + is SessionVerificationData.Decimals -> R.string.screen_session_verification_compare_numbers_title + is SessionVerificationData.Emojis -> R.string.screen_session_verification_compare_emojis_title + } + Step.Completed -> R.string.screen_session_verification_request_success_title + Step.Failure -> R.string.screen_session_verification_request_failure_title + } + val subtitleTextId = when (step) { + Step.Canceled -> R.string.screen_session_verification_request_failure_subtitle + is Step.Initial -> R.string.screen_session_verification_request_subtitle + is Step.Verifying -> when (step.data) { + is SessionVerificationData.Decimals -> R.string.screen_session_verification_compare_numbers_subtitle + is SessionVerificationData.Emojis -> R.string.screen_session_verification_compare_emojis_subtitle + } + Step.Completed -> R.string.screen_session_verification_request_success_subtitle + Step.Failure -> R.string.screen_session_verification_request_failure_subtitle + } + PageTitle( + iconStyle = iconStyle, + title = stringResource(id = titleTextId), + subtitle = stringResource(id = subtitleTextId) + ) +} + +@Composable +private fun IncomingVerificationContent( + step: Step, +) { + when (step) { + is Step.Initial -> ContentInitial(step) + is Step.Verifying -> VerificationContentVerifying(step.data) + else -> Unit + } +} + +@Composable +private fun ContentInitial( + initialIncoming: Step.Initial, +) { + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(24.dp), + ) { + SessionDetailsView( + deviceName = initialIncoming.deviceDisplayName, + deviceId = initialIncoming.deviceId, + signInFormattedTimestamp = initialIncoming.formattedSignInTime, + ) + Text( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(bottom = 16.dp), + text = stringResource(R.string.screen_session_verification_request_footer), + style = ElementTheme.typography.fontBodyMdMedium, + textAlign = TextAlign.Center, + ) + } +} + +@Composable +private fun IncomingVerificationBottomMenu( + state: IncomingVerificationState, +) { + val step = state.step + val eventSink = state.eventSink + + when (step) { + is Step.Initial -> { + if (step.isWaiting) { + VerificationBottomMenu { + Button( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.screen_identity_waiting_on_other_device), + onClick = {}, + enabled = false, + showProgress = true, + ) + InvisibleButton() + } + } else { + VerificationBottomMenu { + Button( + modifier = Modifier.fillMaxWidth(), + text = stringResource(CommonStrings.action_start), + onClick = { eventSink(IncomingVerificationViewEvents.StartVerification) }, + ) + TextButton( + modifier = Modifier.fillMaxWidth(), + text = stringResource(CommonStrings.action_ignore), + onClick = { eventSink(IncomingVerificationViewEvents.IgnoreVerification) }, + ) + } + } + } + is Step.Verifying -> { + if (step.isWaiting) { + VerificationBottomMenu { + Button( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.screen_session_verification_positive_button_verifying_ongoing), + onClick = {}, + enabled = false, + showProgress = true, + ) + InvisibleButton() + } + } else { + VerificationBottomMenu { + Button( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.screen_session_verification_they_match), + onClick = { eventSink(IncomingVerificationViewEvents.ConfirmVerification) }, + ) + TextButton( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.screen_session_verification_they_dont_match), + onClick = { eventSink(IncomingVerificationViewEvents.DeclineVerification) }, + ) + } + } + } + Step.Canceled, + is Step.Completed, + is Step.Failure -> { + VerificationBottomMenu { + Button( + modifier = Modifier.fillMaxWidth(), + text = stringResource(CommonStrings.action_done), + onClick = { eventSink(IncomingVerificationViewEvents.GoBack) }, + ) + } + } + } +} + +@PreviewsDayNight +@Composable +internal fun IncomingVerificationViewPreview(@PreviewParameter(IncomingVerificationStateProvider::class) state: IncomingVerificationState) = ElementPreview { + IncomingVerificationView( + state = state, + ) +} diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewEvents.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewEvents.kt new file mode 100644 index 0000000000..c1fef2ff88 --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewEvents.kt @@ -0,0 +1,16 @@ +/* + * 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.verifysession.impl.incoming + +sealed interface IncomingVerificationViewEvents { + data object GoBack : IncomingVerificationViewEvents + data object StartVerification : IncomingVerificationViewEvents + data object IgnoreVerification : IncomingVerificationViewEvents + data object ConfirmVerification : IncomingVerificationViewEvents + data object DeclineVerification : IncomingVerificationViewEvents +} diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/ui/SessionDetailsView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/ui/SessionDetailsView.kt new file mode 100644 index 0000000000..2a17502176 --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/ui/SessionDetailsView.kt @@ -0,0 +1,92 @@ +/* + * 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.verifysession.impl.incoming.ui + +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.features.verifysession.impl.R +import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom +import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize +import io.element.android.libraries.designsystem.atomic.molecules.TextWithLabelMolecule +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.matrix.api.core.DeviceId +import io.element.android.libraries.ui.strings.CommonStrings + +@Composable +fun SessionDetailsView( + deviceName: String, + deviceId: DeviceId, + signInFormattedTimestamp: String, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxWidth() + .border( + width = 1.dp, + color = ElementTheme.colors.borderDisabled, + shape = RoundedCornerShape(8.dp) + ) + .padding(24.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + RoundedIconAtom( + modifier = Modifier, + size = RoundedIconAtomSize.Big, + resourceId = CompoundDrawables.ic_compound_devices + ) + Text( + text = deviceName, + style = ElementTheme.typography.fontBodyMdMedium, + color = ElementTheme.colors.textPrimary, + ) + } + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + TextWithLabelMolecule( + label = stringResource(R.string.screen_session_verification_request_details_timestamp), + text = signInFormattedTimestamp, + modifier = Modifier.weight(2f), + ) + TextWithLabelMolecule( + label = stringResource(CommonStrings.common_device_id), + text = deviceId.value, + modifier = Modifier.weight(5f), + ) + } + } +} + +@PreviewsDayNight +@Composable +internal fun SessionDetailsViewPreview() = ElementPreview { + SessionDetailsView( + deviceName = "Element X Android", + deviceId = DeviceId("ILAKNDNASDLK"), + signInFormattedTimestamp = "12:34", + ) +} diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/DefaultVerifySessionEntryPoint.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/DefaultVerifySessionEntryPoint.kt similarity index 95% rename from features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/DefaultVerifySessionEntryPoint.kt rename to features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/DefaultVerifySessionEntryPoint.kt index def5c4c84c..4563c8db56 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/DefaultVerifySessionEntryPoint.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/DefaultVerifySessionEntryPoint.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.features.verifysession.impl +package io.element.android.features.verifysession.impl.outgoing import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionNode.kt similarity index 97% rename from features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt rename to features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionNode.kt index 3eb33b0c8d..cf4c7ae084 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionNode.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.features.verifysession.impl +package io.element.android.features.verifysession.impl.outgoing import android.app.Activity import androidx.compose.runtime.Composable diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenter.kt similarity index 70% rename from features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt rename to features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenter.kt index a8667217fe..97778b322b 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenter.kt @@ -7,7 +7,7 @@ @file:OptIn(ExperimentalCoroutinesApi::class) -package io.element.android.features.verifysession.impl +package io.element.android.features.verifysession.impl.outgoing import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -39,8 +39,9 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import io.element.android.features.verifysession.impl.VerifySelfSessionStateMachine.Event as StateMachineEvent -import io.element.android.features.verifysession.impl.VerifySelfSessionStateMachine.State as StateMachineState +import timber.log.Timber +import io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionStateMachine.Event as StateMachineEvent +import io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionStateMachine.State as StateMachineState class VerifySelfSessionPresenter @AssistedInject constructor( @Assisted private val showDeviceVerifiedScreen: Boolean, @@ -61,7 +62,7 @@ class VerifySelfSessionPresenter @AssistedInject constructor( val coroutineScope = rememberCoroutineScope() LaunchedEffect(Unit) { // Force reset, just in case the service was left in a broken state - sessionVerificationService.reset() + sessionVerificationService.reset(true) } val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState() val stateAndDispatch = stateMachine.rememberStateAndDispatch() @@ -70,13 +71,13 @@ class VerifySelfSessionPresenter @AssistedInject constructor( val signOutAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } - val verificationFlowStep by remember { + val step by remember { derivedStateOf { if (skipVerification) { - VerifySelfSessionState.VerificationStep.Skipped + VerifySelfSessionState.Step.Skipped } else { when (sessionVerifiedStatus) { - SessionVerifiedStatus.Unknown -> VerifySelfSessionState.VerificationStep.Loading + SessionVerifiedStatus.Unknown -> VerifySelfSessionState.Step.Loading SessionVerifiedStatus.NotVerified -> { stateAndDispatch.state.value.toVerificationStep( canEnterRecoveryKey = recoveryState == RecoveryState.INCOMPLETE @@ -85,10 +86,10 @@ class VerifySelfSessionPresenter @AssistedInject constructor( SessionVerifiedStatus.Verified -> { if (stateAndDispatch.state.value != StateMachineState.Initial || showDeviceVerifiedScreen) { // The user has verified the session, we need to show the success screen - VerifySelfSessionState.VerificationStep.Completed + VerifySelfSessionState.Step.Completed } else { // Automatic verification, which can happen on freshly created account, in this case, skip the screen - VerifySelfSessionState.VerificationStep.Skipped + VerifySelfSessionState.Step.Skipped } } } @@ -101,7 +102,9 @@ class VerifySelfSessionPresenter @AssistedInject constructor( } fun handleEvents(event: VerifySelfSessionViewEvents) { + Timber.d("Verification user action: ${event::class.simpleName}") when (event) { + VerifySelfSessionViewEvents.UseAnotherDevice -> stateAndDispatch.dispatchAction(StateMachineEvent.UseAnotherDevice) VerifySelfSessionViewEvents.RequestVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.RequestVerification) VerifySelfSessionViewEvents.StartSasVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.StartSasVerification) VerifySelfSessionViewEvents.ConfirmVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.AcceptChallenge) @@ -115,7 +118,7 @@ class VerifySelfSessionPresenter @AssistedInject constructor( } } return VerifySelfSessionState( - verificationFlowStep = verificationFlowStep, + step = step, signOutAction = signOutAction.value, displaySkipButton = buildMeta.isDebuggable, eventSink = ::handleEvents, @@ -124,27 +127,30 @@ class VerifySelfSessionPresenter @AssistedInject constructor( private fun StateMachineState?.toVerificationStep( canEnterRecoveryKey: Boolean - ): VerifySelfSessionState.VerificationStep = + ): VerifySelfSessionState.Step = when (val machineState = this) { StateMachineState.Initial, null -> { - VerifySelfSessionState.VerificationStep.Initial( + VerifySelfSessionState.Step.Initial( canEnterRecoveryKey = canEnterRecoveryKey, isLastDevice = encryptionService.isLastDevice.value ) } + VerifySelfSessionStateMachine.State.UseAnotherDevice -> { + VerifySelfSessionState.Step.UseAnotherDevice + } StateMachineState.RequestingVerification, StateMachineState.StartingSasVerification, StateMachineState.SasVerificationStarted, StateMachineState.Canceling -> { - VerifySelfSessionState.VerificationStep.AwaitingOtherDeviceResponse + VerifySelfSessionState.Step.AwaitingOtherDeviceResponse } StateMachineState.VerificationRequestAccepted -> { - VerifySelfSessionState.VerificationStep.Ready + VerifySelfSessionState.Step.Ready } StateMachineState.Canceled -> { - VerifySelfSessionState.VerificationStep.Canceled + VerifySelfSessionState.Step.Canceled } is StateMachineState.Verifying -> { @@ -152,38 +158,41 @@ class VerifySelfSessionPresenter @AssistedInject constructor( is StateMachineState.Verifying.Replying -> AsyncData.Loading() else -> AsyncData.Uninitialized } - VerifySelfSessionState.VerificationStep.Verifying(machineState.data, async) + VerifySelfSessionState.Step.Verifying(machineState.data, async) } StateMachineState.Completed -> { - VerifySelfSessionState.VerificationStep.Completed + VerifySelfSessionState.Step.Completed } } private fun CoroutineScope.observeVerificationService() { - sessionVerificationService.verificationFlowState.onEach { verificationAttemptState -> - when (verificationAttemptState) { - VerificationFlowState.Initial -> stateMachine.dispatch(VerifySelfSessionStateMachine.Event.Reset) - VerificationFlowState.AcceptedVerificationRequest -> { - stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidAcceptVerificationRequest) - } - VerificationFlowState.StartedSasVerification -> { - stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidStartSasVerification) - } - is VerificationFlowState.ReceivedVerificationData -> { - stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidReceiveChallenge(verificationAttemptState.data)) - } - VerificationFlowState.Finished -> { - stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidAcceptChallenge) - } - VerificationFlowState.Canceled -> { - stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidCancel) - } - VerificationFlowState.Failed -> { - stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidFail) + sessionVerificationService.verificationFlowState + .onEach { Timber.d("Verification flow state: ${it::class.simpleName}") } + .onEach { verificationAttemptState -> + when (verificationAttemptState) { + VerificationFlowState.Initial -> stateMachine.dispatch(VerifySelfSessionStateMachine.Event.Reset) + VerificationFlowState.DidAcceptVerificationRequest -> { + stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidAcceptVerificationRequest) + } + VerificationFlowState.DidStartSasVerification -> { + stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidStartSasVerification) + } + is VerificationFlowState.DidReceiveVerificationData -> { + stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidReceiveChallenge(verificationAttemptState.data)) + } + VerificationFlowState.DidFinish -> { + stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidAcceptChallenge) + } + VerificationFlowState.DidCancel -> { + stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidCancel) + } + VerificationFlowState.DidFail -> { + stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidFail) + } } } - }.launchIn(this) + .launchIn(this) } private fun CoroutineScope.signOut(signOutAction: MutableState>) = launch { diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionState.kt similarity index 60% rename from features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt rename to features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionState.kt index 81062d57c7..32664b347f 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionState.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.features.verifysession.impl +package io.element.android.features.verifysession.impl.outgoing import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable @@ -15,22 +15,23 @@ import io.element.android.libraries.matrix.api.verification.SessionVerificationD @Immutable data class VerifySelfSessionState( - val verificationFlowStep: VerificationStep, + val step: Step, val signOutAction: AsyncAction, val displaySkipButton: Boolean, val eventSink: (VerifySelfSessionViewEvents) -> Unit, ) { @Stable - sealed interface VerificationStep { - data object Loading : VerificationStep + sealed interface Step { + data object Loading : Step // FIXME canEnterRecoveryKey value is never read. - data class Initial(val canEnterRecoveryKey: Boolean, val isLastDevice: Boolean = false) : VerificationStep - data object Canceled : VerificationStep - data object AwaitingOtherDeviceResponse : VerificationStep - data object Ready : VerificationStep - data class Verifying(val data: SessionVerificationData, val state: AsyncData) : VerificationStep - data object Completed : VerificationStep - data object Skipped : VerificationStep + data class Initial(val canEnterRecoveryKey: Boolean, val isLastDevice: Boolean = false) : Step + data object UseAnotherDevice : Step + data object Canceled : Step + data object AwaitingOtherDeviceResponse : Step + data object Ready : Step + data class Verifying(val data: SessionVerificationData, val state: AsyncData) : Step + data object Completed : Step + data object Skipped : Step } } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateMachine.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt similarity index 84% rename from features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateMachine.kt rename to features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt index 55c3d7e94f..a67cc4d331 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateMachine.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt @@ -8,9 +8,11 @@ @file:Suppress("WildcardImport") @file:OptIn(ExperimentalCoroutinesApi::class) -package io.element.android.features.verifysession.impl +package io.element.android.features.verifysession.impl.outgoing import com.freeletics.flowredux.dsl.FlowReduxStateMachine +import io.element.android.features.verifysession.impl.util.andLogStateChange +import io.element.android.features.verifysession.impl.util.logReceivedEvents import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.matrix.api.encryption.EncryptionService @@ -36,11 +38,13 @@ class VerifySelfSessionStateMachine @Inject constructor( init { spec { inState { - on { _: Event.RequestVerification, state -> - state.override { State.RequestingVerification } + on { _: Event.UseAnotherDevice, state -> + state.override { State.UseAnotherDevice.andLogStateChange() } } - on { _: Event.StartSasVerification, state -> - state.override { State.StartingSasVerification } + } + inState { + on { _: Event.RequestVerification, state -> + state.override { State.RequestingVerification.andLogStateChange() } } } inState { @@ -48,7 +52,7 @@ class VerifySelfSessionStateMachine @Inject constructor( sessionVerificationService.requestVerification() } on { _: Event.DidAcceptVerificationRequest, state -> - state.override { State.VerificationRequestAccepted } + state.override { State.VerificationRequestAccepted.andLogStateChange() } } } inState { @@ -58,28 +62,25 @@ class VerifySelfSessionStateMachine @Inject constructor( } inState { on { _: Event.StartSasVerification, state -> - state.override { State.StartingSasVerification } + state.override { State.StartingSasVerification.andLogStateChange() } } } inState { - on { _: Event.RequestVerification, state -> - state.override { State.RequestingVerification } - } on { _: Event.Reset, state -> - state.override { State.Initial } + state.override { State.Initial.andLogStateChange() } } } inState { on { event: Event.DidReceiveChallenge, state -> - state.override { State.Verifying.ChallengeReceived(event.data) } + state.override { State.Verifying.ChallengeReceived(event.data).andLogStateChange() } } } inState { on { _: Event.AcceptChallenge, state -> - state.override { State.Verifying.Replying(state.snapshot.data, accept = true) } + state.override { State.Verifying.Replying(state.snapshot.data, accept = true).andLogStateChange() } } on { _: Event.DeclineChallenge, state -> - state.override { State.Verifying.Replying(state.snapshot.data, accept = false) } + state.override { State.Verifying.Replying(state.snapshot.data, accept = false).andLogStateChange() } } } inState { @@ -100,7 +101,7 @@ class VerifySelfSessionStateMachine @Inject constructor( .first() } } - state.override { State.Completed } + state.override { State.Completed.andLogStateChange() } } } inState { @@ -110,27 +111,29 @@ class VerifySelfSessionStateMachine @Inject constructor( } } inState { + logReceivedEvents() on { _: Event.DidStartSasVerification, state: MachineState -> - state.override { State.SasVerificationStarted } + state.override { State.SasVerificationStarted.andLogStateChange() } } on { _: Event.Cancel, state: MachineState -> when (state.snapshot) { State.Initial, State.Completed, State.Canceled -> state.noChange() + State.UseAnotherDevice -> state.override { State.Initial.andLogStateChange() } // For some reason `cancelVerification` is not calling its delegate `didCancel` method so we don't pass from // `Canceling` state to `Canceled` automatically anymore else -> { sessionVerificationService.cancelVerification() - state.override { State.Canceled } + state.override { State.Canceled.andLogStateChange() } } } } on { _: Event.DidCancel, state: MachineState -> - state.override { State.Canceled } + state.override { State.Canceled.andLogStateChange() } } on { _: Event.DidFail, state: MachineState -> when (state.snapshot) { - is State.RequestingVerification -> state.override { State.Initial } - else -> state.override { State.Canceled } + is State.RequestingVerification -> state.override { State.Initial.andLogStateChange() } + else -> state.override { State.Canceled.andLogStateChange() } } } } @@ -141,6 +144,9 @@ class VerifySelfSessionStateMachine @Inject constructor( /** The initial state, before verification started. */ data object Initial : State + /** Let the user know that they need to get ready on their other session. */ + data object UseAnotherDevice : State + /** Waiting for verification acceptance. */ data object RequestingVerification : State @@ -172,6 +178,9 @@ class VerifySelfSessionStateMachine @Inject constructor( } sealed interface Event { + /** User wants to use another session. */ + data object UseAnotherDevice : Event + /** Request verification. */ data object RequestVerification : Event diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateProvider.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateProvider.kt new file mode 100644 index 0000000000..b9de96aae4 --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateProvider.kt @@ -0,0 +1,76 @@ +/* + * 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.verifysession.impl.outgoing + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionState.Step +import io.element.android.features.verifysession.impl.ui.aDecimalsSessionVerificationData +import io.element.android.features.verifysession.impl.ui.aEmojisSessionVerificationData +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.architecture.AsyncData + +open class VerifySelfSessionStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aVerifySelfSessionState(displaySkipButton = true), + aVerifySelfSessionState( + step = Step.AwaitingOtherDeviceResponse + ), + aVerifySelfSessionState( + step = Step.Verifying(aEmojisSessionVerificationData(), AsyncData.Uninitialized) + ), + aVerifySelfSessionState( + step = Step.Verifying(aEmojisSessionVerificationData(), AsyncData.Loading()) + ), + aVerifySelfSessionState( + step = Step.Canceled + ), + aVerifySelfSessionState( + step = Step.Ready + ), + aVerifySelfSessionState( + step = Step.Verifying(aDecimalsSessionVerificationData(), AsyncData.Uninitialized) + ), + aVerifySelfSessionState( + step = Step.Initial(canEnterRecoveryKey = true) + ), + aVerifySelfSessionState( + step = Step.Initial(canEnterRecoveryKey = true, isLastDevice = true) + ), + aVerifySelfSessionState( + step = Step.Completed, + displaySkipButton = true, + ), + aVerifySelfSessionState( + signOutAction = AsyncAction.Loading, + displaySkipButton = true, + ), + aVerifySelfSessionState( + step = Step.Loading + ), + aVerifySelfSessionState( + step = Step.Skipped + ), + aVerifySelfSessionState( + step = Step.UseAnotherDevice + ), + // Add other state here + ) +} + +internal fun aVerifySelfSessionState( + step: Step = Step.Initial(canEnterRecoveryKey = false), + signOutAction: AsyncAction = AsyncAction.Uninitialized, + displaySkipButton: Boolean = false, + eventSink: (VerifySelfSessionViewEvents) -> Unit = {}, +) = VerifySelfSessionState( + step = step, + displaySkipButton = displaySkipButton, + eventSink = eventSink, + signOutAction = signOutAction, +) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt similarity index 55% rename from features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt rename to features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt index 5b0c9105ad..e1e22eda33 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt @@ -5,25 +5,17 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.features.verifysession.impl +package io.element.android.features.verifysession.impl.outgoing import androidx.activity.compose.BackHandler -import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer 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.foundation.layout.size -import androidx.compose.foundation.layout.widthIn import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -31,18 +23,17 @@ import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow 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.verifysession.impl.emoji.toEmojiResource +import io.element.android.features.verifysession.impl.R +import io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionState.Step +import io.element.android.features.verifysession.impl.ui.VerificationBottomMenu +import io.element.android.features.verifysession.impl.ui.VerificationContentVerifying import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.components.PageTitle @@ -51,14 +42,13 @@ 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.CircularProgressIndicator +import io.element.android.libraries.designsystem.theme.components.InvisibleButton 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.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.api.verification.SessionVerificationData -import io.element.android.libraries.matrix.api.verification.VerificationEmoji import io.element.android.libraries.ui.strings.CommonStrings -import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep as FlowStep @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -71,12 +61,15 @@ fun VerifySelfSessionView( onSuccessLogout: (String?) -> Unit, modifier: Modifier = Modifier, ) { + val step = state.step fun cancelOrResetFlow() { - when (state.verificationFlowStep) { - is FlowStep.Canceled -> state.eventSink(VerifySelfSessionViewEvents.Reset) - is FlowStep.AwaitingOtherDeviceResponse, FlowStep.Ready -> state.eventSink(VerifySelfSessionViewEvents.Cancel) - is FlowStep.Verifying -> { - if (!state.verificationFlowStep.state.isLoading()) { + when (step) { + is Step.Canceled -> state.eventSink(VerifySelfSessionViewEvents.Reset) + is Step.AwaitingOtherDeviceResponse, + Step.UseAnotherDevice, + Step.Ready -> state.eventSink(VerifySelfSessionViewEvents.Cancel) + is Step.Verifying -> { + if (!step.state.isLoading()) { state.eventSink(VerifySelfSessionViewEvents.DeclineVerification) } } @@ -85,18 +78,17 @@ fun VerifySelfSessionView( } val latestOnFinish by rememberUpdatedState(newValue = onFinish) - LaunchedEffect(state.verificationFlowStep, latestOnFinish) { - if (state.verificationFlowStep is FlowStep.Skipped) { + LaunchedEffect(step, latestOnFinish) { + if (step is Step.Skipped) { latestOnFinish() } } BackHandler { cancelOrResetFlow() } - val verificationFlowStep = state.verificationFlowStep - if (state.verificationFlowStep is FlowStep.Loading || - state.verificationFlowStep is FlowStep.Skipped) { + if (step is Step.Loading || + step is Step.Skipped) { // Just display a loader in this case, to avoid UI glitch. Box( modifier = Modifier.fillMaxSize(), @@ -111,7 +103,7 @@ fun VerifySelfSessionView( TopAppBar( title = {}, actions = { - if (state.verificationFlowStep !is FlowStep.Completed && + if (step !is Step.Completed && state.displaySkipButton && LocalInspectionMode.current.not()) { TextButton( @@ -119,7 +111,7 @@ fun VerifySelfSessionView( onClick = { state.eventSink(VerifySelfSessionViewEvents.SkipVerification) } ) } - if (state.verificationFlowStep is FlowStep.Initial) { + if (step is Step.Initial) { TextButton( text = stringResource(CommonStrings.action_signout), onClick = { state.eventSink(VerifySelfSessionViewEvents.SignOut) } @@ -129,10 +121,10 @@ fun VerifySelfSessionView( ) }, header = { - HeaderContent(verificationFlowStep = verificationFlowStep) + VerifySelfSessionHeader(step = step) }, footer = { - BottomMenu( + VerifySelfSessionBottomMenu( screenState = state, onCancelClick = ::cancelOrResetFlow, onEnterRecoveryKey = onEnterRecoveryKey, @@ -141,8 +133,8 @@ fun VerifySelfSessionView( ) } ) { - Content( - flowState = verificationFlowStep, + VerifySelfSessionContent( + flowState = step, onLearnMoreClick = onLearnMoreClick, ) } @@ -165,38 +157,44 @@ fun VerifySelfSessionView( } @Composable -private fun HeaderContent(verificationFlowStep: FlowStep) { - val iconStyle = when (verificationFlowStep) { - VerifySelfSessionState.VerificationStep.Loading -> error("Should not happen") - is FlowStep.Initial, FlowStep.AwaitingOtherDeviceResponse -> BigIcon.Style.Default(CompoundIcons.LockSolid()) - FlowStep.Canceled -> BigIcon.Style.AlertSolid - FlowStep.Ready, is FlowStep.Verifying -> BigIcon.Style.Default(CompoundIcons.Reaction()) - FlowStep.Completed -> BigIcon.Style.SuccessSolid - is FlowStep.Skipped -> return +private fun VerifySelfSessionHeader(step: Step) { + val iconStyle = when (step) { + Step.Loading -> error("Should not happen") + is Step.Initial -> BigIcon.Style.Default(CompoundIcons.LockSolid()) + Step.UseAnotherDevice -> BigIcon.Style.Default(CompoundIcons.Devices()) + Step.AwaitingOtherDeviceResponse -> BigIcon.Style.Default(CompoundIcons.Devices()) + Step.Canceled -> BigIcon.Style.AlertSolid + Step.Ready, is Step.Verifying -> BigIcon.Style.Default(CompoundIcons.Reaction()) + Step.Completed -> BigIcon.Style.SuccessSolid + is Step.Skipped -> return } - val titleTextId = when (verificationFlowStep) { - VerifySelfSessionState.VerificationStep.Loading -> error("Should not happen") - is FlowStep.Initial, FlowStep.AwaitingOtherDeviceResponse -> R.string.screen_identity_confirmation_title - FlowStep.Canceled -> CommonStrings.common_verification_cancelled - FlowStep.Ready -> R.string.screen_session_verification_compare_emojis_title - FlowStep.Completed -> R.string.screen_identity_confirmed_title - is FlowStep.Verifying -> when (verificationFlowStep.data) { + val titleTextId = when (step) { + Step.Loading -> error("Should not happen") + is Step.Initial -> R.string.screen_identity_confirmation_title + Step.UseAnotherDevice -> R.string.screen_session_verification_use_another_device_title + Step.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_another_device_title + Step.Canceled -> CommonStrings.common_verification_failed + Step.Ready -> R.string.screen_session_verification_compare_emojis_title + Step.Completed -> R.string.screen_identity_confirmed_title + is Step.Verifying -> when (step.data) { is SessionVerificationData.Decimals -> R.string.screen_session_verification_compare_numbers_title is SessionVerificationData.Emojis -> R.string.screen_session_verification_compare_emojis_title } - is FlowStep.Skipped -> return + is Step.Skipped -> return } - val subtitleTextId = when (verificationFlowStep) { - VerifySelfSessionState.VerificationStep.Loading -> error("Should not happen") - is FlowStep.Initial, FlowStep.AwaitingOtherDeviceResponse -> R.string.screen_identity_confirmation_subtitle - FlowStep.Canceled -> R.string.screen_session_verification_cancelled_subtitle - FlowStep.Ready -> R.string.screen_session_verification_ready_subtitle - FlowStep.Completed -> R.string.screen_identity_confirmed_subtitle - is FlowStep.Verifying -> when (verificationFlowStep.data) { + val subtitleTextId = when (step) { + Step.Loading -> error("Should not happen") + is Step.Initial -> R.string.screen_identity_confirmation_subtitle + Step.UseAnotherDevice -> R.string.screen_session_verification_use_another_device_subtitle + Step.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_another_device_subtitle + Step.Canceled -> R.string.screen_session_verification_failed_subtitle + Step.Ready -> R.string.screen_session_verification_ready_subtitle + Step.Completed -> R.string.screen_identity_confirmed_subtitle + is Step.Verifying -> when (step.data) { is SessionVerificationData.Decimals -> R.string.screen_session_verification_compare_numbers_subtitle is SessionVerificationData.Emojis -> R.string.screen_session_verification_compare_emojis_subtitle } - is FlowStep.Skipped -> return + is Step.Skipped -> return } PageTitle( @@ -207,16 +205,16 @@ private fun HeaderContent(verificationFlowStep: FlowStep) { } @Composable -private fun Content( - flowState: FlowStep, +private fun VerifySelfSessionContent( + flowState: Step, onLearnMoreClick: () -> Unit, ) { when (flowState) { - is VerifySelfSessionState.VerificationStep.Initial -> { + is Step.Initial -> { ContentInitial(onLearnMoreClick) } - is FlowStep.Verifying -> { - ContentVerifying(flowState) + is Step.Verifying -> { + VerificationContentVerifying(flowState.data) } else -> Unit } @@ -241,98 +239,34 @@ private fun ContentInitial( } @Composable -private fun ContentVerifying(verificationFlowStep: FlowStep.Verifying) { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - when (verificationFlowStep.data) { - is SessionVerificationData.Decimals -> { - val text = verificationFlowStep.data.decimals.joinToString(separator = " - ") { it.toString() } - Text( - modifier = Modifier.fillMaxWidth(), - text = text, - style = ElementTheme.typography.fontHeadingLgBold, - color = MaterialTheme.colorScheme.primary, - textAlign = TextAlign.Center, - ) - } - is SessionVerificationData.Emojis -> { - // We want each row to have up to 4 emojis - val rows = verificationFlowStep.data.emojis.chunked(4) - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(40.dp), - ) { - rows.forEach { emojis -> - Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) { - for (emoji in emojis) { - EmojiItemView(emoji = emoji, modifier = Modifier.widthIn(max = 60.dp)) - } - } - } - } - } - } - } -} - -@Composable -private fun EmojiItemView(emoji: VerificationEmoji, modifier: Modifier = Modifier) { - val emojiResource = emoji.number.toEmojiResource() - Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier) { - Image( - modifier = Modifier.size(48.dp), - painter = painterResource(id = emojiResource.drawableRes), - contentDescription = null, - ) - Spacer(modifier = Modifier.height(16.dp)) - Text( - text = stringResource(id = emojiResource.nameRes), - style = ElementTheme.typography.fontBodyMdRegular, - color = MaterialTheme.colorScheme.secondary, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - } -} - -@Composable -private fun BottomMenu( +private fun VerifySelfSessionBottomMenu( screenState: VerifySelfSessionState, onEnterRecoveryKey: () -> Unit, onResetKey: () -> Unit, onCancelClick: () -> Unit, onContinueClick: () -> Unit, ) { - val verificationViewState = screenState.verificationFlowStep + val verificationViewState = screenState.step val eventSink = screenState.eventSink - val isVerifying = (verificationViewState as? FlowStep.Verifying)?.state is AsyncData.Loading + val isVerifying = (verificationViewState as? Step.Verifying)?.state is AsyncData.Loading when (verificationViewState) { - VerifySelfSessionState.VerificationStep.Loading -> error("Should not happen") - is FlowStep.Initial -> { - BottomMenu { - if (verificationViewState.isLastDevice) { - Button( - modifier = Modifier.fillMaxWidth(), - text = stringResource(R.string.screen_session_verification_enter_recovery_key), - onClick = onEnterRecoveryKey, - ) - } else { + Step.Loading -> error("Should not happen") + is Step.Initial -> { + VerificationBottomMenu { + if (verificationViewState.isLastDevice.not()) { Button( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.screen_identity_use_another_device), - onClick = { eventSink(VerifySelfSessionViewEvents.RequestVerification) }, - ) - Button( - modifier = Modifier.fillMaxWidth(), - text = stringResource(R.string.screen_session_verification_enter_recovery_key), - onClick = onEnterRecoveryKey, + onClick = { eventSink(VerifySelfSessionViewEvents.UseAnotherDevice) }, ) } - // This option should always be displayed + Button( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.screen_session_verification_enter_recovery_key), + onClick = onEnterRecoveryKey, + ) OutlinedButton( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.screen_identity_confirmation_cannot_confirm), @@ -340,22 +274,28 @@ private fun BottomMenu( ) } } - is FlowStep.Canceled -> { - BottomMenu { + is Step.UseAnotherDevice -> { + VerificationBottomMenu { Button( modifier = Modifier.fillMaxWidth(), - text = stringResource(R.string.screen_session_verification_positive_button_canceled), + text = stringResource(CommonStrings.action_start_verification), onClick = { eventSink(VerifySelfSessionViewEvents.RequestVerification) }, ) - TextButton( + InvisibleButton() + } + } + is Step.Canceled -> { + VerificationBottomMenu { + Button( modifier = Modifier.fillMaxWidth(), - text = stringResource(CommonStrings.action_cancel), + text = stringResource(CommonStrings.action_done), onClick = onCancelClick, ) + InvisibleButton() } } - is FlowStep.Ready -> { - BottomMenu { + is Step.Ready -> { + VerificationBottomMenu { Button( modifier = Modifier.fillMaxWidth(), text = stringResource(CommonStrings.action_start), @@ -368,66 +308,58 @@ private fun BottomMenu( ) } } - is FlowStep.AwaitingOtherDeviceResponse -> { - BottomMenu { + is Step.AwaitingOtherDeviceResponse -> { + VerificationBottomMenu { Button( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.screen_identity_waiting_on_other_device), onClick = {}, showProgress = true, + enabled = false, ) - // Placeholder so the 1st button keeps its vertical position - Spacer(modifier = Modifier.height(40.dp)) + InvisibleButton() } } - is FlowStep.Verifying -> { + is Step.Verifying -> { val positiveButtonTitle = if (isVerifying) { stringResource(R.string.screen_session_verification_positive_button_verifying_ongoing) } else { stringResource(R.string.screen_session_verification_they_match) } - BottomMenu { + VerificationBottomMenu { Button( modifier = Modifier.fillMaxWidth(), text = positiveButtonTitle, showProgress = isVerifying, + enabled = !isVerifying, onClick = { if (!isVerifying) { eventSink(VerifySelfSessionViewEvents.ConfirmVerification) } }, ) - TextButton( - modifier = Modifier.fillMaxWidth(), - text = stringResource(R.string.screen_session_verification_they_dont_match), - onClick = { eventSink(VerifySelfSessionViewEvents.DeclineVerification) }, - ) + if (isVerifying) { + InvisibleButton() + } else { + TextButton( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.screen_session_verification_they_dont_match), + onClick = { eventSink(VerifySelfSessionViewEvents.DeclineVerification) }, + ) + } } } - is FlowStep.Completed -> { - BottomMenu { + is Step.Completed -> { + VerificationBottomMenu { Button( modifier = Modifier.fillMaxWidth(), text = stringResource(CommonStrings.action_continue), onClick = onContinueClick, ) - // Placeholder so the 1st button keeps its vertical position - Spacer(modifier = Modifier.height(48.dp)) + InvisibleButton() } } - is FlowStep.Skipped -> return - } -} - -@Composable -private fun BottomMenu( - modifier: Modifier = Modifier, - buttons: @Composable ColumnScope.() -> Unit, -) { - ButtonColumnMolecule( - modifier = modifier.padding(bottom = 16.dp) - ) { - buttons() + is Step.Skipped -> return } } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewEvents.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewEvents.kt similarity index 84% rename from features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewEvents.kt rename to features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewEvents.kt index 1f0c235842..b4af38f780 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewEvents.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewEvents.kt @@ -5,9 +5,10 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.features.verifysession.impl +package io.element.android.features.verifysession.impl.outgoing sealed interface VerifySelfSessionViewEvents { + data object UseAnotherDevice : VerifySelfSessionViewEvents data object RequestVerification : VerifySelfSessionViewEvents data object StartSasVerification : VerifySelfSessionViewEvents data object ConfirmVerification : VerifySelfSessionViewEvents diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/Common.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/Common.kt new file mode 100644 index 0000000000..345663fa11 --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/Common.kt @@ -0,0 +1,33 @@ +/* + * 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.verifysession.impl.ui + +import io.element.android.libraries.matrix.api.verification.SessionVerificationData +import io.element.android.libraries.matrix.api.verification.VerificationEmoji + +internal fun aEmojisSessionVerificationData( + emojiList: List = aVerificationEmojiList(), +): SessionVerificationData { + return SessionVerificationData.Emojis(emojiList) +} + +internal fun aDecimalsSessionVerificationData( + decimals: List = listOf(123, 456, 789), +): SessionVerificationData { + return SessionVerificationData.Decimals(decimals) +} + +private fun aVerificationEmojiList() = listOf( + VerificationEmoji(number = 27, emoji = "🍕", description = "Pizza"), + VerificationEmoji(number = 54, emoji = "🚀", description = "Rocket"), + VerificationEmoji(number = 54, emoji = "🚀", description = "Rocket"), + VerificationEmoji(number = 42, emoji = "📕", description = "Book"), + VerificationEmoji(number = 48, emoji = "🔨", description = "Hammer"), + VerificationEmoji(number = 48, emoji = "🔨", description = "Hammer"), + VerificationEmoji(number = 63, emoji = "📌", description = "Pin"), +) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationBottomMenu.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationBottomMenu.kt new file mode 100644 index 0000000000..33ab0fa378 --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationBottomMenu.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.verifysession.impl.ui + +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule + +@Composable +internal fun VerificationBottomMenu( + modifier: Modifier = Modifier, + buttons: @Composable ColumnScope.() -> Unit, +) { + ButtonColumnMolecule( + modifier = modifier.padding(bottom = 16.dp) + ) { + buttons() + } +} diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationContentVerifying.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationContentVerifying.kt new file mode 100644 index 0000000000..7f988a0d3d --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationContentVerifying.kt @@ -0,0 +1,94 @@ +/* + * 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.verifysession.impl.ui + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.features.verifysession.impl.emoji.toEmojiResource +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.matrix.api.verification.SessionVerificationData +import io.element.android.libraries.matrix.api.verification.VerificationEmoji + +@Composable +internal fun VerificationContentVerifying( + data: SessionVerificationData, + modifier: Modifier = Modifier, +) { + Box( + modifier = modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + when (data) { + is SessionVerificationData.Decimals -> { + val text = data.decimals.joinToString(separator = " - ") { it.toString() } + Text( + modifier = Modifier.fillMaxWidth(), + text = text, + style = ElementTheme.typography.fontHeadingLgBold, + color = MaterialTheme.colorScheme.primary, + textAlign = TextAlign.Center, + ) + } + is SessionVerificationData.Emojis -> { + // We want each row to have up to 4 emojis + val rows = data.emojis.chunked(4) + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(40.dp), + ) { + rows.forEach { emojis -> + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) { + for (emoji in emojis) { + EmojiItemView(emoji = emoji, modifier = Modifier.widthIn(max = 60.dp)) + } + } + } + } + } + } + } +} + +@Composable +private fun EmojiItemView(emoji: VerificationEmoji, modifier: Modifier = Modifier) { + val emojiResource = emoji.number.toEmojiResource() + Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier) { + Image( + modifier = Modifier.size(48.dp), + painter = painterResource(id = emojiResource.drawableRes), + contentDescription = null, + ) + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = stringResource(id = emojiResource.nameRes), + style = ElementTheme.typography.fontBodyMdRegular, + color = MaterialTheme.colorScheme.secondary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } +} diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/util/StateMachineUtil.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/util/StateMachineUtil.kt new file mode 100644 index 0000000000..096a0be9ea --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/util/StateMachineUtil.kt @@ -0,0 +1,25 @@ +/* + * 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.verifysession.impl.util + +import com.freeletics.flowredux.dsl.InStateBuilderBlock +import kotlinx.coroutines.ExperimentalCoroutinesApi +import timber.log.Timber +import com.freeletics.flowredux.dsl.State as MachineState + +internal fun T.andLogStateChange() = also { + Timber.w("Verification: state machine state moved to [${this::class.simpleName}]") +} + +@OptIn(ExperimentalCoroutinesApi::class) +inline fun InStateBuilderBlock.logReceivedEvents() { + on { event: Event, state: MachineState -> + Timber.w("Verification in state [${state.snapshot::class.simpleName}] receiving event [${event::class.simpleName}]") + state.noChange() + } +} diff --git a/features/verifysession/impl/src/main/res/values-fr/translations.xml b/features/verifysession/impl/src/main/res/values-fr/translations.xml index 4632373a92..9fbfbb7f76 100644 --- a/features/verifysession/impl/src/main/res/values-fr/translations.xml +++ b/features/verifysession/impl/src/main/res/values-fr/translations.xml @@ -17,6 +17,7 @@ "Comparez les nombres" "Votre nouvelle session est désormais vérifiée. Elle a accès à vos messages chiffrés et les autres utilisateurs la verront identifiée comme fiable." "Utiliser la clé de récupération" + "Soit la demande a expiré, soit elle a été refusée, soit il y a eu une non-concordance de vérification." "Prouvez qu’il s’agit bien de vous pour accéder à l’historique de vos messages chiffrés." "Ouvrir une session existante" "Réessayer la vérification" @@ -24,8 +25,13 @@ "En attente de correspondance" "Comparer un groupe unique d’Emojis." "Comparez les emoji uniques en veillant à ce qu’ils apparaissent dans le même ordre." + "Connecté" + "Soit la demande a expiré, soit elle a été refusée, soit il y a eu une non-concordance de vérification." + "Échec de la vérification" "Continuez uniquement si c’est vous qui avez commencé cette vérification." "Vérifiez l’autre appareil pour sécuriser l’historique de vos messages." + "Vous pouvez désormais lire ou envoyer des messages en toute sécurité sur votre autre appareil." + "Appareil vérifié" "Vérification demandée" "Ils ne correspondent pas" "Ils correspondent" diff --git a/features/verifysession/impl/src/main/res/values-hu/translations.xml b/features/verifysession/impl/src/main/res/values-hu/translations.xml index 10e0559bca..2f25c0006e 100644 --- a/features/verifysession/impl/src/main/res/values-hu/translations.xml +++ b/features/verifysession/impl/src/main/res/values-hu/translations.xml @@ -26,8 +26,12 @@ "Egyedi emodzsik összehasonlítása." "Hasonlítsa össze az egyedi emodzsikat, meggyőződve arról, hogy azonos a sorrendjük." "Bejelentkezve" + "A kérés túllépte az időkorlátot, el lett utasítva, vagy ellenőrzési eltérés történt." + "Az ellenőrzés sikertelen" "Csak akkor folytassa, ha Ön kezdeményezte ezt az ellenőrzést." "Az üzenetelőzmények biztonságának megőrzése érdekében ellenőrizze a másik eszközt." + "Mostantól biztonságosan olvashat vagy küldhet üzeneteket a másik eszközén." + "Eszköz ellenőrizve" "Ellenőrzés kérve" "Nem egyeznek" "Megegyeznek" diff --git a/features/verifysession/impl/src/main/res/values-in/translations.xml b/features/verifysession/impl/src/main/res/values-in/translations.xml index 961cc20cb3..fb4df0e8a3 100644 --- a/features/verifysession/impl/src/main/res/values-in/translations.xml +++ b/features/verifysession/impl/src/main/res/values-in/translations.xml @@ -17,6 +17,7 @@ "Bandingkan angka" "Sesi baru Anda sekarang diverifikasi. Ini memiliki akses ke pesan terenkripsi Anda, dan pengguna lain akan melihatnya sebagai tepercaya." "Masukkan kunci pemulihan" + "Entah permintaan habis waktu, permintaan ditolak, atau ada ketidakcocokan verifikasi." "Buktikan bahwa ini memang Anda untuk mengakses riwayat pesan terenkripsi Anda." "Buka sesi yang sudah ada" "Verifikasi ulang" @@ -24,6 +25,14 @@ "Menunggu untuk mencocokkan" "Bandingkan satu set emoji yang unik." "Bandingkan emoji unik, dan pastikan emoji tersebut muncul dalam urutan yang sama." + "Sudah masuk" + "Entah permintaan habis waktu, permintaan ditolak, atau ada ketidakcocokan verifikasi." + "Verifikasi gagal" + "Lanjutkan hanya jika Anda memulai verifikasi ini." + "Verifikasi perangkat lain untuk menjaga riwayat pesan Anda tetap aman." + "Sekarang Anda dapat membaca atau mengirim pesan dengan aman di perangkat Anda yang lain." + "Perangkat diverifikasi" + "Verifikasi diminta" "Mereka tidak cocok" "Mereka cocok" "Terima permintaan untuk memulai proses verifikasi di sesi Anda yang lain untuk melanjutkan." 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 bebac551e2..20e588e2db 100644 --- a/features/verifysession/impl/src/main/res/values-nl/translations.xml +++ b/features/verifysession/impl/src/main/res/values-nl/translations.xml @@ -1,10 +1,14 @@ + "Kan ik dit niet bevestigen?" "Maak een nieuwe herstelsleutel" "Verifieer dit apparaat om beveiligde berichten in te stellen." "Bevestig dat jij het bent" + "Gebruik een ander apparaat" + "Gebruik de herstelsleutel" "Nu kun je veilig berichten lezen of verzenden, en iedereen met wie je chat kan dit apparaat ook vertrouwen." "Apparaat geverifieerd" + "Gebruik een ander apparaat" "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." diff --git a/features/verifysession/impl/src/main/res/values-pl/translations.xml b/features/verifysession/impl/src/main/res/values-pl/translations.xml index 41d950f2f1..2ee77b1f7d 100644 --- a/features/verifysession/impl/src/main/res/values-pl/translations.xml +++ b/features/verifysession/impl/src/main/res/values-pl/translations.xml @@ -24,6 +24,12 @@ "Oczekiwanie na dopasowanie" "Porównaj unikalny zestaw emoji." "Porównaj unikalne emoji, upewniając się, że pojawiły się w tej samej kolejności." + "Zalogowano" + "Weryfikacja nie powiodła się" + "Kontynuuj tylko, jeśli to Ty zainicjowałeś tę weryfikację." + "Zweryfikuj drugie urządzenie, aby zabezpieczyć historię wiadomości." + "Urządzenie zweryfikowane" + "Zażądano weryfikacji" "Nie pasują do siebie" "Pasują do siebie" "Zaakceptuj prośbę o rozpoczęcie procesu weryfikacji w innej sesji, aby kontynuować." diff --git a/features/verifysession/impl/src/main/res/values-pt/translations.xml b/features/verifysession/impl/src/main/res/values-pt/translations.xml index d5bdaaf719..66d02f41e2 100644 --- a/features/verifysession/impl/src/main/res/values-pt/translations.xml +++ b/features/verifysession/impl/src/main/res/values-pt/translations.xml @@ -17,6 +17,7 @@ "Comparar números" "A tua nova sessão está agora verificada, pelo que tem acesso às tuas mensagens cifradas e os outros utilizadores vão vê-la como de confiança." "Insere a chave de recuperação" + "O pedido expirou, o pedido foi recusado ou houve um erro de verificação." "Prova que és tu para acederes ao teu histórico de mensagens cifradas." "Abrir sessão existente" "Repetir verificação" @@ -25,8 +26,12 @@ "Compara um conjunto único de emojis." "Compara os emojis únicos, certificando-te de que aparecem pela mesma ordem." "Sessão iniciada" + "O pedido expirou, o pedido foi recusado ou houve um erro de verificação." + "A verificação falhou" "Continue apenas se tiver iniciado esta verificação." "Verifique o outro dispositivo para manter o histórico de mensagens seguro." + "Agora podes ler ou enviar mensagens de forma segura no teu outro dispositivo." + "Dispositivo verificado" "Verificação solicitada" "Não correspondem" "Correspondem" diff --git a/features/verifysession/impl/src/main/res/values-ru/translations.xml b/features/verifysession/impl/src/main/res/values-ru/translations.xml index cc08320dfe..3a6e1a976e 100644 --- a/features/verifysession/impl/src/main/res/values-ru/translations.xml +++ b/features/verifysession/impl/src/main/res/values-ru/translations.xml @@ -17,7 +17,7 @@ "Сравните числа" "Ваш новый сеанс подтвержден. У него есть доступ к вашим зашифрованным сообщениям, и другие пользователи увидят его как доверенное." "Введите ключ восстановления" - "Запрос был отклонен, так как время ожидания запроса истекло, либо произошла ошибка при проверке." + "Время ожидания подтверждения истекло, запрос был отклонён, или при подтверждении произошло несоответствие." "Чтобы получить доступ к зашифрованной истории сообщений, докажите, что это вы." "Открыть существующий сеанс" "Повторить подтверждение" @@ -35,7 +35,7 @@ "Запрошено подтверждение" "Они не совпадают" "Они совпадают" - "Для продолжения работы примите запрос на запуск процесса проверки в другом сеансе." + "Чтобы продолжить, примите запрос на запуск процесса подтверждения в другом сеансе." "Ожидание принятия запроса" "Выполняется выход…" diff --git a/features/verifysession/impl/src/main/res/values/localazy.xml b/features/verifysession/impl/src/main/res/values/localazy.xml index f67a2024b9..db18590cbe 100644 --- a/features/verifysession/impl/src/main/res/values/localazy.xml +++ b/features/verifysession/impl/src/main/res/values/localazy.xml @@ -35,6 +35,10 @@ "Verification requested" "They don’t match" "They match" + "Make sure you have the app open in the other device before starting verification from here." + "Open the app on another verified device" + "You should see a popup on the other device. Start the verification from there now." + "Start verification on the other device" "Accept the request to start the verification process in your other session to continue." "Waiting to accept request" "Signing out…" diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenterTest.kt new file mode 100644 index 0000000000..773b7b390b --- /dev/null +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenterTest.kt @@ -0,0 +1,292 @@ +/* + * 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.verifysession.impl.incoming + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.verifysession.impl.ui.aEmojisSessionVerificationData +import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter +import io.element.android.libraries.dateformatter.test.A_FORMATTED_DATE +import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter +import io.element.android.libraries.matrix.api.core.FlowId +import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails +import io.element.android.libraries.matrix.api.verification.SessionVerificationService +import io.element.android.libraries.matrix.api.verification.VerificationFlowState +import io.element.android.libraries.matrix.test.A_DEVICE_ID +import io.element.android.libraries.matrix.test.A_TIMESTAMP +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService +import io.element.android.tests.testutils.WarmUpRule +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 io.element.android.tests.testutils.test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test + +@ExperimentalCoroutinesApi +class IncomingVerificationPresenterTest { + @get:Rule + val warmUpRule = WarmUpRule() + + @Test + fun `present - nominal case - incoming verification successful`() = runTest { + val acknowledgeVerificationRequestLambda = lambdaRecorder { _ -> } + val acceptVerificationRequestLambda = lambdaRecorder { } + val approveVerificationLambda = lambdaRecorder { } + val resetLambda = lambdaRecorder { } + val fakeSessionVerificationService = FakeSessionVerificationService( + acknowledgeVerificationRequestLambda = acknowledgeVerificationRequestLambda, + acceptVerificationRequestLambda = acceptVerificationRequestLambda, + approveVerificationLambda = approveVerificationLambda, + resetLambda = resetLambda, + ) + createPresenter( + service = fakeSessionVerificationService, + ).test { + val initialState = awaitItem() + assertThat(initialState.step).isEqualTo( + IncomingVerificationState.Step.Initial( + deviceDisplayName = "a device name", + deviceId = A_DEVICE_ID, + formattedSignInTime = A_FORMATTED_DATE, + isWaiting = false, + ) + ) + resetLambda.assertions().isCalledOnce().with(value(false)) + acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(aSessionVerificationRequestDetails)) + acceptVerificationRequestLambda.assertions().isNeverCalled() + // User accept the incoming verification + initialState.eventSink(IncomingVerificationViewEvents.StartVerification) + skipItems(1) + val initialWaitingState = awaitItem() + assertThat((initialWaitingState.step as IncomingVerificationState.Step.Initial).isWaiting).isTrue() + advanceUntilIdle() + acceptVerificationRequestLambda.assertions().isCalledOnce() + // Remote sent the data + fakeSessionVerificationService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest) + fakeSessionVerificationService.emitVerificationFlowState(VerificationFlowState.DidStartSasVerification) + fakeSessionVerificationService.emitVerificationFlowState( + VerificationFlowState.DidReceiveVerificationData( + data = aEmojisSessionVerificationData() + ) + ) + val emojiState = awaitItem() + assertThat(emojiState.step).isEqualTo( + IncomingVerificationState.Step.Verifying( + data = aEmojisSessionVerificationData(), + isWaiting = false + ) + ) + // User claims that the emoji matches + emojiState.eventSink(IncomingVerificationViewEvents.ConfirmVerification) + val emojiWaitingItem = awaitItem() + assertThat((emojiWaitingItem.step as IncomingVerificationState.Step.Verifying).isWaiting).isTrue() + approveVerificationLambda.assertions().isCalledOnce() + // Remote confirm that the emojis match + fakeSessionVerificationService.emitVerificationFlowState( + VerificationFlowState.DidFinish + ) + val finalItem = awaitItem() + assertThat(finalItem.step).isEqualTo(IncomingVerificationState.Step.Completed) + } + } + + @Test + fun `present - emoji not matching case - incoming verification failure`() = runTest { + val acknowledgeVerificationRequestLambda = lambdaRecorder { _ -> } + val acceptVerificationRequestLambda = lambdaRecorder { } + val declineVerificationLambda = lambdaRecorder { } + val resetLambda = lambdaRecorder { } + val fakeSessionVerificationService = FakeSessionVerificationService( + acknowledgeVerificationRequestLambda = acknowledgeVerificationRequestLambda, + acceptVerificationRequestLambda = acceptVerificationRequestLambda, + declineVerificationLambda = declineVerificationLambda, + resetLambda = resetLambda, + ) + createPresenter( + service = fakeSessionVerificationService, + ).test { + val initialState = awaitItem() + assertThat(initialState.step).isEqualTo( + IncomingVerificationState.Step.Initial( + deviceDisplayName = "a device name", + deviceId = A_DEVICE_ID, + formattedSignInTime = A_FORMATTED_DATE, + isWaiting = false, + ) + ) + resetLambda.assertions().isCalledOnce().with(value(false)) + acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(aSessionVerificationRequestDetails)) + acceptVerificationRequestLambda.assertions().isNeverCalled() + // User accept the incoming verification + initialState.eventSink(IncomingVerificationViewEvents.StartVerification) + skipItems(1) + val initialWaitingState = awaitItem() + assertThat((initialWaitingState.step as IncomingVerificationState.Step.Initial).isWaiting).isTrue() + advanceUntilIdle() + acceptVerificationRequestLambda.assertions().isCalledOnce() + // Remote sent the data + fakeSessionVerificationService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest) + fakeSessionVerificationService.emitVerificationFlowState(VerificationFlowState.DidStartSasVerification) + fakeSessionVerificationService.emitVerificationFlowState( + VerificationFlowState.DidReceiveVerificationData( + data = aEmojisSessionVerificationData() + ) + ) + val emojiState = awaitItem() + // User claims that the emojis do not match + emojiState.eventSink(IncomingVerificationViewEvents.DeclineVerification) + val emojiWaitingItem = awaitItem() + assertThat((emojiWaitingItem.step as IncomingVerificationState.Step.Verifying).isWaiting).isTrue() + declineVerificationLambda.assertions().isCalledOnce() + // Remote confirm that there is a failure + fakeSessionVerificationService.emitVerificationFlowState( + VerificationFlowState.DidFail + ) + val finalItem = awaitItem() + assertThat(finalItem.step).isEqualTo(IncomingVerificationState.Step.Failure) + } + } + + @Test + fun `present - incoming verification is remotely canceled`() = runTest { + val acknowledgeVerificationRequestLambda = lambdaRecorder { _ -> } + val acceptVerificationRequestLambda = lambdaRecorder { } + val declineVerificationLambda = lambdaRecorder { } + val resetLambda = lambdaRecorder { } + val onFinishLambda = lambdaRecorder { } + val fakeSessionVerificationService = FakeSessionVerificationService( + acknowledgeVerificationRequestLambda = acknowledgeVerificationRequestLambda, + acceptVerificationRequestLambda = acceptVerificationRequestLambda, + declineVerificationLambda = declineVerificationLambda, + resetLambda = resetLambda, + ) + createPresenter( + service = fakeSessionVerificationService, + navigator = IncomingVerificationNavigator(onFinishLambda), + ).test { + val initialState = awaitItem() + assertThat(initialState.step).isEqualTo( + IncomingVerificationState.Step.Initial( + deviceDisplayName = "a device name", + deviceId = A_DEVICE_ID, + formattedSignInTime = A_FORMATTED_DATE, + isWaiting = false, + ) + ) + // Remote cancel the verification request + fakeSessionVerificationService.emitVerificationFlowState(VerificationFlowState.DidCancel) + // The screen is dismissed + skipItems(2) + onFinishLambda.assertions().isCalledOnce() + } + } + + @Test + fun `present - user goes back when comparing emoji - incoming verification failure`() = runTest { + val acknowledgeVerificationRequestLambda = lambdaRecorder { _ -> } + val acceptVerificationRequestLambda = lambdaRecorder { } + val declineVerificationLambda = lambdaRecorder { } + val resetLambda = lambdaRecorder { } + val fakeSessionVerificationService = FakeSessionVerificationService( + acknowledgeVerificationRequestLambda = acknowledgeVerificationRequestLambda, + acceptVerificationRequestLambda = acceptVerificationRequestLambda, + declineVerificationLambda = declineVerificationLambda, + resetLambda = resetLambda, + ) + createPresenter( + service = fakeSessionVerificationService, + ).test { + val initialState = awaitItem() + assertThat(initialState.step).isEqualTo( + IncomingVerificationState.Step.Initial( + deviceDisplayName = "a device name", + deviceId = A_DEVICE_ID, + formattedSignInTime = A_FORMATTED_DATE, + isWaiting = false, + ) + ) + resetLambda.assertions().isCalledOnce().with(value(false)) + acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(aSessionVerificationRequestDetails)) + acceptVerificationRequestLambda.assertions().isNeverCalled() + // User accept the incoming verification + initialState.eventSink(IncomingVerificationViewEvents.StartVerification) + skipItems(1) + val initialWaitingState = awaitItem() + assertThat((initialWaitingState.step as IncomingVerificationState.Step.Initial).isWaiting).isTrue() + advanceUntilIdle() + acceptVerificationRequestLambda.assertions().isCalledOnce() + // Remote sent the data + fakeSessionVerificationService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest) + fakeSessionVerificationService.emitVerificationFlowState(VerificationFlowState.DidStartSasVerification) + fakeSessionVerificationService.emitVerificationFlowState( + VerificationFlowState.DidReceiveVerificationData( + data = aEmojisSessionVerificationData() + ) + ) + val emojiState = awaitItem() + // User goes back + emojiState.eventSink(IncomingVerificationViewEvents.GoBack) + val emojiWaitingItem = awaitItem() + assertThat((emojiWaitingItem.step as IncomingVerificationState.Step.Verifying).isWaiting).isTrue() + declineVerificationLambda.assertions().isCalledOnce() + // Remote confirm that there is a failure + fakeSessionVerificationService.emitVerificationFlowState( + VerificationFlowState.DidFail + ) + val finalItem = awaitItem() + assertThat(finalItem.step).isEqualTo(IncomingVerificationState.Step.Failure) + } + } + + @Test + fun `present - user ignores incoming request`() = runTest { + val acknowledgeVerificationRequestLambda = lambdaRecorder { _ -> } + val acceptVerificationRequestLambda = lambdaRecorder { } + val resetLambda = lambdaRecorder { } + val fakeSessionVerificationService = FakeSessionVerificationService( + acknowledgeVerificationRequestLambda = acknowledgeVerificationRequestLambda, + acceptVerificationRequestLambda = acceptVerificationRequestLambda, + resetLambda = resetLambda, + ) + val navigatorLambda = lambdaRecorder { } + createPresenter( + service = fakeSessionVerificationService, + navigator = IncomingVerificationNavigator(navigatorLambda), + ).test { + val initialState = awaitItem() + initialState.eventSink(IncomingVerificationViewEvents.IgnoreVerification) + skipItems(1) + navigatorLambda.assertions().isCalledOnce() + } + } + + private val aSessionVerificationRequestDetails = SessionVerificationRequestDetails( + senderId = A_USER_ID, + flowId = FlowId("flowId"), + deviceId = A_DEVICE_ID, + displayName = "a device name", + firstSeenTimestamp = A_TIMESTAMP, + ) + + private fun createPresenter( + sessionVerificationRequestDetails: SessionVerificationRequestDetails = aSessionVerificationRequestDetails, + navigator: IncomingVerificationNavigator = IncomingVerificationNavigator { lambdaError() }, + service: SessionVerificationService = FakeSessionVerificationService(), + dateFormatter: LastMessageTimestampFormatter = FakeLastMessageTimestampFormatter(A_FORMATTED_DATE), + ) = IncomingVerificationPresenter( + sessionVerificationRequestDetails = sessionVerificationRequestDetails, + navigator = navigator, + sessionVerificationService = service, + stateMachine = IncomingVerificationStateMachine(service), + dateFormatter = dateFormatter, + ) +} diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewTest.kt new file mode 100644 index 0000000000..7517486c00 --- /dev/null +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewTest.kt @@ -0,0 +1,217 @@ +/* + * 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.verifysession.impl.incoming + +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.features.verifysession.impl.R +import io.element.android.features.verifysession.impl.ui.aEmojisSessionVerificationData +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.clickOn +import io.element.android.tests.testutils.pressBackKey +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class IncomingVerificationViewTest { + @get:Rule val rule = createAndroidComposeRule() + + // region step Initial + @Test + fun `back key pressed - ignore the verification`() { + val eventsRecorder = EventsRecorder() + rule.setIncomingVerificationView( + anIncomingVerificationState( + step = aStepInitial(), + eventSink = eventsRecorder + ), + ) + rule.pressBackKey() + eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + } + + @Test + fun `ignore incoming verification emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setIncomingVerificationView( + anIncomingVerificationState( + step = aStepInitial(), + eventSink = eventsRecorder + ), + ) + rule.clickOn(CommonStrings.action_ignore) + eventsRecorder.assertSingle(IncomingVerificationViewEvents.IgnoreVerification) + } + + @Test + fun `start incoming verification emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setIncomingVerificationView( + anIncomingVerificationState( + step = aStepInitial(), + eventSink = eventsRecorder + ), + ) + rule.clickOn(CommonStrings.action_start) + eventsRecorder.assertSingle(IncomingVerificationViewEvents.StartVerification) + } + + @Test + fun `back key pressed - when awaiting response cancels the verification`() { + val eventsRecorder = EventsRecorder() + rule.setIncomingVerificationView( + anIncomingVerificationState( + step = aStepInitial( + isWaiting = true, + ), + eventSink = eventsRecorder + ), + ) + rule.pressBackKey() + eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + } + // endregion step Initial + + // region step Verifying + @Test + fun `back key pressed - when ready to verify cancels the verification`() { + val eventsRecorder = EventsRecorder() + rule.setIncomingVerificationView( + anIncomingVerificationState( + step = IncomingVerificationState.Step.Verifying( + data = aEmojisSessionVerificationData(), + isWaiting = false, + ), + eventSink = eventsRecorder + ), + ) + rule.pressBackKey() + eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + } + + @Test + fun `back key pressed - when verifying and loading emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setIncomingVerificationView( + anIncomingVerificationState( + step = IncomingVerificationState.Step.Verifying( + data = aEmojisSessionVerificationData(), + isWaiting = true, + ), + eventSink = eventsRecorder + ), + ) + rule.pressBackKey() + eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + } + + @Test + fun `clicking on they do not match emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setIncomingVerificationView( + anIncomingVerificationState( + step = IncomingVerificationState.Step.Verifying( + data = aEmojisSessionVerificationData(), + isWaiting = false, + ), + eventSink = eventsRecorder + ), + ) + rule.clickOn(R.string.screen_session_verification_they_dont_match) + eventsRecorder.assertSingle(IncomingVerificationViewEvents.DeclineVerification) + } + + @Test + fun `clicking on they match emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setIncomingVerificationView( + anIncomingVerificationState( + step = IncomingVerificationState.Step.Verifying( + data = aEmojisSessionVerificationData(), + isWaiting = false, + ), + eventSink = eventsRecorder + ), + ) + rule.clickOn(R.string.screen_session_verification_they_match) + eventsRecorder.assertSingle(IncomingVerificationViewEvents.ConfirmVerification) + } + // endregion + + // region step Failure + @Test + fun `back key pressed - when failure resets the flow`() { + val eventsRecorder = EventsRecorder() + rule.setIncomingVerificationView( + anIncomingVerificationState( + step = IncomingVerificationState.Step.Failure, + eventSink = eventsRecorder + ), + ) + rule.pressBackKey() + eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + } + + @Test + fun `click on done - when failure resets the flow`() { + val eventsRecorder = EventsRecorder() + rule.setIncomingVerificationView( + anIncomingVerificationState( + step = IncomingVerificationState.Step.Failure, + eventSink = eventsRecorder + ), + ) + rule.clickOn(CommonStrings.action_done) + eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + } + + // endregion + + // region step Completed + @Test + fun `back key pressed - on Completed step emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setIncomingVerificationView( + anIncomingVerificationState( + step = IncomingVerificationState.Step.Completed, + eventSink = eventsRecorder + ), + ) + rule.pressBackKey() + eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + } + + @Test + fun `when flow is completed and the user clicks on the done button, the expected event is emitted`() { + val eventsRecorder = EventsRecorder() + rule.setIncomingVerificationView( + anIncomingVerificationState( + step = IncomingVerificationState.Step.Completed, + eventSink = eventsRecorder + ), + ) + rule.clickOn(CommonStrings.action_done) + eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + } + // endregion + + private fun AndroidComposeTestRule.setIncomingVerificationView( + state: IncomingVerificationState, + ) { + setContent { + IncomingVerificationView( + state = state, + ) + } + } +} diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt similarity index 52% rename from features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTest.kt rename to features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt index 188c895f5c..d8fe2f671c 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt @@ -5,21 +5,19 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.features.verifysession.impl +package io.element.android.features.verifysession.impl.outgoing -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow import app.cash.turbine.ReceiveTurbine -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.logout.api.LogoutUseCase import io.element.android.features.logout.test.FakeLogoutUseCase -import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep +import io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionState.Step import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.verification.SessionVerificationData +import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.api.verification.VerificationEmoji @@ -29,8 +27,10 @@ import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore import io.element.android.tests.testutils.WarmUpRule +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 io.element.android.tests.testutils.test import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -43,12 +43,12 @@ class VerifySelfSessionPresenterTest { @Test fun `present - Initial state is received`() = runTest { - val presenter = createVerifySelfSessionPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val presenter = createVerifySelfSessionPresenter( + service = unverifiedSessionService(), + ) + presenter.test { awaitItem().run { - assertThat(verificationFlowStep).isEqualTo(VerificationStep.Initial(false)) + assertThat(step).isEqualTo(Step.Initial(false)) assertThat(displaySkipButton).isTrue() } } @@ -57,82 +57,66 @@ class VerifySelfSessionPresenterTest { @Test fun `present - hides skip verification button on non-debuggable builds`() = runTest { val buildMeta = aBuildMeta(isDebuggable = false) - val presenter = createVerifySelfSessionPresenter(buildMeta = buildMeta) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val presenter = createVerifySelfSessionPresenter( + service = unverifiedSessionService(), + buildMeta = buildMeta, + ) + presenter.test { assertThat(awaitItem().displaySkipButton).isFalse() } } @Test fun `present - Initial state is received, can use recovery key`() = runTest { + val resetLambda = lambdaRecorder { } val presenter = createVerifySelfSessionPresenter( + service = unverifiedSessionService( + resetLambda = resetLambda + ), encryptionService = FakeEncryptionService().apply { emitRecoveryState(RecoveryState.INCOMPLETE) } ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial(true)) + presenter.test { + assertThat(awaitItem().step).isEqualTo(Step.Initial(true)) + resetLambda.assertions().isCalledOnce().with(value(true)) } } @Test fun `present - Initial state is received, can use recovery key and is last device`() = runTest { val presenter = createVerifySelfSessionPresenter( + service = unverifiedSessionService(), encryptionService = FakeEncryptionService().apply { emitIsLastDevice(true) emitRecoveryState(RecoveryState.INCOMPLETE) } ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial(canEnterRecoveryKey = true, isLastDevice = true)) + presenter.test { + assertThat(awaitItem().step).isEqualTo(Step.Initial(canEnterRecoveryKey = true, isLastDevice = true)) } } @Test fun `present - Handles requestVerification`() = runTest { - val service = unverifiedSessionService() + val service = unverifiedSessionService( + requestVerificationLambda = { }, + startVerificationLambda = { }, + ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { requestVerificationAndAwaitVerifyingState(service) } } @Test - fun `present - Handles startSasVerification`() = runTest { - val service = unverifiedSessionService() - val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() - assertThat(initialState.verificationFlowStep).isEqualTo(VerificationStep.Initial(false)) - val eventSink = initialState.eventSink - eventSink(VerifySelfSessionViewEvents.StartSasVerification) - // Await for other device response: - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.AwaitingOtherDeviceResponse) - // ChallengeReceived: - service.triggerReceiveVerificationData(SessionVerificationData.Emojis(emptyList())) - val verifyingState = awaitItem() - assertThat(verifyingState.verificationFlowStep).isInstanceOf(VerificationStep.Verifying::class.java) - } - } - - @Test - fun `present - Cancelation on initial state does nothing`() = runTest { - val presenter = createVerifySelfSessionPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + fun `present - Cancellation on initial state does nothing`() = runTest { + val presenter = createVerifySelfSessionPresenter( + service = unverifiedSessionService(), + ) + presenter.test { val initialState = awaitItem() - assertThat(initialState.verificationFlowStep).isEqualTo(VerificationStep.Initial(false)) + assertThat(initialState.step).isEqualTo(Step.Initial(false)) val eventSink = initialState.eventSink eventSink(VerifySelfSessionViewEvents.Cancel) expectNoEvents() @@ -141,92 +125,81 @@ class VerifySelfSessionPresenterTest { @Test fun `present - A failure when verifying cancels it`() = runTest { - val service = unverifiedSessionService() + val service = unverifiedSessionService( + requestVerificationLambda = { }, + startVerificationLambda = { }, + approveVerificationLambda = { }, + ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) - service.shouldFail = true state.eventSink(VerifySelfSessionViewEvents.ConfirmVerification) // Cancelling - assertThat(awaitItem().verificationFlowStep).isInstanceOf(VerificationStep.Verifying::class.java) + assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java) + service.emitVerificationFlowState(VerificationFlowState.DidFail) // Cancelled - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Canceled) + assertThat(awaitItem().step).isEqualTo(Step.Canceled) } } @Test fun `present - A fail when requesting verification resets the state to the initial one`() = runTest { - val service = unverifiedSessionService() + val service = unverifiedSessionService( + requestVerificationLambda = { }, + ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - service.shouldFail = true + presenter.test { + awaitItem().eventSink(VerifySelfSessionViewEvents.UseAnotherDevice) awaitItem().eventSink(VerifySelfSessionViewEvents.RequestVerification) - service.shouldFail = false - assertThat(awaitItem().verificationFlowStep).isInstanceOf(VerificationStep.AwaitingOtherDeviceResponse::class.java) - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial(false)) + service.emitVerificationFlowState(VerificationFlowState.DidFail) + assertThat(awaitItem().step).isInstanceOf(Step.AwaitingOtherDeviceResponse::class.java) + assertThat(awaitItem().step).isEqualTo(Step.Initial(false)) } } @Test fun `present - Canceling the flow once it's verifying cancels it`() = runTest { - val service = unverifiedSessionService() + val service = unverifiedSessionService( + requestVerificationLambda = { }, + startVerificationLambda = { }, + cancelVerificationLambda = { }, + ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) state.eventSink(VerifySelfSessionViewEvents.Cancel) - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Canceled) + assertThat(awaitItem().step).isEqualTo(Step.Canceled) } } @Test fun `present - When verifying, if we receive another challenge we ignore it`() = runTest { - val service = unverifiedSessionService() + val service = unverifiedSessionService( + requestVerificationLambda = { }, + startVerificationLambda = { }, + ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { requestVerificationAndAwaitVerifyingState(service) - service.givenVerificationFlowState(VerificationFlowState.ReceivedVerificationData(SessionVerificationData.Emojis(emptyList()))) + service.emitVerificationFlowState(VerificationFlowState.DidReceiveVerificationData(SessionVerificationData.Emojis(emptyList()))) ensureAllEventsConsumed() } } @Test - fun `present - Restart after cancelation returns to requesting verification`() = runTest { - val service = unverifiedSessionService() - val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val state = requestVerificationAndAwaitVerifyingState(service) - service.givenVerificationFlowState(VerificationFlowState.Canceled) - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Canceled) - state.eventSink(VerifySelfSessionViewEvents.RequestVerification) - // Went back to requesting verification - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.AwaitingOtherDeviceResponse) - cancelAndIgnoreRemainingEvents() - } - } - - @Test - fun `present - Go back after cancelation returns to initial state`() = runTest { - val service = unverifiedSessionService() + fun `present - Go back after cancellation returns to initial state`() = runTest { + val service = unverifiedSessionService( + requestVerificationLambda = { }, + startVerificationLambda = { }, + ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) - service.givenVerificationFlowState(VerificationFlowState.Canceled) - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Canceled) + service.emitVerificationFlowState(VerificationFlowState.DidCancel) + assertThat(awaitItem().step).isEqualTo(Step.Canceled) state.eventSink(VerifySelfSessionViewEvents.Reset) // Went back to initial state - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial(false)) + assertThat(awaitItem().step).isEqualTo(Step.Initial(false)) cancelAndIgnoreRemainingEvents() } } @@ -236,110 +209,117 @@ class VerifySelfSessionPresenterTest { val emojis = listOf( VerificationEmoji(number = 30, emoji = "😀", description = "Smiley") ) - val service = unverifiedSessionService() + val service = unverifiedSessionService( + requestVerificationLambda = { }, + startVerificationLambda = { }, + approveVerificationLambda = { }, + ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val state = requestVerificationAndAwaitVerifyingState( service, SessionVerificationData.Emojis(emojis) ) state.eventSink(VerifySelfSessionViewEvents.ConfirmVerification) - assertThat(awaitItem().verificationFlowStep).isEqualTo( - VerificationStep.Verifying( + assertThat(awaitItem().step).isEqualTo( + Step.Verifying( SessionVerificationData.Emojis(emojis), AsyncData.Loading(), ) ) - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Completed) + service.emitVerificationFlowState(VerificationFlowState.DidFinish) + assertThat(awaitItem().step).isEqualTo(Step.Completed) } } @Test fun `present - When verification is declined, the flow is canceled`() = runTest { - val service = unverifiedSessionService() + val service = unverifiedSessionService( + requestVerificationLambda = { }, + startVerificationLambda = { }, + declineVerificationLambda = { }, + ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) state.eventSink(VerifySelfSessionViewEvents.DeclineVerification) - assertThat(awaitItem().verificationFlowStep).isEqualTo( - VerificationStep.Verifying( + assertThat(awaitItem().step).isEqualTo( + Step.Verifying( SessionVerificationData.Emojis(emptyList()), AsyncData.Loading(), ) ) - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Canceled) + service.emitVerificationFlowState(VerificationFlowState.DidCancel) + assertThat(awaitItem().step).isEqualTo(Step.Canceled) } } @Test fun `present - Skip event skips the flow`() = runTest { - val service = unverifiedSessionService() + val service = unverifiedSessionService( + requestVerificationLambda = { }, + startVerificationLambda = { }, + ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) state.eventSink(VerifySelfSessionViewEvents.SkipVerification) - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Skipped) + assertThat(awaitItem().step).isEqualTo(Step.Skipped) } } @Test fun `present - When verification is done using recovery key, the flow is completed`() = runTest { - val service = FakeSessionVerificationService().apply { - givenNeedsSessionVerification(false) - givenVerifiedStatus(SessionVerifiedStatus.Verified) - givenVerificationFlowState(VerificationFlowState.Finished) + val service = FakeSessionVerificationService( + resetLambda = { }, + ).apply { + emitNeedsSessionVerification(false) + emitVerifiedStatus(SessionVerifiedStatus.Verified) + emitVerificationFlowState(VerificationFlowState.DidFinish) } val presenter = createVerifySelfSessionPresenter( service = service, showDeviceVerifiedScreen = true, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Completed) + presenter.test { + assertThat(awaitItem().step).isEqualTo(Step.Completed) } } @Test fun `present - When verification is not needed, the flow is skipped`() = runTest { - val service = FakeSessionVerificationService().apply { - givenNeedsSessionVerification(false) - givenVerifiedStatus(SessionVerifiedStatus.Verified) - givenVerificationFlowState(VerificationFlowState.Finished) + val service = FakeSessionVerificationService( + resetLambda = { }, + ).apply { + emitNeedsSessionVerification(false) + emitVerifiedStatus(SessionVerifiedStatus.Verified) + emitVerificationFlowState(VerificationFlowState.DidFinish) } val presenter = createVerifySelfSessionPresenter( service = service, showDeviceVerifiedScreen = false, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { skipItems(1) - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Skipped) + assertThat(awaitItem().step).isEqualTo(Step.Skipped) } } @Test fun `present - When user request to sign out, the sign out use case is invoked`() = runTest { - val service = FakeSessionVerificationService().apply { - givenNeedsSessionVerification(false) - givenVerifiedStatus(SessionVerifiedStatus.Verified) - givenVerificationFlowState(VerificationFlowState.Finished) + val service = FakeSessionVerificationService( + resetLambda = { }, + ).apply { + emitNeedsSessionVerification(false) + emitVerifiedStatus(SessionVerifiedStatus.Verified) + emitVerificationFlowState(VerificationFlowState.DidFinish) } val signOutLambda = lambdaRecorder { "aUrl" } val presenter = createVerifySelfSessionPresenter( service, logoutUseCase = FakeLogoutUseCase(signOutLambda) ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { skipItems(1) val initialItem = awaitItem() initialItem.eventSink(VerifySelfSessionViewEvents.SignOut) @@ -356,33 +336,56 @@ class VerifySelfSessionPresenterTest { sessionVerificationData: SessionVerificationData = SessionVerificationData.Emojis(emptyList()), ): VerifySelfSessionState { var state = awaitItem() - assertThat(state.verificationFlowStep).isEqualTo(VerificationStep.Initial(false)) + assertThat(state.step).isEqualTo(Step.Initial(false)) + state.eventSink(VerifySelfSessionViewEvents.UseAnotherDevice) + state = awaitItem() + assertThat(state.step).isEqualTo(Step.UseAnotherDevice) state.eventSink(VerifySelfSessionViewEvents.RequestVerification) // Await for other device response: + fakeService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest) state = awaitItem() - assertThat(state.verificationFlowStep).isEqualTo(VerificationStep.AwaitingOtherDeviceResponse) + assertThat(state.step).isEqualTo(Step.AwaitingOtherDeviceResponse) // Await for the state to be Ready state = awaitItem() - assertThat(state.verificationFlowStep).isEqualTo(VerificationStep.Ready) + assertThat(state.step).isEqualTo(Step.Ready) state.eventSink(VerifySelfSessionViewEvents.StartSasVerification) // Await for other device response (again): + fakeService.emitVerificationFlowState(VerificationFlowState.DidStartSasVerification) state = awaitItem() - assertThat(state.verificationFlowStep).isEqualTo(VerificationStep.AwaitingOtherDeviceResponse) - fakeService.triggerReceiveVerificationData(sessionVerificationData) + assertThat(state.step).isEqualTo(Step.AwaitingOtherDeviceResponse) // Finally, ChallengeReceived: + fakeService.emitVerificationFlowState(VerificationFlowState.DidReceiveVerificationData(sessionVerificationData)) state = awaitItem() - assertThat(state.verificationFlowStep).isInstanceOf(VerificationStep.Verifying::class.java) + assertThat(state.step).isInstanceOf(Step.Verifying::class.java) return state } - private fun unverifiedSessionService(): FakeSessionVerificationService { - return FakeSessionVerificationService().apply { - givenVerifiedStatus(SessionVerifiedStatus.NotVerified) + private suspend fun unverifiedSessionService( + requestVerificationLambda: () -> Unit = { lambdaError() }, + cancelVerificationLambda: () -> Unit = { lambdaError() }, + approveVerificationLambda: () -> Unit = { lambdaError() }, + declineVerificationLambda: () -> Unit = { lambdaError() }, + startVerificationLambda: () -> Unit = { lambdaError() }, + resetLambda: (Boolean) -> Unit = { }, + acknowledgeVerificationRequestLambda: (SessionVerificationRequestDetails) -> Unit = { lambdaError() }, + acceptVerificationRequestLambda: () -> Unit = { lambdaError() }, + ): FakeSessionVerificationService { + return FakeSessionVerificationService( + requestVerificationLambda = requestVerificationLambda, + cancelVerificationLambda = cancelVerificationLambda, + approveVerificationLambda = approveVerificationLambda, + declineVerificationLambda = declineVerificationLambda, + startVerificationLambda = startVerificationLambda, + resetLambda = resetLambda, + acknowledgeVerificationRequestLambda = acknowledgeVerificationRequestLambda, + acceptVerificationRequestLambda = acceptVerificationRequestLambda, + ).apply { + emitVerifiedStatus(SessionVerifiedStatus.NotVerified) } } private fun createVerifySelfSessionPresenter( - service: SessionVerificationService = unverifiedSessionService(), + service: SessionVerificationService, encryptionService: EncryptionService = FakeEncryptionService(), buildMeta: BuildMeta = aBuildMeta(), sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(), diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewTest.kt similarity index 87% rename from features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewTest.kt rename to features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewTest.kt index dfe8aaf85d..5429ac3637 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewTest.kt @@ -5,12 +5,14 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.features.verifysession.impl +package io.element.android.features.verifysession.impl.outgoing 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.features.verifysession.impl.R +import io.element.android.features.verifysession.impl.ui.aEmojisSessionVerificationData import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.ui.strings.CommonStrings @@ -36,7 +38,7 @@ class VerifySelfSessionViewTest { val eventsRecorder = EventsRecorder() rule.setVerifySelfSessionView( aVerifySelfSessionState( - verificationFlowStep = VerifySelfSessionState.VerificationStep.Canceled, + step = VerifySelfSessionState.Step.Canceled, eventSink = eventsRecorder ), ) @@ -49,7 +51,7 @@ class VerifySelfSessionViewTest { val eventsRecorder = EventsRecorder() rule.setVerifySelfSessionView( aVerifySelfSessionState( - verificationFlowStep = VerifySelfSessionState.VerificationStep.AwaitingOtherDeviceResponse, + step = VerifySelfSessionState.Step.AwaitingOtherDeviceResponse, eventSink = eventsRecorder ), ) @@ -62,7 +64,7 @@ class VerifySelfSessionViewTest { val eventsRecorder = EventsRecorder() rule.setVerifySelfSessionView( aVerifySelfSessionState( - verificationFlowStep = VerifySelfSessionState.VerificationStep.Ready, + step = VerifySelfSessionState.Step.Ready, eventSink = eventsRecorder ), ) @@ -75,7 +77,7 @@ class VerifySelfSessionViewTest { val eventsRecorder = EventsRecorder() rule.setVerifySelfSessionView( aVerifySelfSessionState( - verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying( + step = VerifySelfSessionState.Step.Verifying( data = aEmojisSessionVerificationData(), state = AsyncData.Uninitialized, ), @@ -91,7 +93,7 @@ class VerifySelfSessionViewTest { val eventsRecorder = EventsRecorder() rule.setVerifySelfSessionView( aVerifySelfSessionState( - verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying( + step = VerifySelfSessionState.Step.Verifying( data = aEmojisSessionVerificationData(), state = AsyncData.Loading(), ), @@ -107,7 +109,7 @@ class VerifySelfSessionViewTest { val eventsRecorder = EventsRecorder() rule.setVerifySelfSessionView( aVerifySelfSessionState( - verificationFlowStep = VerifySelfSessionState.VerificationStep.Completed, + step = VerifySelfSessionState.Step.Completed, eventSink = eventsRecorder ), ) @@ -121,7 +123,7 @@ class VerifySelfSessionViewTest { ensureCalledOnce { callback -> rule.setVerifySelfSessionView( aVerifySelfSessionState( - verificationFlowStep = VerifySelfSessionState.VerificationStep.Completed, + step = VerifySelfSessionState.Step.Completed, eventSink = eventsRecorder ), onFinished = callback, @@ -137,7 +139,7 @@ class VerifySelfSessionViewTest { ensureCalledOnce { callback -> rule.setVerifySelfSessionView( aVerifySelfSessionState( - verificationFlowStep = VerifySelfSessionState.VerificationStep.Initial(true), + step = VerifySelfSessionState.Step.Initial(true), eventSink = eventsRecorder ), onEnterRecoveryKey = callback, @@ -153,7 +155,7 @@ class VerifySelfSessionViewTest { ensureCalledOnce { callback -> rule.setVerifySelfSessionView( aVerifySelfSessionState( - verificationFlowStep = VerifySelfSessionState.VerificationStep.Initial(true), + step = VerifySelfSessionState.Step.Initial(true), eventSink = eventsRecorder ), onLearnMoreClick = callback, @@ -167,7 +169,7 @@ class VerifySelfSessionViewTest { val eventsRecorder = EventsRecorder() rule.setVerifySelfSessionView( aVerifySelfSessionState( - verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying( + step = VerifySelfSessionState.Step.Verifying( data = aEmojisSessionVerificationData(), state = AsyncData.Uninitialized, ), @@ -183,7 +185,7 @@ class VerifySelfSessionViewTest { val eventsRecorder = EventsRecorder() rule.setVerifySelfSessionView( aVerifySelfSessionState( - verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying( + step = VerifySelfSessionState.Step.Verifying( data = aEmojisSessionVerificationData(), state = AsyncData.Uninitialized, ), @@ -199,7 +201,7 @@ class VerifySelfSessionViewTest { val eventsRecorder = EventsRecorder() rule.setVerifySelfSessionView( aVerifySelfSessionState( - verificationFlowStep = VerifySelfSessionState.VerificationStep.Initial(canEnterRecoveryKey = true), + step = VerifySelfSessionState.Step.Initial(canEnterRecoveryKey = true), displaySkipButton = true, eventSink = eventsRecorder ), @@ -213,7 +215,7 @@ class VerifySelfSessionViewTest { ensureCalledOnce { callback -> rule.setVerifySelfSessionView( aVerifySelfSessionState( - verificationFlowStep = VerifySelfSessionState.VerificationStep.Skipped, + step = VerifySelfSessionState.Step.Skipped, displaySkipButton = true, eventSink = EnsureNeverCalledWithParam(), ), diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fe043863a3..1177419d2e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,9 +4,9 @@ [versions] # Project android_gradle_plugin = "8.7.1" -kotlin = "2.0.20" +kotlin = "2.0.21" kotlinpoet = "2.0.0" -ksp = "2.0.20-1.0.25" +ksp = "2.0.21-1.0.26" firebaseAppDistribution = "5.0.0" # AndroidX @@ -17,19 +17,19 @@ core = "1.13.1" # due to the DefaultMigrationStore not behaving as expected. # Stick to 1.0.0 for now, and ensure that this scenario cannot be reproduced when upgrading the version. datastore = "1.0.0" -constraintlayout = "2.1.4" -constraintlayout_compose = "1.0.1" +constraintlayout = "2.2.0" +constraintlayout_compose = "1.1.0" lifecycle = "2.8.6" activity = "1.9.3" media3 = "1.4.1" -camera = "1.3.4" +camera = "1.4.0" # Compose -compose_bom = "2024.10.00" +compose_bom = "2024.10.01" composecompiler = "1.5.15" # Coroutines -coroutines = "1.8.1" +coroutines = "1.9.0" # Accompanist accompanist = "0.36.0" @@ -37,17 +37,21 @@ accompanist = "0.36.0" # Test test_core = "1.6.1" +# Jetbrain +datetime = "0.6.1" +serialization_json = "1.7.3" + #other coil = "2.7.0" -datetime = "0.6.0" -dependencyAnalysis = "2.3.0" -serialization_json = "1.6.3" showkase = "1.0.3" appyx = "1.4.0" sqldelight = "2.0.2" wysiwyg = "2.37.13" telephoto = "0.13.0" +# Dependency analysis +dependencyAnalysis = "2.4.2" + # DI dagger = "2.52" anvil = "0.3.3" @@ -81,7 +85,7 @@ ksp_plugin = { module = "com.google.devtools.ksp:symbol-processing-api", version # AndroidX androidx_core = { module = "androidx.core:core", version.ref = "core" } androidx_corektx = { module = "androidx.core:core-ktx", version.ref = "core" } -androidx_annotationjvm = "androidx.annotation:annotation-jvm:1.9.0" +androidx_annotationjvm = "androidx.annotation:annotation-jvm:1.9.1" androidx_datastore_preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" } androidx_datastore_datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" } androidx_exifinterface = "androidx.exifinterface:exifinterface:1.3.7" @@ -162,14 +166,14 @@ coil_test = { module = "io.coil-kt:coil-test", version.ref = "coil" } compound = { module = "io.element.android:compound-android", version = "0.1.1" } datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" } serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization_json" } -kotlinx_collections_immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7" +kotlinx_collections_immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.8" showkase = { module = "com.airbnb.android:showkase", version.ref = "showkase" } showkase_processor = { module = "com.airbnb.android:showkase-processor", version.ref = "showkase" } 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.58" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.60" 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" } @@ -178,19 +182,19 @@ sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", sqlcipher = "net.zetetic:android-database-sqlcipher:4.5.4" sqlite = "androidx.sqlite:sqlite-ktx:2.4.0" unifiedpush = "com.github.UnifiedPush:android-connector:2.4.0" -otaliastudios_transcoder = "com.otaliastudios:transcoder:0.11.1" +otaliastudios_transcoder = "com.otaliastudios:transcoder:0.11.2" vanniktech_blurhash = "com.vanniktech:blurhash:0.3.0" telephoto_zoomableimage = { module = "me.saket.telephoto:zoomable-image-coil", version.ref = "telephoto" } telephoto_flick = { module = "me.saket.telephoto:flick-android", version.ref = "telephoto" } statemachine = "com.freeletics.flowredux:compose:1.2.2" maplibre = "org.maplibre.gl:android-sdk:11.5.2" maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:3.0.2" -maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:3.0.1" +maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:3.0.2" opusencoder = "io.element.android:opusencoder:1.1.0" zxing_cpp = "io.github.zxing-cpp:android:2.2.0" # Analytics -posthog = "com.posthog:posthog-android:3.8.2" +posthog = "com.posthog:posthog-android:3.9.0" sentry = "io.sentry:sentry-android:7.16.0" # main branch can be tested replacing the version with main-SNAPSHOT matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.28.0" @@ -233,7 +237,7 @@ ktlint = "org.jlleitschuh.gradle.ktlint:12.1.1" dependencygraph = "com.savvasdalkitsis.module-dependency-graph:0.12" dependencycheck = "org.owasp.dependencycheck:10.0.4" dependencyanalysis = { id = "com.autonomousapps.dependency-analysis", version.ref = "dependencyAnalysis" } -paparazzi = "app.cash.paparazzi:1.3.4" +paparazzi = "app.cash.paparazzi:1.3.5" sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } firebaseAppDistribution = { id = "com.google.firebase.appdistribution", version.ref = "firebaseAppDistribution" } knit = { id = "org.jetbrains.kotlinx.knit", version = "0.5.0" } diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/file/TemporaryUriDeleter.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/file/TemporaryUriDeleter.kt new file mode 100644 index 0000000000..4eabda8972 --- /dev/null +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/file/TemporaryUriDeleter.kt @@ -0,0 +1,39 @@ +/* + * 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.androidutils.file + +import android.content.Context +import android.net.Uri +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.ApplicationContext +import timber.log.Timber +import javax.inject.Inject + +interface TemporaryUriDeleter { + /** + * Delete the Uri only if it is a temporary one. + */ + fun delete(uri: Uri?) +} + +@ContributesBinding(AppScope::class) +class DefaultTemporaryUriDeleter @Inject constructor( + @ApplicationContext private val context: Context, +) : TemporaryUriDeleter { + private val baseCacheUri = "content://${context.packageName}.fileprovider/cache" + + override fun delete(uri: Uri?) { + uri ?: return + if (uri.toString().startsWith(baseCacheUri)) { + context.contentResolver.delete(uri, null, null) + } else { + Timber.d("Do not delete the uri") + } + } +} diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/coroutine/DerivedStateFlow.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/coroutine/DerivedStateFlow.kt index 68748ecdb5..a54d0eb9d8 100644 --- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/coroutine/DerivedStateFlow.kt +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/coroutine/DerivedStateFlow.kt @@ -7,6 +7,7 @@ package io.element.android.libraries.core.coroutine +import kotlinx.coroutines.ExperimentalForInheritanceCoroutinesApi import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.FlowCollector @@ -19,6 +20,7 @@ import kotlinx.coroutines.flow.stateIn * A [StateFlow] that derives its value from a [Flow]. * Useful when you want to apply transformations to a [Flow] and expose it as a [StateFlow]. */ +@OptIn(ExperimentalForInheritanceCoroutinesApi::class) class DerivedStateFlow( private val getValue: () -> T, private val flow: Flow diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt index 8dc47d06d8..cacdfddc00 100644 --- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt @@ -38,6 +38,7 @@ object MimeTypes { fun String?.normalizeMimeType() = if (this == BadJpg) Jpeg else this fun String?.isMimeTypeImage() = this?.startsWith("image/").orFalse() + fun String?.isMimeTypeAnimatedImage() = this == Gif || this == WebP fun String?.isMimeTypeVideo() = this?.startsWith("video/").orFalse() fun String?.isMimeTypeAudio() = this?.startsWith("audio/").orFalse() fun String?.isMimeTypeApplication() = this?.startsWith("application/").orFalse() diff --git a/libraries/dateformatter/test/src/main/kotlin/io/element/android/libraries/dateformatter/test/FakeLastMessageTimestampFormatter.kt b/libraries/dateformatter/test/src/main/kotlin/io/element/android/libraries/dateformatter/test/FakeLastMessageTimestampFormatter.kt index db68141a8e..7edcf321cb 100644 --- a/libraries/dateformatter/test/src/main/kotlin/io/element/android/libraries/dateformatter/test/FakeLastMessageTimestampFormatter.kt +++ b/libraries/dateformatter/test/src/main/kotlin/io/element/android/libraries/dateformatter/test/FakeLastMessageTimestampFormatter.kt @@ -11,8 +11,9 @@ import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormat const val A_FORMATTED_DATE = "formatted_date" -class FakeLastMessageTimestampFormatter : LastMessageTimestampFormatter { - private var format = "" +class FakeLastMessageTimestampFormatter( + var format: String = "", +) : LastMessageTimestampFormatter { fun givenFormat(format: String) { this.format = format } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/TextWithLabelMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/TextWithLabelMolecule.kt new file mode 100644 index 0000000000..4562332060 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/TextWithLabelMolecule.kt @@ -0,0 +1,34 @@ +/* + * 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.designsystem.atomic.molecules + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.theme.components.Text + +@Composable +fun TextWithLabelMolecule( + label: String, + text: String, + modifier: Modifier = Modifier, +) { + Column(modifier = modifier) { + Text( + text = label, + style = ElementTheme.typography.fontBodySmRegular, + color = ElementTheme.colors.textSecondary, + ) + Text( + text = text, + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textPrimary, + ) + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt index 01182343f8..9f49bca0bb 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt @@ -17,6 +17,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -118,6 +119,14 @@ fun TextButton( leadingIcon = leadingIcon ) +@Composable +fun InvisibleButton( + modifier: Modifier = Modifier, + size: ButtonSize = ButtonSize.Large, +) { + Spacer(modifier = modifier.height(size.toMinHeight())) +} + @Composable private fun ButtonInternal( text: String, @@ -131,14 +140,7 @@ private fun ButtonInternal( showProgress: Boolean = false, leadingIcon: IconSource? = null, ) { - val minHeight = when (size) { - ButtonSize.Small -> 32.dp - ButtonSize.Medium, - ButtonSize.MediumLowPadding -> 40.dp - ButtonSize.Large, - ButtonSize.LargeLowPadding -> 48.dp - } - + val minHeight = size.toMinHeight() val hasStartDrawable = showProgress || leadingIcon != null val contentPadding = when (size) { @@ -253,6 +255,14 @@ private fun ButtonInternal( } } +private fun ButtonSize.toMinHeight() = when (this) { + ButtonSize.Small -> 32.dp + ButtonSize.Medium, + ButtonSize.MediumLowPadding -> 40.dp + ButtonSize.Large, + ButtonSize.LargeLowPadding -> 48.dp +} + @Immutable sealed interface IconSource { val contentDescription: String? 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 0dce260f06..44d658c5ec 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 @@ -42,6 +42,7 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup * @param trailingContent The content to be displayed after the headline content. * @param style The style to use for the list item. This may change the color and text styles of the contents. [ListItemStyle.Default] is used by default. * @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 alwaysClickable Whether the list item should always be clickable, even when disabled. * @param onClick The callback to be called when the list item is clicked. */ @Suppress("LongParameterList") @@ -54,6 +55,7 @@ fun ListItem( trailingContent: ListItemContent? = null, style: ListItemStyle = ListItemStyle.Default, enabled: Boolean = true, + alwaysClickable: Boolean = false, onClick: (() -> Unit)? = null, ) { val colors = ListItemDefaults.colors( @@ -74,6 +76,7 @@ fun ListItem( trailingContent = trailingContent, colors = colors, enabled = enabled, + alwaysClickable = alwaysClickable, onClick = onClick, ) } @@ -87,6 +90,7 @@ fun ListItem( * @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 alwaysClickable Whether the list item should always be clickable, even when disabled. * @param onClick The callback to be called when the list item is clicked. */ @Suppress("LongParameterList") @@ -99,6 +103,7 @@ fun ListItem( leadingContent: ListItemContent? = null, trailingContent: ListItemContent? = null, enabled: Boolean = true, + alwaysClickable: Boolean = false, onClick: (() -> Unit)? = null, ) { // We cannot just pass the disabled colors, they must be set manually: https://issuetracker.google.com/issues/280480132 @@ -149,7 +154,7 @@ fun ListItem( headlineContent = decoratedHeadlineContent, modifier = if (onClick != null) { Modifier - .clickable(enabled = enabled, onClick = onClick) + .clickable(enabled = enabled || alwaysClickable, onClick = onClick) .then(modifier) } else { modifier diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt index ab7a19a9c7..11873ee7c1 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt @@ -95,7 +95,9 @@ class DefaultPinnedMessagesBannerFormatter @Inject constructor( messageType.bestDescription.prefixWith(CommonStrings.common_audio) } is VoiceMessageType -> { - messageType.bestDescription.prefixWith(CommonStrings.common_voice_message) + // In this case, do not use bestDescription, because the filename is useless, only use the caption if available. + messageType.caption?.prefixWith(sp.getString(CommonStrings.common_voice_message)) + ?: sp.getString(CommonStrings.common_voice_message) } is OtherMessageType -> { messageType.body diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt index 6b43fc3607..4031915445 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt @@ -110,25 +110,27 @@ class DefaultRoomLastMessageFormatter @Inject constructor( messageType.toPlainText(permalinkParser) } is VideoMessageType -> { - sp.getString(CommonStrings.common_video) + messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_video)) } is ImageMessageType -> { - sp.getString(CommonStrings.common_image) + messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_image)) } is StickerMessageType -> { - sp.getString(CommonStrings.common_sticker) + messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_sticker)) } is LocationMessageType -> { sp.getString(CommonStrings.common_shared_location) } is FileMessageType -> { - sp.getString(CommonStrings.common_file) + messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_file)) } is AudioMessageType -> { - sp.getString(CommonStrings.common_audio) + messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_audio)) } is VoiceMessageType -> { - sp.getString(CommonStrings.common_voice_message) + // In this case, do not use bestDescription, because the filename is useless, only use the caption if available. + messageType.caption?.prefixWith(sp.getString(CommonStrings.common_voice_message)) + ?: sp.getString(CommonStrings.common_voice_message) } is OtherMessageType -> { messageType.body @@ -140,7 +142,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor( return message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) } - private fun String.prefixIfNeeded( + private fun CharSequence.prefixIfNeeded( senderDisambiguatedDisplayName: String, isDmRoom: Boolean, isOutgoing: Boolean, 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 dba242a458..1462ac4a24 100644 --- a/libraries/eventformatter/impl/src/main/res/values-nl/translations.xml +++ b/libraries/eventformatter/impl/src/main/res/values-nl/translations.xml @@ -45,6 +45,12 @@ "Je hebt de kamernaam verwijderd" "%1$s heeft geen wijzigingen aangebracht" "Je hebt geen wijzigingen aangebracht" + "%1$s heeft de vastgezette berichten gewijzigd" + "Je hebt de vastgezette berichten gewijzigd" + "%1$s heeft een bericht vastgezet" + "Je hebt een bericht vastgezet" + "%1$s heeft een bericht losgemaakt" + "Je hebt een bericht losgemaakt" "%1$s heeft de uitnodiging afgewezen" "Je hebt de uitnodiging afgewezen" "%1$s heeft %2$s verwijderd" diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt index af347135fb..80a7691a1d 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt @@ -159,11 +159,11 @@ class DefaultPinnedMessagesBannerFormatterTest { val expectedResult = when (type) { is VideoMessageType, is AudioMessageType, - is VoiceMessageType, is ImageMessageType, is StickerMessageType, is FileMessageType, is LocationMessageType -> AnnotatedString::class.java + is VoiceMessageType, is EmoteMessageType, is TextMessageType, is NoticeMessageType, @@ -176,7 +176,7 @@ class DefaultPinnedMessagesBannerFormatterTest { val expectedResult = when (type) { is VideoMessageType -> "Video: Shared body" is AudioMessageType -> "Audio: Shared body" - is VoiceMessageType -> "Voice message: Shared body" + is VoiceMessageType -> "Voice message" is ImageMessageType -> "Image: Shared body" is StickerMessageType -> "Sticker: Shared body" is FileMessageType -> "File: Shared body" diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt index efc748d10f..3c5038069a 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt @@ -208,32 +208,51 @@ class DefaultRoomLastMessageFormatterTest { // Verify results of DM mode for ((type, result) in resultsInDm) { + val string = result.toString() val expectedResult = when (type) { - is VideoMessageType -> "Video" - is AudioMessageType -> "Audio" + is VideoMessageType -> "Video: Shared body" + is AudioMessageType -> "Audio: Shared body" is VoiceMessageType -> "Voice message" - is ImageMessageType -> "Image" - is StickerMessageType -> "Sticker" - is FileMessageType -> "File" + is ImageMessageType -> "Image: Shared body" + is StickerMessageType -> "Sticker: Shared body" + is FileMessageType -> "File: Shared body" is LocationMessageType -> "Shared location" is EmoteMessageType -> "* $senderName ${type.body}" is TextMessageType, is NoticeMessageType, is OtherMessageType -> body } - assertWithMessage("$type was not properly handled for DM").that(result).isEqualTo(expectedResult) + val shouldCreateAnnotatedString = when (type) { + is VideoMessageType -> true + is AudioMessageType -> true + is VoiceMessageType -> false + is ImageMessageType -> true + is StickerMessageType -> true + is FileMessageType -> true + is LocationMessageType -> false + is EmoteMessageType -> false + is TextMessageType -> false + is NoticeMessageType -> false + is OtherMessageType -> false + } + if (shouldCreateAnnotatedString) { + assertWithMessage("$type doesn't produce an AnnotatedString") + .that(result) + .isInstanceOf(AnnotatedString::class.java) + } + assertWithMessage("$type was not properly handled for DM").that(string).isEqualTo(expectedResult) } // Verify results of Room mode for ((type, result) in resultsInRoom) { val string = result.toString() val expectedResult = when (type) { - is VideoMessageType -> "$expectedPrefix: Video" - is AudioMessageType -> "$expectedPrefix: Audio" + is VideoMessageType -> "$expectedPrefix: Video: Shared body" + is AudioMessageType -> "$expectedPrefix: Audio: Shared body" is VoiceMessageType -> "$expectedPrefix: Voice message" - is ImageMessageType -> "$expectedPrefix: Image" - is StickerMessageType -> "$expectedPrefix: Sticker" - is FileMessageType -> "$expectedPrefix: File" + is ImageMessageType -> "$expectedPrefix: Image: Shared body" + is StickerMessageType -> "$expectedPrefix: Sticker: Shared body" + is FileMessageType -> "$expectedPrefix: File: Shared body" is LocationMessageType -> "$expectedPrefix: Shared location" is TextMessageType, is NoticeMessageType, @@ -249,7 +268,8 @@ class DefaultRoomLastMessageFormatterTest { is FileMessageType -> true is LocationMessageType -> false is EmoteMessageType -> false - is TextMessageType, is NoticeMessageType -> true + is TextMessageType -> true + is NoticeMessageType -> true is OtherMessageType -> true } if (shouldCreateAnnotatedString) { 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 3a6bd16f77..490c12ebe0 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 @@ -126,17 +126,18 @@ enum class FeatureFlags( defaultValue = { false }, isFinished = false, ), - IdentityPinningViolationNotifications( - key = "feature.identityPinningViolationNotifications", - title = "Identity pinning violation notifications", - description = null, - defaultValue = { buildMeta -> - when (buildMeta.buildType) { - // Do not enable this feature in release builds - BuildType.RELEASE -> false - else -> true - } - }, + Knock( + key = "feature.knock", + title = "Ask to join", + description = "Allow creating rooms which users can request access to.", + defaultValue = { false }, + isFinished = false, + ), + MediaUploadOnSendQueue( + key = "feature.media_upload_through_send_queue", + title = "Media upload through send queue", + description = "Experimental support for treating media uploads as regular events, with an improved retry and cancellation implementation.", + defaultValue = { buildMeta -> buildMeta.buildType != BuildType.RELEASE }, 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 6af1763e83..504db1c6d9 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 @@ -108,7 +108,14 @@ interface MatrixClient : Closeable { suspend fun trackRecentlyVisitedRoom(roomId: RoomId): Result suspend fun getRecentlyVisitedRooms(): Result> - suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result + + /** + * Resolves the given room alias to a roomID (and a list of servers), if possible. + * @param roomAlias the room alias to resolve + * @return the resolved room alias if any, an empty result if not found,or an error if the resolution failed. + * + */ + suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result> /** * Enables or disables the sending queue, according to the given parameter. 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 b7b44ba45e..019bd0d8f0 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 @@ -56,4 +56,7 @@ interface MatrixAuthenticationService { suspend fun loginWithOidc(callbackUrl: String): Result suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit): Result + + /** Listen to new Matrix clients being created on authentication. */ + fun listenToNewMatrixClients(lambda: (MatrixClient) -> Unit) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/FlowId.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/FlowId.kt new file mode 100644 index 0000000000..c1b298d62a --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/FlowId.kt @@ -0,0 +1,15 @@ +/* + * 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.core + +import java.io.Serializable + +@JvmInline +value class FlowId(val value: String) : Serializable { + override fun toString(): String = value +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt index 940fef7326..2e13623c96 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.matrix.api.createroom import io.element.android.libraries.matrix.api.core.UserId +import java.util.Optional data class CreateRoomParameters( val name: String?, @@ -18,4 +19,6 @@ data class CreateRoomParameters( val preset: RoomPreset, val invite: List? = null, val avatar: String? = null, + val joinRuleOverride: JoinRuleOverride = JoinRuleOverride.None, + val canonicalAlias: Optional = Optional.empty(), ) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/JoinRuleOverride.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/JoinRuleOverride.kt new file mode 100644 index 0000000000..f59f393c3e --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/JoinRuleOverride.kt @@ -0,0 +1,16 @@ +/* + * 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.createroom + +/** + * Rules to override the default room join rules. + */ +sealed interface JoinRuleOverride { + data object Knock : JoinRuleOverride + data object None : JoinRuleOverride +} 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 fcc1fd1812..3bbbf6fdd4 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 @@ -132,8 +132,8 @@ interface MatrixRoom : Closeable { file: File, thumbnailFile: File?, imageInfo: ImageInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback? ): Result @@ -141,8 +141,8 @@ interface MatrixRoom : Closeable { file: File, thumbnailFile: File?, videoInfo: VideoInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback? ): Result diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryList.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryList.kt index 5f362dfda5..88cabf265b 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryList.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryList.kt @@ -10,8 +10,22 @@ package io.element.android.libraries.matrix.api.roomdirectory import kotlinx.coroutines.flow.Flow interface RoomDirectoryList { - suspend fun filter(filter: String?, batchSize: Int): Result + /** + * Starts a filtered search for the server. + * If the filter is not provided it will search for all the rooms. You can specify a batch_size to control the number of rooms to fetch per request. + * If the via_server is not provided it will search in the current homeserver by default. + * This method will clear the current search results and start a new one + */ + suspend fun filter(filter: String?, batchSize: Int, viaServerName: String?): Result + + /** + * Load more rooms from the current search results. + */ suspend fun loadMore(): Result + + /** + * The current search results as a state flow. + */ val state: Flow data class State( diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt index 085c4d49ea..695fe906c5 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt @@ -75,8 +75,8 @@ interface Timeline : AutoCloseable { file: File, thumbnailFile: File?, imageInfo: ImageInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback? ): Result @@ -84,8 +84,8 @@ interface Timeline : AutoCloseable { file: File, thumbnailFile: File?, videoInfo: VideoInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback? ): Result 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 e6a0eae7ed..c95fe46741 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 @@ -34,6 +34,10 @@ sealed interface LocalEventSendState { */ val users: List ) : VerifiedUser + + data class InvalidMimeType(val mimeType: String) : Failed + + data object MissingMediaContent : Failed } data class Sent( diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationRequestDetails.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationRequestDetails.kt new file mode 100644 index 0000000000..93d791a21c --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationRequestDetails.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.libraries.matrix.api.verification + +import android.os.Parcelable +import io.element.android.libraries.matrix.api.core.DeviceId +import io.element.android.libraries.matrix.api.core.FlowId +import io.element.android.libraries.matrix.api.core.UserId +import kotlinx.parcelize.Parcelize + +@Parcelize +data class SessionVerificationRequestDetails( + val senderId: UserId, + val flowId: FlowId, + val deviceId: DeviceId, + val displayName: String?, + val firstSeenTimestamp: Long, +) : Parcelable diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt index 193d5eb48e..4bd30a21fc 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt @@ -56,7 +56,27 @@ interface SessionVerificationService { /** * Returns the verification service state to the initial step. */ - suspend fun reset() + suspend fun reset(cancelAnyPendingVerificationAttempt: Boolean) + + /** + * Register a listener to be notified of incoming session verification requests. + */ + fun setListener(listener: SessionVerificationServiceListener?) + + /** + * Set this particular request as the currently active one and register for + * events pertaining it. + */ + suspend fun acknowledgeVerificationRequest(details: SessionVerificationRequestDetails) + + /** + * Accept the previously acknowledged verification request. + */ + suspend fun acceptVerificationRequest() +} + +interface SessionVerificationServiceListener { + fun onIncomingSessionRequest(sessionVerificationRequestDetails: SessionVerificationRequestDetails) } /** Verification status of the current session. */ @@ -82,20 +102,20 @@ sealed interface VerificationFlowState { data object Initial : VerificationFlowState /** Session verification request was accepted by another device. */ - data object AcceptedVerificationRequest : VerificationFlowState + data object DidAcceptVerificationRequest : VerificationFlowState /** Short Authentication String (SAS) verification started between the 2 devices. */ - data object StartedSasVerification : VerificationFlowState + data object DidStartSasVerification : VerificationFlowState /** Verification data for the SAS verification received. */ - data class ReceivedVerificationData(val data: SessionVerificationData) : VerificationFlowState + data class DidReceiveVerificationData(val data: SessionVerificationData) : VerificationFlowState /** Verification completed successfully. */ - data object Finished : VerificationFlowState + data object DidFinish : VerificationFlowState /** Verification was cancelled by either device. */ - data object Canceled : VerificationFlowState + data object DidCancel : VerificationFlowState /** Verification failed with an error. */ - data object Failed : VerificationFlowState + data object DidFail : VerificationFlowState } 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 101d049659..3165cef1bf 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 @@ -12,6 +12,7 @@ import io.element.android.libraries.androidutils.file.safeDelete import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.childScope +import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.ProgressCallback @@ -21,6 +22,7 @@ import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters +import io.element.android.libraries.matrix.api.createroom.JoinRuleOverride import io.element.android.libraries.matrix.api.createroom.RoomPreset import io.element.android.libraries.matrix.api.createroom.RoomVisibility import io.element.android.libraries.matrix.api.encryption.EncryptionService @@ -32,6 +34,7 @@ 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.MatrixRoom import io.element.android.libraries.matrix.api.room.PendingRoom +import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias import io.element.android.libraries.matrix.api.room.preview.RoomPreview @@ -71,7 +74,6 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.Channel @@ -108,11 +110,11 @@ import kotlin.jvm.optionals.getOrNull import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds import org.matrix.rustcomponents.sdk.CreateRoomParameters as RustCreateRoomParameters +import org.matrix.rustcomponents.sdk.JoinRule as RustJoinRule import org.matrix.rustcomponents.sdk.RoomPreset as RustRoomPreset import org.matrix.rustcomponents.sdk.RoomVisibility as RustRoomVisibility import org.matrix.rustcomponents.sdk.SyncService as ClientSyncService -@OptIn(ExperimentalCoroutinesApi::class) class RustMatrixClient( private val client: Client, private val baseDirectory: File, @@ -124,6 +126,7 @@ class RustMatrixClient( baseCacheDirectory: File, clock: SystemClock, timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory, + featureFlagService: FeatureFlagService, ) : MatrixClient { override val sessionId: UserId = UserId(client.userId()) override val deviceId: DeviceId = DeviceId(client.deviceId()) @@ -187,6 +190,7 @@ class RustMatrixClient( roomContentForwarder = RoomContentForwarder(innerRoomListService), roomSyncSubscriber = roomSyncSubscriber, timelineEventTypeFilterFactory = timelineEventTypeFilterFactory, + featureFlagService = featureFlagService, ) override val mediaLoader: MatrixMediaLoader = RustMediaLoader( @@ -304,14 +308,33 @@ class RustMatrixClient( RoomVisibility.PUBLIC -> RustRoomVisibility.PUBLIC RoomVisibility.PRIVATE -> RustRoomVisibility.PRIVATE }, - preset = when (createRoomParams.preset) { - RoomPreset.PRIVATE_CHAT -> RustRoomPreset.PRIVATE_CHAT - RoomPreset.PUBLIC_CHAT -> RustRoomPreset.PUBLIC_CHAT - RoomPreset.TRUSTED_PRIVATE_CHAT -> RustRoomPreset.TRUSTED_PRIVATE_CHAT + preset = when (createRoomParams.visibility) { + RoomVisibility.PRIVATE -> { + if (createRoomParams.isDirect) { + RustRoomPreset.TRUSTED_PRIVATE_CHAT + } else { + RustRoomPreset.PRIVATE_CHAT + } + } + RoomVisibility.PUBLIC -> { + RustRoomPreset.PUBLIC_CHAT + } }, invite = createRoomParams.invite?.map { it.value }, avatar = createRoomParams.avatar, - powerLevelContentOverride = defaultRoomCreationPowerLevels, + powerLevelContentOverride = defaultRoomCreationPowerLevels.copy( + invite = if (createRoomParams.joinRuleOverride == JoinRuleOverride.Knock) { + // override the invite power level so it's the same as kick. + RoomMember.Role.MODERATOR.powerLevel.toInt() + } else { + null + } + ), + joinRuleOverride = when (createRoomParams.joinRuleOverride) { + JoinRuleOverride.Knock -> RustJoinRule.Knock + JoinRuleOverride.None -> null + }, + canonicalAlias = createRoomParams.canonicalAlias.getOrNull(), ) val roomId = RoomId(client.createRoom(rustParams)) // Wait to receive the room back from the sync but do not returns failure if it fails. @@ -420,13 +443,15 @@ class RustMatrixClient( } } - override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result = withContext(sessionDispatcher) { + override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result> = withContext(sessionDispatcher) { runCatching { - val result = client.resolveRoomAlias(roomAlias.value) - ResolvedRoomAlias( - roomId = RoomId(result.roomId), - servers = result.servers, - ) + val result = client.resolveRoomAlias(roomAlias.value)?.let { + ResolvedRoomAlias( + roomId = RoomId(it.roomId), + servers = it.servers, + ) + } + Optional.ofNullable(result) } } 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 c33ccdb543..e22eda03f2 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 @@ -25,6 +25,7 @@ import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.toolbox.api.systemclock.SystemClock import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.withContext +import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientBuilder import org.matrix.rustcomponents.sdk.Session import org.matrix.rustcomponents.sdk.SlidingSyncVersion @@ -51,8 +52,9 @@ class RustMatrixClientFactory @Inject constructor( private val timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory, private val clientBuilderProvider: ClientBuilderProvider, ) { + private val sessionDelegate = RustClientSessionDelegate(sessionStore, appCoroutineScope, coroutineDispatchers) + suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) { - val sessionDelegate = RustClientSessionDelegate(sessionStore, appCoroutineScope, coroutineDispatchers) val client = getBaseClientBuilder( sessionPaths = sessionData.getSessionPaths(), passphrase = sessionData.passphrase, @@ -60,18 +62,21 @@ class RustMatrixClientFactory @Inject constructor( ) .homeserverUrl(sessionData.homeserverUrl) .username(sessionData.userId) - .setSessionDelegate(sessionDelegate) .use { it.build() } client.restoreSession(sessionData.toSession()) + create(client) + } + + suspend fun create(client: Client): RustMatrixClient { + val (anonymizedAccessToken, anonymizedRefreshToken) = client.session().anonymizedTokens() + val syncService = client.syncService() .withUtdHook(UtdTracker(analyticsService)) .finish() - val (anonymizedAccessToken, anonymizedRefreshToken) = sessionData.anonymizedTokens() - - RustMatrixClient( + return RustMatrixClient( client = client, baseDirectory = baseDirectory, sessionStore = sessionStore, @@ -82,6 +87,7 @@ class RustMatrixClientFactory @Inject constructor( baseCacheDirectory = cacheDirectory, clock = clock, timelineEventTypeFilterFactory = timelineEventTypeFilterFactory, + featureFlagService = featureFlagService, ).also { Timber.tag(it.toString()).d("Creating Client with access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'") } @@ -97,6 +103,7 @@ class RustMatrixClientFactory @Inject constructor( dataPath = sessionPaths.fileDirectory.absolutePath, cachePath = sessionPaths.cacheDirectory.absolutePath, ) + .setSessionDelegate(sessionDelegate) .passphrase(passphrase) .userAgent(userAgentProvider.provide()) .addRootCertificates(userCertificatesProvider.provides()) 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 434b0b2aef..5ea9ce8550 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 @@ -51,7 +51,6 @@ import org.matrix.rustcomponents.sdk.QrCodeData import org.matrix.rustcomponents.sdk.QrCodeDecodeException import org.matrix.rustcomponents.sdk.QrLoginProgress import org.matrix.rustcomponents.sdk.QrLoginProgressListener -import org.matrix.rustcomponents.sdk.use import timber.log.Timber import uniffi.matrix_sdk.OidcAuthorizationData import javax.inject.Inject @@ -77,6 +76,11 @@ class RustMatrixAuthenticationService @Inject constructor( private var currentClient: Client? = null private var currentHomeserver = MutableStateFlow(null) + private var newMatrixClientObserver: ((MatrixClient) -> Unit)? = null + override fun listenToNewMatrixClients(lambda: (MatrixClient) -> Unit) { + newMatrixClientObserver = lambda + } + private fun rotateSessionPath(): SessionPaths { sessionPaths?.deleteRecursively() return sessionPathsFactory.create() @@ -155,7 +159,7 @@ class RustMatrixAuthenticationService @Inject constructor( passphrase = pendingPassphrase, sessionPaths = currentSessionPaths, ) - clear() + newMatrixClientObserver?.invoke(rustMatrixClientFactory.create(client)) sessionStore.storeData(sessionData) SessionId(sessionData.userId) }.mapFailure { failure -> @@ -226,9 +230,9 @@ class RustMatrixAuthenticationService @Inject constructor( passphrase = pendingPassphrase, sessionPaths = currentSessionPaths, ) - clear() pendingOidcAuthorizationData?.close() pendingOidcAuthorizationData = null + newMatrixClientObserver?.invoke(rustMatrixClientFactory.create(client)) sessionStore.storeData(sessionData) SessionId(sessionData.userId) }.mapFailure { failure -> @@ -256,15 +260,14 @@ class RustMatrixAuthenticationService @Inject constructor( oidcConfiguration = oidcConfiguration, progressListener = progressListener, ) - val sessionData = client.use { rustClient -> - rustClient.session() - .toSessionData( - isTokenValid = true, - loginType = LoginType.QR, - passphrase = pendingPassphrase, - sessionPaths = emptySessionPaths, - ) - } + val sessionData = client.session() + .toSessionData( + isTokenValid = true, + loginType = LoginType.QR, + passphrase = pendingPassphrase, + sessionPaths = emptySessionPaths, + ) + newMatrixClientObserver?.invoke(rustMatrixClientFactory.create(client)) sessionStore.storeData(sessionData) SessionId(sessionData.userId) }.mapFailure { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt index 9604d6af6d..17ba1d2c4d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt @@ -37,7 +37,7 @@ class RustMediaLoader( withContext(mediaDispatcher) { runCatching { source.toRustMediaSource().use { source -> - innerClient.getMediaContent(source).toUByteArray().toByteArray() + innerClient.getMediaContent(source) } } } @@ -55,7 +55,7 @@ class RustMediaLoader( mediaSource = mediaSource, width = width.toULong(), height = height.toULong() - ).toUByteArray().toByteArray() + ) } } } 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 31d8ae1f43..8b72282cd5 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 @@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.impl.room import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.childScope import io.element.android.libraries.core.extensions.mapFailure +import io.element.android.libraries.featureflag.api.FeatureFlagService 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 @@ -103,6 +104,7 @@ class RustMatrixRoom( private val roomContentForwarder: RoomContentForwarder, private val roomSyncSubscriber: RoomSyncSubscriber, private val matrixRoomInfoMapper: MatrixRoomInfoMapper, + private val featureFlagService: FeatureFlagService, ) : MatrixRoom { override val roomId = RoomId(innerRoom.id()) @@ -445,22 +447,22 @@ class RustMatrixRoom( file: File, thumbnailFile: File?, imageInfo: ImageInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback?, ): Result { - return liveTimeline.sendImage(file, thumbnailFile, imageInfo, body, formattedBody, progressCallback) + return liveTimeline.sendImage(file, thumbnailFile, imageInfo, caption, formattedCaption, progressCallback) } override suspend fun sendVideo( file: File, thumbnailFile: File?, videoInfo: VideoInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback?, ): Result { - return liveTimeline.sendVideo(file, thumbnailFile, videoInfo, body, formattedBody, progressCallback) + return liveTimeline.sendVideo(file, thumbnailFile, videoInfo, caption, formattedCaption, progressCallback) } override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result { @@ -700,6 +702,7 @@ class RustMatrixRoom( dispatcher = roomDispatcher, roomContentForwarder = roomContentForwarder, onNewSyncedEvent = onNewSyncedEvent, + featureFlagsService = featureFlagService, ) } } 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 e06424e723..1b9fe4115e 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 @@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.impl.room import androidx.collection.lruCache import io.element.android.appconfig.TimelineConfig import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.featureflag.api.FeatureFlagService 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 @@ -49,6 +50,7 @@ class RustRoomFactory( private val innerRoomListService: InnerRoomListService, private val roomSyncSubscriber: RoomSyncSubscriber, private val timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory, + private val featureFlagService: FeatureFlagService, ) { @OptIn(ExperimentalCoroutinesApi::class) private val dispatcher = dispatchers.io.limitedParallelism(1) @@ -117,6 +119,7 @@ class RustRoomFactory( roomContentForwarder = roomContentForwarder, roomSyncSubscriber = roomSyncSubscriber, matrixRoomInfoMapper = matrixRoomInfoMapper, + featureFlagService = featureFlagService, ) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryList.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryList.kt index 7219e9c3ed..576b93d26d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryList.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryList.kt @@ -41,9 +41,9 @@ class RustRoomDirectoryList( .launchIn(coroutineScope) } - override suspend fun filter(filter: String?, batchSize: Int): Result { + override suspend fun filter(filter: String?, batchSize: Int, viaServerName: String?): Result { return execute { - inner.search(filter = filter, batchSize = batchSize.toUInt()) + inner.search(filter = filter, batchSize = batchSize.toUInt(), viaServerName = viaServerName) } } 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 af597a88ab..98f7ccfba1 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 @@ -7,6 +7,8 @@ package io.element.android.libraries.matrix.impl.timeline +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.core.RoomId @@ -85,6 +87,7 @@ class RustTimeline( private val coroutineScope: CoroutineScope, private val dispatcher: CoroutineDispatcher, private val roomContentForwarder: RoomContentForwarder, + private val featureFlagsService: FeatureFlagService, onNewSyncedEvent: () -> Unit, ) : Timeline { private val initLatch = CompletableDeferred() @@ -326,20 +329,21 @@ class RustTimeline( file: File, thumbnailFile: File?, imageInfo: ImageInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback?, ): Result { + val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue) return sendAttachment(listOfNotNull(file, thumbnailFile)) { inner.sendImage( url = file.path, thumbnailUrl = thumbnailFile?.path, imageInfo = imageInfo.map(), - caption = body, - formattedCaption = formattedBody?.let { + caption = caption, + formattedCaption = formattedCaption?.let { FormattedBody(body = it, format = MessageFormat.Html) }, - storeInCache = true, + useSendQueue = useSendQueue, progressWatcher = progressCallback?.toProgressWatcher() ) } @@ -349,26 +353,28 @@ class RustTimeline( file: File, thumbnailFile: File?, videoInfo: VideoInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback?, ): Result { + val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue) return sendAttachment(listOfNotNull(file, thumbnailFile)) { inner.sendVideo( url = file.path, thumbnailUrl = thumbnailFile?.path, videoInfo = videoInfo.map(), - caption = body, - formattedCaption = formattedBody?.let { + caption = caption, + formattedCaption = formattedCaption?.let { FormattedBody(body = it, format = MessageFormat.Html) }, - storeInCache = true, + useSendQueue = useSendQueue, progressWatcher = progressCallback?.toProgressWatcher() ) } } override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result { + val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue) return sendAttachment(listOf(file)) { inner.sendAudio( url = file.path, @@ -376,15 +382,21 @@ class RustTimeline( // Maybe allow a caption in the future? caption = null, formattedCaption = null, - storeInCache = true, + useSendQueue = useSendQueue, progressWatcher = progressCallback?.toProgressWatcher() ) } } override suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result { + val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue) return sendAttachment(listOf(file)) { - inner.sendFile(file.path, fileInfo.map(), false, progressCallback?.toProgressWatcher()) + inner.sendFile( + url = file.path, + fileInfo = fileInfo.map(), + useSendQueue = useSendQueue, + progressWatcher = progressCallback?.toProgressWatcher(), + ) } } @@ -491,17 +503,20 @@ class RustTimeline( audioInfo: AudioInfo, waveform: List, progressCallback: ProgressCallback?, - ): Result = sendAttachment(listOf(file)) { - inner.sendVoiceMessage( - url = file.path, - audioInfo = audioInfo.map(), - waveform = waveform.toMSC3246range(), - // Maybe allow a caption in the future? - caption = null, - formattedCaption = null, - storeInCache = true, - progressWatcher = progressCallback?.toProgressWatcher(), - ) + ): Result { + val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue) + return sendAttachment(listOf(file)) { + inner.sendVoiceMessage( + url = file.path, + audioInfo = audioInfo.map(), + waveform = waveform.toMSC3246range(), + // Maybe allow a caption in the future? + caption = null, + formattedCaption = null, + useSendQueue = useSendQueue, + progressWatcher = progressCallback?.toProgressWatcher(), + ) + } } private fun sendAttachment(files: List, handle: () -> SendAttachmentJoinHandle): Result { 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 cec54d3d20..f93d3fe982 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 @@ -100,6 +100,12 @@ fun RustEventSendState?.map(): LocalEventSendState? { LocalEventSendState.Failed.Unknown(queueWedgeError.msg) } } + is QueueWedgeError.InvalidMimeType -> { + LocalEventSendState.Failed.InvalidMimeType(queueWedgeError.mimeType) + } + is QueueWedgeError.MissingMediaContent -> { + LocalEventSendState.Failed.MissingMediaContent + } } } is RustEventSendState.Sent -> LocalEventSendState.Sent(EventId(eventId)) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt index 17258f08aa..ed3ddec98b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt @@ -9,25 +9,27 @@ package io.element.android.libraries.matrix.impl.verification import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.matrix.api.verification.SessionVerificationData +import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails import io.element.android.libraries.matrix.api.verification.SessionVerificationService +import io.element.android.libraries.matrix.api.verification.SessionVerificationServiceListener import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.api.verification.VerificationEmoji import io.element.android.libraries.matrix.api.verification.VerificationFlowState import io.element.android.libraries.matrix.impl.util.cancelAndDestroy import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.delay +import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.Encryption @@ -35,13 +37,13 @@ import org.matrix.rustcomponents.sdk.RecoveryState import org.matrix.rustcomponents.sdk.RecoveryStateListener import org.matrix.rustcomponents.sdk.SessionVerificationController import org.matrix.rustcomponents.sdk.SessionVerificationControllerDelegate -import org.matrix.rustcomponents.sdk.SessionVerificationRequestDetails import org.matrix.rustcomponents.sdk.VerificationState import org.matrix.rustcomponents.sdk.VerificationStateListener import org.matrix.rustcomponents.sdk.use import timber.log.Timber import kotlin.time.Duration.Companion.seconds import org.matrix.rustcomponents.sdk.SessionVerificationData as RustSessionVerificationData +import org.matrix.rustcomponents.sdk.SessionVerificationRequestDetails as RustSessionVerificationRequestDetails class RustSessionVerificationService( private val client: Client, @@ -57,11 +59,13 @@ class RustSessionVerificationService( private val _sessionVerifiedStatus = MutableStateFlow(SessionVerifiedStatus.Unknown) override val sessionVerifiedStatus: StateFlow = _sessionVerifiedStatus.asStateFlow() + private val recoveryState = MutableStateFlow(RecoveryState.UNKNOWN) + // Listen for changes in verification status and update accordingly private val verificationStateListenerTaskHandle = encryptionService.verificationStateListener(object : VerificationStateListener { override fun onUpdate(status: VerificationState) { Timber.d("New verification state: $status") - sessionCoroutineScope.launch { updateVerificationStatus() } + _sessionVerifiedStatus.value = status.map() } }) @@ -70,7 +74,7 @@ class RustSessionVerificationService( override fun onUpdate(status: RecoveryState) { Timber.d("New recovery state: $status") // We could check the `RecoveryState`, but it's easier to just use the verification state directly - sessionCoroutineScope.launch { updateVerificationStatus() } + recoveryState.value = status } }) @@ -84,21 +88,16 @@ class RustSessionVerificationService( verificationStatus == SessionVerifiedStatus.NotVerified } - init { - // Update initial state in case sliding sync isn't ready - sessionCoroutineScope.launch { updateVerificationStatus() } - - isReady.onEach { isReady -> - if (isReady) { - Timber.d("Starting verification service") - // Immediate status update - updateVerificationStatus() - } else { - Timber.d("Stopping verification service") - updateVerificationStatus() - } - } - .launchIn(sessionCoroutineScope) + private var isOwnVerification = true + + override fun didReceiveVerificationRequest(details: RustSessionVerificationRequestDetails) { + listener?.onIncomingSessionRequest(details.map()) + } + + private var listener: SessionVerificationServiceListener? = null + + override fun setListener(listener: SessionVerificationServiceListener?) { + this.listener = listener } override suspend fun requestVerification() = tryOrFail { @@ -120,9 +119,26 @@ class RustSessionVerificationService( verificationController.startSasVerification() } + override suspend fun acknowledgeVerificationRequest(details: SessionVerificationRequestDetails) = tryOrFail { + isOwnVerification = false + initVerificationControllerIfNeeded() + verificationController.acknowledgeVerificationRequest( + senderId = details.senderId.value, + flowId = details.flowId.value, + ) + } + + override suspend fun acceptVerificationRequest() = tryOrFail { + verificationController.acceptVerificationRequest() + } + private suspend fun tryOrFail(block: suspend () -> Unit) { runCatching { - block() + // Ensure the block cannot be cancelled, else if the Rust SDK emit a new state during the API execution, + // the state machine may cancel the api call. + withContext(NonCancellable) { + block() + } }.onFailure { Timber.e(it, "Failed to verify session") didFail() @@ -133,16 +149,16 @@ class RustSessionVerificationService( // When verification attempt is accepted by the other device override fun didAcceptVerificationRequest() { - _verificationFlowState.value = VerificationFlowState.AcceptedVerificationRequest + _verificationFlowState.value = VerificationFlowState.DidAcceptVerificationRequest } override fun didCancel() { - _verificationFlowState.value = VerificationFlowState.Canceled + _verificationFlowState.value = VerificationFlowState.DidCancel } override fun didFail() { Timber.e("Session verification failed with an unknown error") - _verificationFlowState.value = VerificationFlowState.Failed + _verificationFlowState.value = VerificationFlowState.DidFail } override fun didFinish() { @@ -150,15 +166,23 @@ class RustSessionVerificationService( // Ideally this should be `verificationController?.isVerified().orFalse()` but for some reason it returns false if run immediately // It also sometimes unexpectedly fails to report the session as verified, so we have to handle that possibility and fail if needed runCatching { - withTimeout(30.seconds) { - while (encryptionService.verificationState() != VerificationState.VERIFIED) { - delay(100) - } + withTimeout(20.seconds) { + // Wait until the SDK reports the state as verified + sessionVerifiedStatus.first { it == SessionVerifiedStatus.Verified } } } .onSuccess { - // Order here is important, first set the flow state as finished, then update the verification status - _verificationFlowState.value = VerificationFlowState.Finished + if (isOwnVerification) { + // Try waiting for the final recovery state for better UX, but don't block the verification state on it + tryOrNull { + withTimeout(10.seconds) { + // Wait until the recovery state is either fully loaded or we check it's explicitly disabled + recoveryState.first { it == RecoveryState.ENABLED || it == RecoveryState.DISABLED } + } + } + } + + _verificationFlowState.value = VerificationFlowState.DidFinish updateVerificationStatus() } .onFailure { @@ -169,22 +193,19 @@ class RustSessionVerificationService( } override fun didReceiveVerificationData(data: RustSessionVerificationData) { - _verificationFlowState.value = VerificationFlowState.ReceivedVerificationData(data.map()) + _verificationFlowState.value = VerificationFlowState.DidReceiveVerificationData(data.map()) } // When the actual SAS verification starts override fun didStartSasVerification() { - _verificationFlowState.value = VerificationFlowState.StartedSasVerification - } - - override fun didReceiveVerificationRequest(details: SessionVerificationRequestDetails) { - // TODO + _verificationFlowState.value = VerificationFlowState.DidStartSasVerification } // end-region - override suspend fun reset() { - if (isReady.value) { + override suspend fun reset(cancelAnyPendingVerificationAttempt: Boolean) { + isOwnVerification = true + if (isReady.value && cancelAnyPendingVerificationAttempt) { // Cancel any pending verification attempt tryOrNull { verificationController.cancelVerification() } } @@ -212,37 +233,20 @@ class RustSessionVerificationService( } } - private suspend fun updateVerificationStatus() { - if (verificationFlowState.value == VerificationFlowState.Finished) { - // Calling `encryptionService.verificationState()` performs a network call and it will deadlock if there is no network - // So we need to check that *only* if we know there is network connection, which is the case when the verification flow just finished - Timber.d("Updating verification status: flow just finished") - runCatching { - encryptionService.waitForE2eeInitializationTasks() - }.onSuccess { - _sessionVerifiedStatus.value = when (encryptionService.verificationState()) { - VerificationState.UNKNOWN -> SessionVerifiedStatus.Unknown - VerificationState.VERIFIED -> SessionVerifiedStatus.Verified - VerificationState.UNVERIFIED -> SessionVerifiedStatus.NotVerified - } - Timber.d("New verification status: ${_sessionVerifiedStatus.value}") - } - } else { - // Otherwise, just check the current verification status from the session verification controller instead - Timber.d("Updating verification status: flow is pending or was finished some time ago") - runCatching { - initVerificationControllerIfNeeded() - _sessionVerifiedStatus.value = if (encryptionService.verificationState() == VerificationState.VERIFIED) { - SessionVerifiedStatus.Verified - } else { - SessionVerifiedStatus.NotVerified - } - Timber.d("New verification status: ${_sessionVerifiedStatus.value}") - } + private fun updateVerificationStatus() { + runCatching { + _sessionVerifiedStatus.value = encryptionService.verificationState().map() + Timber.d("New verification status: ${_sessionVerifiedStatus.value}") } } } +private fun VerificationState.map() = when (this) { + VerificationState.UNKNOWN -> SessionVerifiedStatus.Unknown + VerificationState.VERIFIED -> SessionVerifiedStatus.Verified + VerificationState.UNVERIFIED -> SessionVerifiedStatus.NotVerified +} + private fun RustSessionVerificationData.map(): SessionVerificationData { return use { sessionVerificationData -> when (sessionVerificationData) { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/SessionVerificationRequestDetails.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/SessionVerificationRequestDetails.kt new file mode 100644 index 0000000000..0724c82400 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/SessionVerificationRequestDetails.kt @@ -0,0 +1,22 @@ +/* + * 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.verification + +import io.element.android.libraries.matrix.api.core.DeviceId +import io.element.android.libraries.matrix.api.core.FlowId +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails +import org.matrix.rustcomponents.sdk.SessionVerificationRequestDetails as RustSessionVerificationRequestDetails + +fun RustSessionVerificationRequestDetails.map() = SessionVerificationRequestDetails( + senderId = UserId(senderId), + flowId = FlowId(flowId), + deviceId = DeviceId(deviceId), + displayName = displayName, + firstSeenTimestamp = firstSeenTimestamp.toLong(), +) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt index 36c96d2dfe..22fcad92bf 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.matrix.impl import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustClient import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustSyncService import io.element.android.libraries.matrix.impl.room.FakeTimelineEventTypeFilterFactory @@ -46,5 +47,6 @@ class RustMatrixClientTest { baseCacheDirectory = File(""), clock = FakeSystemClock(), timelineEventTypeFilterFactory = FakeTimelineEventTypeFilterFactory(), + featureFlagService = FakeFeatureFlagService(), ) } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustRoomDirectorySearch.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustRoomDirectorySearch.kt index 0569ee55c8..42e078d1c8 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustRoomDirectorySearch.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustRoomDirectorySearch.kt @@ -21,7 +21,7 @@ class FakeRustRoomDirectorySearch( return isAtLastPage } - override suspend fun search(filter: String?, batchSize: UInt) = simulateLongTask { } + override suspend fun search(filter: String?, batchSize: UInt, viaServerName: String?) = simulateLongTask { } override suspend fun nextPage() = simulateLongTask { } private var listener: RoomDirectorySearchEntriesListener? = null diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryListTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryListTest.kt index 134b90adc9..0eca9edde5 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryListTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryListTest.kt @@ -36,7 +36,7 @@ class RustRoomDirectoryListTest { // Let the mxCallback be ready runCurrent() sut.state.test { - sut.filter("", 20) + sut.filter(filter = "", batchSize = 20, viaServerName = null) roomDirectorySearch.emitResult( listOf( RoomDirectorySearchEntryUpdate.Append(listOf(aRustRoomDescription())) 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 2c69048c39..dd7869cdf7 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 @@ -73,7 +73,11 @@ class FakeMatrixClient( private val encryptionService: FakeEncryptionService = FakeEncryptionService(), private val roomDirectoryService: RoomDirectoryService = FakeRoomDirectoryService(), private val accountManagementUrlString: Result = Result.success(null), - private val resolveRoomAliasResult: (RoomAlias) -> Result = { Result.success(ResolvedRoomAlias(A_ROOM_ID, emptyList())) }, + private val resolveRoomAliasResult: (RoomAlias) -> Result> = { + Result.success( + Optional.of(ResolvedRoomAlias(A_ROOM_ID, emptyList())) + ) + }, private val getRoomPreviewResult: (RoomIdOrAlias, List) -> Result = { _, _ -> Result.failure(AN_EXCEPTION) }, private val clearCacheLambda: () -> Unit = { lambdaError() }, private val userIdServerNameLambda: () -> String = { lambdaError() }, @@ -305,7 +309,7 @@ class FakeMatrixClient( return Result.success(Unit) } - override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result = simulateLongTask { + override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result> = simulateLongTask { resolveRoomAliasResult(roomAlias) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt index 83a9b8e5dd..4b4bed0762 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt @@ -61,6 +61,7 @@ const val A_ROOM_RAW_NAME = "A room raw name" const val A_MESSAGE = "Hello world!" const val A_REPLY = "OK, I'll be there!" const val ANOTHER_MESSAGE = "Hello universe!" +const val A_CAPTION = "A media caption" const val A_REDACTION_REASON = "A redaction reason" 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 2846542890..c8c8273275 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 @@ -18,6 +18,7 @@ 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.matrix.test.FakeMatrixClient import io.element.android.libraries.sessionstorage.api.LoggedInState import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder @@ -41,6 +42,7 @@ class FakeMatrixAuthenticationService( private var loginError: Throwable? = null private var changeServerError: Throwable? = null private var matrixClient: MatrixClient? = null + private var onAuthenticationListener: ((MatrixClient) -> Unit)? = null var getLatestSessionIdLambda: (() -> SessionId?) = { null } @@ -55,6 +57,7 @@ class FakeMatrixAuthenticationService( return it.invoke(sessionId) } return if (matrixClient != null) { + onAuthenticationListener?.invoke(matrixClient!!) Result.success(matrixClient!!) } else { Result.failure(IllegalStateException()) @@ -74,7 +77,10 @@ class FakeMatrixAuthenticationService( } override suspend fun login(username: String, password: String): Result = simulateLongTask { - loginError?.let { Result.failure(it) } ?: Result.success(A_USER_ID) + loginError?.let { Result.failure(it) } ?: run { + onAuthenticationListener?.invoke(matrixClient ?: FakeMatrixClient()) + Result.success(A_USER_ID) + } } override suspend fun importCreatedSession(externalSession: ExternalSession): Result = simulateLongTask { @@ -90,13 +96,21 @@ class FakeMatrixAuthenticationService( } override suspend fun loginWithOidc(callbackUrl: String): Result = simulateLongTask { - loginError?.let { Result.failure(it) } ?: Result.success(A_USER_ID) + loginError?.let { Result.failure(it) } ?: run { + onAuthenticationListener?.invoke(matrixClient ?: FakeMatrixClient()) + Result.success(A_USER_ID) + } } override suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit): Result = simulateLongTask { + onAuthenticationListener?.invoke(matrixClient ?: FakeMatrixClient()) loginWithQrCodeResult(qrCodeData, progress) } + override fun listenToNewMatrixClients(lambda: (MatrixClient) -> Unit) { + onAuthenticationListener = lambda + } + fun givenOidcError(throwable: Throwable?) { oidcError = throwable } 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 b46b45f20e..4a740c0732 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 @@ -87,7 +87,16 @@ class FakeMatrixRoom( private val canRedactOtherResult: (UserId) -> Result = { lambdaError() }, private val canSendStateResult: (UserId, StateEventType) -> Result = { _, _ -> lambdaError() }, private val canUserSendMessageResult: (UserId, MessageEventType) -> Result = { _, _ -> lambdaError() }, - private val sendMediaResult: (ProgressCallback?) -> Result = { lambdaError() }, + private val sendImageResult: (File, File?, ImageInfo, String?, String?, ProgressCallback?) -> Result = + { _, _, _, _, _, _ -> lambdaError() }, + private val sendVideoResult: (File, File?, VideoInfo, String?, String?, ProgressCallback?) -> Result = + { _, _, _, _, _, _ -> lambdaError() }, + private val sendFileResult: (File, FileInfo, ProgressCallback?) -> Result = + { _, _, _ -> lambdaError() }, + private val sendAudioResult: (File, AudioInfo, ProgressCallback?) -> Result = + { _, _, _ -> lambdaError() }, + private val sendVoiceMessageResult: (File, AudioInfo, List, ProgressCallback?) -> Result = + { _, _, _, _ -> lambdaError() }, private val setNameResult: (String) -> Result = { lambdaError() }, private val setTopicResult: (String) -> Result = { lambdaError() }, private val updateAvatarResult: (String, ByteArray) -> Result = { _, _ -> lambdaError() }, @@ -312,44 +321,75 @@ class FakeMatrixRoom( file: File, thumbnailFile: File?, imageInfo: ImageInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback? - ): Result = fakeSendMedia(progressCallback) + ): Result = simulateLongTask { + simulateSendMediaProgress(progressCallback) + sendImageResult( + file, + thumbnailFile, + imageInfo, + caption, + formattedCaption, + progressCallback, + ) + } override suspend fun sendVideo( file: File, thumbnailFile: File?, videoInfo: VideoInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback? - ): Result = fakeSendMedia( - progressCallback - ) + ): Result = simulateLongTask { + simulateSendMediaProgress(progressCallback) + sendVideoResult( + file, + thumbnailFile, + videoInfo, + caption, + formattedCaption, + progressCallback, + ) + } override suspend fun sendAudio( file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback? - ): Result = fakeSendMedia(progressCallback) + ): Result = simulateLongTask { + simulateSendMediaProgress(progressCallback) + sendAudioResult( + file, + audioInfo, + progressCallback, + ) + } override suspend fun sendFile( file: File, fileInfo: FileInfo, progressCallback: ProgressCallback? - ): Result = fakeSendMedia(progressCallback) - - override suspend fun forwardEvent(eventId: EventId, roomIds: List): Result = simulateLongTask { - forwardEventResult(eventId, roomIds) + ): Result = simulateLongTask { + simulateSendMediaProgress(progressCallback) + sendFileResult( + file, + fileInfo, + progressCallback, + ) } - private suspend fun fakeSendMedia(progressCallback: ProgressCallback?): Result = simulateLongTask { + private suspend fun simulateSendMediaProgress(progressCallback: ProgressCallback?) { progressCallbackValues.forEach { (current, total) -> progressCallback?.onProgress(current, total) delay(1) } - sendMediaResult(progressCallback) + } + + override suspend fun forwardEvent(eventId: EventId, roomIds: List): Result = simulateLongTask { + forwardEventResult(eventId, roomIds) } override suspend fun updateAvatar(mimeType: String, data: ByteArray): Result = simulateLongTask { @@ -472,7 +512,15 @@ class FakeMatrixRoom( audioInfo: AudioInfo, waveform: List, progressCallback: ProgressCallback? - ): Result = fakeSendMedia(progressCallback) + ): Result = simulateLongTask { + simulateSendMediaProgress(progressCallback) + sendVoiceMessageResult( + file, + audioInfo, + waveform, + progressCallback, + ) + } override suspend fun typingNotice(isTyping: Boolean): Result { return typingNoticeResult(isTyping) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryList.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryList.kt index afe1e52074..6a3fcb3b45 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryList.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryList.kt @@ -13,10 +13,10 @@ import kotlinx.coroutines.flow.emptyFlow class FakeRoomDirectoryList( override val state: Flow = emptyFlow(), - val filterLambda: (String?, Int) -> Result = { _, _ -> Result.success(Unit) }, + val filterLambda: (String?, Int, String?) -> Result = { _, _, _ -> Result.success(Unit) }, val loadMoreLambda: () -> Result = { Result.success(Unit) } ) : RoomDirectoryList { - override suspend fun filter(filter: String?, batchSize: Int) = filterLambda(filter, batchSize) + override suspend fun filter(filter: String?, batchSize: Int, viaServerName: String?): Result = filterLambda(filter, batchSize, viaServerName) override suspend fun loadMore(): Result = loadMoreLambda() } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt index 0395bc2328..ae40a0a51e 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt @@ -131,15 +131,15 @@ class FakeTimeline( file: File, thumbnailFile: File?, imageInfo: ImageInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback?, ): Result = sendImageLambda( file, thumbnailFile, imageInfo, - body, - formattedBody, + caption, + formattedCaption, progressCallback ) @@ -158,15 +158,15 @@ class FakeTimeline( file: File, thumbnailFile: File?, videoInfo: VideoInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback?, ): Result = sendVideoLambda( file, thumbnailFile, videoInfo, - body, - formattedBody, + caption, + formattedCaption, progressCallback ) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/verification/FakeSessionVerificationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/verification/FakeSessionVerificationService.kt index f6a9ea9fb5..2449ced681 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/verification/FakeSessionVerificationService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/verification/FakeSessionVerificationService.kt @@ -7,79 +7,84 @@ package io.element.android.libraries.matrix.test.verification -import io.element.android.libraries.matrix.api.verification.SessionVerificationData +import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails import io.element.android.libraries.matrix.api.verification.SessionVerificationService +import io.element.android.libraries.matrix.api.verification.SessionVerificationServiceListener import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.api.verification.VerificationFlowState +import io.element.android.tests.testutils.lambda.lambdaError +import io.element.android.tests.testutils.simulateLongTask import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow class FakeSessionVerificationService( initialSessionVerifiedStatus: SessionVerifiedStatus = SessionVerifiedStatus.Unknown, + private val requestVerificationLambda: () -> Unit = { lambdaError() }, + private val cancelVerificationLambda: () -> Unit = { lambdaError() }, + private val approveVerificationLambda: () -> Unit = { lambdaError() }, + private val declineVerificationLambda: () -> Unit = { lambdaError() }, + private val startVerificationLambda: () -> Unit = { lambdaError() }, + private val resetLambda: (Boolean) -> Unit = { lambdaError() }, + private val acknowledgeVerificationRequestLambda: (SessionVerificationRequestDetails) -> Unit = { lambdaError() }, + private val acceptVerificationRequestLambda: () -> Unit = { lambdaError() }, ) : SessionVerificationService { private val _sessionVerifiedStatus = MutableStateFlow(initialSessionVerifiedStatus) private var _verificationFlowState = MutableStateFlow(VerificationFlowState.Initial) private var _needsSessionVerification = MutableStateFlow(true) - var shouldFail = false override val verificationFlowState: StateFlow = _verificationFlowState override val sessionVerifiedStatus: StateFlow = _sessionVerifiedStatus override val needsSessionVerification: Flow = _needsSessionVerification override suspend fun requestVerification() { - if (!shouldFail) { - _verificationFlowState.value = VerificationFlowState.AcceptedVerificationRequest - } else { - _verificationFlowState.value = VerificationFlowState.Failed - } + requestVerificationLambda() } override suspend fun cancelVerification() { - _verificationFlowState.value = VerificationFlowState.Canceled + cancelVerificationLambda() } override suspend fun approveVerification() { - if (!shouldFail) { - _verificationFlowState.value = VerificationFlowState.Finished - } else { - _verificationFlowState.value = VerificationFlowState.Failed - } + approveVerificationLambda() } override suspend fun declineVerification() { - if (!shouldFail) { - _verificationFlowState.value = VerificationFlowState.Canceled - } else { - _verificationFlowState.value = VerificationFlowState.Failed - } + declineVerificationLambda() } - fun triggerReceiveVerificationData(sessionVerificationData: SessionVerificationData) { - _verificationFlowState.value = VerificationFlowState.ReceivedVerificationData(sessionVerificationData) + override suspend fun startVerification() { + startVerificationLambda() } - override suspend fun startVerification() { - _verificationFlowState.value = VerificationFlowState.StartedSasVerification + override suspend fun reset(cancelAnyPendingVerificationAttempt: Boolean) { + resetLambda(cancelAnyPendingVerificationAttempt) } - fun givenVerifiedStatus(status: SessionVerifiedStatus) { - _sessionVerifiedStatus.value = status + var listener: SessionVerificationServiceListener? = null + private set + + override fun setListener(listener: SessionVerificationServiceListener?) { + this.listener = listener } - suspend fun emitVerifiedStatus(status: SessionVerifiedStatus) { - _sessionVerifiedStatus.emit(status) + override suspend fun acknowledgeVerificationRequest(details: SessionVerificationRequestDetails) { + acknowledgeVerificationRequestLambda(details) } - fun givenVerificationFlowState(state: VerificationFlowState) { - _verificationFlowState.value = state + override suspend fun acceptVerificationRequest() = simulateLongTask { + acceptVerificationRequestLambda() } - fun givenNeedsSessionVerification(needsVerification: Boolean) { - _needsSessionVerification.value = needsVerification + suspend fun emitVerificationFlowState(state: VerificationFlowState) { + _verificationFlowState.emit(state) + } + + suspend fun emitVerifiedStatus(status: SessionVerifiedStatus) { + _sessionVerifiedStatus.emit(status) } - override suspend fun reset() { - _verificationFlowState.value = VerificationFlowState.Initial + suspend fun emitNeedsSessionVerification(needsVerification: Boolean) { + _needsSessionVerification.emit(needsVerification) } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt index 38499d15fb..79f2d6a0b6 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt @@ -37,3 +37,9 @@ data class MediaRequestData( } } } + +/** Max width a thumbnail can have according to [the spec](https://spec.matrix.org/v1.10/client-server-api/#thumbnails). */ +const val MAX_THUMBNAIL_WIDTH = 800L + +/** Max height a thumbnail can have according to [the spec](https://spec.matrix.org/v1.10/client-server-api/#thumbnails). */ +const val MAX_THUMBNAIL_HEIGHT = 600L diff --git a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt index e224955164..e0a82e7773 100644 --- a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt +++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt @@ -18,6 +18,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.libraries.core.mimetype.MimeTypes 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.media.AudioInfo @@ -580,7 +581,7 @@ fun anImageInfo(): ImageInfo { return ImageInfo( height = 100, width = 100, - mimetype = "image/jpeg", + mimetype = MimeTypes.Jpeg, size = 1000, thumbnailInfo = null, thumbnailSource = aMediaSource(), diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt index 19b0a0700d..75a4be1cf3 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt @@ -18,8 +18,8 @@ interface MediaPreProcessor { suspend fun process( uri: Uri, mimeType: String, - deleteOriginal: Boolean = false, - compressIfPossible: Boolean + deleteOriginal: Boolean, + compressIfPossible: Boolean, ): Result data class Failure(override val cause: Throwable?) : Exception(cause) diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt index a47629d4f2..09b8de4b41 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt @@ -39,7 +39,7 @@ class MediaSender @Inject constructor( .process( uri = uri, mimeType = mimeType, - deleteOriginal = true, + deleteOriginal = false, compressIfPossible = compressIfPossible, ) .flatMapCatching { info -> @@ -106,8 +106,8 @@ class MediaSender @Inject constructor( file = uploadInfo.file, thumbnailFile = uploadInfo.thumbnailFile, imageInfo = uploadInfo.imageInfo, - body = caption, - formattedBody = formattedCaption, + caption = caption, + formattedCaption = formattedCaption, progressCallback = progressCallback ) } @@ -116,8 +116,8 @@ class MediaSender @Inject constructor( file = uploadInfo.file, thumbnailFile = uploadInfo.thumbnailFile, videoInfo = uploadInfo.videoInfo, - body = caption, - formattedBody = formattedCaption, + caption = caption, + formattedCaption = formattedCaption, progressCallback = progressCallback ) } diff --git a/libraries/mediaupload/api/src/test/kotlin/io/element/android/libraries/mediaupload/api/MediaSenderTest.kt b/libraries/mediaupload/api/src/test/kotlin/io/element/android/libraries/mediaupload/api/MediaSenderTest.kt index 7724855cc3..1cfb46c20e 100644 --- a/libraries/mediaupload/api/src/test/kotlin/io/element/android/libraries/mediaupload/api/MediaSenderTest.kt +++ b/libraries/mediaupload/api/src/test/kotlin/io/element/android/libraries/mediaupload/api/MediaSenderTest.kt @@ -11,6 +11,8 @@ import android.net.Uri import com.google.common.truth.Truth.assertThat import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.core.ProgressCallback +import io.element.android.libraries.matrix.api.media.FileInfo +import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler import io.element.android.libraries.matrix.test.room.FakeMatrixRoom @@ -26,13 +28,14 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner +import java.io.File @RunWith(RobolectricTestRunner::class) class MediaSenderTest { @Test fun `given an attachment when sending it the preprocessor always runs`() = runTest { val preProcessor = FakeMediaPreProcessor() - val sender = aMediaSender(preProcessor) + val sender = createMediaSender(preProcessor) val uri = Uri.parse("content://image.jpg") sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg) @@ -42,17 +45,17 @@ class MediaSenderTest { @Test fun `given an attachment when sending it the MatrixRoom will call sendMedia`() = runTest { - val sendMediaResult = lambdaRecorder> { - Result.success(FakeMediaUploadHandler()) - } + val sendImageResult = + lambdaRecorder> { _, _, _, _, _, _ -> + Result.success(FakeMediaUploadHandler()) + } val room = FakeMatrixRoom( - sendMediaResult = sendMediaResult + sendImageResult = sendImageResult ) - val sender = aMediaSender(room = room) + val sender = createMediaSender(room = room) val uri = Uri.parse("content://image.jpg") sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg) - sendMediaResult.assertions().isCalledOnce() } @Test @@ -60,7 +63,7 @@ class MediaSenderTest { val preProcessor = FakeMediaPreProcessor().apply { givenResult(Result.failure(Exception())) } - val sender = aMediaSender(preProcessor) + val sender = createMediaSender(preProcessor) val uri = Uri.parse("content://image.jpg") val result = sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg) @@ -70,10 +73,14 @@ class MediaSenderTest { @Test fun `given a failure in the media upload when sending the whole process fails`() = runTest { + val sendImageResult = + lambdaRecorder> { _, _, _, _, _, _ -> + Result.failure(Exception()) + } val room = FakeMatrixRoom( - sendMediaResult = { Result.failure(Exception()) } + sendImageResult = sendImageResult ) - val sender = aMediaSender(room = room) + val sender = createMediaSender(room = room) val uri = Uri.parse("content://image.jpg") val result = sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg) @@ -84,10 +91,13 @@ class MediaSenderTest { @OptIn(ExperimentalCoroutinesApi::class) @Test fun `given a cancellation in the media upload when sending the job is cancelled`() = runTest(StandardTestDispatcher()) { + val sendFileResult = lambdaRecorder> { _, _, _ -> + Result.success(FakeMediaUploadHandler()) + } val room = FakeMatrixRoom( - sendMediaResult = { Result.success(FakeMediaUploadHandler()) } + sendFileResult = sendFileResult ) - val sender = aMediaSender(room = room) + val sender = createMediaSender(room = room) val sendJob = launch { val uri = Uri.parse("content://image.jpg") sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg) @@ -106,9 +116,10 @@ class MediaSenderTest { // Assert the file is not being uploaded anymore assertThat(sender.hasOngoingMediaUploads).isFalse() + sendFileResult.assertions().isCalledOnce() } - private fun aMediaSender( + private fun createMediaSender( preProcessor: MediaPreProcessor = FakeMediaPreProcessor(), room: MatrixRoom = FakeMatrixRoom(), sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(), diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt index 1ea1acae82..54b880369b 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt @@ -13,6 +13,7 @@ import android.media.MediaMetadataRetriever import android.net.Uri import androidx.exifinterface.media.ExifInterface import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.androidutils.file.TemporaryUriDeleter import io.element.android.libraries.androidutils.file.createTmpFile import io.element.android.libraries.androidutils.file.getFileName import io.element.android.libraries.androidutils.file.safeRenameTo @@ -36,6 +37,7 @@ import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.withContext +import timber.log.Timber import java.io.File import java.io.InputStream import javax.inject.Inject @@ -49,6 +51,7 @@ class AndroidMediaPreProcessor @Inject constructor( private val imageCompressor: ImageCompressor, private val videoCompressor: VideoCompressor, private val coroutineDispatchers: CoroutineDispatchers, + private val temporaryUriDeleter: TemporaryUriDeleter, ) : MediaPreProcessor { companion object { /** @@ -82,8 +85,11 @@ class AndroidMediaPreProcessor @Inject constructor( } if (deleteOriginal) { tryOrNull { + Timber.w("Deleting original uri $uri") contentResolver.delete(uri, null, null) } + } else { + temporaryUriDeleter.delete(uri) } result.postProcess(uri) } @@ -125,9 +131,13 @@ class AndroidMediaPreProcessor @Inject constructor( val compressionResult = imageCompressor.compressToTmpFile( inputStreamProvider = { contentResolver.openInputStream(uri)!! }, resizeMode = ResizeMode.Approximate(IMAGE_SCALE_REF_SIZE, IMAGE_SCALE_REF_SIZE), + mimeType = mimeType, orientation = orientation, ).getOrThrow() - val thumbnailResult = thumbnailFactory.createImageThumbnail(compressionResult.file) + val thumbnailResult = thumbnailFactory.createImageThumbnail( + file = compressionResult.file, + mimeType = mimeType, + ) val imageInfo = compressionResult.toImageInfo( mimeType = mimeType, thumbnailResult = thumbnailResult @@ -142,7 +152,10 @@ class AndroidMediaPreProcessor @Inject constructor( suspend fun processImageWithoutCompression(): MediaUploadInfo { val file = copyToTmpFile(uri) - val thumbnailResult = thumbnailFactory.createImageThumbnail(file) + val thumbnailResult = thumbnailFactory.createImageThumbnail( + file = file, + mimeType = mimeType, + ) val imageInfo = contentResolver.openInputStream(uri).use { input -> val bitmap = BitmapFactory.decodeStream(input, null, null)!! ImageInfo( @@ -171,17 +184,13 @@ class AndroidMediaPreProcessor @Inject constructor( } private suspend fun processVideo(uri: Uri, mimeType: String?, shouldBeCompressed: Boolean): MediaUploadInfo { - val resultFile = if (shouldBeCompressed) { - videoCompressor.compress(uri) - .onEach { - // TODO handle progress - } - .filterIsInstance() - .first() - .file - } else { - copyToTmpFile(uri) - } + val resultFile = videoCompressor.compress(uri, shouldBeCompressed) + .onEach { + // TODO handle progress + } + .filterIsInstance() + .first() + .file val thumbnailInfo = thumbnailFactory.createVideoThumbnail(resultFile) val videoInfo = extractVideoMetadata(resultFile, mimeType, thumbnailInfo) return MediaUploadInfo.Video( @@ -207,12 +216,44 @@ class AndroidMediaPreProcessor @Inject constructor( private fun removeSensitiveImageMetadata(file: File) { // Remove GPS info, user comments and subject location tags - val exifInterface = ExifInterface(file) - // See ExifInterface.TAG_GPS_INFO_IFD_POINTER - exifInterface.setAttribute("GPSInfoIFDPointer", null) - exifInterface.setAttribute(ExifInterface.TAG_USER_COMMENT, null) - exifInterface.setAttribute(ExifInterface.TAG_SUBJECT_LOCATION, null) - tryOrNull { exifInterface.saveAttributes() } + ExifInterface(file).apply { + // See ExifInterface.TAG_GPS_INFO_IFD_POINTER + setAttribute("GPSInfoIFDPointer", null) + setAttribute(ExifInterface.TAG_USER_COMMENT, null) + setAttribute(ExifInterface.TAG_IMAGE_DESCRIPTION, null) + + setAttribute(ExifInterface.TAG_GPS_VERSION_ID, null) + setAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF, null) + setAttribute(ExifInterface.TAG_GPS_ALTITUDE, null) + setAttribute(ExifInterface.TAG_GPS_TIMESTAMP, null) + setAttribute(ExifInterface.TAG_GPS_DATESTAMP, null) + setAttribute(ExifInterface.TAG_GPS_SATELLITES, null) + setAttribute(ExifInterface.TAG_GPS_STATUS, null) + setAttribute(ExifInterface.TAG_GPS_MEASURE_MODE, null) + setAttribute(ExifInterface.TAG_GPS_DOP, null) + setAttribute(ExifInterface.TAG_GPS_SPEED_REF, null) + setAttribute(ExifInterface.TAG_GPS_SPEED, null) + setAttribute(ExifInterface.TAG_GPS_TRACK_REF, null) + setAttribute(ExifInterface.TAG_GPS_TRACK, null) + setAttribute(ExifInterface.TAG_GPS_IMG_DIRECTION_REF, null) + setAttribute(ExifInterface.TAG_GPS_IMG_DIRECTION, null) + setAttribute(ExifInterface.TAG_GPS_MAP_DATUM, null) + setAttribute(ExifInterface.TAG_GPS_DEST_BEARING_REF, null) + setAttribute(ExifInterface.TAG_GPS_DEST_BEARING, null) + setAttribute(ExifInterface.TAG_GPS_DEST_DISTANCE_REF, null) + setAttribute(ExifInterface.TAG_GPS_DEST_DISTANCE, null) + setAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD, null) + setAttribute(ExifInterface.TAG_GPS_AREA_INFORMATION, null) + setAttribute(ExifInterface.TAG_GPS_DIFFERENTIAL, null) + setAttribute(ExifInterface.TAG_GPS_H_POSITIONING_ERROR, null) + setAttribute(ExifInterface.TAG_GPS_LATITUDE, null) + setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, null) + setAttribute(ExifInterface.TAG_GPS_LONGITUDE, null) + setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, null) + setAttribute(ExifInterface.TAG_GPS_DEST_LONGITUDE, null) + setAttribute(ExifInterface.TAG_GPS_DEST_LONGITUDE_REF, null) + tryOrNull { saveAttributes() } + } } private suspend fun createTmpFileWithInput(inputStream: InputStream): File? { diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ImageCompressor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ImageCompressor.kt index 69d2e41baa..a08a9222e0 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ImageCompressor.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ImageCompressor.kt @@ -34,14 +34,16 @@ class ImageCompressor @Inject constructor( suspend fun compressToTmpFile( inputStreamProvider: () -> InputStream, resizeMode: ResizeMode, - format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG, + mimeType: String, orientation: Int = ExifInterface.ORIENTATION_UNDEFINED, desiredQuality: Int = 78, ): Result = withContext(dispatchers.io) { runCatching { + val format = mimeTypeToCompressFormat(mimeType) + val extension = mimeTypeToCompressFileExtension(mimeType) val compressedBitmap = compressToBitmap(inputStreamProvider, resizeMode, orientation).getOrThrow() // Encode bitmap to the destination temporary file - val tmpFile = context.createTmpFile(extension = "jpeg") + val tmpFile = context.createTmpFile(extension = extension) tmpFile.outputStream().use { compressedBitmap.compress(format, desiredQuality, it) } diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/MimeTypeUtil.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/MimeTypeUtil.kt new file mode 100644 index 0000000000..6ab327e6cb --- /dev/null +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/MimeTypeUtil.kt @@ -0,0 +1,26 @@ +/* + * 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.mediaupload.impl + +import android.graphics.Bitmap +import io.element.android.libraries.core.mimetype.MimeTypes + +fun mimeTypeToCompressFormat(mimeType: String) = when (mimeType) { + MimeTypes.Png -> Bitmap.CompressFormat.PNG + else -> Bitmap.CompressFormat.JPEG +} + +fun mimeTypeToCompressFileExtension(mimeType: String) = when (mimeType) { + MimeTypes.Png -> "png" + else -> "jpeg" +} + +fun mimeTypeToThumbnailMimeType(mimeType: String) = when (mimeType) { + MimeTypes.Png -> MimeTypes.Png + else -> MimeTypes.Jpeg +} diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ThumbnailFactory.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ThumbnailFactory.kt index 7258102eb3..95d3e2c4f8 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ThumbnailFactory.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ThumbnailFactory.kt @@ -53,8 +53,11 @@ class ThumbnailFactory @Inject constructor( private val sdkIntProvider: BuildVersionSdkIntProvider ) { @SuppressLint("NewApi") - suspend fun createImageThumbnail(file: File): ThumbnailResult? { - return createThumbnail { cancellationSignal -> + suspend fun createImageThumbnail( + file: File, + mimeType: String, + ): ThumbnailResult? { + return createThumbnail(mimeType = mimeType) { cancellationSignal -> try { // This API works correctly with GIF if (sdkIntProvider.isAtLeast(Build.VERSION_CODES.Q)) { @@ -83,7 +86,7 @@ class ThumbnailFactory @Inject constructor( } suspend fun createVideoThumbnail(file: File): ThumbnailResult? { - return createThumbnail { + return createThumbnail(mimeType = MimeTypes.Jpeg) { MediaMetadataRetriever().runAndRelease { setDataSource(context, file.toUri()) getFrameAtTime(VIDEO_THUMB_FRAME) @@ -91,7 +94,10 @@ class ThumbnailFactory @Inject constructor( } } - private suspend fun createThumbnail(bitmapFactory: (CancellationSignal) -> Bitmap?): ThumbnailResult? = suspendCancellableCoroutine { continuation -> + private suspend fun createThumbnail( + mimeType: String, + bitmapFactory: (CancellationSignal) -> Bitmap?, + ): ThumbnailResult? = suspendCancellableCoroutine { continuation -> val cancellationSignal = CancellationSignal() continuation.invokeOnCancellation { cancellationSignal.cancel() @@ -101,9 +107,11 @@ class ThumbnailFactory @Inject constructor( continuation.resume(null) return@suspendCancellableCoroutine } - val thumbnailFile = context.createTmpFile(extension = "jpeg") + val format = mimeTypeToCompressFormat(mimeType) + val extension = mimeTypeToCompressFileExtension(mimeType) + val thumbnailFile = context.createTmpFile(extension = extension) thumbnailFile.outputStream().use { outputStream -> - bitmapThumbnail.compress(Bitmap.CompressFormat.JPEG, 80, outputStream) + bitmapThumbnail.compress(format, 78, outputStream) } val blurhash = BlurHash.encode(bitmapThumbnail, 3, 3) val thumbnailResult = ThumbnailResult( @@ -111,7 +119,7 @@ class ThumbnailFactory @Inject constructor( info = ThumbnailInfo( height = bitmapThumbnail.height.toLong(), width = bitmapThumbnail.width.toLong(), - mimetype = MimeTypes.Jpeg, + mimetype = mimeTypeToThumbnailMimeType(mimeType), size = thumbnailFile.length() ), blurhash = blurhash diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/VideoCompressor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/VideoCompressor.kt index 760679b32f..8db2fd878a 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/VideoCompressor.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/VideoCompressor.kt @@ -24,12 +24,20 @@ import javax.inject.Inject class VideoCompressor @Inject constructor( @ApplicationContext private val context: Context, ) { - fun compress(uri: Uri) = callbackFlow { + fun compress(uri: Uri, shouldBeCompressed: Boolean) = callbackFlow { val tmpFile = context.createTmpFile(extension = "mp4") val future = Transcoder.into(tmpFile.path) .setVideoTrackStrategy( DefaultVideoStrategy.Builder() - .addResizer(AtMostResizer(720, 480)) + .addResizer( + AtMostResizer( + if (shouldBeCompressed) { + 720 + } else { + 1080 + } + ) + ) .build() ) .addDataSource(context, uri) diff --git a/libraries/mediaupload/impl/src/test/assets/image.jpeg b/libraries/mediaupload/impl/src/test/assets/image.jpeg new file mode 100644 index 0000000000..3d75513889 --- /dev/null +++ b/libraries/mediaupload/impl/src/test/assets/image.jpeg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:77276f9b174f8823eaf787ab0a659199ef5d30c0361ec8b9b4f0890adb1907a1 +size 9986336 diff --git a/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt b/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt index 832d82d210..a9e9c3f7c2 100644 --- a/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt +++ b/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt @@ -8,10 +8,12 @@ package io.element.android.libraries.mediaupload.impl import android.content.Context +import android.net.Uri import android.os.Build import androidx.core.net.toUri import androidx.test.platform.app.InstrumentationRegistry import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.androidutils.file.TemporaryUriDeleter import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.FileInfo @@ -21,6 +23,8 @@ import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.mediaupload.api.MediaUploadInfo import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider +import io.element.android.tests.testutils.fake.FakeTemporaryUriDeleter +import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest @@ -35,113 +39,200 @@ import kotlin.time.Duration @RunWith(RobolectricTestRunner::class) class AndroidMediaPreProcessorTest { - @Test - fun `test processing image`() = runTest { + private suspend fun TestScope.process( + asset: Asset, + compressIfPossible: Boolean, + sdkIntVersion: Int = Build.VERSION_CODES.P, + deleteOriginal: Boolean = false, + ): MediaUploadInfo { val context = InstrumentationRegistry.getInstrumentation().context - val sut = createAndroidMediaPreProcessor(context) - val file = getFileFromAssets(context, "image.png") + val deleteCallback = lambdaRecorder {} + val sut = createAndroidMediaPreProcessor( + context = context, + sdkIntVersion = sdkIntVersion, + temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), + ) + val file = getFileFromAssets(context, asset.filename) val result = sut.process( uri = file.toUri(), - mimeType = MimeTypes.Png, - deleteOriginal = false, - compressIfPossible = true, + mimeType = asset.mimeType, + deleteOriginal = deleteOriginal, + compressIfPossible = compressIfPossible, ) val data = result.getOrThrow() - assertThat(data.file.path).endsWith("image.png") - val info = data as MediaUploadInfo.Image + assertThat(data.file.path).endsWith(asset.filename) + deleteCallback.assertions().isCalledExactly(if (deleteOriginal) 0 else 1) + return data + } + + @Test + fun `test processing png`() = runTest { + val mediaUploadInfo = process( + asset = assetImagePng, + compressIfPossible = true, + ) + val info = mediaUploadInfo as MediaUploadInfo.Image assertThat(info.thumbnailFile).isNotNull() assertThat(info.imageInfo).isEqualTo( ImageInfo( - height = 1_178, - width = 1_818, - mimetype = MimeTypes.Png, - size = 109_908, - ThumbnailInfo(height = 294, width = 454, mimetype = "image/jpeg", size = 4484), + height = assetImagePng.height, + width = assetImagePng.width, + mimetype = assetImagePng.mimeType, + size = 2_026_433, + ThumbnailInfo(height = 25, width = 25, mimetype = MimeTypes.Png, size = 91), thumbnailSource = null, - blurhash = "K13]7q%zWC00R4of%\$baad" + blurhash = "K00000fQfQfQfQfQfQfQfQ" ) ) - assertThat(file.exists()).isTrue() } @Test - fun `test processing image api Q`() = runTest { - val context = InstrumentationRegistry.getInstrumentation().context - val sut = createAndroidMediaPreProcessor(context, sdkIntVersion = Build.VERSION_CODES.Q) - val file = getFileFromAssets(context, "image.png") - val result = sut.process( - uri = file.toUri(), - mimeType = MimeTypes.Png, - deleteOriginal = false, + fun `test processing png api Q`() = runTest { + val mediaUploadInfo = process( + asset = assetImagePng, compressIfPossible = true, + sdkIntVersion = Build.VERSION_CODES.Q, ) - val data = result.getOrThrow() - assertThat(data.file.path).endsWith("image.png") - val info = data as MediaUploadInfo.Image + val info = mediaUploadInfo as MediaUploadInfo.Image assertThat(info.thumbnailFile).isNull() assertThat(info.imageInfo).isEqualTo( ImageInfo( - height = 1_178, - width = 1_818, - mimetype = MimeTypes.Png, - size = 109_908, + height = assetImagePng.height, + width = assetImagePng.width, + mimetype = assetImagePng.mimeType, + size = 2_026_433, thumbnailInfo = null, thumbnailSource = null, blurhash = null, ) ) - assertThat(file.exists()).isTrue() } @Test - fun `test processing image no compression`() = runTest { - val context = InstrumentationRegistry.getInstrumentation().context - val sut = createAndroidMediaPreProcessor(context) - val file = getFileFromAssets(context, "image.png") - val result = sut.process( - uri = file.toUri(), - mimeType = MimeTypes.Png, - deleteOriginal = false, + fun `test processing png no compression`() = runTest { + val mediaUploadInfo = process( + asset = assetImagePng, compressIfPossible = false, - ).getOrThrow() - assertThat(result.file.path).endsWith("image.png") - val info = result as MediaUploadInfo.Image + ) + val info = mediaUploadInfo as MediaUploadInfo.Image assertThat(info.thumbnailFile).isNotNull() assertThat(info.imageInfo).isEqualTo( ImageInfo( - height = 1_178, - width = 1_818, - mimetype = MimeTypes.Png, - size = 1_856_786, - thumbnailInfo = ThumbnailInfo(height = 25, width = 25, mimetype = MimeTypes.Jpeg, size = 643), + height = assetImagePng.height, + width = assetImagePng.width, + mimetype = assetImagePng.mimeType, + size = assetImagePng.size, + thumbnailInfo = ThumbnailInfo(height = 25, width = 25, mimetype = MimeTypes.Png, size = 91), thumbnailSource = null, blurhash = "K00000fQfQfQfQfQfQfQfQ", ) ) - assertThat(file.exists()).isTrue() } @Test - fun `test processing image and delete`() = runTest { - val context = InstrumentationRegistry.getInstrumentation().context - val sut = createAndroidMediaPreProcessor(context) - val file = getFileFromAssets(context, "image.png") - val result = sut.process( - uri = file.toUri(), - mimeType = MimeTypes.Png, + fun `test processing png and delete`() = runTest { + val mediaUploadInfo = process( + asset = assetImagePng, + compressIfPossible = false, deleteOriginal = true, + ) + val info = mediaUploadInfo as MediaUploadInfo.Image + assertThat(info.thumbnailFile).isNotNull() + assertThat(info.imageInfo).isEqualTo( + ImageInfo( + height = assetImagePng.height, + width = assetImagePng.width, + mimetype = assetImagePng.mimeType, + size = assetImagePng.size, + thumbnailInfo = ThumbnailInfo(height = 25, width = 25, mimetype = MimeTypes.Png, size = 91), + thumbnailSource = null, + blurhash = "K00000fQfQfQfQfQfQfQfQ", + ) + ) + // Does not work + // assertThat(file.exists()).isFalse() + } + + @Test + fun `test processing jpeg`() = runTest { + val mediaUploadInfo = process( + asset = assetImageJpeg, + compressIfPossible = true, + ) + val info = mediaUploadInfo as MediaUploadInfo.Image + assertThat(info.thumbnailFile).isNotNull() + assertThat(info.imageInfo).isEqualTo( + ImageInfo( + height = 979, + width = 3006, + mimetype = MimeTypes.Jpeg, + size = 84_845, + ThumbnailInfo(height = 244, width = 751, mimetype = MimeTypes.Jpeg, size = 7_178), + thumbnailSource = null, + blurhash = "K07gBzX=j_D4xZjoaSe,s:" + ) + ) + } + + @Test + fun `test processing jpeg api Q`() = runTest { + val mediaUploadInfo = process( + asset = assetImageJpeg, + compressIfPossible = true, + sdkIntVersion = Build.VERSION_CODES.Q, + ) + val info = mediaUploadInfo as MediaUploadInfo.Image + assertThat(info.thumbnailFile).isNull() + assertThat(info.imageInfo).isEqualTo( + ImageInfo( + height = 979, + width = 3_006, + mimetype = MimeTypes.Jpeg, + size = 84_845, + thumbnailInfo = null, + thumbnailSource = null, + blurhash = null, + ) + ) + } + + @Test + fun `test processing jpeg no compression`() = runTest { + val mediaUploadInfo = process( + asset = assetImageJpeg, compressIfPossible = false, - ).getOrThrow() - assertThat(result.file.path).endsWith("image.png") - val info = result as MediaUploadInfo.Image + ) + val info = mediaUploadInfo as MediaUploadInfo.Image assertThat(info.thumbnailFile).isNotNull() assertThat(info.imageInfo).isEqualTo( ImageInfo( - height = 1_178, - width = 1_818, - mimetype = MimeTypes.Png, - size = 1_856_786, - thumbnailInfo = ThumbnailInfo(height = 25, width = 25, mimetype = MimeTypes.Jpeg, size = 643), + height = assetImageJpeg.height, + width = assetImageJpeg.width, + mimetype = assetImageJpeg.mimeType, + size = assetImageJpeg.size, + thumbnailInfo = ThumbnailInfo(height = 6, width = 6, mimetype = MimeTypes.Jpeg, size = 631), + thumbnailSource = null, + blurhash = "K00000fQfQfQfQfQfQfQfQ", + ) + ) + } + + @Test + fun `test processing jpeg and delete`() = runTest { + val mediaUploadInfo = process( + asset = assetImageJpeg, + compressIfPossible = false, + deleteOriginal = true, + ) + val info = mediaUploadInfo as MediaUploadInfo.Image + assertThat(info.thumbnailFile).isNotNull() + assertThat(info.imageInfo).isEqualTo( + ImageInfo( + height = assetImageJpeg.height, + width = assetImageJpeg.width, + mimetype = assetImageJpeg.mimeType, + size = assetImageJpeg.size, + thumbnailInfo = ThumbnailInfo(height = 6, width = 6, mimetype = MimeTypes.Jpeg, size = 631), thumbnailSource = null, blurhash = "K00000fQfQfQfQfQfQfQfQ", ) @@ -152,70 +243,50 @@ class AndroidMediaPreProcessorTest { @Test fun `test processing gif`() = runTest { - val context = InstrumentationRegistry.getInstrumentation().context - val sut = createAndroidMediaPreProcessor(context) - val file = getFileFromAssets(context, "animated_gif.gif") - val result = sut.process( - uri = file.toUri(), - mimeType = MimeTypes.Gif, - deleteOriginal = false, + val mediaUploadInfo = process( + asset = assetAnimatedGif, compressIfPossible = true, - ).getOrThrow() - assertThat(result.file.path).endsWith("animated_gif.gif") - val info = result as MediaUploadInfo.Image + ) + val info = mediaUploadInfo as MediaUploadInfo.Image assertThat(info.thumbnailFile).isNotNull() assertThat(info.imageInfo).isEqualTo( ImageInfo( - height = 600, - width = 800, - mimetype = MimeTypes.Gif, - size = 687_979, + height = assetAnimatedGif.height, + width = assetAnimatedGif.width, + mimetype = assetAnimatedGif.mimeType, + size = assetAnimatedGif.size, thumbnailInfo = ThumbnailInfo(height = 50, width = 50, mimetype = MimeTypes.Jpeg, size = 691), thumbnailSource = null, blurhash = "K00000fQfQfQfQfQfQfQfQ", ) ) - assertThat(file.exists()).isTrue() } @Test fun `test processing file`() = runTest { - val context = InstrumentationRegistry.getInstrumentation().context - val sut = createAndroidMediaPreProcessor(context) - val file = getFileFromAssets(context, "text.txt") - val result = sut.process( - uri = file.toUri(), - mimeType = MimeTypes.PlainText, - deleteOriginal = false, + val mediaUploadInfo = process( + asset = assetText, compressIfPossible = true, - ).getOrThrow() - assertThat(result.file.path).endsWith("text.txt") - val info = result as MediaUploadInfo.AnyFile + ) + val info = mediaUploadInfo as MediaUploadInfo.AnyFile assertThat(info.fileInfo).isEqualTo( FileInfo( - mimetype = MimeTypes.PlainText, - size = 13, + mimetype = assetText.mimeType, + size = assetText.size, thumbnailInfo = null, thumbnailSource = null, ) ) - assertThat(file.exists()).isTrue() } @Ignore("Compressing video is not working with Robolectric") @Test fun `test processing video`() = runTest { - val context = InstrumentationRegistry.getInstrumentation().context - val sut = createAndroidMediaPreProcessor(context) - val file = getFileFromAssets(context, "video.mp4") - val result = sut.process( - uri = file.toUri(), - mimeType = MimeTypes.Mp4, - deleteOriginal = false, + val mediaUploadInfo = process( + asset = assetVideo, compressIfPossible = true, - ).getOrThrow() - assertThat(result.file.path).endsWith("video.mp4") - val info = result as MediaUploadInfo.Video + ) + val info = mediaUploadInfo as MediaUploadInfo.Video assertThat(info.thumbnailFile).isNotNull() assertThat(info.videoInfo).isEqualTo( VideoInfo( @@ -230,22 +301,16 @@ class AndroidMediaPreProcessorTest { blurhash = null, ) ) - assertThat(file.exists()).isTrue() } + @Ignore("Compressing video is not working with Robolectric") @Test fun `test processing video no compression`() = runTest { - val context = InstrumentationRegistry.getInstrumentation().context - val sut = createAndroidMediaPreProcessor(context) - val file = getFileFromAssets(context, "video.mp4") - val result = sut.process( - uri = file.toUri(), - mimeType = MimeTypes.Mp4, - deleteOriginal = false, + val mediaUploadInfo = process( + asset = assetVideo, compressIfPossible = false, - ).getOrThrow() - assertThat(result.file.path).endsWith("video.mp4") - val info = result as MediaUploadInfo.Video + ) + val info = mediaUploadInfo as MediaUploadInfo.Video // Computing thumbnailFile is failing with Robolectric assertThat(info.thumbnailFile).isNull() assertThat(info.videoInfo).isEqualTo( @@ -263,22 +328,15 @@ class AndroidMediaPreProcessorTest { blurhash = null, ) ) - assertThat(file.exists()).isTrue() } @Test fun `test processing audio`() = runTest { - val context = InstrumentationRegistry.getInstrumentation().context - val sut = createAndroidMediaPreProcessor(context) - val file = getFileFromAssets(context, "sample3s.mp3") - val result = sut.process( - uri = file.toUri(), - mimeType = MimeTypes.Mp3, - deleteOriginal = false, + val mediaUploadInfo = process( + asset = assetAudio, compressIfPossible = true, - ).getOrThrow() - assertThat(result.file.path).endsWith("sample3s.mp3") - val info = result as MediaUploadInfo.Audio + ) + val info = mediaUploadInfo as MediaUploadInfo.Audio assertThat(info.audioInfo).isEqualTo( AudioInfo( // Not available with Robolectric? @@ -287,7 +345,6 @@ class AndroidMediaPreProcessorTest { mimetype = MimeTypes.Mp3, ) ) - assertThat(file.exists()).isTrue() } @Test @@ -309,13 +366,15 @@ class AndroidMediaPreProcessorTest { private fun TestScope.createAndroidMediaPreProcessor( context: Context, - sdkIntVersion: Int = Build.VERSION_CODES.P + sdkIntVersion: Int = Build.VERSION_CODES.P, + temporaryUriDeleter: TemporaryUriDeleter = FakeTemporaryUriDeleter(), ) = AndroidMediaPreProcessor( context = context, thumbnailFactory = ThumbnailFactory(context, FakeBuildVersionSdkIntProvider(sdkIntVersion)), imageCompressor = ImageCompressor(context, testCoroutineDispatchers()), videoCompressor = VideoCompressor(context), coroutineDispatchers = testCoroutineDispatchers(), + temporaryUriDeleter = temporaryUriDeleter, ) @Throws(IOException::class) diff --git a/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/Asset.kt b/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/Asset.kt new file mode 100644 index 0000000000..d923d9977a --- /dev/null +++ b/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/Asset.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.libraries.mediaupload.impl + +import io.element.android.libraries.core.mimetype.MimeTypes + +data class Asset( + val filename: String, + val mimeType: String, + val size: Long, + val width: Long?, + val height: Long?, +) + +/** + * "image.png" is a 1_818 x 1_178 PNG image with a size of 1_856_786 bytes. + */ +val assetImagePng = Asset( + filename = "image.png", + mimeType = MimeTypes.Png, + size = 1_856_786, + width = 1_818, + height = 1_178, +) + +/** + * "image.jpeg" is a 12_024 x 3_916, JPEG image with a size of 9_986_336 bytes. + */ +val assetImageJpeg = Asset( + filename = "image.jpeg", + mimeType = MimeTypes.Jpeg, + size = 9_986_336, + width = 12_024, + height = 3_916, +) + +/** + * "video.mp4" is a 1_280 x 720, MP4 video with a size of 1_673_712 bytes. + */ +val assetVideo = Asset( + filename = "video.mp4", + mimeType = MimeTypes.Mp4, + size = 1_673_712, + width = 1_280, + height = 720, +) + +/** + * "sample3s.mp3" is a 3 seconds MP3 audio file with a size of 52_079 bytes. + */ +val assetAudio = Asset( + filename = "sample3s.mp3", + mimeType = MimeTypes.Mp3, + size = 52_079, + width = null, + height = null, +) + +/** + * "text.txt" is a 13 bytes text file. + */ +val assetText = Asset( + filename = "text.txt", + mimeType = MimeTypes.PlainText, + size = 13, + width = null, + height = null, +) + +/** + * "animated_gif.gif" is a 800 x 600, GIF image with a size of 687_979 bytes. + */ +val assetAnimatedGif = Asset( + filename = "animated_gif.gif", + mimeType = MimeTypes.Gif, + size = 687_979, + width = 800, + height = 600, +) diff --git a/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt b/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt index 634cb24be7..efb3b77ed8 100644 --- a/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt +++ b/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt @@ -11,6 +11,8 @@ import android.net.Uri import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.FileInfo +import io.element.android.libraries.matrix.api.media.ImageInfo +import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.mediaupload.api.MediaUploadInfo import io.element.android.tests.testutils.simulateLongTask @@ -61,4 +63,45 @@ class FakeMediaPreProcessor : MediaPreProcessor { ) ) } + + fun givenImageResult() { + givenResult( + Result.success( + MediaUploadInfo.Image( + file = File("image.jpg"), + imageInfo = ImageInfo( + height = 100, + width = 100, + mimetype = MimeTypes.Jpeg, + size = 1000, + thumbnailInfo = null, + thumbnailSource = null, + blurhash = null, + ), + thumbnailFile = null, + ) + ) + ) + } + + fun givenVideoResult() { + givenResult( + Result.success( + MediaUploadInfo.Video( + file = File("image.jpg"), + videoInfo = VideoInfo( + duration = 1000.seconds, + height = 100, + width = 100, + mimetype = MimeTypes.Mp4, + size = 1000, + thumbnailInfo = null, + thumbnailSource = null, + blurhash = null, + ), + thumbnailFile = null, + ) + ) + ) + } } diff --git a/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/webview/OidcView.kt b/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/webview/OidcView.kt index 055d516f97..d58eebf8ae 100644 --- a/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/webview/OidcView.kt +++ b/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/webview/OidcView.kt @@ -86,6 +86,7 @@ fun OidcView( javaScriptEnabled = true allowContentAccess = true allowFileAccess = true + @Suppress("DEPRECATION") databaseEnabled = true domStorageEnabled = true } diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt index 3fa33eab93..32d92a4a76 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt @@ -83,7 +83,7 @@ class DefaultSessionPreferencesStore( override fun isSessionVerificationSkipped(): Flow = get(skipSessionVerification) { false } override suspend fun setCompressMedia(compress: Boolean) = update(compressMedia, compress) - override fun doesCompressMedia(): Flow = get(compressMedia) { false } + override fun doesCompressMedia(): Flow = get(compressMedia) { true } override suspend fun clear() { dataStoreFile.safeDelete() diff --git a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemorySessionPreferencesStore.kt b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemorySessionPreferencesStore.kt index dde117adc0..7bb2258c6e 100644 --- a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemorySessionPreferencesStore.kt +++ b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemorySessionPreferencesStore.kt @@ -18,7 +18,7 @@ class InMemorySessionPreferencesStore( isSendTypingNotificationsEnabled: Boolean = true, isRenderTypingNotificationsEnabled: Boolean = true, isSessionVerificationSkipped: Boolean = false, - doesCompressMedia: Boolean = false, + doesCompressMedia: Boolean = true, ) : SessionPreferencesStore { private val isSharePresenceEnabled = MutableStateFlow(isSharePresenceEnabled) private val isSendPublicReadReceiptsEnabled = MutableStateFlow(isSendPublicReadReceiptsEnabled) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt index 2fb6186bd4..97e80f4dbf 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt @@ -107,6 +107,7 @@ class DefaultNotifiableEventResolver @Inject constructor( senderDisambiguatedDisplayName = senderDisambiguatedDisplayName, body = messageBody, imageUriString = content.fetchImageIfPresent(client)?.toString(), + imageMimeType = content.getImageMimetype(), roomName = roomDisplayName, roomIsDm = isDm, roomAvatarPath = roomAvatarUrl, @@ -316,6 +317,17 @@ class DefaultNotifiableEventResolver @Inject constructor( } .getOrNull() } + + private suspend fun NotificationContent.MessageLike.RoomMessage.getImageMimetype(): String? { + if (appPreferencesStore.doesHideImagesAndVideosFlow().first()) { + return null + } + return when (val messageType = messageType) { + is ImageMessageType -> messageType.info?.mimetype + is VideoMessageType -> null // Use the thumbnail here? + else -> null + } + } } @Suppress("LongParameterList") @@ -333,6 +345,7 @@ internal fun buildNotifiableMessageEvent( // We cannot use Uri? type here, as that could trigger a // NotSerializableException when persisting this to storage imageUriString: String? = null, + imageMimeType: String? = null, threadId: ThreadId? = null, roomName: String? = null, roomIsDm: Boolean = false, @@ -358,6 +371,7 @@ internal fun buildNotifiableMessageEvent( senderDisambiguatedDisplayName = senderDisambiguatedDisplayName, body = body, imageUriString = imageUriString, + imageMimeType = imageMimeType, threadId = threadId, roomName = roomName, roomIsDm = roomIsDm, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt index 97c53e945b..a3d553ba91 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt @@ -150,6 +150,7 @@ class NotificationBroadcastReceiverHandler @Inject constructor( ?: stringProvider.getString(R.string.notification_sender_me), body = message, imageUriString = null, + imageMimeType = null, threadId = threadId, roomName = room.displayName, roomIsDm = room.isDm, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt index dc57b0fe7b..559322eaae 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt @@ -419,11 +419,22 @@ class DefaultNotificationCreator @Inject constructor( senderPerson ).also { message -> event.imageUri?.let { - message.setData("image/", it) + message.setData(event.imageMimeType ?: "image/", it) } message.extras.putString(MESSAGE_EVENT_ID, event.eventId.value) } addMessage(message) + + // Add additional message for captions + if (event.imageUri != null && event.body != null) { + addMessage( + MessagingStyle.Message( + event.body, + event.timestamp, + senderPerson, + ) + ) + } } } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt index 4a4d2612ae..d51c7e09de 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt @@ -31,7 +31,8 @@ data class NotifiableMessageEvent( val body: String?, // We cannot use Uri? type here, as that could trigger a // NotSerializableException when persisting this to storage - val imageUriString: String?, + private val imageUriString: String?, + val imageMimeType: String?, val threadId: ThreadId?, val roomName: String?, val roomIsDm: Boolean = false, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTest.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTest.kt index e91f4351f7..57c255a406 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTest.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTest.kt @@ -52,9 +52,10 @@ class PushLoopbackTest @Inject constructor( val testPushResult = try { pushService.testPush() } catch (pusherRejected: PushGatewayFailure.PusherRejected) { + val hasQuickFix = pushService.getCurrentPushProvider()?.canRotateToken() == true delegate.updateState( description = stringProvider.getString(R.string.troubleshoot_notifications_test_push_loop_back_failure_1), - status = NotificationTroubleshootTestState.Status.Failure(false) + status = NotificationTroubleshootTestState.Status.Failure(hasQuickFix) ) job.cancel() return @@ -96,5 +97,11 @@ class PushLoopbackTest @Inject constructor( ) } + override suspend fun quickFix(coroutineScope: CoroutineScope) { + delegate.start() + pushService.getCurrentPushProvider()?.rotateToken() + run(coroutineScope) + } + override suspend fun reset() = delegate.reset() } diff --git a/libraries/push/impl/src/main/res/values-in/translations.xml b/libraries/push/impl/src/main/res/values-in/translations.xml index 180903265f..4c12681fbe 100644 --- a/libraries/push/impl/src/main/res/values-in/translations.xml +++ b/libraries/push/impl/src/main/res/values-in/translations.xml @@ -20,6 +20,7 @@ "%d undangan" "Mengundang Anda untuk mengobrol" + "%1$s mengundang Anda untuk mengobrol" "Menyebutkan Anda: %1$s" "Pesan Baru" @@ -29,6 +30,7 @@ "Tandai sebagai dibaca" "Balas cepat" "Mengundang Anda untuk bergabung ke ruangan" + "%1$s mengundang Anda untuk bergabung dengan ruangan" "Saya" "%1$s disebut atau dibalas" "Anda sedang melihat pemberitahuan ini! Klik saya!" 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 73ff35ebce..6954296e4a 100644 --- a/libraries/push/impl/src/main/res/values-nl/translations.xml +++ b/libraries/push/impl/src/main/res/values-nl/translations.xml @@ -23,6 +23,7 @@ "%d uitnodigingen" "Nodigde je uit om te chatten" + "%1$s nodigde je uit om te chatten" "Heeft je genoemd: %1$s" "Nieuwe berichten" @@ -33,7 +34,9 @@ "Markeren als gelezen" "Snel antwoord" "Nodigde je uit om tot de kamer toe te treden" + "%1$s nodigde je uit om tot de kamer toe te treden" "Mij" + "%1$s heeft vermeld of beantwoord" "Je bekijkt de melding! Klik hier!" "%1$s: %2$s" "%1$s: %2$s %3$s" diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt index de29fcf1f0..153ebe1ffc 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt @@ -590,6 +590,7 @@ class DefaultNotifiableEventResolverTest { senderDisambiguatedDisplayName = A_USER_NAME_2, body = "Call in progress (unsupported)", imageUriString = null, + imageMimeType = null, threadId = null, roomName = A_ROOM_NAME, roomAvatarPath = null, @@ -669,6 +670,7 @@ class DefaultNotifiableEventResolverTest { canBeReplaced = false, isRedacted = false, imageUriString = null, + imageMimeType = null, type = EventType.CALL_NOTIFY, ) ) @@ -704,6 +706,7 @@ class DefaultNotifiableEventResolverTest { canBeReplaced = false, isRedacted = false, imageUriString = null, + imageMimeType = null, type = EventType.CALL_NOTIFY, ) ) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt index faba6a6c65..438718582f 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt @@ -100,6 +100,7 @@ fun aNotifiableMessageEvent( canBeReplaced = false, isRedacted = isRedacted, imageUriString = null, + imageMimeType = null, roomAvatarPath = null, senderAvatarPath = null, soundName = null, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTestTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTestTest.kt index b12e0cf80b..57ba07a5f6 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTestTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTestTest.kt @@ -13,9 +13,11 @@ import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_FAILURE_REASON import io.element.android.libraries.push.api.gateway.PushGatewayFailure import io.element.android.libraries.push.test.FakePushService +import io.element.android.libraries.pushproviders.test.FakePushProvider import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState import io.element.android.services.toolbox.test.strings.FakeStringProvider import io.element.android.services.toolbox.test.systemclock.FakeSystemClock +import io.element.android.tests.testutils.lambda.lambdaRecorder import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest import org.junit.Test @@ -67,6 +69,41 @@ class PushLoopbackTestTest { } } + @Test + fun `test PushLoopbackTest PusherRejected error with quick fix`() = runTest { + val diagnosticPushHandler = DiagnosticPushHandler() + val rotateTokenLambda = lambdaRecorder> { Result.success(Unit) } + val sut = PushLoopbackTest( + pushService = FakePushService( + testPushBlock = { + throw PushGatewayFailure.PusherRejected() + }, + currentPushProvider = { + FakePushProvider( + canRotateTokenResult = { true }, + rotateTokenLambda = rotateTokenLambda, + ) + } + ), + diagnosticPushHandler = diagnosticPushHandler, + clock = FakeSystemClock(), + stringProvider = FakeStringProvider(), + ) + launch { + sut.run(this) + } + sut.state.test { + assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true)) + assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress) + val lastItem = awaitItem() + assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(true)) + sut.quickFix(this) + assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress) + assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(true)) + rotateTokenLambda.assertions().isCalledOnce() + } + } + @Test fun `test PushLoopbackTest setup error`() = runTest { val diagnosticPushHandler = DiagnosticPushHandler() diff --git a/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushProvider.kt b/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushProvider.kt index 0f3cde8033..90b4cb0465 100644 --- a/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushProvider.kt +++ b/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushProvider.kt @@ -44,4 +44,10 @@ interface PushProvider { suspend fun unregister(matrixClient: MatrixClient): Result suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig? + + fun canRotateToken(): Boolean + + suspend fun rotateToken(): Result { + error("rotateToken() not implemented, you need to override this method in your implementation") + } } diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProvider.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProvider.kt index 6bbdb1efe8..b1e31a2303 100644 --- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProvider.kt +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProvider.kt @@ -25,6 +25,7 @@ class FirebasePushProvider @Inject constructor( private val firebaseStore: FirebaseStore, private val pusherSubscriber: PusherSubscriber, private val isPlayServiceAvailable: IsPlayServiceAvailable, + private val firebaseTokenRotator: FirebaseTokenRotator, ) : PushProvider { override val index = FirebaseConfig.INDEX override val name = FirebaseConfig.NAME @@ -71,6 +72,12 @@ class FirebasePushProvider @Inject constructor( } } + override fun canRotateToken(): Boolean = true + + override suspend fun rotateToken(): Result { + return firebaseTokenRotator.rotate() + } + companion object { private val firebaseDistributor = Distributor("Firebase", "Firebase") } diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseStore.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseStore.kt index 2058f716d4..4ad3619454 100644 --- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseStore.kt +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseStore.kt @@ -11,6 +11,10 @@ import android.content.SharedPreferences import androidx.core.content.edit import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.di.AppScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.onCompletion +import kotlinx.coroutines.flow.onStart import javax.inject.Inject /** @@ -18,6 +22,7 @@ import javax.inject.Inject */ interface FirebaseStore { fun getFcmToken(): String? + fun fcmTokenFlow(): Flow fun storeFcmToken(token: String?) } @@ -29,6 +34,22 @@ class SharedPreferencesFirebaseStore @Inject constructor( return sharedPreferences.getString(PREFS_KEY_FCM_TOKEN, null) } + override fun fcmTokenFlow(): Flow { + val flow = MutableStateFlow(getFcmToken()) + val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, k -> + if (k == PREFS_KEY_FCM_TOKEN) { + try { + flow.value = getFcmToken() + } catch (e: Exception) { + flow.value = null + } + } + } + return flow + .onStart { sharedPreferences.registerOnSharedPreferenceChangeListener(listener) } + .onCompletion { sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener) } + } + override fun storeFcmToken(token: String?) { sharedPreferences.edit { putString(PREFS_KEY_FCM_TOKEN, token) diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseTokenDeleter.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseTokenDeleter.kt new file mode 100644 index 0000000000..597a39493f --- /dev/null +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseTokenDeleter.kt @@ -0,0 +1,49 @@ +/* + * 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.libraries.pushproviders.firebase + +import com.google.firebase.messaging.FirebaseMessaging +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope +import timber.log.Timber +import javax.inject.Inject +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +interface FirebaseTokenDeleter { + /** + * Deletes the current Firebase token. + */ + suspend fun delete() +} + +@ContributesBinding(AppScope::class) +class DefaultFirebaseTokenDeleter @Inject constructor( + private val isPlayServiceAvailable: IsPlayServiceAvailable, +) : FirebaseTokenDeleter { + override suspend fun delete() { + // 'app should always check the device for a compatible Google Play services APK before accessing Google Play services features' + isPlayServiceAvailable.checkAvailableOrThrow() + suspendCoroutine { continuation -> + try { + FirebaseMessaging.getInstance().deleteToken() + .addOnSuccessListener { + continuation.resume(Unit) + } + .addOnFailureListener { e -> + Timber.e(e, "## deleteFirebaseToken() : failed") + continuation.resumeWithException(e) + } + } catch (e: Throwable) { + Timber.e(e, "## deleteFirebaseToken() : failed") + continuation.resumeWithException(e) + } + } + } +} diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseTokenGetter.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseTokenGetter.kt new file mode 100644 index 0000000000..a15d5a250c --- /dev/null +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseTokenGetter.kt @@ -0,0 +1,50 @@ +/* + * 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.libraries.pushproviders.firebase + +import com.google.firebase.messaging.FirebaseMessaging +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope +import timber.log.Timber +import javax.inject.Inject +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +interface FirebaseTokenGetter { + /** + * Read the current Firebase token from FirebaseMessaging. + * If the token does not exist, it will be generated. + */ + suspend fun get(): String +} + +@ContributesBinding(AppScope::class) +class DefaultFirebaseTokenGetter @Inject constructor( + private val isPlayServiceAvailable: IsPlayServiceAvailable, +) : FirebaseTokenGetter { + override suspend fun get(): String { + // 'app should always check the device for a compatible Google Play services APK before accessing Google Play services features' + isPlayServiceAvailable.checkAvailableOrThrow() + return suspendCoroutine { continuation -> + try { + FirebaseMessaging.getInstance().token + .addOnSuccessListener { token -> + continuation.resume(token) + } + .addOnFailureListener { e -> + Timber.e(e, "## retrievedFirebaseToken() : failed") + continuation.resumeWithException(e) + } + } catch (e: Throwable) { + Timber.e(e, "## retrievedFirebaseToken() : failed") + continuation.resumeWithException(e) + } + } + } +} diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseTokenRotator.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseTokenRotator.kt new file mode 100644 index 0000000000..fc55d11a77 --- /dev/null +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseTokenRotator.kt @@ -0,0 +1,32 @@ +/* + * 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.pushproviders.firebase + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope +import javax.inject.Inject + +interface FirebaseTokenRotator { + suspend fun rotate(): Result +} + +/** + * This class delete the Firebase token and generate a new one. + */ +@ContributesBinding(AppScope::class) +class DefaultFirebaseTokenRotator @Inject constructor( + private val firebaseTokenDeleter: FirebaseTokenDeleter, + private val firebaseTokenGetter: FirebaseTokenGetter, +) : FirebaseTokenRotator { + override suspend fun rotate(): Result { + return runCatching { + firebaseTokenDeleter.delete() + firebaseTokenGetter.get() + } + } +} diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseTroubleshooter.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseTroubleshooter.kt index 6ef6931ea1..6991da5f25 100644 --- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseTroubleshooter.kt +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseTroubleshooter.kt @@ -7,14 +7,9 @@ package io.element.android.libraries.pushproviders.firebase -import com.google.firebase.messaging.FirebaseMessaging import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.di.AppScope -import timber.log.Timber import javax.inject.Inject -import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException -import kotlin.coroutines.suspendCoroutine interface FirebaseTroubleshooter { suspend fun troubleshoot(): Result @@ -26,37 +21,12 @@ interface FirebaseTroubleshooter { @ContributesBinding(AppScope::class) class DefaultFirebaseTroubleshooter @Inject constructor( private val newTokenHandler: FirebaseNewTokenHandler, - private val isPlayServiceAvailable: IsPlayServiceAvailable, + private val firebaseTokenGetter: FirebaseTokenGetter, ) : FirebaseTroubleshooter { override suspend fun troubleshoot(): Result { return runCatching { - val token = retrievedFirebaseToken() + val token = firebaseTokenGetter.get() newTokenHandler.handle(token) } } - - private suspend fun retrievedFirebaseToken(): String { - return suspendCoroutine { continuation -> - // 'app should always check the device for a compatible Google Play services APK before accessing Google Play services features' - if (isPlayServiceAvailable.isAvailable()) { - try { - FirebaseMessaging.getInstance().token - .addOnSuccessListener { token -> - continuation.resume(token) - } - .addOnFailureListener { e -> - Timber.e(e, "## retrievedFirebaseToken() : failed") - continuation.resumeWithException(e) - } - } catch (e: Throwable) { - Timber.e(e, "## retrievedFirebaseToken() : failed") - continuation.resumeWithException(e) - } - } else { - val e = Exception("No valid Google Play Services found. Cannot use FCM.") - Timber.e(e) - continuation.resumeWithException(e) - } - } - } } diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/IsPlayServiceAvailable.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/IsPlayServiceAvailable.kt index 02cf66cb74..a0177ef757 100644 --- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/IsPlayServiceAvailable.kt +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/IsPlayServiceAvailable.kt @@ -20,6 +20,12 @@ interface IsPlayServiceAvailable { fun isAvailable(): Boolean } +fun IsPlayServiceAvailable.checkAvailableOrThrow() { + if (!isAvailable()) { + throw Exception("No valid Google Play Services found. Cannot use FCM.").also(Timber::e) + } +} + @ContributesBinding(AppScope::class) class DefaultIsPlayServiceAvailable @Inject constructor( @ApplicationContext private val context: Context, diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseTokenTest.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseTokenTest.kt index a0d6d22d10..7d04239669 100644 --- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseTokenTest.kt +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseTokenTest.kt @@ -19,7 +19,10 @@ import io.element.android.libraries.troubleshoot.api.test.NotificationTroublesho import io.element.android.libraries.troubleshoot.api.test.TestFilterData import io.element.android.services.toolbox.api.strings.StringProvider import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import javax.inject.Inject @ContributesMultibinding(AppScope::class) @@ -41,23 +44,28 @@ class FirebaseTokenTest @Inject constructor( return data.currentPushProviderName == FirebaseConfig.NAME } + private var currentJob: Job? = null override suspend fun run(coroutineScope: CoroutineScope) { + currentJob?.cancel() delegate.start() - val token = firebaseStore.getFcmToken() - if (token != null) { - delegate.updateState( - description = stringProvider.getString( - R.string.troubleshoot_notifications_test_firebase_token_success, - "${token.take(8)}*****" - ), - status = NotificationTroubleshootTestState.Status.Success - ) - } else { - delegate.updateState( - description = stringProvider.getString(R.string.troubleshoot_notifications_test_firebase_token_failure), - status = NotificationTroubleshootTestState.Status.Failure(true) - ) - } + currentJob = firebaseStore.fcmTokenFlow() + .onEach { token -> + if (token != null) { + delegate.updateState( + description = stringProvider.getString( + R.string.troubleshoot_notifications_test_firebase_token_success, + "*****${token.takeLast(8)}" + ), + status = NotificationTroubleshootTestState.Status.Success + ) + } else { + delegate.updateState( + description = stringProvider.getString(R.string.troubleshoot_notifications_test_firebase_token_failure), + status = NotificationTroubleshootTestState.Status.Failure(true) + ) + } + } + .launchIn(coroutineScope) } override suspend fun reset() = delegate.reset() diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeFirebaseTokenRotator.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeFirebaseTokenRotator.kt new file mode 100644 index 0000000000..c96c67c3fa --- /dev/null +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeFirebaseTokenRotator.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.libraries.pushproviders.firebase + +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeFirebaseTokenRotator( + private val rotateWithResult: () -> Result = { lambdaError() } +) : FirebaseTokenRotator { + override suspend fun rotate(): Result { + return rotateWithResult() + } +} diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt index f94f4ebac8..6a3a9474e4 100644 --- a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt @@ -166,15 +166,27 @@ class FirebasePushProviderTest { assertThat(result).isEqualTo(CurrentUserPushConfig(FirebaseConfig.PUSHER_HTTP_URL, "aToken")) } + @Test + fun `rotateToken invokes the FirebaseTokenRotator`() = runTest { + val lambda = lambdaRecorder> { Result.success(Unit) } + val firebasePushProvider = createFirebasePushProvider( + firebaseTokenRotator = FakeFirebaseTokenRotator(lambda), + ) + firebasePushProvider.rotateToken() + lambda.assertions().isCalledOnce() + } + private fun createFirebasePushProvider( firebaseStore: FirebaseStore = InMemoryFirebaseStore(), pusherSubscriber: PusherSubscriber = FakePusherSubscriber(), isPlayServiceAvailable: IsPlayServiceAvailable = FakeIsPlayServiceAvailable(false), + firebaseTokenRotator: FirebaseTokenRotator = FakeFirebaseTokenRotator(), ): FirebasePushProvider { return FirebasePushProvider( firebaseStore = firebaseStore, pusherSubscriber = pusherSubscriber, isPlayServiceAvailable = isPlayServiceAvailable, + firebaseTokenRotator = firebaseTokenRotator, ) } } diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/InMemoryFirebaseStore.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/InMemoryFirebaseStore.kt index 3a3e582f7f..6728fd0490 100644 --- a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/InMemoryFirebaseStore.kt +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/InMemoryFirebaseStore.kt @@ -7,11 +7,16 @@ package io.element.android.libraries.pushproviders.firebase +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf + class InMemoryFirebaseStore( private var token: String? = null ) : FirebaseStore { override fun getFcmToken(): String? = token + override fun fcmTokenFlow(): Flow = flowOf(token) + override fun storeFcmToken(token: String?) { this.token = token } diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseTokenTestTest.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseTokenTestTest.kt index adf50300e7..e67c59c886 100644 --- a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseTokenTestTest.kt +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseTokenTestTest.kt @@ -35,7 +35,7 @@ class FirebaseTokenTestTest { assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress) val lastItem = awaitItem() assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Success) - assertThat(lastItem.description).contains(FAKE_TOKEN.take(8)) + assertThat(lastItem.description).contains(FAKE_TOKEN.takeLast(8)) assertThat(lastItem.description).doesNotContain(FAKE_TOKEN) } } diff --git a/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt b/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt index 9fe222e1cb..6f5bdda5b7 100644 --- a/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt +++ b/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt @@ -21,6 +21,8 @@ class FakePushProvider( private val currentUserPushConfig: CurrentUserPushConfig? = null, private val registerWithResult: (MatrixClient, Distributor) -> Result = { _, _ -> lambdaError() }, private val unregisterWithResult: (MatrixClient) -> Result = { lambdaError() }, + private val canRotateTokenResult: () -> Boolean = { lambdaError() }, + private val rotateTokenLambda: () -> Result = { lambdaError() }, ) : PushProvider { override fun getDistributors(): List = distributors @@ -39,4 +41,12 @@ class FakePushProvider( override suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig? { return currentUserPushConfig } + + override fun canRotateToken(): Boolean { + return canRotateTokenResult() + } + + override suspend fun rotateToken(): Result { + return rotateTokenLambda() + } } diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProvider.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProvider.kt index 9a2d5d5c10..1ce929a75b 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProvider.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProvider.kt @@ -53,4 +53,6 @@ class UnifiedPushProvider @Inject constructor( override suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig? { return unifiedPushCurrentUserPushConfigProvider.provide() } + + override fun canRotateToken(): Boolean = false } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index f7b19f9d9a..eca7340219 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -57,7 +57,6 @@ import io.element.android.libraries.textcomposer.components.VoiceMessagePreview import io.element.android.libraries.textcomposer.components.VoiceMessageRecorderButton import io.element.android.libraries.textcomposer.components.VoiceMessageRecording import io.element.android.libraries.textcomposer.components.markdown.MarkdownTextInput -import io.element.android.libraries.textcomposer.components.markdown.aMarkdownTextEditorState import io.element.android.libraries.textcomposer.components.textInputRoundedCornerShape import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.textcomposer.model.Suggestion @@ -65,12 +64,15 @@ import io.element.android.libraries.textcomposer.model.TextEditorState import io.element.android.libraries.textcomposer.model.VoiceMessagePlayerEvent import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent import io.element.android.libraries.textcomposer.model.VoiceMessageState +import io.element.android.libraries.textcomposer.model.aTextEditorStateMarkdown +import io.element.android.libraries.textcomposer.model.aTextEditorStateRich import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.wysiwyg.compose.RichTextEditor import io.element.android.wysiwyg.compose.RichTextEditorState import io.element.android.wysiwyg.display.TextDisplay import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList import uniffi.wysiwyg_composer.MenuAction import kotlin.time.Duration.Companion.seconds @@ -124,62 +126,74 @@ fun TextComposer( val composerOptionsButton: @Composable () -> Unit = remember { @Composable { - ComposerOptionsButton( - modifier = Modifier - .size(48.dp), - onClick = onAddAttachment - ) + if (composerMode is MessageComposerMode.Attachment) { + Spacer(modifier = Modifier.width(9.dp)) + } else { + ComposerOptionsButton( + modifier = Modifier + .size(48.dp), + onClick = onAddAttachment + ) + } } } val placeholder = if (composerMode.inThread) { stringResource(id = CommonStrings.action_reply_in_thread) + } else if (composerMode is MessageComposerMode.Attachment) { + stringResource(id = R.string.rich_text_editor_composer_caption_placeholder) } else { stringResource(id = R.string.rich_text_editor_composer_placeholder) } - val textInput: @Composable () -> Unit = when (state) { - is TextEditorState.Rich -> { - remember(state.richTextEditorState, subcomposing, composerMode, onResetComposerMode, onError) { + val textInput: @Composable () -> Unit = if ((composerMode as? MessageComposerMode.Attachment)?.allowCaption == false) { + { + // No text input when in attachment mode and caption not allowed. + } + } else { + when (state) { + is TextEditorState.Rich -> { + remember(state.richTextEditorState, subcomposing, composerMode, onResetComposerMode, onError) { + @Composable { + TextInput( + state = state.richTextEditorState, + subcomposing = subcomposing, + placeholder = placeholder, + composerMode = composerMode, + onResetComposerMode = onResetComposerMode, + resolveMentionDisplay = resolveMentionDisplay, + resolveRoomMentionDisplay = { resolveMentionDisplay("@room", "#") }, + onError = onError, + onTyping = onTyping, + onSelectRichContent = onSelectRichContent, + ) + } + } + } + is TextEditorState.Markdown -> { @Composable { - TextInput( - state = state.richTextEditorState, - subcomposing = subcomposing, - placeholder = placeholder, + val style = ElementRichTextEditorStyle.composerStyle(hasFocus = state.hasFocus()) + TextInputBox( composerMode = composerMode, onResetComposerMode = onResetComposerMode, - resolveMentionDisplay = resolveMentionDisplay, - resolveRoomMentionDisplay = { resolveMentionDisplay("@room", "#") }, - onError = onError, - onTyping = onTyping, - onSelectRichContent = onSelectRichContent, - ) - } - } - } - is TextEditorState.Markdown -> { - @Composable { - val style = ElementRichTextEditorStyle.composerStyle(hasFocus = state.hasFocus()) - TextInputBox( - composerMode = composerMode, - onResetComposerMode = onResetComposerMode, - placeholder = placeholder, - showPlaceholder = { state.state.text.value().isEmpty() }, - subcomposing = subcomposing, - ) { - MarkdownTextInput( - state = state.state, + placeholder = placeholder, + showPlaceholder = { state.state.text.value().isEmpty() }, subcomposing = subcomposing, - onTyping = onTyping, - onReceiveSuggestion = onReceiveSuggestion, - richTextEditorStyle = style, - onSelectRichContent = onSelectRichContent, - ) + ) { + MarkdownTextInput( + state = state.state, + subcomposing = subcomposing, + onTyping = onTyping, + onReceiveSuggestion = onReceiveSuggestion, + richTextEditorStyle = style, + onSelectRichContent = onSelectRichContent, + ) + } } } } } - val canSendMessage = markdown.isNotBlank() + val canSendMessage = markdown.isNotBlank() || composerMode is MessageComposerMode.Attachment val sendButton = @Composable { SendButton( canSendMessage = canSendMessage, @@ -493,185 +507,172 @@ private fun TextInput( } } +private fun aTextEditorStateMarkdownList() = persistentListOf( + aTextEditorStateMarkdown(initialText = "", initialFocus = true), + aTextEditorStateMarkdown(initialText = "A message", initialFocus = true), + aTextEditorStateMarkdown( + initialText = "A message\nWith several lines\nTo preview larger textfields and long lines with overflow", + initialFocus = true, + ), + aTextEditorStateMarkdown(initialText = "A message without focus", initialFocus = false), +) + +private fun aTextEditorStateRichList() = persistentListOf( + aTextEditorStateRich(initialFocus = true), + aTextEditorStateRich(initialText = "A message", initialFocus = true), + aTextEditorStateRich( + initialText = "A message\nWith several lines\nTo preview larger textfields and long lines with overflow", + initialFocus = true + ), + aTextEditorStateRich(initialText = "A message without focus", initialFocus = false), +) + @PreviewsDayNight @Composable internal fun TextComposerSimplePreview() = ElementPreview { PreviewColumn( - items = persistentListOf( - { - ATextComposer( - TextEditorState.Markdown(aMarkdownTextEditorState(initialText = "", initialFocus = true)), - voiceMessageState = VoiceMessageState.Idle, - composerMode = MessageComposerMode.Normal, - enableVoiceMessages = true, - ) - }, - { - ATextComposer( - TextEditorState.Markdown(aMarkdownTextEditorState(initialText = "A message", initialFocus = true)), - voiceMessageState = VoiceMessageState.Idle, - composerMode = MessageComposerMode.Normal, - enableVoiceMessages = true, - ) - }, - { - ATextComposer( - TextEditorState.Markdown( - aMarkdownTextEditorState( - initialText = "A message\nWith several lines\nTo preview larger textfields and long lines with overflow", - initialFocus = true - ) - ), - voiceMessageState = VoiceMessageState.Idle, - composerMode = MessageComposerMode.Normal, - enableVoiceMessages = true, - ) - }, - { - ATextComposer( - TextEditorState.Markdown(aMarkdownTextEditorState(initialText = "A message without focus", initialFocus = false)), - voiceMessageState = VoiceMessageState.Idle, - composerMode = MessageComposerMode.Normal, - enableVoiceMessages = true, - ) - } + items = aTextEditorStateMarkdownList() + ) { _, textEditorState -> + ATextComposer( + state = textEditorState, + voiceMessageState = VoiceMessageState.Idle, + composerMode = MessageComposerMode.Normal, + enableVoiceMessages = true, ) - ) + } } @PreviewsDayNight @Composable internal fun TextComposerFormattingPreview() = ElementPreview { - PreviewColumn(items = persistentListOf({ - ATextComposer( - TextEditorState.Rich(aRichTextEditorState()), - voiceMessageState = VoiceMessageState.Idle, - showTextFormatting = true, - composerMode = MessageComposerMode.Normal, - enableVoiceMessages = true, - ) - }, { - ATextComposer( - TextEditorState.Rich(aRichTextEditorState(initialText = "A message")), - voiceMessageState = VoiceMessageState.Idle, - showTextFormatting = true, - composerMode = MessageComposerMode.Normal, - enableVoiceMessages = true, - ) - }, { + PreviewColumn( + items = aTextEditorStateRichList() + ) { _, textEditorState -> ATextComposer( - TextEditorState.Rich( - aRichTextEditorState( - initialText = "A message\nWith several lines\nTo preview larger textfields and long lines with overflow", - ) - ), + state = textEditorState, voiceMessageState = VoiceMessageState.Idle, showTextFormatting = true, composerMode = MessageComposerMode.Normal, enableVoiceMessages = true, ) - })) + } } @PreviewsDayNight @Composable internal fun TextComposerEditPreview() = ElementPreview { - PreviewColumn(items = persistentListOf({ + PreviewColumn( + items = aTextEditorStateRichList() + ) { _, textEditorState -> ATextComposer( - TextEditorState.Rich(aRichTextEditorState(initialText = "A message", initialFocus = true)), + state = textEditorState, voiceMessageState = VoiceMessageState.Idle, composerMode = aMessageComposerModeEdit(), enableVoiceMessages = true, ) - })) + } } @PreviewsDayNight @Composable internal fun MarkdownTextComposerEditPreview() = ElementPreview { - PreviewColumn(items = persistentListOf({ + PreviewColumn( + items = aTextEditorStateMarkdownList() + ) { _, textEditorState -> ATextComposer( - TextEditorState.Markdown(aMarkdownTextEditorState(initialText = "A message", initialFocus = true)), + state = textEditorState, voiceMessageState = VoiceMessageState.Idle, composerMode = aMessageComposerModeEdit(), enableVoiceMessages = true, ) - })) + } } @PreviewsDayNight @Composable internal fun TextComposerReplyPreview(@PreviewParameter(InReplyToDetailsProvider::class) inReplyToDetails: InReplyToDetails) = ElementPreview { - ATextComposer( - state = TextEditorState.Rich(aRichTextEditorState()), - voiceMessageState = VoiceMessageState.Idle, - composerMode = aMessageComposerModeReply( - replyToDetails = inReplyToDetails, - ), - enableVoiceMessages = true, - ) + PreviewColumn( + items = aTextEditorStateRichList() + ) { _, textEditorState -> + ATextComposer( + state = textEditorState, + voiceMessageState = VoiceMessageState.Idle, + composerMode = aMessageComposerModeReply( + replyToDetails = inReplyToDetails, + ), + enableVoiceMessages = true, + ) + } +} + +@PreviewsDayNight +@Composable +internal fun TextComposerCaptionPreview() = ElementPreview { + val list = aTextEditorStateMarkdownList() + PreviewColumn( + items = (list + aTextEditorStateMarkdown(initialText = "NO_CAPTION", initialFocus = true)).toPersistentList() + ) { index, textEditorState -> + ATextComposer( + state = textEditorState, + voiceMessageState = VoiceMessageState.Idle, + composerMode = MessageComposerMode.Attachment(allowCaption = index < list.size), + enableVoiceMessages = false, + ) + } } @PreviewsDayNight @Composable internal fun TextComposerVoicePreview() = ElementPreview { - @Composable - fun VoicePreview( - voiceMessageState: VoiceMessageState - ) = ATextComposer( - TextEditorState.Rich(aRichTextEditorState(initialFocus = true)), - voiceMessageState = voiceMessageState, - composerMode = MessageComposerMode.Normal, - enableVoiceMessages = true, - ) - PreviewColumn(items = persistentListOf({ - VoicePreview(voiceMessageState = VoiceMessageState.Recording(61.seconds, createFakeWaveform())) - }, { - VoicePreview( - voiceMessageState = VoiceMessageState.Preview( + PreviewColumn( + items = persistentListOf( + VoiceMessageState.Recording(61.seconds, createFakeWaveform()), + VoiceMessageState.Preview( isSending = false, isPlaying = false, showCursor = false, waveform = createFakeWaveform(), time = 0.seconds, playbackProgress = 0.0f - ) - ) - }, { - VoicePreview( - voiceMessageState = VoiceMessageState.Preview( + ), + VoiceMessageState.Preview( isSending = false, isPlaying = true, showCursor = true, waveform = createFakeWaveform(), time = 3.seconds, playbackProgress = 0.2f - ) - ) - }, { - VoicePreview( - voiceMessageState = VoiceMessageState.Preview( + ), + VoiceMessageState.Preview( isSending = true, isPlaying = false, showCursor = false, waveform = createFakeWaveform(), time = 61.seconds, playbackProgress = 0.0f - ) + ), + ) + ) { _, voiceMessageState -> + ATextComposer( + state = aTextEditorStateRich(initialFocus = true), + voiceMessageState = voiceMessageState, + composerMode = MessageComposerMode.Normal, + enableVoiceMessages = true, ) - })) + } } @Composable -private fun PreviewColumn( - items: ImmutableList<@Composable () -> Unit>, +private fun PreviewColumn( + items: ImmutableList, + view: @Composable (Int, T) -> Unit, ) { Column { - items.forEach { item -> + items.forEachIndexed { index, item -> Box( modifier = Modifier.height(IntrinsicSize.Min) ) { - item() + view(index, item) } } } @@ -708,17 +709,6 @@ private fun ATextComposer( ) } -fun aRichTextEditorState( - initialText: String = "", - initialHtml: String = initialText, - initialMarkdown: String = initialText, - initialFocus: Boolean = false, -) = RichTextEditorState( - initialHtml = initialHtml, - initialMarkdown = initialMarkdown, - initialFocus = initialFocus, -) - fun aMessageComposerModeEdit( eventOrTransactionId: EventOrTransactionId = EventId("$1234").toEventOrTransactionId(), content: String = "Some text", diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/TextFormatting.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/TextFormatting.kt index ad14d40016..45150e0bd9 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/TextFormatting.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/TextFormatting.kt @@ -26,7 +26,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.textcomposer.R import io.element.android.libraries.textcomposer.TextComposerLinkDialog -import io.element.android.libraries.textcomposer.aRichTextEditorState +import io.element.android.libraries.textcomposer.model.aRichTextEditorState import io.element.android.wysiwyg.compose.RichTextEditorState import io.element.android.wysiwyg.view.models.InlineFormat import io.element.android.wysiwyg.view.models.LinkAction diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt index 2c72c81cc4..77f9cbb320 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt @@ -36,6 +36,7 @@ import io.element.android.libraries.textcomposer.mentions.updateMentionStyles import io.element.android.libraries.textcomposer.model.MarkdownTextEditorState import io.element.android.libraries.textcomposer.model.Suggestion import io.element.android.libraries.textcomposer.model.SuggestionType +import io.element.android.libraries.textcomposer.model.aMarkdownTextEditorState import io.element.android.wysiwyg.compose.RichTextEditorStyle import io.element.android.wysiwyg.compose.internal.applyStyleInCompose @@ -184,7 +185,7 @@ internal fun MarkdownTextInputPreview() { ElementPreview { val style = ElementRichTextEditorStyle.composerStyle(hasFocus = true) MarkdownTextInput( - state = aMarkdownTextEditorState(), + state = aMarkdownTextEditorState(initialText = "Hello, World!"), subcomposing = false, onTyping = {}, onReceiveSuggestion = {}, @@ -193,11 +194,3 @@ internal fun MarkdownTextInputPreview() { ) } } - -internal fun aMarkdownTextEditorState( - initialText: String = "Hello, World!", - initialFocus: Boolean = true, -) = MarkdownTextEditorState( - initialText = initialText, - initialFocus = initialFocus, -) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/Fixtures.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/Fixtures.kt new file mode 100644 index 0000000000..c4afd3ea67 --- /dev/null +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/Fixtures.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.libraries.textcomposer.model + +import io.element.android.wysiwyg.compose.RichTextEditorState + +fun aTextEditorStateMarkdown( + initialText: String? = "", + initialFocus: Boolean = false, +): TextEditorState { + return TextEditorState.Markdown( + aMarkdownTextEditorState( + initialText = initialText, + initialFocus = initialFocus, + ) + ) +} + +fun aMarkdownTextEditorState( + initialText: String? = "", + initialFocus: Boolean = false, +): MarkdownTextEditorState { + return MarkdownTextEditorState( + initialText = initialText, + initialFocus = initialFocus, + ) +} + +fun aTextEditorStateRich( + initialText: String = "", + initialHtml: String = initialText, + initialMarkdown: String = initialText, + initialFocus: Boolean = false, +): TextEditorState { + return TextEditorState.Rich( + aRichTextEditorState( + initialText = initialText, + initialHtml = initialHtml, + initialMarkdown = initialMarkdown, + initialFocus = initialFocus, + ) + ) +} + +fun aRichTextEditorState( + initialText: String = "", + initialHtml: String = initialText, + initialMarkdown: String = initialText, + initialFocus: Boolean = false, +): RichTextEditorState { + return RichTextEditorState( + initialHtml = initialHtml, + initialMarkdown = initialMarkdown, + initialFocus = initialFocus, + ) +} diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt index ef96000d2b..1915359c83 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt @@ -18,6 +18,8 @@ import io.element.android.libraries.matrix.ui.messages.reply.eventId sealed interface MessageComposerMode { data object Normal : MessageComposerMode + data class Attachment(val allowCaption: Boolean) : MessageComposerMode + sealed interface Special : MessageComposerMode data class Edit( @@ -34,7 +36,8 @@ sealed interface MessageComposerMode { val relatedEventId: EventId? get() = when (this) { - is Normal -> null + is Normal, + is Attachment -> null is Edit -> eventOrTransactionId.eventId is Reply -> eventId } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/TextEditorState.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/TextEditorState.kt index 88c970d848..b1f7d56d07 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/TextEditorState.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/TextEditorState.kt @@ -36,6 +36,7 @@ sealed interface TextEditorState { is Rich -> richTextEditorState.hasFocus } + // Note: for test only suspend fun setHtml(html: String) { when (this) { is Markdown -> Unit @@ -43,6 +44,7 @@ sealed interface TextEditorState { } } + // Note: for test only suspend fun setMarkdown(text: String) { when (this) { is Markdown -> state.text.update(text, true) diff --git a/libraries/textcomposer/impl/src/main/res/values-et/translations.xml b/libraries/textcomposer/impl/src/main/res/values-et/translations.xml index 902177bd7e..1cb9d1a990 100644 --- a/libraries/textcomposer/impl/src/main/res/values-et/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-et/translations.xml @@ -4,6 +4,7 @@ "Lülita mummudega loend sisse/välja" "Sulge vorminduse valikud" "Lülita lähtekoodi lõik sisse/välja" + "Pealkiri, kui soovid lisada…" "Sõnum…" "Lisa link" "Muuda linki" diff --git a/libraries/textcomposer/impl/src/main/res/values-hu/translations.xml b/libraries/textcomposer/impl/src/main/res/values-hu/translations.xml index 9405901975..0353dca419 100644 --- a/libraries/textcomposer/impl/src/main/res/values-hu/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-hu/translations.xml @@ -4,6 +4,7 @@ "Felsorolás be/ki" "Formázási beállítások bezárása" "Kódblokk be/ki" + "Nem kötelező felirat…" "Üzenet…" "Hivatkozás létrehozása" "Hivatkozás szerkesztése" diff --git a/libraries/textcomposer/impl/src/main/res/values-in/translations.xml b/libraries/textcomposer/impl/src/main/res/values-in/translations.xml index b3ab93fd94..df7d6992e2 100644 --- a/libraries/textcomposer/impl/src/main/res/values-in/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-in/translations.xml @@ -4,6 +4,7 @@ "Alihkan daftar poin" "Tutup opsi pemformatan" "Alihkan blok kode" + "Keterangan opsional…" "Kirim pesan…" "Buat tautan" "Sunting tautan" diff --git a/libraries/textcomposer/impl/src/main/res/values-pt/translations.xml b/libraries/textcomposer/impl/src/main/res/values-pt/translations.xml index 6d11e3110c..c26355bf35 100644 --- a/libraries/textcomposer/impl/src/main/res/values-pt/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-pt/translations.xml @@ -4,6 +4,7 @@ "Ativar/desativar lista de pontos" "Fechar opções de formatação" "Ativar/desativar bloco de código" + "Legenda opcional…" "Mensagem…" "Criar uma ligação" "Editar ligação" diff --git a/libraries/textcomposer/impl/src/main/res/values-ru/translations.xml b/libraries/textcomposer/impl/src/main/res/values-ru/translations.xml index 4a7fb7b854..22ad2db0b3 100644 --- a/libraries/textcomposer/impl/src/main/res/values-ru/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-ru/translations.xml @@ -4,6 +4,7 @@ "Переключить список маркеров" "Закрыть параметры форматирования" "Переключить блок кода" + "Необязательный заголовок…" "Сообщение…" "Создать ссылку" "Редактировать ссылку" diff --git a/libraries/textcomposer/impl/src/main/res/values/localazy.xml b/libraries/textcomposer/impl/src/main/res/values/localazy.xml index 10289d2c98..e7cbb5da9a 100644 --- a/libraries/textcomposer/impl/src/main/res/values/localazy.xml +++ b/libraries/textcomposer/impl/src/main/res/values/localazy.xml @@ -4,6 +4,7 @@ "Toggle bullet list" "Close formatting options" "Toggle code block" + "Optional caption…" "Message…" "Create a link" "Edit link" diff --git a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/components/markdown/MarkdownTextInputTest.kt b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/components/markdown/MarkdownTextInputTest.kt index b3429eb0d7..40892d1ded 100644 --- a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/components/markdown/MarkdownTextInputTest.kt +++ b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/components/markdown/MarkdownTextInputTest.kt @@ -22,13 +22,13 @@ import io.element.android.libraries.matrix.test.room.aRoomMember import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.textcomposer.ElementRichTextEditorStyle import io.element.android.libraries.textcomposer.components.markdown.MarkdownTextInput -import io.element.android.libraries.textcomposer.components.markdown.aMarkdownTextEditorState import io.element.android.libraries.textcomposer.mentions.MentionSpan import io.element.android.libraries.textcomposer.mentions.MentionSpanProvider import io.element.android.libraries.textcomposer.mentions.ResolvedSuggestion import io.element.android.libraries.textcomposer.model.MarkdownTextEditorState import io.element.android.libraries.textcomposer.model.Suggestion import io.element.android.libraries.textcomposer.model.SuggestionType +import io.element.android.libraries.textcomposer.model.aMarkdownTextEditorState import io.element.android.tests.testutils.EnsureCalledOnceWithParam import io.element.android.tests.testutils.EventsRecorder import kotlinx.coroutines.test.runTest diff --git a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/model/MarkdownTextEditorStateTest.kt b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/model/MarkdownTextEditorStateTest.kt index c0a9612eab..66b7bf2e47 100644 --- a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/model/MarkdownTextEditorStateTest.kt +++ b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/model/MarkdownTextEditorStateTest.kt @@ -23,9 +23,9 @@ import io.element.android.libraries.matrix.test.room.aRoomMember import io.element.android.libraries.textcomposer.mentions.MentionSpan import io.element.android.libraries.textcomposer.mentions.MentionSpanProvider import io.element.android.libraries.textcomposer.mentions.ResolvedSuggestion -import io.element.android.libraries.textcomposer.model.MarkdownTextEditorState import io.element.android.libraries.textcomposer.model.Suggestion import io.element.android.libraries.textcomposer.model.SuggestionType +import io.element.android.libraries.textcomposer.model.aMarkdownTextEditorState import org.junit.Test import org.junit.runner.RunWith @@ -33,7 +33,7 @@ import org.junit.runner.RunWith class MarkdownTextEditorStateTest { @Test fun `insertMention - room alias - getMentions return empty list`() { - val state = MarkdownTextEditorState(initialText = "Hello @", initialFocus = true) + val state = aMarkdownTextEditorState(initialText = "Hello @", initialFocus = true) val suggestion = aRoomAliasSuggestion() val permalinkBuilder = FakePermalinkBuilder() val mentionSpanProvider = aMentionSpanProvider() @@ -43,7 +43,7 @@ class MarkdownTextEditorStateTest { @Test fun `insertSuggestion - room alias - with member but failed PermalinkBuilder result`() { - val state = MarkdownTextEditorState(initialText = "Hello #", initialFocus = true).apply { + val state = aMarkdownTextEditorState(initialText = "Hello #", initialFocus = true).apply { currentSuggestion = Suggestion(start = 6, end = 7, type = SuggestionType.Room, text = "") } val suggestion = aRoomAliasSuggestion() @@ -55,7 +55,7 @@ class MarkdownTextEditorStateTest { @Test fun `insertSuggestion - room alias`() { - val state = MarkdownTextEditorState(initialText = "Hello #", initialFocus = true).apply { + val state = aMarkdownTextEditorState(initialText = "Hello #", initialFocus = true).apply { currentSuggestion = Suggestion(start = 6, end = 7, type = SuggestionType.Room, text = "") } val suggestion = aRoomAliasSuggestion() @@ -67,7 +67,7 @@ class MarkdownTextEditorStateTest { @Test fun `insertSuggestion - with no currentMentionSuggestion does nothing`() { - val state = MarkdownTextEditorState(initialText = "Hello @", initialFocus = true) + val state = aMarkdownTextEditorState(initialText = "Hello @", initialFocus = true) val member = aRoomMember() val mention = ResolvedSuggestion.Member(member) val permalinkBuilder = FakePermalinkBuilder() @@ -80,7 +80,7 @@ class MarkdownTextEditorStateTest { @Test fun `insertSuggestion - with member but failed PermalinkBuilder result`() { - val state = MarkdownTextEditorState(initialText = "Hello @", initialFocus = true).apply { + val state = aMarkdownTextEditorState(initialText = "Hello @", initialFocus = true).apply { currentSuggestion = Suggestion(start = 6, end = 7, type = SuggestionType.Mention, text = "") } val member = aRoomMember() @@ -97,7 +97,7 @@ class MarkdownTextEditorStateTest { @Test fun `insertSuggestion - with member`() { - val state = MarkdownTextEditorState(initialText = "Hello @", initialFocus = true).apply { + val state = aMarkdownTextEditorState(initialText = "Hello @", initialFocus = true).apply { currentSuggestion = Suggestion(start = 6, end = 7, type = SuggestionType.Mention, text = "") } val member = aRoomMember() @@ -115,7 +115,7 @@ class MarkdownTextEditorStateTest { @Test fun `insertSuggestion - with @room`() { - val state = MarkdownTextEditorState(initialText = "Hello @", initialFocus = true).apply { + val state = aMarkdownTextEditorState(initialText = "Hello @", initialFocus = true).apply { currentSuggestion = Suggestion(start = 6, end = 7, type = SuggestionType.Mention, text = "") } val mention = ResolvedSuggestion.AtRoom @@ -133,7 +133,7 @@ class MarkdownTextEditorStateTest { @Test fun `getMessageMarkdown - when there are no MentionSpans returns the same text`() { val text = "No mentions here" - val state = MarkdownTextEditorState(initialText = text, initialFocus = true) + val state = aMarkdownTextEditorState(initialText = text, initialFocus = true) val markdown = state.getMessageMarkdown(FakePermalinkBuilder()) @@ -147,7 +147,7 @@ class MarkdownTextEditorStateTest { permalinkForUserLambda = { Result.success("https://matrix.to/#/$it") }, permalinkForRoomAliasLambda = { Result.success("https://matrix.to/#/$it") }, ) - val state = MarkdownTextEditorState(initialText = text, initialFocus = true) + val state = aMarkdownTextEditorState(initialText = text, initialFocus = true) state.text.update(aMarkdownTextWithMentions(), needsDisplaying = false) val markdown = state.getMessageMarkdown(permalinkBuilder = permalinkBuilder) @@ -160,14 +160,14 @@ class MarkdownTextEditorStateTest { @Test fun `getMentions - when there are no MentionSpans returns empty list of mentions`() { - val state = MarkdownTextEditorState(initialText = "Hello @", initialFocus = true) + val state = aMarkdownTextEditorState(initialText = "Hello @", initialFocus = true) assertThat(state.getMentions()).isEmpty() } @Test fun `getMentions - when there are MentionSpans returns a list of mentions`() { - val state = MarkdownTextEditorState(initialText = "Hello @", initialFocus = true) + val state = aMarkdownTextEditorState(initialText = "Hello @", initialFocus = true) state.text.update(aMarkdownTextWithMentions(), needsDisplaying = false) val mentions = state.getMentions() diff --git a/libraries/troubleshoot/impl/src/main/res/values-pl/translations.xml b/libraries/troubleshoot/impl/src/main/res/values-pl/translations.xml index 62d8e0c5d3..31d165d3d6 100644 --- a/libraries/troubleshoot/impl/src/main/res/values-pl/translations.xml +++ b/libraries/troubleshoot/impl/src/main/res/values-pl/translations.xml @@ -6,6 +6,6 @@ "Uruchom testy, aby wykryć potencjalne problemy z konfiguracją, jeśli powiadomienia nie działają prawidłowo." "Spróbuj naprawić" "Wszystkie testy przebiegły pomyślnie." - "Powiadomienia rozwiązywania problemów" + "Rozwiązywanie problemów powiadomień" "Niektóre testy wymagają Twojej uwagi. Sprawdź szczegóły." diff --git a/libraries/ui-strings/src/main/res/values-be/translations.xml b/libraries/ui-strings/src/main/res/values-be/translations.xml index 52272f43e3..38de3c79a9 100644 --- a/libraries/ui-strings/src/main/res/values-be/translations.xml +++ b/libraries/ui-strings/src/main/res/values-be/translations.xml @@ -284,9 +284,6 @@ "Гэй, пагавары са мной у %1$s: %2$s" "%1$s Android" "Паведаміць аб памылцы з дапамогай Rageshake" - "Хто заўгодна" - "Доступ у пакой" - "Папрасіце далучыцца" "Не ўдалося выбраць носьбіт, паўтарыце спробу." "Не атрымалася апрацаваць медыяфайл для загрузкі, паспрабуйце яшчэ раз." "Не атрымалася загрузіць медыяфайлы, паспрабуйце яшчэ раз." 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 d85721d0ff..421b5b8523 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -161,6 +161,7 @@ Důvod: %1$s." "Soubor" "Soubor byl uložen do složky Stažené soubory" "Přeposlat zprávu" + "Často používané" "GIF" "Obrázek" "V odpovědi na %1$s" @@ -257,6 +258,7 @@ Důvod: %1$s." "Ověření se nezdařilo" "Ověřeno" "Ověřit zařízení" + "Ověření totožnosti" "Video" "Hlasová zpráva" "Čekání…" @@ -293,14 +295,6 @@ Důvod: %1$s." "Ahoj, ozvi se mi na %1$s: %2$s" "%1$s Android" "Zatřeste zařízením pro nahlášení chyby" - "Do této místnosti může vstoupit kdokoli" - "Kdokoliv" - "Přístup do místnosti" - "Kdokoli může požádat o vstup do místnosti, ale správce nebo moderátor bude muset žádost přijmout" - "Požádat o připojení" - "Aby byla tato místnost viditelná v adresáři veřejných místností, budete potřebovat adresu místnosti." - "Adresa místnosti" - "Viditelnost místnosti" "Výběr média se nezdařil, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." 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 e737fa106c..de35c4d797 100644 --- a/libraries/ui-strings/src/main/res/values-el/translations.xml +++ b/libraries/ui-strings/src/main/res/values-el/translations.xml @@ -285,11 +285,6 @@ "Γεια, μίλα μου στην εφαρμογή %1$s :%2$s" "%1$s Android" "Κούνησε δυνατά τη συσκευή σου για να αναφέρεις κάποιο σφάλμα" - "Οποιοσδήποτε μπορεί να συμμετάσχει σε αυτό το δωμάτιο" - "Οποιοσδήποτε" - "Πρόσβαση Δωματίου" - "Οποιοσδήποτε μπορεί να ζητήσει να συμμετάσχει στο δωμάτιο, αλλά ένας διαχειριστής ή συντονιστής θα πρέπει να αποδεχθεί το αίτημα" - "Αίτημα συμμετοχής" "Αποτυχία επιλογής πολυμέσου, δοκίμασε ξανά." "Αποτυχία μεταφόρτωσης μέσου, δοκίμασε ξανά." "Αποτυχία μεταφόρτωσης πολυμέσων, δοκίμασε ξανά." 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 9f7efaef57..c0607c60e4 100644 --- a/libraries/ui-strings/src/main/res/values-et/translations.xml +++ b/libraries/ui-strings/src/main/res/values-et/translations.xml @@ -254,6 +254,7 @@ Põhjus: %1$s." "Verifitseerimine ei õnnestunud" "Verifitseeritud" "Verifitseeri seade" + "Verifitseeri võrguidentiteet" "Video" "Häälsõnum" "Ootame…" @@ -290,14 +291,6 @@ Põhjus: %1$s." "Hei, suhtle minuga %1$s võrgus: %2$s" "%1$s Android" "Veast teatamiseks raputa nutiseadet ägedalt" - "Kõik võivad selle jututoaga liituda" - "Kõik" - "Ligipääs jututoale" - "Kõik võivad paluda selle jututoaga liitumist, kuid peakasutaja või moderaator peavad selle kinnitama" - "Küsi võimalust liitumiseks" - "Selleks, et see jututuba oleks nähtav jututubade avalikus kataloogis, sa vajad jututoa aadressi." - "Jututoa aadress" - "Jututoa nähtavus" "Meediafaili valimine ei õnnestunud. Palun proovi uuesti." "Meediafaili töötlemine enne üleslaadimist ei õnnestunud. Palun proovi uuesti." "Meediafaili üleslaadimine ei õnnestunud. Palun proovi uuesti." 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 9772173f7f..f9159b6d6c 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -145,6 +145,7 @@ "(modifié)" "Édition" "* %1$s %2$s" + "Chiffrement" "Chiffrement activé" "Saisissez votre code PIN" "Erreur" @@ -158,6 +159,7 @@ Raison: %1$s." "Fichier" "Fichier enregistré dans Téléchargements" "Transférer le message" + "Fréquemment utilisé" "GIF" "Image" "En réponse à %1$s" @@ -238,7 +240,9 @@ Raison: %1$s." "Sujet" "De quoi s’agit-il dans ce salon ?" "Échec de déchiffrement" + "Envoyé depuis un appareil non sécurisé" "Vous ne pouvez pas voir ce message" + "L’identité vérifiée de l’expéditeur a changé" "Les invitations n’ont pas pu être envoyées à un ou plusieurs utilisateurs." "Impossible d’envoyer une ou plusieurs invitations" "Déverrouillage" @@ -247,6 +251,7 @@ Raison: %1$s." "Nom d’utilisateur" "Vérification annulée" "Vérification terminée" + "Échec de la vérification" "Vérifié(e)" "Vérifier la session" "Vidéo" @@ -285,11 +290,6 @@ Raison: %1$s." "Salut, parle-moi sur %1$s : %2$s" "%1$s Android" "Rageshake pour signaler un problème" - "Tout le monde peut rejoindre ce salon" - "Tout le monde" - "Accès au salon" - "Tout le monde peut demander à rejoindre le salon, mais un administrateur ou un modérateur devra accepter la demande" - "Demander à rejoindre" "Échec de la sélection du média, veuillez réessayer." "Échec du traitement des médias à télécharger, veuillez réessayer." "Échec du téléchargement du média, veuillez réessayer." @@ -318,6 +318,7 @@ Raison: %1$s." "Chargement du message…" "Voir tout" "Discussion" + "Demande d’adhésion envoyée" "Partage de position" "Partager ma position" "Ouvrir dans Apple Maps" 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 0a38194f26..a80e8de9e5 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -145,6 +145,7 @@ "(szerkesztve)" "Szerkesztés" "* %1$s %2$s" + "Titkosítás" "Titkosítás engedélyezve" "Adja meg a PIN-kódját" "Hiba" @@ -158,6 +159,7 @@ Ok: %1$s." "Fájl" "A fájl a Letöltések mappába mentve" "Üzenet továbbítása" + "Gyakran használt" "GIF" "Kép" "Válasz erre: %1$s" @@ -238,7 +240,9 @@ Ok: %1$s." "Téma" "Miről szól ez a szoba?" "Nem lehet visszafejteni" + "Nem biztonságos eszközről küldve" "Nincs hozzáférése ehhez az üzenethez" + "A feladó ellenőrzött személyazonossága megváltozott" "Nem sikerült meghívót küldeni egy vagy több felhasználónak." "Nem sikerült elküldeni a meghívót (meghívókat)" "Feloldás" @@ -250,6 +254,7 @@ Ok: %1$s." "Az ellenőrzés sikertelen" "Ellenőrizve" "Eszköz ellenőrzése" + "Személyazonosság ellenőrzése" "Videó" "Hangüzenet" "Várakozás…" @@ -286,11 +291,6 @@ Ok: %1$s." "Beszélgessünk itt: %1$s, %2$s" "%1$s Android" "Az eszköz rázása a hibajelentéshez" - "Bárki csatlakozhat ehhez a szobához" - "Bárki" - "Szobahozzáférés" - "Bárki kérheti, hogy csatlakozzon a szobához, de egy adminisztrátornak vagy moderátornak el kell fogadnia a kérést" - "Csatlakozás kérése" "Nem sikerült kiválasztani a médiát, próbálja újra." "Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra." "Nem sikerült a média feltöltése, próbálja újra." @@ -319,6 +319,7 @@ Ok: %1$s." "Üzenet betöltése…" "Összes megtekintése" "Csevegés" + "Csatlakozási kérés elküldve" "Hely megosztása" "Saját hely megosztása" "Megnyitás az Apple Mapsben" diff --git a/libraries/ui-strings/src/main/res/values-in/translations.xml b/libraries/ui-strings/src/main/res/values-in/translations.xml index 2b245af9d1..a5beab01d3 100644 --- a/libraries/ui-strings/src/main/res/values-in/translations.xml +++ b/libraries/ui-strings/src/main/res/values-in/translations.xml @@ -40,12 +40,15 @@ "Tutup" "Selesaikan verifikasi" "Konfirmasi" + "Konfirmasi kata sandi" "Lanjutkan" "Salin" "Salin tautan" "Salin tautan ke pesan" "Buat" "Buat ruangan" + "Nonaktifkan" + "Nonaktifkan akun" "Tolak" "Hapus pemungutan suara" "Nonaktifkan" @@ -59,6 +62,7 @@ "Lupa kata sandi?" "Teruskan" "Kembali" + "Abaikan" "Undang" "Undang orang-orang" "Undang orang-orang ke %1$s" @@ -99,6 +103,7 @@ "Kirim pesan" "Bagikan" "Bagikan tautan" + "Tampilkan" "Masuk lagi" "Keluar dari akun" "Keluar saja" @@ -125,17 +130,20 @@ "Panggilan sedang berjalan (tidak didukung)" "Panggilan dimulai" "Pencadangan percakapan" + "Disalin ke papan klip" "Hak cipta" "Membuat ruangan…" "Keluar dari ruangan" "Gelap" "Kesalahan dekripsi" "Opsi pengembang" + "ID Perangkat" "Obrolan langsung" "Jangan tampilkan ini lagi" "(disunting)" "Penyuntingan" "* %1$s %2$s" + "Enkripsi" "Enkripsi diaktifkan" "Masukkan PIN Anda" "Eror" @@ -149,6 +157,7 @@ Alasan: %1$s." "Berkas" "Berkas disimpan ke Unduhan" "Teruskan pesan" + "Sering digunakan" "GIF" "Gambar" "Membalas kepada %1$s" @@ -227,7 +236,9 @@ Alasan: %1$s." "Topik" "Tentang apa ruangan ini?" "Tidak dapat mendekripsi" + "Dikirim dari perangkat yang tidak aman" "Anda tidak memiliki akses ke pesan ini" + "Identitas terverifikasi pengirim telah berubah" "Undangan tidak dapat dikirim ke satu atau beberapa pengguna." "Tidak dapat mengirim undangan" "Buka kunci" @@ -236,17 +247,25 @@ Alasan: %1$s." "Nama pengguna" "Verifikasi dibatalkan" "Verifikasi selesai" + "Verifikasi gagal" + "Terverifikasi" "Verifikasi perangkat" + "Verifikasi identitas" "Video" "Pesan suara" "Menunggu…" "Menunggu pesan ini" + "Anda" + "Identitas %1$s tampaknya telah berubah. %2$s" + "Identitas %1$s yang %2$s tampaknya telah berubah. %3$s" + "(%1$s)" "Konfirmasi" "Eror" "Berhasil" "Peringatan" "Perubahan Anda belum disimpan. Apakah Anda yakin ingin kembali?" "Simpan perubahan?" + "Homeserver Anda perlu ditingkatkan untuk mendukung Matrix Authentication Service dan pembuatan akun." "Gagal membuat tautan permanen" "%1$s tidak dapat memuat peta. Silakan coba lagi nanti." "Gagal memuat pesan" @@ -279,9 +298,14 @@ Alasan: %1$s." "Pesan yang disematkan" "Anda akan pergi ke akun %1$s Anda untuk mengatur ulang identitas Anda. Setelah itu Anda akan dibawa kembali ke aplikasi." "Tidak dapat mengonfirmasi? Buka akun Anda untuk mengatur ulang identitas Anda." + "Tarik verifikasi dan kirim" + "Anda dapat menarik verifikasi dan tetap mengirim pesan ini, atau Anda dapat membatalkan untuk saat ini dan mencoba lagi nanti setelah memverifikasi ulang %1$s." + "Pesan Anda tidak terkirim karena identitas terverifikasi %1$s telah berubah" "Kirim pesan saja" "%1$s menggunakan satu atau beberapa perangkat yang belum diverifikasi. Anda tetap dapat mengirim pesan, atau Anda dapat membatalkan untuk saat ini dan mencoba lagi nanti setelah %2$s telah memverifikasi semua perangkat mereka." "Pesan Anda tidak terkirim karena %1$s belum memverifikasi semua perangkat" + "Satu atau beberapa perangkat Anda tidak terverifikasi. Anda tetap dapat mengirim pesan, atau Anda dapat membatalkannya dan mencoba lagi nanti setelah Anda memverifikasi semua perangkat." + "Pesan Anda tidak terkirim karena Anda belum memverifikasi satu atau beberapa perangkat Anda" "Pesan yang disematkan" "Gagal memproses media untuk diunggah, silakan coba lagi." "Tidak dapat mengambil detail pengguna" @@ -290,6 +314,7 @@ Alasan: %1$s." "Memuat pesan…" "Lihat Semua" "Obrolan" + "Permintaan untuk bergabung dikirim" "Bagikan lokasi" "Bagikan lokasi saya" "Buka di Apple Maps" @@ -298,6 +323,7 @@ Alasan: %1$s." "Bagikan lokasi ini" "Pesan tidak terkirim karena identitas terverifikasi %1$s telah berubah." "Pesan tidak terkirim karena %1$s belum memverifikasi semua perangkat." + "Pesan tidak terkirim karena Anda belum memverifikasi satu atau beberapa perangkat Anda." "Lokasi" "Versi: %1$s (%2$s)" "id" 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 d6c7d640b5..4b10c17180 100644 --- a/libraries/ui-strings/src/main/res/values-nl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-nl/translations.xml @@ -36,17 +36,21 @@ "Terug" "Bellen" "Annuleren" + "Voor nu annuleren" "Kies foto" "Wissen" "Sluiten" "Verificatie voltooien" "Bevestigen" + "Bevestig wachtwoord" "Voortzetten" "Kopiëren" "Kopieer link" "Kopieer link naar bericht" "Aanmaken" "Creëer een kamer" + "Sluiten" + "Account sluiten" "Weigeren" "Peiling verwijderen" "Uitschakelen" @@ -80,6 +84,7 @@ "OK" "Instellingen" "Openen met" + "Vastmaken" "Snel antwoord" "Citeren" "Reageren" @@ -90,6 +95,7 @@ "Probleem melden" "Inhoud melden" "Opnieuw instellen" + "Identiteit opnieuw instellen" "Opnieuw proberen" "Decryptie opnieuw proberen" "Opslaan" @@ -98,6 +104,7 @@ "Bericht verzenden" "Delen" "Link delen" + "Toon" "Log opnieuw in" "Uitloggen" "Toch uitloggen" @@ -109,6 +116,8 @@ "Foto maken" "Tik voor opties" "Probeer het opnieuw" + "Losmaken" + "Bekijk in tijdlijn" "Bron weergeven" "Ja" "Over" @@ -122,6 +131,7 @@ "Gesprek bezig (niet ondersteund)" "Oproep gestart" "Chat back-up" + "Gekopieerd naar klembord" "Copyright" "Kamer maken…" "Heeft de kamer verlaten" @@ -129,12 +139,16 @@ "Decryptie fout" "Ontwikkelaarsopties" "Directe chat" + "Dit niet meer weergeven" "(bewerkt)" "Bewerken" "* %1$s %2$s" "Encryptie ingeschakeld" "Voer je pincode in" "Fout" + "Er is een fout opgetreden, je ontvangt mogelijk geen meldingen voor nieuwe berichten. Los problemen met meldingen op in de instellingen. + +Reden: %1$s." "Iedereen" "Mislukt" "Favoriet" @@ -164,11 +178,13 @@ "Geen resultaten" "Geen kamernaam" "Offline" + "Open-sourcelicenties" "of" "Wachtwoord" "Personen" "Permalink" "Toestemming" + "Vastgezet" "Even geduld…" "Weet je zeker dat je deze peiling wilt beëindigen?" "Peiling: %1$s" @@ -229,17 +245,23 @@ "Gebruikersnaam" "Verificatie geannuleerd" "Verificatie voltooid" + "Geverifieerd" "Apparaat verifiëren" "Video" "Spraakbericht" "Wachten…" "Wachten op dit bericht" + "Jij" + "%1$s\'s identiteit lijkt te zijn veranderd. %2$s" + "%1$s\'s %2$s identiteit lijkt te zijn veranderd. %3$s" + "(%1$s)" "Bevestiging" "Fout" "Geslaagd" "Waarschuwing" "Je wijzigingen zijn niet opgeslagen. Weet je zeker dat je terug wilt gaan?" "Wijzigingen opslaan?" + "Je homeserver moet worden geüpgraded om de Matrix Authentication Service en het aanmaken van accounts te ondersteunen." "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" @@ -251,6 +273,12 @@ "%1$s heeft geen toegang tot je microfoon. Schakel toegang in om een spraakbericht op te nemen." "Sommige berichten zijn niet verzonden" "Sorry, er is een fout opgetreden" + "De echtheid van dit versleutelde bericht kan op dit apparaat niet worden gegarandeerd." + "Versleuteld door een eerder geverifieerde gebruiker." + "Niet versleuteld." + "Versleuteld door een onbekend of verwijderd apparaat." + "Versleuteld door een apparaat dat niet is geverifieerd door de eigenaar." + "Versleuteld door een niet-geverifieerde gebruiker." "🔐️ Sluit je bij mij aan op %1$s" "Hé, praat met me op %1$s: %2$s" "%1$s Android" @@ -258,8 +286,30 @@ "Het selecteren van media is mislukt. Probeer het opnieuw." "Het verwerken van media voor uploaden is mislukt. Probeer het opnieuw." "Het uploaden van media is mislukt. Probeer het opnieuw." + "Druk op een bericht en kies „%1$s” om het hier toe te voegen." + "Zet belangrijke berichten vast zodat ze gemakkelijk te vinden zijn" + + "%1$d Vastgezet bericht" + "%1$d Vastgezette berichten" + + "Vastgezette berichten" + "Je staat op het punt naar je %1$s account te gaan om je identiteit opnieuw in te stellen. Daarna kom je terug naar de app." + "Kun je dit niet bevestigen? Ga naar je account om je identiteit opnieuw in te stellen." + "Verificatie intrekken en verzenden" + "Je kunt je verificatie intrekken en dit bericht toch verzenden, of je kunt het voorlopig annuleren en het later opnieuw proberen nadat je %1$s opnieuw hebt geverifieerd." + "Je bericht is niet verzonden omdat %1$s\'s geverifieerde identiteit is gewijzigd" + "Bericht toch versturen" + "%1$s gebruikt een of meer niet-geverifieerde apparaten. Je kunt het bericht toch verzenden, of je kunt het voorlopig annuleren en het later opnieuw proberen nadat %2$s alle apparaten heeft geverifieerd." + "Je bericht is niet verzonden omdat %1$s niet alle apparaten heeft geverifieerd" + "Een of meer van je apparaten zijn niet geverifieerd. Je kunt het bericht toch verzenden, of je kunt het voorlopig annuleren en het later opnieuw proberen nadat je al je apparaten hebt geverifieerd." + "Je bericht is niet verzonden omdat je een of meerdere apparaten niet geverifieerd hebt" + "Vastgezette berichten" "Het verwerken van media voor uploaden is mislukt. Probeer het opnieuw." "Kon gebruikersgegevens niet ophalen" + "%1$s van %2$s" + "%1$s Vastgezette berichten" + "Bericht laden…" + "Bekijk alles" "Chat" "Locatie delen" "Deel mijn locatie" @@ -267,6 +317,9 @@ "Openen in Google Maps" "Openen in OpenStreetMap" "Deel deze locatie" + "Bericht niet verzonden omdat %1$s\'s geverifieerde identiteit is gewijzigd." + "Bericht niet verzonden omdat %1$s niet alle apparaten heeft geverifieerd." + "Bericht is niet verzonden omdat je een of meerdere apparaten niet geverifieerd hebt" "Locatie" "Versie: %1$s (%2$s)" "en" diff --git a/libraries/ui-strings/src/main/res/values-pl/translations.xml b/libraries/ui-strings/src/main/res/values-pl/translations.xml index c4d7684591..77decc684f 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -66,6 +66,7 @@ "Nie pamiętasz hasła?" "Przekaż dalej" "Wróć" + "Ignoruj" "Zaproś" "Zaproś znajomych" "Zaproś znajomych do %1$s" @@ -106,6 +107,7 @@ "Wyślij wiadomość" "Udostępnij" "Udostępnij link" + "Pokaż" "Zaloguj się ponownie" "Wyloguj" "Wyloguj mimo to" @@ -139,11 +141,13 @@ "Ciemny" "Błąd deszyfrowania" "Opcje programisty" + "ID urządzenia" "Czat prywatny" "Nie pokazuj ponownie" "(edytowane)" "Edytowanie" "* %1$s %2$s" + "Szyfrowanie" "Szyfrowanie włączone" "Wprowadź kod PIN" "Błąd" @@ -239,7 +243,9 @@ Powód: %1$s." "Temat" "O czym jest ten pokój?" "Nie można odszyfrować" + "Wysłane z niebezpiecznego urządzenia" "Nie masz uprawnień do tej wiadomości" + "Zweryfikowana tożsamość nadawcy uległa zmianie" "Nie udało się wysłać zaproszenia do jednego lub więcej użytkowników." "Nie można wysłać zaproszeń" "Odblokuj" @@ -248,6 +254,8 @@ Powód: %1$s." "Nazwa użytkownika" "Weryfikacja anulowana" "Weryfikacja zakończona" + "Weryfikacja nie powiodła się" + "Zweryfikowano" "Weryfikuj urządzenie" "Film" "Wiadomość głosowa" @@ -255,6 +263,7 @@ Powód: %1$s." "Oczekiwanie na tę wiadomość" "Ty" "Tożsamość %1$s mogła ulec zmianie. %2$s" + "(%1$s)" "Potwierdzenie" "Błąd" "Sukces" @@ -312,6 +321,7 @@ Powód: %1$s." "Wczytywanie wiadomości…" "Wyświetl wszystkie" "Czat" + "Wysłano prośbę o dołączenie" "Udostępnij lokalizację" "Udostępnij moją lokalizację" "Otwórz w Apple Maps" diff --git a/libraries/ui-strings/src/main/res/values-pt/translations.xml b/libraries/ui-strings/src/main/res/values-pt/translations.xml index fd7ef45963..65e2a5d483 100644 --- a/libraries/ui-strings/src/main/res/values-pt/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pt/translations.xml @@ -145,6 +145,7 @@ "(editada)" "A editar" "* %1$s %2$s" + "Encriptação" "Cifragem ativada" "Introduz o teu PIN" "Erro" @@ -158,6 +159,7 @@ Razão: %1$s." "Ficheiro" "Ficheiro guardado nas Transferências" "Reencaminhar mensagem" + "Frequentemente utilizado" "GIF" "Imagem" "Em resposta a %1$s" @@ -238,7 +240,9 @@ Razão: %1$s." "Descrição" "Sobre o que é esta sala?" "Incapaz de decifrar" + "Enviado de um dispositivo inseguro" "Não tens acesso a esta mensagem" + "A identidade verificada do remetente foi alterada" "Não foi possível enviar convites a um ou mais utilizadores." "Não foi possível enviar convite(s)" "Desbloquear" @@ -247,8 +251,10 @@ Razão: %1$s." "Nome de utilizador" "Verificação cancelada" "Verificação concluída" + "A verificação falhou" "Verificado" "Verificar o dispositivo" + "Verifica a identidade" "Vídeo" "Mensagem de voz" "A aguardar…" @@ -285,11 +291,6 @@ Razão: %1$s." "Alô! Fala comigo na %1$s: %2$s" "%1$s Android" "Agita o dispositivo em fúria para comunicar um problema" - "Qualquer pessoa pode entrar nesta sala" - "Qualquer pessoa" - "Acesso à sala" - "Qualquer pessoa pode pedir para entrar na sala, mas um administrador ou um moderador terá de aceitar o pedido" - "Pedir para participar" "Falha ao selecionar multimédia, por favor tente novamente." "Falha ao processar multimédia para carregamento, por favor tente novamente." "Falhar ao carregar multimédia, por favor tente novamente." @@ -318,6 +319,7 @@ Razão: %1$s." "A carregar mensagem…" "Ver todas" "Conversa" + "Pedido de adesão enviado" "Partilhar localização" "Partilhar a minha localização" "Abrir no 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 6b2c5a99e5..e6f3bc4767 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -258,6 +258,7 @@ "Сбой проверки" "Проверено" "Подтверждение устройства" + "Подтвердить личность" "Видео" "Голосовое сообщение" "Ожидание…" @@ -294,14 +295,6 @@ "Привет, поговори со мной по %1$s: %2$s" "%1$s Android" "Встряхните устройство, чтобы сообщить об ошибке" - "Любой желающий может присоединиться к этой комнате" - "Любой" - "Доступ в комнату" - "Любой желающий может подать заявку на присоединение к комнате, но администратор или модератор должен будет принять запрос." - "Попросить присоединиться" - "Чтобы эта комната была видна в каталоге общедоступных, вам необходим ее адрес" - "Адрес комнаты" - "Видимость комнаты" "Не удалось выбрать носитель, попробуйте еще раз." "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." "Не удалось загрузить медиафайлы, попробуйте еще раз." @@ -316,7 +309,7 @@ "Вы собираетесь перейти в свою учетную запись %1$s, чтобы сбросить идентификацию. После этого вы вернетесь в приложение." "Не можете подтвердить? Перейдите в свою учетную запись, чтобы сбросить свою идентификацию." "Отозвать статус и отправить" - "Вы можете отозвать свою верификацию и отправить это сообщение в любом случае или вы можете отменить ее сейчас и повторить попытку позже после повторной верификации %1$s." + "Вы можете либо отозвать свой статус подтверждения и всё равно отправить это сообщение, либо отменить его сейчас и повторить попытку после повторного подтверждения %1$s." "Ваше сообщение не было отправлено, потому что изменилась подтвержденная личность %1$s" "Отправь сообщение в любом случае" "%1$s использует одно или несколько непроверенных устройств. Вы все равно можете отправить сообщение или отменить его пока и повторить попытку позже %2$s, проверив все устройства пользователя." 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 3df443c6cf..3d1cc9d416 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -293,14 +293,6 @@ Dôvod: %1$s." "Ahoj, porozprávajte sa so mnou na %1$s: %2$s" "%1$s Android" "Zúrivo potriasť pre nahlásenie chyby" - "Do tejto miestnosti sa môže pripojiť ktokoľvek" - "Ktokoľvek" - "Prístup do miestnosti" - "Ktokoľvek môže požiadať o pripojenie sa k miestnosti, ale administrátor alebo moderátor bude musieť žiadosť schváliť" - "Požiadať o pripojenie" - "Aby bola táto miestnosť viditeľná v adresári verejných miestností, budete potrebovať adresu miestnosti." - "Adresa miestnosti" - "Viditeľnosť miestnosti" "Nepodarilo sa vybrať médium, skúste to prosím znova." "Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova." "Nepodarilo sa nahrať médiá, skúste to prosím znova." diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 25818fc017..d1f17f147f 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -254,6 +254,7 @@ Reason: %1$s." "Verification failed" "Verified" "Verify device" + "Verify identity" "Video" "Voice message" "Waiting…" @@ -290,14 +291,6 @@ Reason: %1$s." "Hey, talk to me on %1$s: %2$s" "%1$s Android" "Rageshake to report bug" - "Anyone can join this room" - "Anyone" - "Room Access" - "Anyone can ask to join the room but an administrator or a moderator will have to accept the request" - "Ask to join" - "In order for this room to be visible in the public room directory, you will need a room address." - "Room address" - "Room visibility" "Failed selecting media, please try again." "Failed processing media to upload, please try again." "Failed uploading media, please try again." diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index ce7f8351d7..8a14ce5161 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -47,13 +47,13 @@ private const val versionMinor = 7 // 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 = 2 +private const val versionPatch = 3 object Versions { - val versionCode = 4_000_000 + versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch - val versionName = "$versionMajor.$versionMinor.$versionPatch" - const val compileSdk = 34 - const val targetSdk = 34 + const val VERSION_CODE = 4_000_000 + versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch + const val VERSION_NAME = "$versionMajor.$versionMinor.$versionPatch" + const val COMPILE_SDK = 35 + const val TARGET_SDK = 35 // When updating the `minSdk`, make sure to update the value of `minSdkVersion` in the file `tools/release/release.sh` val minSdk = if (isEnterpriseBuild) 26 else 24 diff --git a/plugins/src/main/kotlin/extension/CommonExtension.kt b/plugins/src/main/kotlin/extension/CommonExtension.kt index 4f3f23d707..7c96386a6b 100644 --- a/plugins/src/main/kotlin/extension/CommonExtension.kt +++ b/plugins/src/main/kotlin/extension/CommonExtension.kt @@ -15,7 +15,7 @@ import java.io.File fun CommonExtension<*, *, *, *, *, *>.androidConfig(project: Project) { defaultConfig { - compileSdk = Versions.compileSdk + compileSdk = Versions.COMPILE_SDK minSdk = Versions.minSdk testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/samples/minimal/build.gradle.kts b/samples/minimal/build.gradle.kts index e32054a272..48713af442 100644 --- a/samples/minimal/build.gradle.kts +++ b/samples/minimal/build.gradle.kts @@ -14,9 +14,9 @@ android { defaultConfig { applicationId = "io.element.android.samples.minimal" - targetSdk = Versions.targetSdk - versionCode = Versions.versionCode - versionName = Versions.versionName + targetSdk = Versions.TARGET_SDK + versionCode = Versions.VERSION_CODE + versionName = Versions.VERSION_NAME testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_de.png b/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_de.png index 4f48d60c43..024c6e92b2 100644 --- a/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_de.png +++ b/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b0a6de2e051249f63b1cf8f105fcc48d4e1e26ce7af76f54f68240c1646dca57 -size 148994 +oid sha256:d3dbde9e29f3b2a47689958e0a6cf12c9d0defc8cb8a2219e8fb9f135c421440 +size 151346 diff --git a/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_de.png b/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_de.png index 4ec834041d..bfd10d8d88 100644 --- a/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_de.png +++ b/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ed6b3415914062f8f6af9a8e56425864715288e356cc7fe0689a9155210a296 -size 154626 +oid sha256:40668ef04dc1c8a44b74dbf046ca812cc49ae8b6761d65a002392e88fdefa086 +size 157214 diff --git a/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_de.png b/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_de.png index d5b3e1d614..6d9082abcf 100644 --- a/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_de.png +++ b/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f419fddc0817dedf193d56d60f1b9e4e0fb4c66c341d049a1fd4519fc40357b -size 148203 +oid sha256:7e948e173f81dff5a7b9c26af31ff8fa814a76ac89245b43c1c5aa27ac1644de +size 150550 diff --git a/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_de.png b/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_de.png index 5fcc4cde68..747aab9e90 100644 --- a/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_de.png +++ b/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3efe04926766a7a8b46386f8c58c8d5e40ea7b36b37cd41d8c9363e2c44747a -size 150143 +oid sha256:28fc5693f332060f41852d2ea3825881cd1c34acd447d6e0c64d055b5ccdb9bf +size 152708 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_de.png index e143b4642b..d27448c6d0 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d4c8e5308da7b96d7be6966e36d9005327c33f6324bec828550e7aa4135d69c -size 50688 +oid sha256:352fb53b25f4406dd015b751a638b95b6f62c867c78b4f143460a41a49f931dd +size 64158 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_de.png index 1b412929da..922c3cd9ff 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab8b4cbecd87d4ef5998633d5245b9ce32dbe0cec21d20194a3b0d5308500983 -size 50462 +oid sha256:c1b44b5f03ec9fc35d70b7ea99f6d80a634ae0b0e054c43b8bbf7edd1bac29de +size 63928 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_de.png index ec5afb1ceb..5c5334c17a 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f38915099cec8ab7c20230c5ffd960effe74752715ac50ae12221f20950cd4c6 -size 33351 +oid sha256:8b62f107635c081db2941d1d9eb17be8e2c1ed9a09ba5d87328e3c5cf188eb76 +size 34110 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_de.png index 6628802df7..0fa887a89b 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6765c5e5fdcc0a0483f323433138a6e8672b43ebf6839c0ce9d81188e43cf8d -size 50472 +oid sha256:4035d14464ba0260626c8bb01f2381f521bdc051e4fae9531b48e748c10d7a20 +size 63965 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_de.png new file mode 100644 index 0000000000..ca163fb0c8 --- /dev/null +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:45123998d710d83e7bf1094493b16c3210a70e34e406b12ce227bd7cc78b3d87 +size 64020 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_0_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_0_de.png index f32e69ff8b..1a33248017 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_0_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a12575a26cbb9e3c6d936dbd139584fed33cd97be453d1f9bc2b7e96259bb8c7 -size 38465 +oid sha256:11fa8c3567744e5e9c7020ebdb2b9a6fa45ea5e970db337850a30c12d5eab37b +size 38148 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_1_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_1_de.png index 78f861c3e0..1c0c289730 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_1_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ac360c420327dff438755fca427901d7d5b15f9ba28d6491c429f94dadcfcc2 -size 38215 +oid sha256:8d24fdf8a38152d2e7800fef245fa62af144281faba4a66e692f9b2ab2e5bb49 +size 37911 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_0_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_0_de.png index 06f2a0c5fa..2cb1e4fd51 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_0_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5ea47b0933533cc802bab3f4ffa38c6687ccbaeeb390299a83476fb32fcbd27 -size 39442 +oid sha256:7e436f63c5c966f52ea636fb8ce8899f2ec41af253a5c2452929bdf35cf8b7fc +size 39075 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_1_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_1_de.png index 5b66d88432..a3f6171365 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_1_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e7c2933c34d34c1ea48376c3633cf6014ff16c14c247f31c1b88aadb6e7a491d -size 39448 +oid sha256:3f98728c171887bb999ae38f0ca90aaaa932303dce159aeb35328a6a6bde9ef8 +size 39098 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 d6e2d96573..77c327d1f4 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:7b4183dcbc0fde7b393fa4f4aa43ed4d95157fa944a79cdc05307eb1baae7479 -size 35248 +oid sha256:dd5e8d1e70cb7a71b526c2babd07ed14bd7e18de89953b3f040e9edc7c72f1e0 +size 38100 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 3b48b3b909..daf7ef9507 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:42b6668d0325ceb12ee4aef8754d8903b6cad2b2da5ae405469a703a7b6bc58a -size 102580 +oid sha256:73a300a4dc220986afd1e6442795cb3299bbd29fa94d59674e7d743a491964cb +size 105285 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 b6bfff3d70..6735fc5a86 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:0f60fb69e52ecb0a16b38d1a79320db3f3b6635ce1516d977fa486a7c31fa4a7 -size 71906 +oid sha256:f1b122ef1923016968db25815391af5bb372068f554d092cce520a46bfdcf77e +size 72958 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 19a8ac71e0..6735fc5a86 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:81b2ac20a3f42c5af119227d409554d6f00ee5f19b85a5458e82ab00aa70fc3b -size 54416 +oid sha256:f1b122ef1923016968db25815391af5bb372068f554d092cce520a46bfdcf77e +size 72958 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 1ae4a0f5bd..5e332619ba 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:88650645de244e762dd395302998609ca5a05a9773aaddf8627bd06e90d0ca0a -size 72442 +oid sha256:695b215f1e215b93cf82f64814b21162cb472549a375ccf493d98663b1915bf0 +size 73482 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 da432791d9..4e2f4beb5e 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:d72a0982fb6d03b901a411feab9155db7b0200bd11ed12104e2ac19a28db11d9 -size 41937 +oid sha256:f90b47b9b762a5305012bd73543ab8323a0c4b74ed4e87be09b8951419d4b3cf +size 42987 diff --git a/screenshots/de/features.securebackup.impl.enable_SecureBackupEnableView_Day_0_de.png b/screenshots/de/features.securebackup.impl.enable_SecureBackupEnableView_Day_0_de.png deleted file mode 100644 index 0cf0bbc9ca..0000000000 --- a/screenshots/de/features.securebackup.impl.enable_SecureBackupEnableView_Day_0_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:47e4038381a918854e8f300c57281c5e61f73d18b009dbae2f52d381fd036227 -size 14859 diff --git a/screenshots/de/features.securebackup.impl.enable_SecureBackupEnableView_Day_1_de.png b/screenshots/de/features.securebackup.impl.enable_SecureBackupEnableView_Day_1_de.png deleted file mode 100644 index 701677d20f..0000000000 --- a/screenshots/de/features.securebackup.impl.enable_SecureBackupEnableView_Day_1_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d6858c788b16564030c505fc691617af88ebd27eff9d55e9dfd882eda3e486dd -size 15485 diff --git a/screenshots/de/features.securebackup.impl.enable_SecureBackupEnableView_Day_2_de.png b/screenshots/de/features.securebackup.impl.enable_SecureBackupEnableView_Day_2_de.png deleted file mode 100644 index 818ad9be90..0000000000 --- a/screenshots/de/features.securebackup.impl.enable_SecureBackupEnableView_Day_2_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e078831bd77618d8768d433da333931f2ae60e0f1c09f7c0bc78d8520959d989 -size 20347 diff --git a/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_de.png b/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_de.png index e9c791c00a..7cd3ce948d 100644 --- a/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_de.png +++ b/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1aa3ee3208c690c6594107c774eff804b435bfdd691e0154418ca8db11c596b6 -size 43377 +oid sha256:6314eeda5c45213d44b8fd97069c49f6a3d4b690365aa59c1b30aeb0d218304e +size 38783 diff --git a/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_de.png b/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_de.png index cabb39872e..a9382b2613 100644 --- a/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_de.png +++ b/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0141c82238bd2a8b3c8137dca5e9d0e351db6f667cd1f338ada8026e7caf7255 -size 54044 +oid sha256:f091402b301654b498066f938bf7f4e307b2fa6ac6f22e7e479169c258ceeaca +size 49806 diff --git a/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_de.png b/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_de.png index 664808eeef..dfadd20378 100644 --- a/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_de.png +++ b/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3af24e0a81573a0d0d152d43eecaf0c18cb1f808e271acf26389e7391f4e88ba -size 52434 +oid sha256:22be75f1c0fb9ae4f73149c90d4c0f537be304c8ce754c360a07322f697324b2 +size 48137 diff --git a/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_de.png b/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_de.png index c5fbbf578f..c8e784643f 100644 --- a/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_de.png +++ b/screenshots/de/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f13efb7f324be6e39efeb691cda69ccfc1d4ffa2a7c96bd7c2c5873109e03b0 -size 45415 +oid sha256:963b54459978896792b77dcd1b4ad1394eb45306b616894d53afb9fd15e1ec3b +size 40839 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_0_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_0_de.png index 45be20d156..690f086352 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_0_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d15c5482ee3ff6e83fa44af93fd489ca36b858a9ce0e6921109e4d8a0b3a970 -size 23244 +oid sha256:c323c13474785b3dc08400a7bd408d549c964db66012d09132094526f5de7e4d +size 25252 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_10_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_10_de.png index 45be20d156..690f086352 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_10_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d15c5482ee3ff6e83fa44af93fd489ca36b858a9ce0e6921109e4d8a0b3a970 -size 23244 +oid sha256:c323c13474785b3dc08400a7bd408d549c964db66012d09132094526f5de7e4d +size 25252 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_11_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_11_de.png index 5632f77cc9..945bc6b0f0 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_11_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1339a9e8b240506fea6011c0b81be79a840de43fbeb66f68a6733d8b10796249 -size 29110 +oid sha256:5e90577ef426511ba1e63bc179a6acc34cdff38b712e32c4c4b9ab4f98f86783 +size 26037 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_12_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_12_de.png index a45558fb83..945bc6b0f0 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_12_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52a9bec3dacf4ab4c6263e1b7f232d5e9736aa42fc1e449d94118342ec5c7a5d -size 45732 +oid sha256:5e90577ef426511ba1e63bc179a6acc34cdff38b712e32c4c4b9ab4f98f86783 +size 26037 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_13_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_13_de.png index a5bbff3c99..1c65f225b8 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_13_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:83bd850a59839347a3be0cd660eab531bc3e52397fb26868bf5c10997d2a1011 -size 37043 +oid sha256:b0a5ca246bc6a1c2ad9e983e025dd02b9f205ec09293e1e676ee88491e017e37 +size 48712 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_14_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_14_de.png new file mode 100644 index 0000000000..dc14b8dcbd --- /dev/null +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_14_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11c25fc7c638ce5ab76981752109fc39aec07f1c3f1a90786b37af98e54233ac +size 48962 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_15_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_15_de.png new file mode 100644 index 0000000000..564282a844 --- /dev/null +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_15_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e596d6454d6edb86d6ec9da01b96f6dd46798d062614da75d27e2a4b5e14f83 +size 40260 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_16_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_16_de.png new file mode 100644 index 0000000000..77f522ed2f --- /dev/null +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_16_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35c99f0390cfe7dd87cbdd8cd4f931f3fa8aa6d41db578e9c7398c86a23f64e0 +size 46810 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_17_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_17_de.png new file mode 100644 index 0000000000..66ffba64e5 --- /dev/null +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_17_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:acad340964847803a06f3d0ea38c93b706d1b2e35369c122ffe0a1e77ce177ba +size 36929 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_1_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_1_de.png index fc22438566..945bc6b0f0 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_1_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49cf88250e221f57f21b3c91aea2a122ae5fb70c22bbbeb5dfa39f87428bf8e0 -size 25271 +oid sha256:5e90577ef426511ba1e63bc179a6acc34cdff38b712e32c4c4b9ab4f98f86783 +size 26037 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_2_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_2_de.png index 487dcacd49..4e68672dc4 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_2_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72a1512650bfd8fbc545956bb43aa565b8cf3e6aeac3ce18ef661b9f504f4e77 -size 24872 +oid sha256:eee8ee71e1fdbec8b0872fa2b1fd9dfc3094c808a5923e0f4bb21fe565472d50 +size 26310 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_3_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_3_de.png index 05d0e7571e..d06959de0d 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_3_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49867e3a125619132b1c61fa1b6b433d63a1cad36ca8b1e5a0dbf5650680a453 -size 34778 +oid sha256:206b2f37f6efa2b25bfc84a5a8b0b110703d06197b87676c46faf0daca24d4e3 +size 26688 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_4_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_4_de.png index 9a92473b21..690f086352 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_4_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1673876e3df80c737cc56b7fdc18919607d0491b29816539149085354849f885 -size 21685 +oid sha256:c323c13474785b3dc08400a7bd408d549c964db66012d09132094526f5de7e4d +size 25252 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_5_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_5_de.png index fc22438566..945bc6b0f0 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_5_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49cf88250e221f57f21b3c91aea2a122ae5fb70c22bbbeb5dfa39f87428bf8e0 -size 25271 +oid sha256:5e90577ef426511ba1e63bc179a6acc34cdff38b712e32c4c4b9ab4f98f86783 +size 26037 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_6_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_6_de.png index fc22438566..86b5a69e6a 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_6_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49cf88250e221f57f21b3c91aea2a122ae5fb70c22bbbeb5dfa39f87428bf8e0 -size 25271 +oid sha256:aa0f1d7099c533d80c6420b6e74db32acd9421cdc662873b6d5201dddeacf63d +size 27767 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_7_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_7_de.png index fc22438566..945bc6b0f0 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_7_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49cf88250e221f57f21b3c91aea2a122ae5fb70c22bbbeb5dfa39f87428bf8e0 -size 25271 +oid sha256:5e90577ef426511ba1e63bc179a6acc34cdff38b712e32c4c4b9ab4f98f86783 +size 26037 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_8_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_8_de.png index 5e05c1a8b5..945bc6b0f0 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_8_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5db7620b417796b9ef6eae5877f53ca73a5e51c93a873d12ff0b65aa422b631a -size 23245 +oid sha256:5e90577ef426511ba1e63bc179a6acc34cdff38b712e32c4c4b9ab4f98f86783 +size 26037 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_9_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_9_de.png index fc22438566..945bc6b0f0 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_9_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49cf88250e221f57f21b3c91aea2a122ae5fb70c22bbbeb5dfa39f87428bf8e0 -size 25271 +oid sha256:5e90577ef426511ba1e63bc179a6acc34cdff38b712e32c4c4b9ab4f98f86783 +size 26037 diff --git a/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_de.png b/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_de.png index 5d84bce440..c6cdc771ff 100644 --- a/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_de.png +++ b/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c15cc18dd4e8adc55b0117e4ceac23815d4edb4db5457d1d4eb83c4ff9536ab3 -size 28054 +oid sha256:423cce16225a8f6666dc737ffae65e3f90388042ddb934f5fc2e3d35879247bf +size 27425 diff --git a/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_de.png b/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_de.png index 9503d4062a..15c6851cd3 100644 --- a/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_de.png +++ b/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6e21b399b7785db3706c530235cc17ae29277e4c6126214bd548595165b30d99 -size 24905 +oid sha256:a04c2f10e6fbe6a944b7083974689202c3d2a469b359ec5fa007fbdf87376c32 +size 24243 diff --git a/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_de.png b/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_de.png index 5d84bce440..c6cdc771ff 100644 --- a/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_de.png +++ b/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c15cc18dd4e8adc55b0117e4ceac23815d4edb4db5457d1d4eb83c4ff9536ab3 -size 28054 +oid sha256:423cce16225a8f6666dc737ffae65e3f90388042ddb934f5fc2e3d35879247bf +size 27425 diff --git a/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_de.png b/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_de.png index 9503d4062a..15c6851cd3 100644 --- a/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_de.png +++ b/screenshots/de/features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6e21b399b7785db3706c530235cc17ae29277e4c6126214bd548595165b30d99 -size 24905 +oid sha256:a04c2f10e6fbe6a944b7083974689202c3d2a469b359ec5fa007fbdf87376c32 +size 24243 diff --git a/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_de.png b/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_de.png index 5c0e448e9d..0d3cbd4a13 100644 --- a/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_de.png +++ b/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bba2ce4a79d7605078de9660b405c717d78f55019dfd37ad6abe0b2854edb60 -size 65340 +oid sha256:7d017296e712146b37b04ad0f10027a5325c3bfbb8d0085fb5ea9a31858c575f +size 64991 diff --git a/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_de.png b/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_de.png index 75e9952085..e84d80baf7 100644 --- a/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_de.png +++ b/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7eb73aca3c702f0adba72e5faa1c1d351410c5930ebf132aeca157569510e32b -size 61825 +oid sha256:02f5ed000cf42a4a3e924372ed1c1d7adfef331ab3ae9b28e3c1e720b9c34ebd +size 61468 diff --git a/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupView_Day_0_de.png b/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupView_Day_0_de.png index 3760c19485..1c36b780aa 100644 --- a/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupView_Day_0_de.png +++ b/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d70004f75a79ef47bc2c93274c2c41bcb4bd04cf2137eeeb152f4a6b02399408 -size 70330 +oid sha256:e4a5c31371cd02e16f1db063ff7e3bee34f57706431fd5ecd4d380ee76176f88 +size 70014 diff --git a/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupView_Day_1_de.png b/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupView_Day_1_de.png index d49c35e3af..9874fb4693 100644 --- a/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupView_Day_1_de.png +++ b/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a8027c8c051a0916d5fa75803aebde4c403dd1f51f52aaf6b44882471161528 -size 67014 +oid sha256:6f1ba73459572271b9ee1b9a75b3e8d158fe6198e84f776057f4be7034579ef8 +size 66710 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_0_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_0_de.png index 8eed78893b..ff0981041c 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_0_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:592ae0537a34e5479944847adeb150fdc1988f39ea6d7e9d2ac387c593385747 -size 29475 +oid sha256:4d5708cb60a8d4b7a0d1c5c4b1621bc320dc0af3865ed79f17d9fa61ddcdf679 +size 29727 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_1_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_1_de.png index afdb40a9fd..89f37b3886 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_1_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f464ddfb840672263e63df6b6f7b67b06b9b430a3ac0dfbee6067f2110b9f0a8 -size 29844 +oid sha256:cf667fb981046b9e99c3cb689d5f23292471805b45d6aeefbe76ca876a4cca19 +size 27791 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_6_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_6_de.png index 7611d2043a..fe0d92427b 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_6_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b4113ecdf9eedca5cd0f9179f0c2a15416928fc776278160a9d9ec3b039e188b -size 28371 +oid sha256:e735d995e2300af280d5533ad4f31ab4dc0d71d58ef5a73f6c597d815691f0ef +size 28501 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_7_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_7_de.png index 3e0e3b8b0e..27cfccb226 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_7_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aca66b8a455ee9eb510dc3bf8b7a22abbbbf44accbb4c90964d9409b6c630bd5 -size 30564 +oid sha256:042b2541dbc95ef5eb6db6d42da826493e458326ba5a576975263e3774b2c4bd +size 30805 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_1_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_1_de.png new file mode 100644 index 0000000000..8950dc9e36 --- /dev/null +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8693473e2f9f8f0b18585645745ee113b94b0a52d2b62399f8e3d9e54df22ecd +size 41774 diff --git a/screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_2_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_de.png similarity index 100% rename from screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_2_de.png rename to screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_de.png diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_3_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_3_de.png new file mode 100644 index 0000000000..e4512a48cf --- /dev/null +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_3_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5aec87f10dae7b22d5eb88fcc4127557247fd191ca53e74ac3ffc8bf58b3e270 +size 50002 diff --git a/screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_6_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_de.png similarity index 100% rename from screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_6_de.png rename to screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_de.png diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_5_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_5_de.png new file mode 100644 index 0000000000..3e68cea981 --- /dev/null +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_5_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:038ff5080cdae88761104d16cd4e8183c0a48baec559bffe85d0b0a86735a884 +size 22229 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_6_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_6_de.png new file mode 100644 index 0000000000..1bf33aa875 --- /dev/null +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_6_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3079fb1d9f25f6ca86a7c41e7df95a7c1593019e3f27594e004e9be874e67669 +size 24474 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_de.png new file mode 100644 index 0000000000..63832b54a0 --- /dev/null +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0eb079a6b91d1862d95f2ef3b99021b5185f66a5677eeb9fef6add9f6a6d25b +size 30001 diff --git a/screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_0_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_0_de.png similarity index 100% rename from screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_0_de.png rename to screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_0_de.png diff --git a/screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_10_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_10_de.png similarity index 100% rename from screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_10_de.png rename to screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_10_de.png diff --git a/screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_1_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_de.png similarity index 100% rename from screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_1_de.png rename to screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_de.png diff --git a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_de.png new file mode 100644 index 0000000000..340196e551 --- /dev/null +++ b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91da377954ad5d2058a34ff1dbccc9f54e4ed490186901edc53e7bf4423525d5 +size 53244 diff --git a/screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_3_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_de.png similarity index 100% rename from screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_3_de.png rename to screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_de.png diff --git a/screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_4_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_de.png similarity index 100% rename from screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_4_de.png rename to screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_de.png diff --git a/screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_5_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_5_de.png similarity index 100% rename from screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_5_de.png rename to screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_5_de.png diff --git a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_de.png new file mode 100644 index 0000000000..f79c3fe443 --- /dev/null +++ b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4ec5652409e18bb7076f07abfdef63c0e26b43103f7a11850daa192fb0b25574 +size 35687 diff --git a/screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_7_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_7_de.png similarity index 100% rename from screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_7_de.png rename to screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_7_de.png diff --git a/screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_8_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_8_de.png similarity index 100% rename from screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_8_de.png rename to screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_8_de.png diff --git a/screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_9_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_9_de.png similarity index 100% rename from screenshots/de/features.verifysession.impl_VerifySelfSessionView_Day_9_de.png rename to screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_9_de.png diff --git a/screenshots/de/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_de.png b/screenshots/de/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_de.png index 8f7b3941de..c96d2568c1 100644 --- a/screenshots/de/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_de.png +++ b/screenshots/de/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:80945a11c7d0b46721c344a3409502b9ba88c0147231527fdfea57a05286c615 -size 13176 +oid sha256:1811d5354e2ad932bc2b50d7bf67973b5e94bc0dcba936ef8b7d846c60df0385 +size 52412 diff --git a/screenshots/de/libraries.textcomposer_TextComposerEdit_Day_0_de.png b/screenshots/de/libraries.textcomposer_TextComposerEdit_Day_0_de.png index 8f7b3941de..c96d2568c1 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerEdit_Day_0_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerEdit_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:80945a11c7d0b46721c344a3409502b9ba88c0147231527fdfea57a05286c615 -size 13176 +oid sha256:1811d5354e2ad932bc2b50d7bf67973b5e94bc0dcba936ef8b7d846c60df0385 +size 52412 diff --git a/screenshots/de/libraries.textcomposer_TextComposerFormatting_Day_0_de.png b/screenshots/de/libraries.textcomposer_TextComposerFormatting_Day_0_de.png index 4e35edbe2c..1678554d8f 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerFormatting_Day_0_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerFormatting_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2713c1a6becca31bd61ac33cb30ea706941cb3ec287e951f2fdaad938a7232b -size 40601 +oid sha256:e404709734894ce50100f22ce69c5f968fbdf1b2a05f9a37368281b1c0c11792 +size 51485 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_0_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_0_de.png index a62149072c..0cd1ec1d20 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_0_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3d1ba80e07a7b575055565bc003b61fecf71e0b77d674ffbe82798c53cb2e2d -size 19475 +oid sha256:d2b7861629578025a37fa19c43c269cdeccfd6ffa0e6724ddd0abcdd85f2c0fa +size 74096 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_10_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_10_de.png index 401f566cfa..b95b3144c7 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_10_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d4df060e018febc240d72aa3370b020b43d85bb50d7c652874cab2714263acb8 -size 14947 +oid sha256:3eac9973783875e97b56255de03b2244d0932c3ef40fdb44724ddb73955dc06e +size 57369 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_11_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_11_de.png index 4e20922a3b..3769f9f5db 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_11_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ecae15f2e602777469676ed077ef29bff074024cd794311158d04b9811d52e4 -size 18860 +oid sha256:56d68d9a3c07b6681a61391d5fbd67e2c8f05ea9d228005252cc6b7bd333338a +size 72096 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_1_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_1_de.png index 7cd756a085..05f89acc8f 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_1_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4238ca250f17086205bb34379ea3a8dbb5cd1b24027109e3659283d260b50713 -size 22010 +oid sha256:549e7209cbfebc28982c2b93ff119115fee1be9fc64316c57827f49665a07021 +size 83558 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_2_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_2_de.png index c8b88b599d..d2beb2bbdc 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_2_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74144c27f19aabfdb4cd4066678a2b471b9064c4cdd3ff119e053428e6c4936e -size 15772 +oid sha256:838c627751a00937768125d67bc71ebb79fdd0d1d10fd0f309767d5e15c8ea1d +size 60513 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_3_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_3_de.png index b8704f2e1d..eed4719808 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_3_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca4bf6b0c4949b59754f4cd8085a5d9fce635e270d8d7d12126a7e2e3aff6c4a -size 15346 +oid sha256:25b4ea4e83cc677b26c2f77c5dacaa9abafaf7bde61c589159f45183772a2327 +size 58836 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_4_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_4_de.png index bc8ba42475..16a1d822b8 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_4_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94471b0c80c21c983963e59bbceda3c017608f0c68dbb8f0c02fbf8a21b2c904 -size 17554 +oid sha256:ae4497bdc4fb90e3d5c8f45fc40b4ada370ac8c8b8eb2322b8e5b04c93a37422 +size 66859 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_5_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_5_de.png index e5b1ce5577..b5bd1ccc64 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_5_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aef08a107ee1be46c9f1cc24be0c825c247d3f57efe8057cfd1009716ba8513a -size 15067 +oid sha256:decb63ebce376a9ba21c399a700be9a88e174cf7665ee27bcde83f588f102972 +size 57781 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_6_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_6_de.png index 17ec85b220..5114aa83c9 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_6_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:705c20277eb5578f0f656ca8daf6652171fa2b8b2af37848db0163e805d05889 -size 15278 +oid sha256:ad1db2ac97fb998d06fe62f61c4cd9e46683dcebcc58848d6eb15a7d47dfca8e +size 58580 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_7_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_7_de.png index d1db0eea09..f1db8b495b 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_7_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d8abccc9cd15fb9d6f508110c5b66a8184402f9865691b9ea10289729320f8a -size 15801 +oid sha256:8869265fa93baeb8e444f82abc032cd67e6395da6bede9b2d91cbe5d48c7b804 +size 60713 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_8_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_8_de.png index c32e876b3b..128b9d24dc 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_8_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d271a75c5581f68950c6d697a597c35b04184e9aab72a59401475f097583bbf -size 18160 +oid sha256:88720940583f94513d68f3059201f3129f4d3c4883bcb92c74a8215af96f86e0 +size 68839 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_9_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_9_de.png index 3da149125a..877f59a8cd 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_9_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13717aaaa5fbb7b160446febdfb9cb1b08afaa36bb23050b629d5119894b18d6 -size 15151 +oid sha256:477bf8c6b10383ab280339a3878fef27288449b8d18e85eccec93e99deb7e965 +size 58054 diff --git a/screenshots/html/data.js b/screenshots/html/data.js index c099eed388..19d46e4d56 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -1,58 +1,59 @@ // 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",20021,], +["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20027,], ["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",20021,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_2_en","features.invite.impl.response_AcceptDeclineInviteView_Night_2_en",20021,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_3_en","features.invite.impl.response_AcceptDeclineInviteView_Night_3_en",20021,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_4_en","features.invite.impl.response_AcceptDeclineInviteView_Night_4_en",20021,], -["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20021,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_1_en","features.invite.impl.response_AcceptDeclineInviteView_Night_1_en",20027,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_2_en","features.invite.impl.response_AcceptDeclineInviteView_Night_2_en",20027,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_3_en","features.invite.impl.response_AcceptDeclineInviteView_Night_3_en",20027,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_4_en","features.invite.impl.response_AcceptDeclineInviteView_Night_4_en",20027,], +["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20027,], ["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",0,], -["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20021,], -["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20021,], -["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20021,], +["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20027,], +["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20027,], +["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20027,], ["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.messages.impl.actionlist_ActionListViewContent_Day_0_en","features.messages.impl.actionlist_ActionListViewContent_Night_0_en",0,], -["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20021,], -["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20021,], -["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20021,], +["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20027,], +["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20027,], +["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20027,], ["features.messages.impl.actionlist_ActionListViewContent_Day_1_en","features.messages.impl.actionlist_ActionListViewContent_Night_1_en",0,], -["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20021,], -["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20021,], -["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20021,], -["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20021,], -["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20021,], -["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20021,], -["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20021,], -["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20021,], -["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20021,], -["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20021,], -["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20021,], -["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20021,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en",20021,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en",20021,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en",20021,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en",20021,], -["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20021,], -["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20021,], -["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20021,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20021,], -["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20021,], -["services.apperror.impl_AppErrorView_Day_0_en","services.apperror.impl_AppErrorView_Night_0_en",20021,], +["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20027,], +["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20027,], +["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20027,], +["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20027,], +["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20027,], +["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20027,], +["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20027,], +["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20027,], +["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20027,], +["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20027,], +["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20027,], +["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20027,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en",20027,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en",20027,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en",20027,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en",20027,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_4_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_4_en",20031,], +["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20027,], +["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20027,], +["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20027,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20027,], +["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20027,], +["services.apperror.impl_AppErrorView_Day_0_en","services.apperror.impl_AppErrorView_Night_0_en",20027,], ["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",20021,], +["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",20027,], ["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",20021,], +["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",20027,], ["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",20021,], +["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",20027,], ["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",20021,], +["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",20027,], ["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,], @@ -62,11 +63,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","",20021,], -["features.messages.impl.attachments.preview_AttachmentsView_1_en","",20021,], -["features.messages.impl.attachments.preview_AttachmentsView_2_en","",20021,], -["features.messages.impl.attachments.preview_AttachmentsView_3_en","",20021,], -["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20021,], +["features.messages.impl.attachments.preview_AttachmentsView_0_en","",20027,], +["features.messages.impl.attachments.preview_AttachmentsView_1_en","",20027,], +["features.messages.impl.attachments.preview_AttachmentsView_2_en","",20027,], +["features.messages.impl.attachments.preview_AttachmentsView_3_en","",20027,], +["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20027,], ["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,], @@ -149,13 +150,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",20021,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20021,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20021,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20021,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20021,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20021,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20021,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20027,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20027,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20027,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20027,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20027,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20027,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20027,], ["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,], @@ -166,100 +167,100 @@ 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",20021,], -["features.rageshake.impl.bugreport_BugReportView_Day_1_en","features.rageshake.impl.bugreport_BugReportView_Night_1_en",20021,], -["features.rageshake.impl.bugreport_BugReportView_Day_2_en","features.rageshake.impl.bugreport_BugReportView_Night_2_en",20021,], -["features.rageshake.impl.bugreport_BugReportView_Day_3_en","features.rageshake.impl.bugreport_BugReportView_Night_3_en",20021,], -["features.rageshake.impl.bugreport_BugReportView_Day_4_en","features.rageshake.impl.bugreport_BugReportView_Night_4_en",20021,], +["features.rageshake.impl.bugreport_BugReportView_Day_0_en","features.rageshake.impl.bugreport_BugReportView_Night_0_en",20027,], +["features.rageshake.impl.bugreport_BugReportView_Day_1_en","features.rageshake.impl.bugreport_BugReportView_Night_1_en",20027,], +["features.rageshake.impl.bugreport_BugReportView_Day_2_en","features.rageshake.impl.bugreport_BugReportView_Night_2_en",20027,], +["features.rageshake.impl.bugreport_BugReportView_Day_3_en","features.rageshake.impl.bugreport_BugReportView_Night_3_en",20027,], +["features.rageshake.impl.bugreport_BugReportView_Day_4_en","features.rageshake.impl.bugreport_BugReportView_Night_4_en",20027,], ["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",20021,], -["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20021,], -["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20021,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20021,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_0_en",20021,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_10_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_10_en",20021,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_1_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_1_en",20021,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_2_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_2_en",20021,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_3_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_3_en",20021,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_4_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_4_en",20021,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_5_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_5_en",20021,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_6_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_6_en",20021,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_7_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_7_en",20021,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_8_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_8_en",20021,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_9_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_9_en",20021,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en",20021,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en",20021,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en",20021,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en",20021,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en",20021,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en",20021,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en",20021,], +["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20027,], +["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20027,], +["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20027,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20027,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_0_en",20027,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_10_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_10_en",20027,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_1_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_1_en",20027,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_2_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_2_en",20027,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_3_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_3_en",20027,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_4_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_4_en",20027,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_5_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_5_en",20027,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_6_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_6_en",20027,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_7_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_7_en",20027,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_8_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_8_en",20027,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_9_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_9_en",20027,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en",20027,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en",20027,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en",20027,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en",20027,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en",20027,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en",20027,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en",20027,], ["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",20021,], -["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20021,], +["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",20027,], +["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20027,], ["libraries.matrix.ui.components_CheckableResolvedUserRow_en","",0,], -["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20021,], +["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20027,], ["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.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20021,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20021,], -["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20021,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20027,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20027,], +["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20027,], ["libraries.textcomposer_ComposerModeView_Day_1_en","libraries.textcomposer_ComposerModeView_Night_1_en",0,], ["libraries.textcomposer_ComposerModeView_Day_2_en","libraries.textcomposer_ComposerModeView_Night_2_en",0,], ["libraries.textcomposer_ComposerModeView_Day_3_en","libraries.textcomposer_ComposerModeView_Night_3_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",20021,], -["features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en","features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en",20021,], +["features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en","features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en",20027,], +["features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en","features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en",20027,], ["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",20021,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20021,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20021,], -["features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20021,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20027,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20027,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20027,], +["features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20027,], ["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",20021,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20021,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20021,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20021,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20021,], -["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20021,], -["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20021,], -["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20021,], -["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20021,], -["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20021,], -["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20021,], -["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20021,], -["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20021,], -["features.createroom.impl.root_CreateRoomRootView_Day_0_en","features.createroom.impl.root_CreateRoomRootView_Night_0_en",20021,], -["features.createroom.impl.root_CreateRoomRootView_Day_1_en","features.createroom.impl.root_CreateRoomRootView_Night_1_en",20021,], -["features.createroom.impl.root_CreateRoomRootView_Day_2_en","features.createroom.impl.root_CreateRoomRootView_Night_2_en",20021,], -["features.createroom.impl.root_CreateRoomRootView_Day_3_en","features.createroom.impl.root_CreateRoomRootView_Night_3_en",20021,], -["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime pickers_en","",20021,], -["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime pickers_en","",20021,], +["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20027,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20027,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20027,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20027,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20027,], +["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20027,], +["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20027,], +["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20027,], +["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20027,], +["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20027,], +["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20027,], +["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20027,], +["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20027,], +["features.createroom.impl.root_CreateRoomRootView_Day_0_en","features.createroom.impl.root_CreateRoomRootView_Night_0_en",20027,], +["features.createroom.impl.root_CreateRoomRootView_Day_1_en","features.createroom.impl.root_CreateRoomRootView_Night_1_en",20027,], +["features.createroom.impl.root_CreateRoomRootView_Day_2_en","features.createroom.impl.root_CreateRoomRootView_Night_2_en",20027,], +["features.createroom.impl.root_CreateRoomRootView_Day_3_en","features.createroom.impl.root_CreateRoomRootView_Night_3_en",20027,], +["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime pickers_en","",20027,], +["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime pickers_en","",20027,], ["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",20021,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20021,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20021,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",20027,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20027,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20027,], ["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",20021,], -["features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en",20021,], -["features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en",20021,], +["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",20027,], +["features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en",20027,], +["features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en",20027,], ["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",20021,], -["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20021,], -["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20021,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20021,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20021,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20021,], -["libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Day_0_en","libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Night_0_en",20021,], +["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20027,], +["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20027,], +["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20027,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20027,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20027,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20027,], +["libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Day_0_en","libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Night_0_en",20027,], ["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,], @@ -271,12 +272,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",20021,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20021,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20021,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20021,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20021,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20021,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20027,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20027,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20027,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20027,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20027,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20027,], ["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,], @@ -286,9 +287,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,], -["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20021,], -["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20021,], -["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20021,], +["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20027,], +["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20027,], +["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20027,], ["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,], @@ -299,15 +300,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",20021,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20021,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20021,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20027,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20027,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20027,], ["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",20021,], -["features.roomlist.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.roomlist.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20021,], +["features.messages.impl.forward_ForwardMessagesView_Day_3_en","features.messages.impl.forward_ForwardMessagesView_Night_3_en",20027,], +["features.roomlist.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.roomlist.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20027,], ["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,], @@ -328,46 +329,54 @@ export const screenshots = [ ["libraries.designsystem.icons_IconsCompound_Day_4_en","libraries.designsystem.icons_IconsCompound_Night_4_en",0,], ["libraries.designsystem.icons_IconsOther_Day_0_en","libraries.designsystem.icons_IconsOther_Night_0_en",0,], ["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_0_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_0_en",0,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20021,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20021,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20027,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20027,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_0_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_0_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_10_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_10_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_11_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_11_en",0,], ["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",20021,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20027,], ["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",20021,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",20027,], ["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",20021,], +["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",20027,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",0,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20031,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20031,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20031,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20031,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20031,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20031,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20031,], ["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",20021,], +["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20027,], ["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_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",0,], -["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20021,], -["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20021,], -["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20021,], -["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20021,], -["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20021,], -["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20021,], -["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20021,], -["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20021,], -["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20021,], +["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20027,], +["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20027,], +["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20027,], +["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20027,], +["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20027,], +["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20027,], +["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20027,], +["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20027,], +["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20027,], ["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",20021,], -["features.leaveroom.api_LeaveRoomView_Day_2_en","features.leaveroom.api_LeaveRoomView_Night_2_en",20021,], -["features.leaveroom.api_LeaveRoomView_Day_3_en","features.leaveroom.api_LeaveRoomView_Night_3_en",20021,], -["features.leaveroom.api_LeaveRoomView_Day_4_en","features.leaveroom.api_LeaveRoomView_Night_4_en",20021,], -["features.leaveroom.api_LeaveRoomView_Day_5_en","features.leaveroom.api_LeaveRoomView_Night_5_en",20021,], -["features.leaveroom.api_LeaveRoomView_Day_6_en","features.leaveroom.api_LeaveRoomView_Night_6_en",20021,], +["features.leaveroom.api_LeaveRoomView_Day_1_en","features.leaveroom.api_LeaveRoomView_Night_1_en",20027,], +["features.leaveroom.api_LeaveRoomView_Day_2_en","features.leaveroom.api_LeaveRoomView_Night_2_en",20027,], +["features.leaveroom.api_LeaveRoomView_Day_3_en","features.leaveroom.api_LeaveRoomView_Night_3_en",20027,], +["features.leaveroom.api_LeaveRoomView_Day_4_en","features.leaveroom.api_LeaveRoomView_Night_4_en",20027,], +["features.leaveroom.api_LeaveRoomView_Day_5_en","features.leaveroom.api_LeaveRoomView_Night_5_en",20027,], +["features.leaveroom.api_LeaveRoomView_Day_6_en","features.leaveroom.api_LeaveRoomView_Night_6_en",20027,], ["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,], @@ -424,29 +433,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",20021,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20021,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20021,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20021,], +["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20027,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20027,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20027,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20027,], ["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",20021,], -["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20021,], -["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20021,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20021,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20021,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20021,], -["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20021,], -["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20021,], -["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20021,], -["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20021,], -["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20021,], -["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20021,], -["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20021,], -["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20021,], -["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20021,], -["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20021,], +["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",20027,], +["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20027,], +["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20027,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20027,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20027,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20027,], +["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20027,], +["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20027,], +["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20027,], +["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20027,], +["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20027,], +["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20027,], +["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20027,], +["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20027,], +["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20027,], +["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20027,], ["libraries.designsystem.components.button_MainActionButton_Buttons_en","",0,], -["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20021,], +["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20027,], ["libraries.textcomposer.components.markdown_MarkdownTextInput_Day_0_en","libraries.textcomposer.components.markdown_MarkdownTextInput_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomNeutral_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomNeutral_Night_0_en",0,], @@ -459,7 +468,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","",20021,], +["libraries.mediaviewer.api.viewer_MediaViewerView_2_en","",20027,], ["libraries.mediaviewer.api.viewer_MediaViewerView_3_en","",0,], ["libraries.mediaviewer.api.viewer_MediaViewerView_4_en","",0,], ["libraries.mediaviewer.api.viewer_MediaViewerView_5_en","",0,], @@ -471,7 +480,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",20021,], +["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",20027,], ["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,], @@ -488,7 +497,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",20021,], +["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20027,], ["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,], @@ -496,25 +505,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.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20021,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20021,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20021,], -["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20021,], -["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20021,], -["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20021,], -["features.messages.impl_MessagesView_Day_12_en","features.messages.impl_MessagesView_Night_12_en",20021,], -["features.messages.impl_MessagesView_Day_13_en","features.messages.impl_MessagesView_Night_13_en",20021,], -["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20021,], -["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20021,], -["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20021,], -["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20021,], -["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20021,], -["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20021,], -["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20021,], -["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20021,], -["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20021,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20027,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20027,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20027,], +["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20027,], +["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20027,], +["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20027,], +["features.messages.impl_MessagesView_Day_12_en","features.messages.impl_MessagesView_Night_12_en",20027,], +["features.messages.impl_MessagesView_Day_13_en","features.messages.impl_MessagesView_Night_13_en",20027,], +["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20027,], +["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20027,], +["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20027,], +["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20027,], +["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20027,], +["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20027,], +["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20027,], +["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20027,], +["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20027,], ["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",20021,], +["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",20027,], ["libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom Sheets_en","",0,], ["libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom Sheets_en","",0,], ["appicon.element_MonochromeIcon_en","",0,], @@ -523,29 +532,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.roomlist.impl.components_NativeSlidingSyncMigrationBanner_Day_0_en","features.roomlist.impl.components_NativeSlidingSyncMigrationBanner_Night_0_en",20021,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20021,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20021,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20021,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20021,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20021,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20021,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20021,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20021,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20021,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20021,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20021,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20021,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20021,], -["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20021,], +["features.roomlist.impl.components_NativeSlidingSyncMigrationBanner_Day_0_en","features.roomlist.impl.components_NativeSlidingSyncMigrationBanner_Night_0_en",20027,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20027,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20027,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20027,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20027,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20027,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20027,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20027,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20027,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20027,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20027,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20027,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20027,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20027,], +["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20027,], ["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",20021,], -["features.onboarding.impl_OnBoardingView_Day_1_en","features.onboarding.impl_OnBoardingView_Night_1_en",20021,], -["features.onboarding.impl_OnBoardingView_Day_2_en","features.onboarding.impl_OnBoardingView_Night_2_en",20021,], -["features.onboarding.impl_OnBoardingView_Day_3_en","features.onboarding.impl_OnBoardingView_Night_3_en",20021,], -["features.onboarding.impl_OnBoardingView_Day_4_en","features.onboarding.impl_OnBoardingView_Night_4_en",20021,], +["features.onboarding.impl_OnBoardingView_Day_0_en","features.onboarding.impl_OnBoardingView_Night_0_en",20027,], +["features.onboarding.impl_OnBoardingView_Day_1_en","features.onboarding.impl_OnBoardingView_Night_1_en",20027,], +["features.onboarding.impl_OnBoardingView_Day_2_en","features.onboarding.impl_OnBoardingView_Night_2_en",20027,], +["features.onboarding.impl_OnBoardingView_Day_3_en","features.onboarding.impl_OnBoardingView_Night_3_en",20027,], +["features.onboarding.impl_OnBoardingView_Day_4_en","features.onboarding.impl_OnBoardingView_Night_4_en",20027,], ["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,], @@ -560,65 +569,65 @@ 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,], -["libraries.mediaviewer.api.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.api.local.pdf_PdfPagesErrorView_Night_0_en",20021,], -["features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Night_0_en",20021,], -["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20021,], -["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20021,], -["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20021,], -["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20021,], +["libraries.mediaviewer.api.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.api.local.pdf_PdfPagesErrorView_Night_0_en",20027,], +["features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Night_0_en",20027,], +["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20027,], +["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20027,], +["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20027,], +["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20027,], ["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",20021,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20021,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20021,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20021,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20021,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20021,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20021,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20021,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20021,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20021,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20021,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20021,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20021,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20021,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20021,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20021,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20027,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20027,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20027,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20027,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20027,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20027,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20027,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20027,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20027,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20027,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20027,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20027,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20027,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20027,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20027,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20027,], ["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",20021,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20021,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20021,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20021,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20021,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20021,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20021,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20021,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20021,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20021,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20021,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20021,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20021,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20021,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20027,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20027,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20027,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20027,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20027,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20027,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20027,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20027,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20027,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20027,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20027,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20027,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20027,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20027,], ["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",20021,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20021,], -["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20021,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20021,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20021,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20027,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20027,], +["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20027,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20027,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20027,], ["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",20021,], -["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20021,], -["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20021,], -["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20021,], -["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20021,], -["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20021,], -["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20021,], -["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20021,], -["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20021,], -["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20021,], -["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20021,], +["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",20027,], +["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20027,], +["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20027,], +["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20027,], +["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20027,], +["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20027,], +["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20027,], +["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20027,], +["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20027,], +["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20027,], +["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20027,], ["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,], @@ -635,194 +644,194 @@ 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","",20021,], -["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20021,], -["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20021,], -["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20021,], +["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20027,], +["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20027,], +["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20027,], +["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20027,], ["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","",20021,], -["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20021,], +["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",20027,], +["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20027,], ["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",0,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20021,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20021,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20021,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20021,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20021,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20021,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20021,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20021,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20021,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20021,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20021,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20021,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20021,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20021,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20021,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20021,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20027,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20027,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20027,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20027,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20027,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20027,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20027,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20027,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20027,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20027,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20027,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20027,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20027,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20027,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20027,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20027,], ["libraries.designsystem.theme.components_RadioButton_Toggles_en","",0,], -["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20021,], -["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20021,], +["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20027,], +["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20027,], ["features.rageshake.api.preferences_RageshakePreferencesView_Day_1_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_1_en",0,], ["features.messages.impl.timeline.components.reactionsummary_ReactionSummaryViewContent_Day_0_en","features.messages.impl.timeline.components.reactionsummary_ReactionSummaryViewContent_Night_0_en",0,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20021,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20021,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20021,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20021,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20021,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20021,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20021,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20021,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20021,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20021,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20021,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20021,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20021,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20021,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20021,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20021,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20021,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20021,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20021,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20021,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20027,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20027,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20027,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20027,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20027,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20027,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20027,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20027,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20027,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20027,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20027,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20027,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20027,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20027,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20027,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20027,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20027,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20027,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20027,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20027,], ["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",20021,], -["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20021,], -["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20021,], -["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20021,], -["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20021,], -["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20021,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20021,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20021,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20021,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20021,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20021,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20021,], +["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20027,], +["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20027,], +["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20027,], +["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20027,], +["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20027,], +["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20027,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20027,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20027,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20027,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20027,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20027,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20027,], ["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",20021,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20021,], -["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20021,], -["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20021,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en",20021,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en",20021,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en",20021,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en",20021,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en",20021,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en",20021,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en",20021,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en",20021,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20027,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20027,], +["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20027,], +["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20027,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en",20027,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en",20027,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en",20027,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en",20027,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en",20027,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en",20027,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en",20027,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en",20027,], ["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",20021,], -["features.roomdetails.impl_RoomDetailsDark_0_en","",20021,], -["features.roomdetails.impl_RoomDetailsDark_10_en","",20021,], -["features.roomdetails.impl_RoomDetailsDark_11_en","",20021,], -["features.roomdetails.impl_RoomDetailsDark_12_en","",20021,], -["features.roomdetails.impl_RoomDetailsDark_13_en","",20021,], -["features.roomdetails.impl_RoomDetailsDark_1_en","",20021,], -["features.roomdetails.impl_RoomDetailsDark_2_en","",20021,], -["features.roomdetails.impl_RoomDetailsDark_3_en","",20021,], -["features.roomdetails.impl_RoomDetailsDark_4_en","",20021,], -["features.roomdetails.impl_RoomDetailsDark_5_en","",20021,], -["features.roomdetails.impl_RoomDetailsDark_6_en","",20021,], -["features.roomdetails.impl_RoomDetailsDark_7_en","",20021,], -["features.roomdetails.impl_RoomDetailsDark_8_en","",20021,], -["features.roomdetails.impl_RoomDetailsDark_9_en","",20021,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_0_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_0_en",20021,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_1_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_1_en",20021,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_2_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_2_en",20021,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_3_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_3_en",20021,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_4_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_4_en",20021,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_5_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_5_en",20021,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_6_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_6_en",20021,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_7_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_7_en",20021,], -["features.roomdetails.impl_RoomDetails_0_en","",20021,], -["features.roomdetails.impl_RoomDetails_10_en","",20021,], -["features.roomdetails.impl_RoomDetails_11_en","",20021,], -["features.roomdetails.impl_RoomDetails_12_en","",20021,], -["features.roomdetails.impl_RoomDetails_13_en","",20021,], -["features.roomdetails.impl_RoomDetails_1_en","",20021,], -["features.roomdetails.impl_RoomDetails_2_en","",20021,], -["features.roomdetails.impl_RoomDetails_3_en","",20021,], -["features.roomdetails.impl_RoomDetails_4_en","",20021,], -["features.roomdetails.impl_RoomDetails_5_en","",20021,], -["features.roomdetails.impl_RoomDetails_6_en","",20021,], -["features.roomdetails.impl_RoomDetails_7_en","",20021,], -["features.roomdetails.impl_RoomDetails_8_en","",20021,], -["features.roomdetails.impl_RoomDetails_9_en","",20021,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20021,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20021,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20021,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20021,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20021,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20021,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20021,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_4_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_4_en",20021,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_5_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_5_en",20021,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_6_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_6_en",20021,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_7_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_7_en",20021,], -["features.roomlist.impl.components_RoomListContentView_Day_0_en","features.roomlist.impl.components_RoomListContentView_Night_0_en",20021,], -["features.roomlist.impl.components_RoomListContentView_Day_1_en","features.roomlist.impl.components_RoomListContentView_Night_1_en",20021,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20027,], +["features.roomdetails.impl_RoomDetailsDark_0_en","",20027,], +["features.roomdetails.impl_RoomDetailsDark_10_en","",20027,], +["features.roomdetails.impl_RoomDetailsDark_11_en","",20027,], +["features.roomdetails.impl_RoomDetailsDark_12_en","",20027,], +["features.roomdetails.impl_RoomDetailsDark_13_en","",20027,], +["features.roomdetails.impl_RoomDetailsDark_1_en","",20027,], +["features.roomdetails.impl_RoomDetailsDark_2_en","",20027,], +["features.roomdetails.impl_RoomDetailsDark_3_en","",20027,], +["features.roomdetails.impl_RoomDetailsDark_4_en","",20027,], +["features.roomdetails.impl_RoomDetailsDark_5_en","",20027,], +["features.roomdetails.impl_RoomDetailsDark_6_en","",20027,], +["features.roomdetails.impl_RoomDetailsDark_7_en","",20027,], +["features.roomdetails.impl_RoomDetailsDark_8_en","",20027,], +["features.roomdetails.impl_RoomDetailsDark_9_en","",20027,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_0_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_0_en",20027,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_1_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_1_en",20027,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_2_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_2_en",20027,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_3_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_3_en",20027,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_4_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_4_en",20027,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_5_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_5_en",20027,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_6_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_6_en",20027,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_7_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_7_en",20027,], +["features.roomdetails.impl_RoomDetails_0_en","",20027,], +["features.roomdetails.impl_RoomDetails_10_en","",20027,], +["features.roomdetails.impl_RoomDetails_11_en","",20027,], +["features.roomdetails.impl_RoomDetails_12_en","",20027,], +["features.roomdetails.impl_RoomDetails_13_en","",20027,], +["features.roomdetails.impl_RoomDetails_1_en","",20027,], +["features.roomdetails.impl_RoomDetails_2_en","",20027,], +["features.roomdetails.impl_RoomDetails_3_en","",20027,], +["features.roomdetails.impl_RoomDetails_4_en","",20027,], +["features.roomdetails.impl_RoomDetails_5_en","",20027,], +["features.roomdetails.impl_RoomDetails_6_en","",20027,], +["features.roomdetails.impl_RoomDetails_7_en","",20027,], +["features.roomdetails.impl_RoomDetails_8_en","",20027,], +["features.roomdetails.impl_RoomDetails_9_en","",20027,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20027,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20027,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20027,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20027,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20027,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20027,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20027,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_4_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_4_en",20027,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_5_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_5_en",20027,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_6_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_6_en",20027,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_7_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_7_en",20027,], +["features.roomlist.impl.components_RoomListContentView_Day_0_en","features.roomlist.impl.components_RoomListContentView_Night_0_en",20027,], +["features.roomlist.impl.components_RoomListContentView_Day_1_en","features.roomlist.impl.components_RoomListContentView_Night_1_en",20027,], ["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",20021,], -["features.roomlist.impl.components_RoomListContentView_Day_4_en","features.roomlist.impl.components_RoomListContentView_Night_4_en",20021,], -["features.roomlist.impl.filters_RoomListFiltersView_Day_0_en","features.roomlist.impl.filters_RoomListFiltersView_Night_0_en",20021,], -["features.roomlist.impl.filters_RoomListFiltersView_Day_1_en","features.roomlist.impl.filters_RoomListFiltersView_Night_1_en",20021,], -["features.roomlist.impl_RoomListModalBottomSheetContent_Day_0_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_0_en",20021,], -["features.roomlist.impl_RoomListModalBottomSheetContent_Day_1_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_1_en",20021,], -["features.roomlist.impl_RoomListModalBottomSheetContent_Day_2_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_2_en",20021,], +["features.roomlist.impl.components_RoomListContentView_Day_3_en","features.roomlist.impl.components_RoomListContentView_Night_3_en",20027,], +["features.roomlist.impl.components_RoomListContentView_Day_4_en","features.roomlist.impl.components_RoomListContentView_Night_4_en",20027,], +["features.roomlist.impl.filters_RoomListFiltersView_Day_0_en","features.roomlist.impl.filters_RoomListFiltersView_Night_0_en",20027,], +["features.roomlist.impl.filters_RoomListFiltersView_Day_1_en","features.roomlist.impl.filters_RoomListFiltersView_Night_1_en",20027,], +["features.roomlist.impl_RoomListModalBottomSheetContent_Day_0_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_0_en",20027,], +["features.roomlist.impl_RoomListModalBottomSheetContent_Day_1_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_1_en",20027,], +["features.roomlist.impl_RoomListModalBottomSheetContent_Day_2_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_2_en",20027,], ["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",20021,], -["features.roomlist.impl.search_RoomListSearchContent_Day_2_en","features.roomlist.impl.search_RoomListSearchContent_Night_2_en",20021,], -["features.roomlist.impl_RoomListView_Day_0_en","features.roomlist.impl_RoomListView_Night_0_en",20021,], -["features.roomlist.impl_RoomListView_Day_10_en","features.roomlist.impl_RoomListView_Night_10_en",20021,], -["features.roomlist.impl_RoomListView_Day_1_en","features.roomlist.impl_RoomListView_Night_1_en",20021,], -["features.roomlist.impl_RoomListView_Day_2_en","features.roomlist.impl_RoomListView_Night_2_en",20021,], -["features.roomlist.impl_RoomListView_Day_3_en","features.roomlist.impl_RoomListView_Night_3_en",20021,], -["features.roomlist.impl_RoomListView_Day_4_en","features.roomlist.impl_RoomListView_Night_4_en",20021,], -["features.roomlist.impl_RoomListView_Day_5_en","features.roomlist.impl_RoomListView_Night_5_en",20021,], -["features.roomlist.impl_RoomListView_Day_6_en","features.roomlist.impl_RoomListView_Night_6_en",20021,], -["features.roomlist.impl_RoomListView_Day_7_en","features.roomlist.impl_RoomListView_Night_7_en",20021,], +["features.roomlist.impl.search_RoomListSearchContent_Day_1_en","features.roomlist.impl.search_RoomListSearchContent_Night_1_en",20027,], +["features.roomlist.impl.search_RoomListSearchContent_Day_2_en","features.roomlist.impl.search_RoomListSearchContent_Night_2_en",20027,], +["features.roomlist.impl_RoomListView_Day_0_en","features.roomlist.impl_RoomListView_Night_0_en",20027,], +["features.roomlist.impl_RoomListView_Day_10_en","features.roomlist.impl_RoomListView_Night_10_en",20027,], +["features.roomlist.impl_RoomListView_Day_1_en","features.roomlist.impl_RoomListView_Night_1_en",20027,], +["features.roomlist.impl_RoomListView_Day_2_en","features.roomlist.impl_RoomListView_Night_2_en",20027,], +["features.roomlist.impl_RoomListView_Day_3_en","features.roomlist.impl_RoomListView_Night_3_en",20027,], +["features.roomlist.impl_RoomListView_Day_4_en","features.roomlist.impl_RoomListView_Night_4_en",20027,], +["features.roomlist.impl_RoomListView_Day_5_en","features.roomlist.impl_RoomListView_Night_5_en",20027,], +["features.roomlist.impl_RoomListView_Day_6_en","features.roomlist.impl_RoomListView_Night_6_en",20027,], +["features.roomlist.impl_RoomListView_Day_7_en","features.roomlist.impl_RoomListView_Night_7_en",20027,], ["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",20021,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_1_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_1_en",20021,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_2_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_2_en",20021,], -["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20021,], -["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20021,], -["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20021,], -["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20021,], -["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20021,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_0_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_0_en",20027,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_1_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_1_en",20027,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_2_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_2_en",20027,], +["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20027,], +["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20027,], +["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20027,], +["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20027,], +["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20027,], ["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",20021,], -["features.roomdetails.impl.members_RoomMemberListView_Day_7_en","features.roomdetails.impl.members_RoomMemberListView_Night_7_en",20021,], -["features.roomdetails.impl.members_RoomMemberListView_Day_8_en","features.roomdetails.impl.members_RoomMemberListView_Night_8_en",20021,], +["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20027,], +["features.roomdetails.impl.members_RoomMemberListView_Day_7_en","features.roomdetails.impl.members_RoomMemberListView_Night_7_en",20027,], +["features.roomdetails.impl.members_RoomMemberListView_Day_8_en","features.roomdetails.impl.members_RoomMemberListView_Night_8_en",20027,], ["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",20021,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en",20021,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en",20021,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_3_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_3_en",20021,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_4_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_4_en",20021,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en",20027,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en",20027,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en",20027,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_3_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_3_en",20027,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_4_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_4_en",20027,], ["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en",0,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_6_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_6_en",20021,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_7_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_7_en",20021,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_8_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_8_en",20021,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_6_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_6_en",20027,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_7_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_7_en",20027,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_8_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_8_en",20027,], ["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",20021,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20021,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20021,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20021,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20021,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20021,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20021,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20021,], -["features.createroom.impl.components_RoomPrivacyOption_Day_0_en","features.createroom.impl.components_RoomPrivacyOption_Night_0_en",20021,], -["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20021,], -["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20021,], -["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20021,], -["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20021,], -["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20021,], -["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20021,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20027,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20027,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20027,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20027,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20027,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20027,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20027,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20027,], +["features.createroom.impl.components_RoomPrivacyOption_Day_0_en","features.createroom.impl.components_RoomPrivacyOption_Night_0_en",20027,], +["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20027,], +["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20027,], +["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20027,], +["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20027,], +["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20027,], +["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20027,], ["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,], @@ -845,10 +854,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",20021,], -["features.roomlist.impl.components_RoomSummaryRow_Day_2_en","features.roomlist.impl.components_RoomSummaryRow_Night_2_en",20021,], -["features.roomlist.impl.components_RoomSummaryRow_Day_30_en","features.roomlist.impl.components_RoomSummaryRow_Night_30_en",20021,], -["features.roomlist.impl.components_RoomSummaryRow_Day_31_en","features.roomlist.impl.components_RoomSummaryRow_Night_31_en",20021,], +["features.roomlist.impl.components_RoomSummaryRow_Day_29_en","features.roomlist.impl.components_RoomSummaryRow_Night_29_en",20027,], +["features.roomlist.impl.components_RoomSummaryRow_Day_2_en","features.roomlist.impl.components_RoomSummaryRow_Night_2_en",20027,], +["features.roomlist.impl.components_RoomSummaryRow_Day_30_en","features.roomlist.impl.components_RoomSummaryRow_Night_30_en",20027,], +["features.roomlist.impl.components_RoomSummaryRow_Day_31_en","features.roomlist.impl.components_RoomSummaryRow_Night_31_en",20027,], ["features.roomlist.impl.components_RoomSummaryRow_Day_32_en","features.roomlist.impl.components_RoomSummaryRow_Night_32_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_33_en","features.roomlist.impl.components_RoomSummaryRow_Night_33_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_3_en","features.roomlist.impl.components_RoomSummaryRow_Night_3_en",0,], @@ -858,58 +867,59 @@ 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",20021,], -["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20021,], -["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20021,], +["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20027,], +["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20027,], +["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20027,], ["appicon.enterprise_RoundIcon_en","",0,], ["appicon.element_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",20021,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20021,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20021,], +["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20027,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20027,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20027,], ["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","",20021,], +["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search views_en","",20027,], ["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","",20021,], -["features.createroom.impl.components_SearchSingleUserResultItem_en","",20021,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20021,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20021,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20021,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20021,], -["features.securebackup.impl.enable_SecureBackupEnableView_Day_0_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_0_en",20021,], -["features.securebackup.impl.enable_SecureBackupEnableView_Day_1_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_1_en",20021,], -["features.securebackup.impl.enable_SecureBackupEnableView_Day_2_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_2_en",20021,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20021,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20021,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20021,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20021,], -["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20021,], -["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20021,], -["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20021,], -["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20021,], -["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20021,], -["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20021,], -["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20021,], -["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20021,], -["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20021,], -["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20021,], -["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20021,], -["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20021,], -["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20021,], -["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20021,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20021,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20021,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20021,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20021,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20021,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20021,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20021,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20021,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20021,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20021,], +["features.createroom.impl.components_SearchMultipleUsersResultItem_en","",20027,], +["features.createroom.impl.components_SearchSingleUserResultItem_en","",20027,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20027,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20027,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20027,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20027,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20027,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20027,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20027,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20027,], +["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20027,], +["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20027,], +["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20027,], +["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20027,], +["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20027,], +["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20031,], +["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20031,], +["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20031,], +["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20031,], +["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20027,], +["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20027,], +["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20027,], +["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20027,], +["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20027,], +["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20027,], +["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20027,], +["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20027,], +["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20027,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20027,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20027,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20027,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20027,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20027,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20027,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20027,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20027,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20027,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20027,], ["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_SelectedRoom_Day_2_en","libraries.matrix.ui.components_SelectedRoom_Night_2_en",0,], @@ -917,11 +927,11 @@ export const screenshots = [ ["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",20021,], -["features.location.impl.send_SendLocationView_Day_1_en","features.location.impl.send_SendLocationView_Night_1_en",20021,], -["features.location.impl.send_SendLocationView_Day_2_en","features.location.impl.send_SendLocationView_Night_2_en",20021,], -["features.location.impl.send_SendLocationView_Day_3_en","features.location.impl.send_SendLocationView_Night_3_en",20021,], -["features.location.impl.send_SendLocationView_Day_4_en","features.location.impl.send_SendLocationView_Night_4_en",20021,], +["features.location.impl.send_SendLocationView_Day_0_en","features.location.impl.send_SendLocationView_Night_0_en",20027,], +["features.location.impl.send_SendLocationView_Day_1_en","features.location.impl.send_SendLocationView_Night_1_en",20027,], +["features.location.impl.send_SendLocationView_Day_2_en","features.location.impl.send_SendLocationView_Night_2_en",20027,], +["features.location.impl.send_SendLocationView_Day_3_en","features.location.impl.send_SendLocationView_Night_3_en",20027,], +["features.location.impl.send_SendLocationView_Day_4_en","features.location.impl.send_SendLocationView_Night_4_en",20027,], ["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,], @@ -931,26 +941,27 @@ 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",20021,], -["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20021,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20021,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20021,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20021,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20021,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20021,], +["features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",0,], +["features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20027,], +["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20027,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20027,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20027,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20027,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20027,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20027,], ["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",20021,], -["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20021,], -["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20021,], -["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20021,], -["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20021,], -["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20021,], -["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20021,], -["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20021,], -["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20021,], -["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20021,], +["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",20027,], +["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20027,], +["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20027,], +["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20027,], +["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20027,], +["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20027,], +["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20027,], +["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20027,], +["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20027,], +["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20027,], ["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,], @@ -959,7 +970,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",20021,], +["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20027,], ["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,], @@ -969,37 +980,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",20021,], +["features.location.api.internal_StaticMapPlaceholder_Day_1_en","features.location.api.internal_StaticMapPlaceholder_Night_1_en",20027,], ["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",20021,], +["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",20027,], ["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",20021,], +["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",20027,], ["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",20021,], -["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20021,], -["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20021,], -["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20021,], -["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20021,], -["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20021,], -["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20021,], -["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20021,], -["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20021,], -["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20021,], -["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20021,], -["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20021,], -["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20021,], -["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20021,], -["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20021,], -["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20021,], -["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20021,], -["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20021,], +["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20027,], +["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20027,], +["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20027,], +["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20027,], +["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20027,], +["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20027,], +["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20027,], +["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20027,], +["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20027,], +["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20027,], +["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20027,], +["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20027,], +["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20027,], +["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20027,], +["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20027,], +["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20027,], +["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20027,], +["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20027,], ["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,], @@ -1011,28 +1022,28 @@ 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","",20021,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime pickers_en","",20021,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime pickers_en","",20021,], +["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime pickers_en","",20027,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime pickers_en","",20027,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime pickers_en","",20027,], ["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",20021,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20021,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",20027,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20027,], ["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",20021,], +["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20027,], ["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",20021,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20021,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",20027,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20027,], ["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20024,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20027,], ["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,], @@ -1040,17 +1051,17 @@ 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",20021,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20021,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20027,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20027,], ["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_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20024,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20021,], +["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20027,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20027,], ["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",20021,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20021,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",20027,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20027,], ["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,], @@ -1059,38 +1070,38 @@ 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",20021,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20027,], ["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",20021,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20027,], ["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","",20021,], +["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20027,], ["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",20021,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20021,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20027,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20027,], ["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",0,], ["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_TimelineItemImageView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_3_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",20021,], +["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20027,], ["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",20021,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20021,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20021,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20021,], -["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20021,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20027,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20027,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20027,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20027,], +["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20027,], ["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",20021,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20021,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",20027,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20027,], ["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",20021,], +["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",20027,], ["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,], @@ -1099,8 +1110,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",20021,], -["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20021,], +["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20027,], +["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20027,], ["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,], @@ -1113,7 +1124,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",20021,], +["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20027,], ["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",0,], ["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,], @@ -1137,83 +1148,83 @@ 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",20021,], -["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20021,], +["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20027,], +["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20027,], ["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",20021,], -["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20021,], -["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20021,], -["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20021,], -["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20021,], -["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20021,], -["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20021,], -["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20021,], +["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20027,], +["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20027,], +["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20027,], +["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20027,], +["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20027,], +["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20027,], +["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20027,], +["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20027,], ["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",20021,], +["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",20027,], ["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",20021,], +["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",20027,], ["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",20021,], +["features.messages.impl.timeline_TimelineView_Day_8_en","features.messages.impl.timeline_TimelineView_Night_8_en",20027,], ["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",20021,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20021,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20021,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20021,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20021,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20021,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20021,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20021,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20027,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20027,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20027,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20027,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20027,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20027,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20027,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20027,], ["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",20021,], -["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20021,], -["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20021,], -["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20021,], -["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20021,], -["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20021,], +["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",20027,], +["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20027,], +["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20027,], +["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20027,], +["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20027,], +["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20027,], ["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","",20021,], +["libraries.matrix.ui.components_UnresolvedUserRow_en","",20027,], ["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",20021,], -["features.createroom.impl.components_UserListView_Day_0_en","features.createroom.impl.components_UserListView_Night_0_en",20021,], -["features.createroom.impl.components_UserListView_Day_1_en","features.createroom.impl.components_UserListView_Night_1_en",20021,], -["features.createroom.impl.components_UserListView_Day_2_en","features.createroom.impl.components_UserListView_Night_2_en",20021,], +["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20027,], +["features.createroom.impl.components_UserListView_Day_0_en","features.createroom.impl.components_UserListView_Night_0_en",20027,], +["features.createroom.impl.components_UserListView_Day_1_en","features.createroom.impl.components_UserListView_Night_1_en",20027,], +["features.createroom.impl.components_UserListView_Day_2_en","features.createroom.impl.components_UserListView_Night_2_en",20027,], ["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",20021,], +["features.createroom.impl.components_UserListView_Day_7_en","features.createroom.impl.components_UserListView_Night_7_en",20027,], ["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",20021,], +["features.createroom.impl.components_UserListView_Day_9_en","features.createroom.impl.components_UserListView_Night_9_en",20027,], ["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",20021,], -["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20021,], -["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20021,], -["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20021,], -["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20021,], -["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20021,], -["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20021,], -["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20021,], -["features.verifysession.impl_VerifySelfSessionView_Day_0_en","features.verifysession.impl_VerifySelfSessionView_Night_0_en",20021,], -["features.verifysession.impl_VerifySelfSessionView_Day_10_en","features.verifysession.impl_VerifySelfSessionView_Night_10_en",20021,], -["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",20021,], -["features.verifysession.impl_VerifySelfSessionView_Day_2_en","features.verifysession.impl_VerifySelfSessionView_Night_2_en",20021,], -["features.verifysession.impl_VerifySelfSessionView_Day_3_en","features.verifysession.impl_VerifySelfSessionView_Night_3_en",20021,], -["features.verifysession.impl_VerifySelfSessionView_Day_4_en","features.verifysession.impl_VerifySelfSessionView_Night_4_en",20021,], -["features.verifysession.impl_VerifySelfSessionView_Day_5_en","features.verifysession.impl_VerifySelfSessionView_Night_5_en",20021,], -["features.verifysession.impl_VerifySelfSessionView_Day_6_en","features.verifysession.impl_VerifySelfSessionView_Night_6_en",20021,], -["features.verifysession.impl_VerifySelfSessionView_Day_7_en","features.verifysession.impl_VerifySelfSessionView_Night_7_en",20021,], -["features.verifysession.impl_VerifySelfSessionView_Day_8_en","features.verifysession.impl_VerifySelfSessionView_Night_8_en",20021,], -["features.verifysession.impl_VerifySelfSessionView_Day_9_en","features.verifysession.impl_VerifySelfSessionView_Night_9_en",20021,], +["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20027,], +["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20027,], +["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20027,], +["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20027,], +["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20027,], +["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20027,], +["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20027,], +["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20027,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_0_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_0_en",20031,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_10_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_10_en",20031,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_11_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_11_en",0,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_12_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_12_en",0,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en",20031,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_2_en",20031,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_3_en",20031,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en",20031,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_5_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_5_en",20031,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_6_en",20031,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_7_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_7_en",20031,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_8_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_8_en",20031,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_9_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_9_en",20031,], ["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,], @@ -1228,6 +1239,6 @@ export const screenshots = [ ["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,], ["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",20021,], +["features.ftue.impl.welcome_WelcomeView_Day_0_en","features.ftue.impl.welcome_WelcomeView_Night_0_en",20027,], ["libraries.designsystem.ruler_WithRulers_Day_0_en","libraries.designsystem.ruler_WithRulers_Night_0_en",0,], ]; diff --git a/services/analytics/test/src/main/kotlin/io/element/android/services/analytics/test/FakeScreenTracker.kt b/services/analytics/test/src/main/kotlin/io/element/android/services/analytics/test/FakeScreenTracker.kt index 7065016204..4ac0f7ffd2 100644 --- a/services/analytics/test/src/main/kotlin/io/element/android/services/analytics/test/FakeScreenTracker.kt +++ b/services/analytics/test/src/main/kotlin/io/element/android/services/analytics/test/FakeScreenTracker.kt @@ -8,6 +8,7 @@ package io.element.android.services.analytics.test import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.services.analytics.api.ScreenTracker import io.element.android.tests.testutils.lambda.lambdaError @@ -17,6 +18,8 @@ class FakeScreenTracker( ) : ScreenTracker { @Composable override fun TrackScreen(screen: MobileScreen.ScreenName) { - trackScreenLambda(screen) + LaunchedEffect(Unit) { + trackScreenLambda(screen) + } } } diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistComposableTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistComposableTest.kt index 7d5ea75f2e..098ec0c9f7 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistComposableTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistComposableTest.kt @@ -32,9 +32,10 @@ class KonsistComposableTest { .withoutReceiverType() .withoutName( // Add some exceptions... + "InvisibleButton", "OutlinedButton", - "TextButton", "SimpleAlertDialogContent", + "TextButton", ) .assertTrue( additionalMessage = diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt index c00d72fffb..83541624a1 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt @@ -101,6 +101,7 @@ class KonsistPreviewTest { "SasEmojisPreview", "SecureBackupSetupViewChangePreview", "SelectedUserCannotRemovePreview", + "TextComposerCaptionPreview", "TextComposerEditPreview", "TextComposerFormattingPreview", "TextComposerLinkDialogCreateLinkPreview", diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/fake/FakeTemporaryUriDeleter.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/fake/FakeTemporaryUriDeleter.kt new file mode 100644 index 0000000000..bb14d2783d --- /dev/null +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/fake/FakeTemporaryUriDeleter.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.tests.testutils.fake + +import android.net.Uri +import io.element.android.libraries.androidutils.file.TemporaryUriDeleter +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeTemporaryUriDeleter( + val deleteLambda: (uri: Uri?) -> Unit = { lambdaError() } +) : TemporaryUriDeleter { + override fun delete(uri: Uri?) { + deleteLambda(uri) + } +} diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_1_en.png index 06c8c0ce2d..efffb41f05 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8321503209048b152d1ed52cf2339c8c96276b4f40d71660a92b9a8b919efcf -size 8386 +oid sha256:6740fe72794a7ef2bc65248c14e554e8bd4815363a8299ffb319c3346f658ffd +size 8360 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_1_en.png index ff4dcfc525..fe6935c049 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3fcde8c552b5080a87e1339d6f23fd9f0b291161525563197bffbdca2e6802e0 -size 6994 +oid sha256:969b847685de20734fba19b7d0c804702a0c57a489b386a25f010ff669a17a6e +size 6992 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateView_Day_0_en.png index ccfbb4c8cb..1a3f272eb7 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e88a14813e0923365b98dc046479b4625b732bef2befe4bd1ddddacc8598aaf7 -size 8389 +oid sha256:ef76fb8ff9c40cf28ceddc56a053c2af4ae5c1410857904ffea805e2e0d80e1f +size 8367 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateView_Night_0_en.png index afa9a14d8d..15bdab6e71 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:092a39015ccb36d09f65ed18760eb1e36dcab1e0809d404bfd9f4aed9961eca4 -size 7110 +oid sha256:4d86353b0a8e5b290fece7490790a0929d1ac326db3f4854aa1ef74dd167a3a9 +size 7107 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Day_0_en.png deleted file mode 100644 index dff2945ebf..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Day_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5ef1c5aa8a1be3fc5c2fd55b8fde64f7ec7c3d12037e4e2b08049c6a1ea21a3d -size 30128 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Night_0_en.png deleted file mode 100644 index ca8cc5e534..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Night_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5f5762dcb065b0406441e7f67d2b3a120f11bed71d1f56b96cb334b831d96ce3 -size 29511 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en.png index 8337a1e532..28f4bd5c7d 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8344c5a707a3af3aaeab22006e84bd93f1cdf8389199d88beb276a8b672b0a4a -size 50817 +oid sha256:e7de44cc8f686264788aa79cde24b9896c6ca4dabc49b246abbd1d3dd0311e25 +size 57891 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en.png index 0f46643716..3131e96896 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c39135c177aa7b22c9451ef86bee41a216931759448ed2764d3bef4ea410230 -size 72685 +oid sha256:2df49a9c82438dbf1ba0ba0670cadd8e7bb47a7e25de27793fd756760cf7c5f2 +size 79562 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_2_en.png new file mode 100644 index 0000000000..3131e96896 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2df49a9c82438dbf1ba0ba0670cadd8e7bb47a7e25de27793fd756760cf7c5f2 +size 79562 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en.png index 82896e9306..a1a2247eb8 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b05e067c92747ac92f10efcec20df0a1a7e1040c0be7a4b04009a5cf50c0aeb5 -size 49720 +oid sha256:c754daf307554dc0c83ba0f89c38f5fa7f5c2edff82907b0826c2b781498dbcb +size 56047 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en.png index 02e544dca1..2a7721de80 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79b1423947e9f87d9139f1742f845a80d6474c0b36d3557b1c30fee6a3e808ad -size 71946 +oid sha256:b6e43161abdccacbede4a8c4b4ff446026035df4be0894a55c6364681458bb2f +size 78805 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_2_en.png new file mode 100644 index 0000000000..2a7721de80 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6e43161abdccacbede4a8c4b4ff446026035df4be0894a55c6364681458bb2f +size 78805 diff --git a/tests/uitests/src/test/snapshots/images/features.ftue.impl.notifications_NotificationsOptInView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.ftue.impl.notifications_NotificationsOptInView_Day_0_en.png index 196fa7a129..ec56a647c0 100644 --- a/tests/uitests/src/test/snapshots/images/features.ftue.impl.notifications_NotificationsOptInView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.ftue.impl.notifications_NotificationsOptInView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc56ae93010a808f49cacacc84ed2321e8eb792bc2761afbc32fb628511aaf8b -size 68133 +oid sha256:3b5294950885cb417983db822b7615dfd0c4988c0eef7eceb466b751c0834020 +size 68101 diff --git a/tests/uitests/src/test/snapshots/images/features.ftue.impl.notifications_NotificationsOptInView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.ftue.impl.notifications_NotificationsOptInView_Night_0_en.png index 7844559eb0..3f29a09d02 100644 --- a/tests/uitests/src/test/snapshots/images/features.ftue.impl.notifications_NotificationsOptInView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.ftue.impl.notifications_NotificationsOptInView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3954d38335b27519c37b3ef0703d982bfea6b9979a07f392b5479abe5186d81 -size 59187 +oid sha256:c6ea578f8aa88e306f50407c48cc08243e147635bd5e94f13c4f054ff02ed380 +size 59199 diff --git a/tests/uitests/src/test/snapshots/images/features.ftue.impl.welcome_WelcomeView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.ftue.impl.welcome_WelcomeView_Day_0_en.png index d0921b0596..316f03246a 100644 --- a/tests/uitests/src/test/snapshots/images/features.ftue.impl.welcome_WelcomeView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.ftue.impl.welcome_WelcomeView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ce33509fe55cb38d69509bf62115d1a6153cc05f075a6ac4f936b9931a41c0c -size 260888 +oid sha256:b80198aeffe7bfec1e5747edf031af3bb892797950a98bdbe4063841fcf69da6 +size 269244 diff --git a/tests/uitests/src/test/snapshots/images/features.ftue.impl.welcome_WelcomeView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.ftue.impl.welcome_WelcomeView_Night_0_en.png index 3ce1b0a35a..6271012c12 100644 --- a/tests/uitests/src/test/snapshots/images/features.ftue.impl.welcome_WelcomeView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.ftue.impl.welcome_WelcomeView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b002b3b64da8a68786fca4493211c4c942d685e1abf77e2c02fba9a299a9432 -size 341411 +oid sha256:5188a7358c698d1a67ee483a5e9277cf1158dbeb98b94933ed71f779918cf1ab +size 345464 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_0_en.png index 90b948d775..7ac77ce99b 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf2b91524b4473d09727fe7d36f49edbedaf37b9b2eec8efa3c24ce33acc8324 -size 20087 +oid sha256:522b94ccb2ff37b449c554890fd21be992d444afd5e5cc95fe11b45bf5401ca1 +size 20166 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_1_en.png index 2e6169c50f..83e15d2345 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9103c7f1229f24d304bb6f27d2815e2c3d8be1e2ad2e3f456fb3a61468b5fb13 -size 35300 +oid sha256:6827324561b4390a53dfe10c69c3504e8f5deb4035ba7d0c914eb84e09ce5b04 +size 35363 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_2_en.png index 96169cb023..8a3c0cb919 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eaf6158448321441b2f26b5ee33e25175bf98cd06c0ff07c9e0f470395f4c963 -size 33546 +oid sha256:85cd2509839c359f4500928f0466dc6e03ab41b8bc480dfdbfe421afafbe43f6 +size 33606 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_3_en.png index 90b948d775..7ac77ce99b 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf2b91524b4473d09727fe7d36f49edbedaf37b9b2eec8efa3c24ce33acc8324 -size 20087 +oid sha256:522b94ccb2ff37b449c554890fd21be992d444afd5e5cc95fe11b45bf5401ca1 +size 20166 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_4_en.png index 9a23f07aa8..2c1cf66fde 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a8343cdc53c5dffb3bc2de981f83181978658bcf669bf0cd05e57976f439d88 -size 20176 +oid sha256:9f6f1cb0d707a79ddddf0b1c5089eeb0e35216560541b85817c902d0b172415a +size 20252 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_0_en.png index a2d5607418..c75dd536e9 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1db0d3d9f43d11fcac7f9d1594a3a6fe8464927722734c922b1ae7422c76cac0 -size 19492 +oid sha256:c2024df21d3b248a8edd6737fcbadf707f7ffdee6ecfe89a4c9eed27aa1b41d4 +size 19518 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_1_en.png index 6f7cdaf85d..9abffd7a0f 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:87e47d7855948254f9ce554a014fe542f9850d3f7afa3522e4319a578481af0e -size 33141 +oid sha256:ff1819f6d67bed90e9aefc7acea91b9921729b42e605620bf92ae65f28986718 +size 33204 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_2_en.png index ea2a4c7382..7ab331c24c 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:45cc940472481624a26bffba0666c7e065436fc07ffb25c0b02ab6b64e0b5f94 -size 31747 +oid sha256:3acd4bea7ab4224bb0b8b42d9fdeb0b4a669e69fd23e8a63c2e919d792b5cdc6 +size 31809 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_3_en.png index a2d5607418..c75dd536e9 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1db0d3d9f43d11fcac7f9d1594a3a6fe8464927722734c922b1ae7422c76cac0 -size 19492 +oid sha256:c2024df21d3b248a8edd6737fcbadf707f7ffdee6ecfe89a4c9eed27aa1b41d4 +size 19518 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_4_en.png index ebd357c68b..9d624f724e 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.send_SendLocationView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b865d001ad470645ca1bca8dd03bf22a02ab221233002ab93b9e65eff8e159aa -size 19703 +oid sha256:b6490d5ffc0c412a3fc4738b3eaf058d42621b929fd1ccbd9323923c6b2d818a +size 19753 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_0_en.png index 604f9c2bd4..aafa465d89 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fbffe7e34ac02d5329ac0d83bcb55108e374050deef36e43cc8d93ade25ed5be -size 11980 +oid sha256:2f5f9afdf10404a81be8c8c52b6010edd97d99693491d11f0fe0bcc4fe16c7ae +size 12045 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_1_en.png index 89f58331d0..7dd19161f3 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6006fb4ce02dc2019d8cc23f0e4ca7afa514e79802cbc61f32075ab0daf59f47 -size 29976 +oid sha256:925b317539d8a7ee968487e839eea3708e84507b701a83cae557da1bbe331095 +size 30029 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_2_en.png index 84bb700597..9d62628111 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9f8da8e3b17213a914bfb75b862a3219f24886280fac217b9999d97e76c3897 -size 28327 +oid sha256:3a4c0f632f79d4195e619e74f43d926eb9d9c074efc9ec065974562b95b28cd9 +size 28380 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_3_en.png index 604f9c2bd4..aafa465d89 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fbffe7e34ac02d5329ac0d83bcb55108e374050deef36e43cc8d93ade25ed5be -size 11980 +oid sha256:2f5f9afdf10404a81be8c8c52b6010edd97d99693491d11f0fe0bcc4fe16c7ae +size 12045 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_4_en.png index c47517f83e..465b12d2eb 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a99b54873f8d8441f2884f7de01dede84452d5bd55e7ce2b3d3846e87f4cb81 -size 12169 +oid sha256:68b91dd3884b78567f2d07a0c95eed16dfba6ed995fb1b32af0d2249f16a1eb7 +size 12243 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_5_en.png index a87c052096..a6271b10b9 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3f9d62e5987a04e440c22833d5a3d74bf0e4366ebe46c41c04ce81d0f29d8a8 -size 15755 +oid sha256:3e22f506097d964b21a66508219644f8f4b989ec7bf8ae22aa4f5c44ec337818 +size 15815 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_6_en.png index ba4728cc5a..0096c63e18 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:428e58eca464163f8ffe2a238816c15321d35ee7bb81cbdf0610e5e828abf289 -size 24348 +oid sha256:2867b178672ad79489cbc97977aeffc5ff1575bbf41f42d5b9183abd871df737 +size 24412 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_7_en.png index 8f6cd62fff..40f21a974a 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d44c6221d1222119ee2ebdd103f1936cc82c539f508cc6ed818a74e56da2f10 -size 26709 +oid sha256:937198fe6c7e90e5b55c449eca3d020358c02bc658ae9541702ea4b41e589448 +size 26772 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_0_en.png index 47a23ad386..69c6ba35d8 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db633069997ba5f56ff75a483cdd778f3c6c39c1df994ee407567c6a0adfc097 -size 11480 +oid sha256:e7468007bd3ae791b1d440040ee4479cc9af4377275c115f8f8d78b74eaf62d4 +size 11524 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_1_en.png index f0cd351ca1..114a7f393b 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:152ca180534db4396a91171ad8bf1df3bb720d73325655be0949b0681e1180cf -size 28082 +oid sha256:476ece6c2918246031474c055fdb3f21b2b4847d86efb894b6fef417d194c29d +size 28110 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_2_en.png index 3bd1da689d..f335cc7f54 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab188e19a887a01537cb2bd4daccb8e5495cb61396f97e5d3064ffeec1601c0e -size 26694 +oid sha256:39267f7cd81b8022ecff4726fef1d5c46a473d37bff59dba95ebd8dbef2f67cb +size 26721 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_3_en.png index 47a23ad386..69c6ba35d8 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db633069997ba5f56ff75a483cdd778f3c6c39c1df994ee407567c6a0adfc097 -size 11480 +oid sha256:e7468007bd3ae791b1d440040ee4479cc9af4377275c115f8f8d78b74eaf62d4 +size 11524 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_4_en.png index bda4121d72..33b3884a54 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:807eb444f3594f8590287d176c1f05cb7a296e9475ae273df77b4f91bd123959 -size 11668 +oid sha256:1c3e3e4eba5dc82e22908640e400c57c0a2d01b8adb007ee2aaee4fec175ee1f +size 11706 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_5_en.png index 5d13bafae8..173c800553 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:531dc0c74b9ab506c173ed2ad309910feb1201b77271063aee8682c0a3d6be5f -size 14964 +oid sha256:ae22df52afd433d81c2e146cef6b455630de4e1d76eb2b30628ec9b8f87a2522 +size 15011 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_6_en.png index 9ebe6037b8..fe303acdf1 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2b9390804bdc98113ee8de5e7863e3238e1bfaf1fca0cb3a9a22bd8419ae839 -size 23367 +oid sha256:6663d8bafc3176cfe83d486821ec91ab3204043ee468e689e159ff0af9e9fa5d +size 23411 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_7_en.png index d418b63582..257576e140 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e9ea055dfcdc87173c592d595a0694ac8217c0279db26c14a53d4272052163d -size 25885 +oid sha256:e763e8bc81c459b36caeb1b8a74ba262109e3c0ee1bbcf8436d944a1737c8601 +size 25920 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_0_en.png index 622f3dc9c1..dd26109be0 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b8b87bd63d1f6febead491dda1cecc0fa9fad0cdca317cf29086fbeec6a9231 -size 390555 +oid sha256:899eff34c421e13bf62d6828582c715f87588cfb17c1063aae65baae79a472cf +size 394631 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_1_en.png index 438fe60e31..7514170564 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ba4294693123669a24fbdec8093d3c572c639325763c15a1aa53c8f1e4a7659 -size 15230 +oid sha256:c41c7438f46e62a4a6f115647040005aba9fd057599fbb17aba844337642d525 +size 15963 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_2_en.png index 68bbcc5e4c..b5ca13b59e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16c19e5b2a95da604052a86bd3cdb00d4260ee7539d79e8bb96784e2a2920836 -size 47019 +oid sha256:4a9d51bdba64cbd7c453ac177e9c77fb6aa3c611ac16746701b22b024abf3560 +size 13770 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_3_en.png index 8c3ad63622..d9ea161fab 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a42e8f411d6e54656104cf9472a00713b150ee9978b8bde26b22acd858478bfc -size 84755 +oid sha256:9be7c12e6de6bd2975f11ff06eab1b6fa973edcda0ca90c93eed164cb1d6bf18 +size 14841 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_4_en.png new file mode 100644 index 0000000000..5f643fefc6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9434a531c57fa65c9996f5b5c6254af73ac50433ceeeaa7f1dd1243fe3c3b1c6 +size 50355 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_5_en.png new file mode 100644 index 0000000000..ac695699f0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c36d5b0d29f1533829f80c33e042bb88648890ad2b629136f8a2af01c511f7a +size 87977 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 index 89bbbe7c2a..8ad64f6d22 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a48e666ed705b5c873e423e2555c96a7549bbc25dfd5499ba77feb2cc403784 -size 56615 +oid sha256:b1bf1424ee914c60298255f489b5df011c930112159f8b978152727ab1f7a553 +size 56543 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 index 0f8da5dc68..cbd07418a2 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7338522b75d002cd88b31cbdf245a2143b28862ca2b5fa0f82d9df1c66de5d04 -size 54719 +oid sha256:650304d70f57fab91df310d61ae5a816d46c7d00c2b0f02887f176ecdeccc8c8 +size 54643 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 index ddb66a771b..3720d75672 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:708d72610cdddcd525ae27dd695d6cecfd2227e6b6b9dd4d5398befc1a414693 -size 55118 +oid sha256:4a1bdd84ad99effe4448d089473851431c886c1e79f025eb7bf2b52e5b5bfca0 +size 54851 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 index 94ca63b15e..b0a41c0001 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4936ee16763c77f1c76a85ad2cf137cf92a3547c0e4d2b64158244e9d0edbe58 -size 52900 +oid sha256:4bf367d9a4ab122b34fa4daa0bdcdd2488360f571d51ed503a82472073a31828 +size 52717 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineImageWithCaptionRow_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineImageWithCaptionRow_Day_0_en.png index 399d942e18..91eb069a23 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineImageWithCaptionRow_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineImageWithCaptionRow_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:926904f8e96ed206835166d6a6d206a8ed2c0ea6895f2e48951b43a5b92aeee7 -size 225862 +oid sha256:971d327ffaf387e7fab5d64787fc37d7bd4aa2d54e34be66685009172c04e36e +size 233849 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineImageWithCaptionRow_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineImageWithCaptionRow_Night_0_en.png index aa161e4266..442ba06553 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineImageWithCaptionRow_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineImageWithCaptionRow_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:727a7e925722c7addc8b8021259ad15f3cb0e664803bbd4c67959ec1e9ca036a -size 226638 +oid sha256:71e0536389e7f914996b300cb5b53eb3728b8008ef225f7107e7401a5bdab7b3 +size 234021 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Day_0_en.png index 847ae35de9..56489451bb 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0db8b0043cc020fc8f7c7f5b44059486e022dd13080b02368723e253b1c616d7 -size 225018 +oid sha256:2be7690996fccc6133a3d61114228a0d53dd14bc15018e968d7e9720b151e50e +size 233146 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Night_0_en.png index 81247e3e07..22320f7644 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:feaf14edc0d7a8ff1349e9fb051ce883b0de75a2528f9a6650df398b81e82b68 -size 225300 +oid sha256:b7f8e6bd1eec91e55f1b0ea7fb6c591e5ddcb5f06fd1fc2e00994d657da9477e +size 234646 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_0_en.png new file mode 100644 index 0000000000..4b5528411e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a85dfe911ca378a94b3a8fab85efab4b0915d330e804ded3472c6e6a101a9f8 +size 4016 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_1_en.png new file mode 100644 index 0000000000..09f9005925 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f135ec4c24292d06f853d0ca17b90b61a8fab144e72021862152966cd750ecf9 +size 3970 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_2_en.png new file mode 100644 index 0000000000..d5b37d48d6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b512a152991e335a54d75d83a9190c87e09aaff34aa05d57fea3c585767cb1c5 +size 5658 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_3_en.png new file mode 100644 index 0000000000..653139d523 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ca4e84ced7c4ce27b4ff5319387756a3990edd2a8861a3b998ec5ed8705ece7 +size 5381 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_4_en.png new file mode 100644 index 0000000000..1b6fb4bab8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_4_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.timeline.components_CallMenuItem_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_0_en.png new file mode 100644 index 0000000000..29d29ebd5f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75256919486ff2345c7db724caf5ee3767bbfa1d4a757c3ade9fb83802a65923 +size 4056 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_1_en.png new file mode 100644 index 0000000000..4dcdf87172 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e124fbad6f6dd867353937772b0911ec2ce521344a4acb9af1d8ade941ca0d5 +size 3980 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_2_en.png new file mode 100644 index 0000000000..edf7311f7f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5777c1bc314b886fe1b195a06c27ed8144464c466fa63c9a7fd6f00d20f8ebf0 +size 5433 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_3_en.png new file mode 100644 index 0000000000..34f71876f3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63a67d21058f9a0a9077c0a8c774ab3f5aff9dcee2dbdd197b571682296ba598 +size 5314 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_4_en.png new file mode 100644 index 0000000000..d6fd8eeb70 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_4_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.timeline.components_TimelineItemCallNotifyView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en.png index 464ad64ddd..b5371b0448 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2246cd94deba78469805df1a8ba4aa36fafee4179a5d124eb555e7f9408442e -size 18644 +oid sha256:178eef1c52bac961c9f29bc8c4df63923c8441884b1c717c9e516b9621ff3230 +size 37944 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en.png index 5936fd898b..b37717cfc7 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:029ffeafb441ebc800ba531c927cfa73259a04e06a04724310a0440238442b0e -size 18515 +oid sha256:9445d05edb98b6c3bed2a2aa3354031b7e116bf7753f9aa85b4db388751154a3 +size 38154 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Day_0_en.png index 4a4aac9d4a..53b231bc1d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5570ff934240912f93649d15032c73619e630aceb357e66e41dce47e9d76f08f -size 166272 +oid sha256:0cf7e89fac181c1a0829e14d4ade4f236267e4e19a3aa2aa5c216f7f86e6aa87 +size 169050 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Night_0_en.png index 9b8f4dfece..6a79ccf27f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5dd36474e36bf27f1355cf714e0b96a608ce48c21b364bcd928cc342a0df14cb -size 165299 +oid sha256:20ff9eca2a1ce793143485d886416503fef228e967ee606595dcd726a85c9832 +size 168179 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en.png index 0008ddcf87..2e06388c92 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1574b190a911c47c56e6bed804bc43df35abfb54bf7beb7c2e927fdd2ff7536 -size 149028 +oid sha256:c86e8244db3e63a63c173c554a419eab63445b4c4bcfe0f69a4900fe19faf6d3 +size 151392 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png index 8b76d2a9da..8c00dc6f7c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d03e48af7b9b231ae2f1a559925548b1867b714835c505031a904f4f033ef64 -size 154441 +oid sha256:8400bc01f801b6d6c32dd5d4f6792fa8f89b831e10228f7875e8f7e89146d1f4 +size 156895 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en.png index 36ef1115e1..145494bf10 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2502ede187b87dae2f4dd2a2aca1fbd889fd662aac022d7f491a043e7fdfbec9 -size 148753 +oid sha256:77817ceca6ab94dfd5ec239f281e15a0fd4be7a0e0a7e036f825710d5cdfe53d +size 151421 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en.png index 8ae3461baf..cc7b9b4548 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce8393e7b0fe7bc049873002af6c3d476a8efd72872d5de4de3e22daea00bc33 -size 153901 +oid sha256:e9365f8bbf8aca62b6e76a0492dd7c9b28481d14e16ff89f44d4ab57f000a991 +size 156559 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en.png index e06bc2bb0f..9d71a5386a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b26c57ff6d960c53bfe25306529397c5e4f649ab239b3a53ac97161e6b0696d -size 134762 +oid sha256:45553883378459e04d228dd13f5e835c81718b1dd07db10f1800dd93afc32ee9 +size 142634 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png index d8d9bbc201..0db679cf2d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f35c5da381410d573704d51fdecf578986ee413de664367328429e148dba9933 -size 144013 +oid sha256:fc0da74b89149a4d0fd136bf56e2aab545229c01fd83a72cd4fd4fb9d8fa84d9 +size 151647 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en.png index 60e1cd3f01..81cff9aa70 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0527b6fee58ee0aee884c9027c56df6056aeded45f732c85fe821d2684fff8b7 -size 134434 +oid sha256:545647e1c8c03e1517c5cb6fac8caca4eb293775714f5d8ba3c44b15e9191a5e +size 141956 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png index e9085203cf..d3d6501ab3 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c036d41362553348b7d0e94a375cdcfe8ff25fd6a87d5322265f93347646698c -size 142914 +oid sha256:a5ee904904ca3a9167174a61f5ceaec59d3ba67fc783639bae168959f078647a +size 150760 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en.png index 21c0edc885..33c722995d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec3ceb17cad25bdc7fd400de87fed007652260fdb844f1c94726703d220eec18 -size 153858 +oid sha256:3c54d751ee267a696c0dcf591636ee00080db05208692fa4c7eda444396727dc +size 156265 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_10_en.png index de590eacef..468fdd6071 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82e2841a18615cb4111ddbb8d2ad6f224f3925dca7cbaaaa032e8ca3fdc9943b -size 139873 +oid sha256:90e38333fd30ac4714249ab8709a30621c4fc1f2e0ebe753df0a114678df019a +size 142215 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en.png index b810769eaa..30e8d2501a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4295f1505656671238f52bc266db484999ce9b54e2c51549d8e6550817a986a3 -size 151754 +oid sha256:48fe571432922b2ee2891a582dfb52fd535ca8c2515df98ed44016d2533c27db +size 154251 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png index 767b7bfe8b..badbd682e8 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71a7151373fbe647116cbb45f941f8d2a237208f2ce0b09c159e607a2085a5d7 -size 160534 +oid sha256:8f9e3ed9354b363106baeaea5002a85bc1e030e5cde5581a823ae996fc0346a6 +size 163443 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en.png index 8843da9418..d8e7d37068 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:01d35f90258839990fd145d7705dcc922ee215301025b9190b1d2dabaa54c9d5 -size 142376 +oid sha256:f8b4b1a0b5778fd07679a866d00306c9068fc9c72a1512704f87ef071446f772 +size 144974 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en.png index c53f38c882..e773d290b3 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2fe6ff541b02a70684ddee8ab23bb5e9ebf5ebbf8894736b502046c457e37af0 -size 141012 +oid sha256:6e8566e74889b763b3843c3f1d98e6dd002ddadd67e26a6de7b91dff2bd57932 +size 143450 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en.png index 8974b868ff..13d7642a55 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd7c918de1d5ae1df41f6ecc8f71ff96c4a7e447958b060c846c130d53ca8a28 -size 148543 +oid sha256:08caf86f4b7242139a1e53a0b8d0736782be02496b858b1af9a1aa6209ec33d3 +size 150903 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png index c8009989ed..ed5b5e3b38 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e47997345500dbde05be43640da93880c4ddbef86c2643e941c0c48a8b2b1333 -size 140148 +oid sha256:b6be795f587d11a9bcb8ffffb097ff6cf2bda119fafda15ea48421d4b4af05e0 +size 142574 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en.png index 9ac562354c..e92235eb7c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6096e1365d2ab4987d5d1d827e4d2fe8d14cb7977fca35a4d7cc7aad157ba639 -size 140904 +oid sha256:d31b4335075205b9332baf8e04daed8e57e8a5d4b8c145cad91498acaa5f9cf6 +size 143373 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en.png index 3b27f79f8f..2db6bea09b 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2151e5ad75f2df1358e0717ed73afcc729f1dd83e2c65bd0b4b6a49d58bd5fa -size 142585 +oid sha256:74d7af64a2c2736f16e7f367116d0be0c6512d89bd1fff859f4a99207b9894ee +size 145167 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en.png index 12cd318e57..633c7ceebe 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31cd37875c57588ee2403ea21dcda0ac35b37bbedc11d249ba0bc836b3cbac76 -size 149014 +oid sha256:104b43c0106da2234ceb27250c632e17e9acdb5351cd4768e0a345ac5143f340 +size 151442 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png index 84061966e9..597638f245 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:739b154f1f4cc6432f317f35be43f484d970a30b186da0170edbab61029c9141 -size 140418 +oid sha256:d6aceaaa2ca90fc3fb6ab3b1af31e8fab8676f737574617a700119a1dbc4bc48 +size 142845 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en.png index 9caf623da0..db8b4f61c5 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c58fc185623d5485722b15ff4da898bd67f2de943b829fb4e2822cc672089e18 -size 153446 +oid sha256:aee3dba6d86d7a042649a39757df61f27ec23fa957ffe8a5cd0c77fac6a3925e +size 156135 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_10_en.png index 7853f51db9..1fa64f4708 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d203450dd2e6a46f79a0358c8babc758f70eed8d3ce0c1d8d24ca09befb6353 -size 139286 +oid sha256:c29dc6c1af653d70969e73cf1f67c9787a5156b336d7832993d4323ba5505bfe +size 141996 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en.png index 9a1250bf34..e66ad5870d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:09fce25d56a9b6591faa7a54b357d35914c5033a53d354600afaf9cf5235ef48 -size 151615 +oid sha256:9adeb033047491e9eff08f087bd4aa3bb1a3cedca4587d0f04935a10548126be +size 154279 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png index a8194c7901..23b32cf68d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22d7a6274ec746f9c393b0c30a430672214b95488afe4eb34b95788271750d06 -size 159040 +oid sha256:ad72a84a11b175855cf80c608a0b22fbd3f582b25d511dea35a91e7d860a59f4 +size 162172 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en.png index f05b150164..beb4e13cb5 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f8cfe0b07aeae86800264afd18ff065726bc2ef13d091f903278ea94dd07a4d -size 141923 +oid sha256:8bf85afb864f47b9d11ddf00be361e9b60be7f6c92072ffbbbd0c0a75d4d2d9f +size 144907 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en.png index cefd305781..f1af7350c4 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:14ce8cbd856ec966f7c2f6eef97f143012eafb497cf8b44229ca7d626ebc78d7 -size 140541 +oid sha256:bb600b890d2823da01ba95f7a5e1a44c71cf7c87c00dd4178a9939ababa66971 +size 143402 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en.png index a83fd98bd6..40069060f8 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11609976ab2f65de562f2db39e60e0c6d54105823c36ccff3e8976117ecab4eb -size 148378 +oid sha256:e0e9dad1c0d8cd9ef6a59a05f6d10806c499bae39502c8bacb4655a9c99517f8 +size 150857 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png index b697e0f6d3..56ce701944 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b64cb4cadd68db4a6d7de7cf02f9e9a0bb252a20a412e37f4ee46e6062527549 -size 139622 +oid sha256:bf9232176cb465087624f5e26a7d03cbcff0287000767fc328f71c8e7397a6c5 +size 142471 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en.png index a0e27e3fd8..2d67fad738 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98fdf3c429ec9b1a49bdfdbbd8d615ef0f7bcd566bda646fc1f45d021bcbca1a -size 140315 +oid sha256:e7b29247d5f3d9a6e598b661542fba680e849352c6fe8bc77f8d206d89ecf49f +size 143071 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en.png index 0720ff0498..658dce9da0 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:25f4054db92cad3406b148c98cbc639d968e976174a956a89b73c65eaf0c4f07 -size 142279 +oid sha256:9cfdfc3db13e98e93653e71ed8030eae544a74ce4bacc7d9fcb18165a69d9484 +size 145144 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en.png index d867aa40a5..a0f31948d3 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a0eb3967c5e6a3bde67cbd16a62ab74e2331a699c122a51d1743c9f8b59d69f -size 148797 +oid sha256:2138f335a2e17ba9beaf9f9e30f3a02517bf3a3841d48ab9287621f7954fca8e +size 151370 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png index 2f3709d352..d341c7c055 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d9b85054f616057e3ae37dd4e0cf1870502c841d77d2a005c06eebcfd1517d98 -size 139799 +oid sha256:d1e0c669adf450d9720df45424dd59c51f2d2a5d867bd166a56f9e7e3cbe0483 +size 142607 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en.png index 6f32f236ae..24ff548a89 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19c0b06cfaa83e76b816adfe19a82b3d95fde44aa3b65beee2962a41999c5708 +oid sha256:e143104a74d7e12126b8497eb66c19b42f458de3c56f1bc2809ab24c50b54a5b size 37294 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en.png index a61ce46ff3..549693771e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ed5ef667b5c7122be26c643eded534612d6b7f38958e4565981297a24073795 -size 35091 +oid sha256:c5140bdad78914b67e585a8a53ec27b616ae743ece4e3461e6db40727f5c88e6 +size 35094 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_0_en.png index 9ee10bf83f..a22e03550f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07b56a24b5bd8903c6086b0a5367bb82b97d36aeb9e7cc323e4463448e88d817 -size 50130 +oid sha256:36f98cfbf710150976311cfad03d28f1a6f4cecfac417fa6b562f6acb5b32a69 +size 50144 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_10_en.png index 3a4ca8a81d..603946dc27 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7aabbbc3b6ec3df97f38a6eba031a9cbeb9abcbe8f3f11580035dc38669ae193 -size 331239 +oid sha256:88e93741ed388372d2c2fbf0acdae2e0d40cee76f955d73166009e557eebdbcf +size 331229 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_11_en.png index d88b601369..cc3728a12d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a803637685f21736b75d08735031674ae3e407d02631f1fb713a68ec9cdb60af -size 85225 +oid sha256:de798b2e9c58ec6a2b3ca4addc0a1c1ee0d42ad8ea4cc4cfe148a68c55dec76c +size 85203 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_12_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_12_en.png index 515d9bf92c..d7e78df0f5 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d2f76a3dd81888f2b97a10f61a96ca24fb55d14386940eb4aa1989a1498e25e -size 51618 +oid sha256:1519dc4ee64be29dbe82c552ac843031fb5f01df8ee17dbb1ccb6b5e924178f7 +size 51630 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_13_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_13_en.png index cd0330271b..b19d991f9b 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:290985153d97f2588a37d3cfca87bb7f0e25c0643fb15dffe3fc995f4d0c7741 -size 63387 +oid sha256:ca067c9ebf1c44c761dc60736987170e458dd04aa7c00d9cbacb89856a93d03c +size 63388 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_14_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_14_en.png index aa2e39b213..58da9219b4 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_14_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_14_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3519286487e66ebf0857cee55f1b5c0a8804889773f55c68af6406015de96425 -size 47997 +oid sha256:6a9101972099ef39d05896261e00a6fdfb35aa8792c4c2f18dec7b8c9cbb7cbb +size 48006 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_15_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_15_en.png index 7b0ecaa398..a20fc1db15 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_15_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_15_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a26bd749e6dda1f3e3f8f4875f5b764d3cb83c1036a353bb3c947ae1e19183d9 -size 64449 +oid sha256:f7d84338cdffb77eb42badccc37d9e21b2bbd4b25cb0256944ad60d525724f24 +size 64455 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_16_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_16_en.png index c83e199ef2..45f88fe612 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_16_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_16_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79266ac1746ef8c14c77954ef5a5c5a7492b0745262334fba6c78fc47211729d -size 55183 +oid sha256:bf2215400c0a91cac40b5eb9e4e350443858654c2eca2bc098cd6a05a1807a97 +size 55197 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_17_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_17_en.png index 7a2dc090d9..4da3e6185d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_17_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_17_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf5f019af499ff9124157de1e46071f316b6a68bf67f9e23b756c8490aa392b9 -size 64238 +oid sha256:9604363c3bcf9818ca6946e3682bbcaee0220a557c3e481e92fc67be68cb4671 +size 64236 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_1_en.png index 5cfa02c7e4..5c4336be5d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6853ee78ec79829516310b6cd1b6ddf7375bc0baca390a037f6d212301c8cdc -size 71137 +oid sha256:e5c4849fb5125781a2caf190a8f3340035e755cebd4b97d235d26ce54f227ab9 +size 71139 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_2_en.png index af811632f4..93a042ec5c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08152eb43e666ee129e75bb7a0d3c618af30a71f4b94ed5fcf850a23a42fe76b -size 200809 +oid sha256:9c0531cdcc5d6af2d51e3cea9fea6fb23dd8fcf957a525470c27db5ebf740f04 +size 200824 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_3_en.png index ff77124e36..33d5dcf9b2 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ee4f0a0cb5ae0c6b1af7dd2e298f8b90c4764022a167750cb0e952a4cf2e82f -size 201856 +oid sha256:ba729e8bbd503e7fe73c3562f57c234ff2b02e56399b0e40e58ff5f30ae6e81b +size 201867 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_4_en.png index feb74c4340..4cbc5eb7c7 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c7119e9a9b531e83bb5d5d21bf6a5e9e61394b0dd56e7faf3f8344928b68f7da -size 70257 +oid sha256:1df19b2aa5d3544efdef608585e0ad6305c81fbd3e0ad8017ae950c14f58a1e3 +size 70255 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_5_en.png index 0276fea74f..a0b6ec2605 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d10bae63e418018bdaa6d8736ea6f3e3bb706b8cd14db9a767ab44f34ad2a8aa -size 84987 +oid sha256:c2fe80dd136456c6b8a091165d6bba33631a994c014da4248f59efbb51a736e5 +size 84989 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_6_en.png index 0d9123b63d..60428c55e3 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28c094fc4635241c22b96f8483ded0d81ab8d1f79a767aa7b6c744915a0b643f -size 72750 +oid sha256:a105bbd340c597da31876ddacb89bc90ba8e7d5454813755edf5a6252ea6f985 +size 72749 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_7_en.png index 48eecfbc2a..733f835563 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:813d6a1b4c979bc256e76419eda8cd402d74b634bda5defcf0cbfa9bceb49854 -size 103044 +oid sha256:d61791b1bc4d3ae255a63029b772644e39befa5a2fc4022e5c8753a4178522e5 +size 103041 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_8_en.png index 9dc09ab494..8738868568 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c1b4428df6c80d12d66220ccf6285814803de9755827dfc415825aa89a1d25c -size 53123 +oid sha256:84a743de111a89828996968d5a7812a075faaab73e637cc9714c256a39771654 +size 53116 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_9_en.png index 218aebf798..63a6bb4892 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15dccdfe86900365f1bff581c333ee06e1f9f1fe4d9dbb858a527b58b067abce -size 374240 +oid sha256:f2080039781871927f6afcaefc6e8fd5df6a4155138d1c78244663bae4a964d0 +size 374223 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_0_en.png index 58e94c8b96..3373710f31 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b87ceb71f82ad5ac34baf34f0ad34c6e4112bd64f6d394b69cf15544a435bb0 -size 49206 +oid sha256:59b63153b6f88ccc39529a73311c5e7459764e5a10208c98ae5217ad4e391daf +size 49214 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_10_en.png index 8a118b54a6..d913158c67 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bd317fbe9d0ae33f0d3df9b51df1c862b7873b44bd9e336f615ec02d55b1b46 -size 148671 +oid sha256:583ece5dcbbf4769cb9254ade84a7ff99854295e2e9102f248191f062db16c5d +size 148661 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_11_en.png index b5feaa4561..ba97caf3c8 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9dc71aa3c2ef36e67296ef94b83f84646a263599cabb71d0c9d0ad7c5b1b8c1 -size 86338 +oid sha256:0850c06b9ae1240e7c9fffbbf9a1168197c5a87ee04607054844bd38fd2cf2b1 +size 86334 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_12_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_12_en.png index bb2500ebe2..636ec36c91 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9197739c33689efb2c8d4c741869ff350391adace8dc54a41a4dc8019079fb98 -size 50853 +oid sha256:7b4867382de313aaeec90be3a3bf2ce86fd136044deeab681793434ad5bfc4bd +size 50862 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_13_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_13_en.png index 25bc1c8ed7..bc8c73c290 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:afb31d8543f04fddd1eabce8984871b7b417018c4e93425de132c3c621fa47bf +oid sha256:f38974847a9d279b7c15403923a24525f75700eacee4b0eddb147231c03c121a size 61904 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_14_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_14_en.png index 12ca4d52dc..dbc4a992e4 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_14_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_14_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e600c2786e3245919456f08583ee99d379b49ee087bc40222659d649c0badecf -size 47157 +oid sha256:51dc16c48ae9cd10bed51bec32b0d99b4367dad0da8fcc1816d0fb32d651dc00 +size 47162 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_15_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_15_en.png index 591eb79725..ceb9d679d5 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_15_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_15_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30d7b3d551faec10a647f011da62183f78e7514fbea52b536ea4f468f2eabfd2 -size 62856 +oid sha256:c570b3c97adff92acbbf4f4c9e9bdab4b0bb7a7612e69a4c715bb9e0d19abd6d +size 62861 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_16_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_16_en.png index bfe9570e1b..431d4e0ddc 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_16_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_16_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c490dd6b8ba10ae7ff0fb05cc7b133cb744abd28e5a991b12a6afaca16a42092 -size 53909 +oid sha256:83e76acb9e992577c499e316a679f6c1567afa07d6dc1d097488ab6cd115f8ee +size 53915 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_17_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_17_en.png index 48da42ecbd..eed3a30a42 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_17_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_17_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d616e1f1f7c111400e1752cee9daa67ece0720cf336442e38baa1991bd984e0 -size 64280 +oid sha256:b3e6be9ea405183bdd747c788e21f0928f6cabcb10a3f1eb471797ee8728e792 +size 64275 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_1_en.png index 0f594470cd..ae95a98bf7 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c599dcf6d41dbd5cd4cb42f62ac10f1c26a3bbcc6830aa31895e851d1bde5ac5 -size 69339 +oid sha256:3c948c4b9eccedde55d53da19f6cd1c8cd5d6a6e16ba909edf796b5535e1d079 +size 69346 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_2_en.png index d60ab37edf..07a1027631 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0b8b104b369c87ff399e18a554cce689c11f8cdd5d0e8af7f07d8e04843733e8 -size 199687 +oid sha256:c0a18c96105384799acc08205fe1f132a4432942b0fb9b58a2d5e2617bc6590e +size 199675 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_3_en.png index f6a0fa1157..0c145e25b0 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30681758065ed18ee1ecf5c34f2748e1a49d36b189d0aaddc30711be534d1477 -size 200432 +oid sha256:0c84d0ddc031112b9838dc15fafa12951bdb26ed3af3c1b94ced43d313251b25 +size 200421 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_4_en.png index c8be22a8c9..b396066ff9 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7183e0c153650cddbb561384421f54a22580b59d306ddaf74a0e8e4a920be6b6 -size 68805 +oid sha256:642cbd2fad7a9b7cffdb531f5de021a137f2379c2415b5dc25b818ad232df54c +size 68795 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_5_en.png index 81000a5a38..0ec2e46dd5 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f47641cba7db91fabd9d784a48de39da476da15de5c115a0cf8b884eb5ccf856 -size 82916 +oid sha256:d87b25d58faca74f0caf580370e7ea218b315b8533071e9fdae5058b8b028f7f +size 82915 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_6_en.png index 95a4607337..de3c35303e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3646fb87adfe319c29804d56f5efd4be1a7597283a04cd2433001cacc63a17b -size 71688 +oid sha256:319e3a13d378e4c29621f374d96f33790162d944dc033cddc68c4c9a5027f53a +size 71682 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_7_en.png index b9d4fdf51a..070ad9b1f9 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21121ba654a63f1b6b5157f422037c054c938eafb8180de8e7e5c79dc6b2d988 -size 100877 +oid sha256:0560cb1813e06b78f421dc08859bc396adf2e8d348f0439cc9ca82c507c8bea0 +size 100866 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_8_en.png index 68ce096019..b8d2ce8869 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c329898591c0d3b3e2a9e9032219af35de81e1a371d0fe128949fa17316371a4 -size 52741 +oid sha256:3cbbbff4861ffdc4b75b9b435e898349c86d0e72167d1ff512a4ef88089eecff +size 52758 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_9_en.png index fd1387c2e0..852bfc233f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineView_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d4bb7d7b93e2153d42eb208611cb8307211a204d60ffeaa7818d2cac21785bb -size 153216 +oid sha256:798082e7d988202f48413d76c0ae5c286fce2d3c22cd182a69f30aab0d6fbe47 +size 153199 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png index b00fabeb87..abeb47fb3d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab03b875ef8b9988ef443122641e6e6a67cc2c9a992fb7a735fb4c6805c58a3e -size 56767 +oid sha256:6f1646315278ac6e3346a14e2559b5cb308cc08082e9654ede2d49d1d710a671 +size 56795 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png index 9fd7f195a9..4d2eddbede 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:37f724a6f3253a5b9382062b2d544ab0542a1383e0d68c15128fdc99c459d7dc -size 58341 +oid sha256:6df3677e4bba63bbde74adbd2731be5c97afdce1bcee7064965b917a5ddd8ef1 +size 58370 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png index 352ed39365..33d8266124 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:094ccf2dedbdd6c4ae0445e85dd266ac1887c466e475b809dcee8401045528ed -size 48519 +oid sha256:55be986aa88651bdfd7d6c81a0617609496e30d81c949d41cb16896b5e37e387 +size 48526 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_12_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_12_en.png index deb9792881..16e6068a60 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab1fdf76117423bc1619a3b012a2cc87350af056a2bac78fb7b131d47f340d89 -size 56792 +oid sha256:f2a487db559d6638db0d9b93c98ed795bca2a82f68e26a85cd0a7a76f006bb63 +size 56817 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_13_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_13_en.png index 04e03219bd..85056e15fc 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc098e01a00c0635f719eb7421d9774a108971b4afef1a5d86fa5d9eb1d35e7e -size 59859 +oid sha256:ccf967ae3867651786c1e47e5bfef60aa0ad03bc62d5a72db329e95f758f07ea +size 59886 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png index f6379b0626..d1b3466534 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9867dc81318bf7a463f4a437c9c0d6ae5e29fae84b7ec6387fd502b52962b88b -size 55942 +oid sha256:b839efb7173b3f18624cf1570f56b62aeee25a097f977c50c9950e7edc907a17 +size 55968 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png index 61ec6b71aa..9444673709 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5c516399c67cbaf59e9565b3083481eeae2d2fcd684608bc29d8bd456eda9f6 -size 25800 +oid sha256:dca6dfd32a2128fe68de26e03156742cbbfd3cb858f82871c940a693516ce223 +size 39171 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png index bc87a81620..9cb1f42f41 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:673663a51ee0c976753d7e90f6919b00bb912cc75b931709a7329ed6ace32876 -size 59838 +oid sha256:1521e7abc43b6cee8d783e395f9ab67295bfab09acfaa26f271f6583a6c008e2 +size 59831 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png index 572fc53291..0301d7fa91 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ad728edd8fb715a66154c052b9517116f75f3256193e4022bc3e410d3dbcdf0 -size 54604 +oid sha256:19a591c39e3be571e4ebb852696313fdcef008e583818c21df300d9900bca320 +size 54610 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png index f3841aa4a9..5391f51acf 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa25c4e31ca828c06830847a79b29ef1f1649cac4fc47ca71839aa3732855395 -size 54599 +oid sha256:0dff30d6403a88c9c237865fe9bc1a65d1ea8b5b439df70ad1c7622fa199f372 +size 54627 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png index f4dd44edc0..423532b6ce 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af35a0036e99921d591f2abb56d03eea41e96c0099ed26056f8fbd506c349cba -size 54369 +oid sha256:9c48911e2132a696c4a0d2cc9a233ceacc0c5b3bb02f9b655e82cb7d98480ce6 +size 54361 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png index 82aa0526a3..596b04b01d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8417392e8f7728273f6675847183fd5c71db958c3686ff2c2603ade909167dc6 -size 57722 +oid sha256:da4713d6094c00750ae3f2ad486ab180b33e1e46e69b9604590f5169681b66f8 +size 57729 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png index c2e3110023..6f19d0f9d7 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b96bbc50a7fa92abc6a9448143a73cac2b347cf1faa8db590a8242242dc14a3c -size 39996 +oid sha256:4ad0d2f2e326fd05997ef115e31971727067543c543883029411b735ce8909ff +size 40004 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_9_en.png index 5f7e8509e5..44ec95c3e1 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:24e32f248f152388d866205c20432a696443de7df1be74d73d750dca5bffbfbc -size 39944 +oid sha256:281639101ede7b20bee08efdeac8c346fd13f9a9c4e3f322b6a6f37a417647b4 +size 39953 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png index 34634be07d..21fb9e301e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:172b1b27263136dee77cf8fd781a2ad5d84ebb3efbf3f6dbd97aaf4a4be00433 -size 56495 +oid sha256:c1033d39367ea410596ffb4029ea22bc90840f59645cd303cdc8d019fb0415d6 +size 56494 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png index f82efa3afa..5815ce453e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:92ca278062813f53b0f7531cf68ec6b6fa03821e25d836a97657aa0443ca8841 -size 57948 +oid sha256:d57c4a876860276a5a13e630fd80391e87ad068d3ab622179d0142d9e54d91a3 +size 57952 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png index 8a20ba0561..ace8091885 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae837cab1d1290b48c389886025a2bae20ccf66454976856e2747d00326ed015 -size 44410 +oid sha256:ce0840cc1849a2e074be9b7f86f156e1ac494a7658cc72531cbd90b6dba105d7 +size 44414 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_12_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_12_en.png index c4ebc5e758..37c20e1304 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e45f0da05123b566449a46cd1fa7c54bbedac785b094c0c7a8515fd56ab10d5 +oid sha256:ac6c99cb780160d47290324c93c9d0a93e3c3e1d5837bc146cdbdce5a46e72b3 size 56539 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_13_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_13_en.png index 89bb5b75e7..c4a70cc709 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f90b17cf8ec2307ebb17399f4700f4782ccb40996c3837716587873b2ff8183 -size 59229 +oid sha256:3e8d23096a1be9ec638a40d8e14dd25a2f49b373de3ff49863ec5720b33f45a7 +size 59232 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png index d36bb661ee..7d1e4f7bee 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95a3a250d24523e04ae11c4db7a5eca986f67e39762a86cd98ba7e2c7bea9a28 -size 55386 +oid sha256:2558438b5dce74e03bf203f9aeb951ffd5cb6f52eab5d28fea9d48a34c74c7d1 +size 55393 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png index 9721de1253..515b536adb 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2977fda41dcaf1c6528dee113781f81329fc31a76338ee81768e3b858394178 -size 23947 +oid sha256:b4d9f0142ed6d83339bfadd1736a1f832bffed776f31f7995f0afdace690a2c3 +size 37300 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png index 69c701a11c..5c69f0e415 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6404b12690c838e4e5dc396ef5b7665851eeab4d2a60c9d1044ceb79731d5c32 -size 59232 +oid sha256:9dcf745eec6b0cdf29be48bd1d7e637fbd7fb4d70a42f0c55f0fd53ad5d70106 +size 59235 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png index 315c0ad993..d681df3ad5 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:156af9ce76aa014cb9f25bab91394cfc2fdc1f1d57c65e0b0b1827f4a3826fd7 -size 50654 +oid sha256:867175a499ed1e1dc0fad33e83e51190af105342a8f79e3a125926ffcaac465d +size 50656 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png index 0b2d5e5f7d..c17b705762 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce1a774249006fe142253d87c85cfd812f2ad2779bf6fa86250c7cbc1efe083d -size 54269 +oid sha256:0dda6dc9737457bcc8001e886cd369a70baa1db568bbf3da237325e15881dc14 +size 54272 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png index fc3839f132..bfb73f77ed 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cbef425f1d1666bef4ee1eca6bfe70a149efd7eadb3926d6c68cdb80f346b68d -size 53919 +oid sha256:77a7e2084010487034b19258cdda04a3b39c810f43e06cf3c13e207ffc9a9e75 +size 53909 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png index a5fa4a610e..be1ffe8a6b 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36cccedf89c1e8ababfc5c4a639b429ad0fcf738fe8d719acf90a0604e0dc982 -size 53461 +oid sha256:b10080f2a866f28a65604ba3b895f8bb79769f05a51536b1f33a7611dfe26615 +size 53465 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png index e34cd56838..ce9c1e0af5 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee40525ddf73dd81e3850b77186cf0dde1c01ef177e325fb8b5b57922202601d -size 37388 +oid sha256:48c4460c9b57f0a438c9d0ae268eaa3823bddf3f1054e5c6b5caa64c6d075c6c +size 37392 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_9_en.png index 995e83f27d..c3f90f4170 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b4de1129f3034f766df25c53a3682c224c62e5c4c7a4e07803d6daa78ce12c99 -size 37168 +oid sha256:8226bbd316c5c4ac01f5660b262623be32ec0a274c696c7a547c8baf59bf069b +size 37171 diff --git a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_0_en.png index 0c71121528..51d29f8292 100644 --- a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6150b629d4a08036849a3cfdccb804a41f9a53503190269cee392d2240cccc3c -size 301859 +oid sha256:9ce6f663ca845a54b4e1aca55f1405e00d29084a95b6a61721e5136efe30f7fc +size 311755 diff --git a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_1_en.png index 5960d6c196..d712c96ce3 100644 --- a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7999de0eac9ec04abc1820e7096188aef997c50675b8a6748e918ab18ba79026 -size 296905 +oid sha256:5bd68fbd9f16f6d20d9fd8caffc1c766ec98c80cd60e33a31ba262241104e8a0 +size 306554 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 7621393731..a303b4946f 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:d342ab1a3873461526f43ed1e65e7041f734bc44a2f741343c135de259cd531a -size 300487 +oid sha256:563c2a20cbf651c88e85dfba98c02fa60197b298ee621b5b4b1bdc4af51a456e +size 310172 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 ae88d0a24b..202144a2d8 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:935d4e73357506db12757cd594eedbc4f06d5fa302fe7dc544305830c965f101 -size 294683 +oid sha256:b833e535b35cb63190900aed69d7b7e0e34a4499fde6a3cbbb93980d7d79bccd +size 304787 diff --git a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_4_en.png index ba5b5b5bcf..1b1e654182 100644 --- a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6fc29b28e17d18a05f3c0e003dda13fea8ef0eb33322cc83087510ddc65aea1f -size 302654 +oid sha256:4f756552f2d88693ae654f92c24b5c23a31cfa6086309e0996444f17bd33f07a +size 312449 diff --git a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_0_en.png index e5208ccc2a..3e65f22a15 100644 --- a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0fe6148ecc64395169ec4bbd7fce78c4e8be6233334b3d761ea523e1fdc94479 -size 387905 +oid sha256:e41a284f2eedb0c7158079f059e05f8f505436ba91a82194faaff459bb30e4b4 +size 392983 diff --git a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_1_en.png index 3938c1f4c2..06aa397de0 100644 --- a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5ead68818ce32ac0254f06ee127aa6ebdcf4d0865e7a5a608ab00b9e8ab9e70 -size 375084 +oid sha256:c37df776d96b70e3c1dcb8818022f04e08fde2dab52b731f4c3b5f690d925de7 +size 380243 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 d091368900..a167708560 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:0af010aedbec3505637f3934534e1f4e453d046938fb85d13e59423368b1f93f -size 378342 +oid sha256:4f2c6932790cd4bbdff4dd07a728290aeb2eca47811c0b3d220e17aed5e8e34e +size 383588 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 5f1df4b3a2..076ebc920d 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:76e7ca557f970634d4ca63eb7705c3a09c6379881c47ca5735092ed54cbcabb4 -size 360016 +oid sha256:f59fc12655ab0696f8af05a725f817bad80b71f1b8f004e20ce40b963208e07c +size 365118 diff --git a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_4_en.png index 28f2856291..f8bfd58281 100644 --- a/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.onboarding.impl_OnBoardingView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:92f6789eb3b041f2f5ded591ba04a466b6636981ea28edce100e040a8b14e603 -size 388627 +oid sha256:6992b4234fab8b623c05c01c6823791f09bf561b83052cd57d69952380762557 +size 393746 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en.png index 74f6d0bd9d..a38b090e83 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13e7793d8dd6d08e182b128a9b3dac2877557e8bdd220561d36c2ce1650b94ff -size 48107 +oid sha256:cd172b454fdaf7966e8c85481959000c28310ab2ffccd1b13dd9af3b1e392a3d +size 54935 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en.png index 889f655996..34d54ae010 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bac5247c3a4990eb9155a21f72a49059cbaa93288380ba1ab6be2def8b3a6a9 -size 47876 +oid sha256:cff335c3b36ad364bf237b5635577da8f5c82e86bdd35bfb1c138e222e616640 +size 54698 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en.png index 1fec0eae29..348c17ddf4 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11c969c1d04150cef68da64865bade2ea3bfc1aa5f0d790262315131b57d6233 -size 31702 +oid sha256:397049b4a74bd122c09e1e16d4ee72dcf5abc6562bccfe839d394388b1b0a2b9 +size 31741 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en.png index 0ab8ad267c..4b27052cd0 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc1aa9348e470e9d7e7e1e838e18a3403159c2effb49fa359b2b2db97dd81961 -size 47901 +oid sha256:7b5cee26851a31850647b550225071bd0a5c4d16026b42771f43e395fc1f0dcc +size 54766 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_en.png index a1acf97a47..4a1809c127 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90d1184879c5a34dc27cc942ec3110e14ccd9a90c152b10a1844fde0566d54fe -size 47841 +oid sha256:21378e572926adcafa78ecbf16042f1abab81790ead55c2da6134d84d3e88145 +size 54742 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en.png index 1ca045272d..dd123faa03 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2beb4f4f190f6aca2d4da338d980e8611283ffb9bfd2f402482f8ecc629cf22 -size 46759 +oid sha256:9dcd26f37cea379f17d4b7d35b66c8dabdef2b83afe168f261c7007149ac7044 +size 53658 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en.png index 52825bc9d6..5a1be87eff 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b65acfd3126efc5cd4fe1a929a786468cc18ee371cb4462ef0cf9f6e8963fcea -size 46456 +oid sha256:d38e06cbbabfd6fdab433008ec2249f2ca3838a9a95a5f6300e79fce5b805733 +size 53366 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en.png index 528327c715..80fb2f3e56 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e90bbde9aac7e710703e836f293a00b2a5f35447d4d63c9732de21a0f291449 -size 29336 +oid sha256:2836655a2f3ef9aca1f2ba94222bc92f9bedab82589169e34ea0a325cb1c0e52 +size 29363 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en.png index 5a3f3c1ed3..53a47e0baf 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6da0cf1729162fa1745bcb4dd86be06f90d3bcf6bf034e4a64e1ee9119b6cdd5 -size 46501 +oid sha256:6823de7de81b167d27c1c9c8aadab6b027976673552590f1dbea9dad7748919f +size 53413 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_4_en.png index 387f0e65d3..e549902114 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66109868b1e893bc569d70110e3e587d7a17d777838cfc3e5c3d3189338d924e -size 46423 +oid sha256:fc4b71bbb0d276540c887a4c6869eca003ff500ac1d8b319d63483be21cc04d0 +size 53377 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en.png index cfdff06aa1..96ee8d78c5 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a1ee65e2fc9f52de1287f2c0f544d1c677d17282977d5d4ce14634e9ae234119 -size 61526 +oid sha256:612db28de816160b1626999713b8d4eb30f93318e3f4218a4c5fd402a6a35492 +size 61492 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en.png index 004dd59872..f7c87dbfc0 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7705d3fa49210822414ad3a863ae0bca9bf0bbf4d223c14514834b00c0d12e47 -size 61141 +oid sha256:ee168b2c0ae096d43b2afd046711c4fca166d492e38734106cc2f20292458639 +size 61093 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png index 1635050091..8d2416c7f4 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de7e8056b66afcf13d8ebf2d333d535beaca30c882f3b14468e90df71a6848d3 -size 61651 +oid sha256:63dff0746699b9b4271dd37f4af9e0e1dd9ef707900bccda4445f8b35569a1fc +size 61675 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en.png index c79ff53179..39b2c05ff4 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:441cca0f48414ed65ec3874de4b1ea225461939ac32c69fd8a73b1dee0ba4407 -size 61393 +oid sha256:fdcc93298932d516f5576b1c9ac0f20853d82a0c5300045494b9676f3139c141 +size 61408 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_0_en.png index 6328b35ca1..faf2367a0b 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f16fcc4cc994ceeb80df964e9a9c40bf8f85869cd6c11ec14ea327d3b0fa80b -size 38074 +oid sha256:48320aed4570138a76b04b08f37f67098a72e1b63f13273fd6d9c0a6e33b7e10 +size 37955 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_1_en.png index af9d5fb9ba..5ba26e89ea 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a432b76372ca2bda9a8f84b8aa779c0eca61e61934ba81d8428e1affdd1f35ae -size 37818 +oid sha256:6a71b634518191f299c924f8ddd39b44ddc1255698e981796bb8b034377c515f +size 37712 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_0_en.png index 0f474547b3..b64b5e290d 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e8b512463d51570c4645311fe652ee704fe7e67cf2d8f2f6bdf936b987828ea -size 38908 +oid sha256:e33a80d4f6dc4a1bd1cd86b2bfd64471926871f92d8d83cae6b32a79459c8ea0 +size 38775 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_1_en.png index a14b3a6c7b..344a42b7a5 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fdf9185ce8776451ad07410e1cd8169918da9c4734ece14a91f19716d6b3d00d -size 38915 +oid sha256:c69e1d5748e65b35525df64b83c6616ab439299284ab2bda3a8408d5f4139d2f +size 38802 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en.png index 5aec452dc0..e7cbc171c5 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:346b55226273559de0d0d7298ef877c7f4e5441cd672bf224c2eead14d336c76 -size 16835 +oid sha256:055c81bf67eade1f203cf3e02a2b5096c89f73918b3f9123f0926d9af7a4175c +size 16744 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en.png index 31d097182e..d9479c2777 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5cf37333070202ab26168d65639fd3913e1dd2cd0508b52e021789d4827ece3 -size 21362 +oid sha256:a4cf5881fe38c4d666a7c61d64d58ecc22fb82e50931857a79370c14301af444 +size 21293 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en.png index 3b75bbad3d..0ad86e2683 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49d02751c7a36b988249c03e355af1b038459ad20410e8e88256f767ebfc9b42 -size 26323 +oid sha256:3a61566b38636c7f66d4443ddf42330b2123eb4dbe5829df561e18c85a13dc59 +size 26245 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_3_en.png index becf3ea4ed..908e311bb1 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ad5026af82db340904d6bb23a8d5baeec0e233a60df901d26331607e4269682 -size 9428 +oid sha256:2d06a58bb3b3fe7b01740cc04ed3128d24a13717be84cb4a70ced228d2256b41 +size 9435 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_4_en.png index 26c7c67579..31231ab57a 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df8ee30b1a35b35edac0a456eeae685ac1b10d350b3d3c3d6ff9c484af5cb649 -size 9064 +oid sha256:571207325446195a58251e7ece1ecaabc8a782cbb526315abe2a82f21e6441eb +size 9006 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_6_en.png index 0462444183..6f43e3db48 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2d87d19e4be1995b3adbd4ce09eef71908d3afcc6fab7c0c0f331a2bd87ad30 -size 7608 +oid sha256:f83d60c7e1d963ccef24668c219bd9df36494dc8bd8f57af7aa63a3b73ee6882 +size 7545 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en.png index ef15138a1e..7463908c41 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e24080b2731014102634cea433fc357ab5cb76bfb149a266a61e345d1c718a28 -size 15875 +oid sha256:0b7570c4dc464557985e06a3d90fac595e5363f53aea02cce86dab76a58863d3 +size 15612 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en.png index 9d45f7b9c6..81b4f49cb3 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b0a79c489f22ae47dcdc8d212dcf2cea595d2922b816e1625f163be4085182c6 -size 20271 +oid sha256:822fe5f5e803509db19834d72fe7d1960cf0c8e0d53255f1b25bdae590277fd3 +size 19991 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en.png index f4f6854298..dac29e6155 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:907246ef2913f37b087e3bc96b8361b75e3a672854d613c7788b3dcf66393796 -size 25036 +oid sha256:ae41bb5c48e710ad31e37c78a1db6c928028ac52ac2d0a8e4574ebc75497f5b3 +size 24743 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_3_en.png index 6a5279938e..13b79b35d3 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a6b1e57445c874750a23c71411f026e04b83337c5426d2ffd415b31c809b3c4 -size 8183 +oid sha256:273043114950e000953fbede780f3ad8adcce9d3d579edea2cad58f8e3c48881 +size 8175 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_4_en.png index 62780c8ba8..3631712dc9 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28d100b801bc13aa3c088b4ff1ba6850f3bf52683d2cede959c7f263a2c25dbb -size 7631 +oid sha256:2b0d51e2c5b255fb2f94959734293d4b7807f002acb0217c84856f0002297293 +size 7636 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_6_en.png index a0602abbca..08349f1edb 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7274f8fd13d6004ed757fa78ec1e5913a76250f4862232e53cf3155f59f25fa -size 6385 +oid sha256:ba213b12d181b04695b9271670cb7d96ce83dfccc3c2031ee002ba1cf180cc8d +size 6408 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_9_en.png index 04e823413b..abb4e70edf 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:387dc9b6bcf0dec4290a388fc92cca03b847426762c5bb809d5bfe0c241f18cc -size 64704 +oid sha256:35e233bddf6480d217ff63ce7af4d37433e6cb1bee90a26428ed2e57adfc1432 +size 64668 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_9_en.png index 9c889909bf..74fe5848e7 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3e45b2135359b8094b9f8de1e21bb11d93cbeabd31329a54520a9faa594d57a -size 64350 +oid sha256:7f8427c6786075496146988aaa114639cca599d6a6d7c8c6efc753378eaddcbf +size 64348 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en.png index d8498fd392..c1ac92f83a 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5e7576ea8ec6f504070c59997482135c4cd2e6d647b1b6f6570a0d23df73509 -size 42293 +oid sha256:6ba118648bb2587492b8381417b2ea7d125e715a1a047ae46feceb28f257af5c +size 56973 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en.png index 98b2583f91..7cdf882032 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9cabe4d952cff321039898fa130da7b92d9aafa96e9c0bc2e0a6f2a3f610031 -size 40136 +oid sha256:ca1abbdb9f265f6372e172a23024c667df4ee16897dcf5ba3d86891dae255dcd +size 54515 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Day_0_en.png index cf91d5c85a..530e8c25eb 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f6151a8574c95aac3473a0c1d42565a03c7616e32ccb41f31073f495b69409ba -size 27711 +oid sha256:079d045edf1bf2905af49a08fac7cc80401af7f34787477bc492b7433895b1dd +size 25702 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Night_0_en.png index 99ac375b38..ad10def5a5 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0c5f643228e0a3330f977ada7133ae0f43fab528aae01cb43a72d7591924c68 -size 26756 +oid sha256:6629e69953bc5a58fcb1708557504940b873118f9c515cab118169a239e01807 +size 24666 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en.png index af5d935a8d..c528154bfb 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3ea3591ecafd089a834c818551eaeb919f303cedb23106cc4295eb443273506 -size 37192 +oid sha256:e94bf908067fbea798749b67aae88f26867bc1633a73d0f6b96ab085a4ccbb6c +size 41080 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en.png index 2c3a18ad3d..510fccb59e 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5a213813a4539a0a3d32960e7ec93f6972a5fe8e75d26037a82f0d9eaa47926 -size 47000 +oid sha256:febe2f1b89d81289ec248f61f3c0058136143dadc43763ebc074b23be36ad5ea +size 49305 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en.png index 738725584c..fa86c41af4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6aee0fb024f80729f7e39b89f638ce6d6b5e2af8f134b4745c523cebb542b04 -size 36918 +oid sha256:1210e30466e2561333261762640db7b29339034f6b9bd8b371ebde43d2118e7d +size 40788 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en.png index 6f3a1ce963..ff5cad9907 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c291e16eb5eb3c7853ada8c968291169c7e17ae8cde0f77b0a6d3f00c856e254 -size 46648 +oid sha256:aac26520f2e8e9ca823958191d4e51abc31072fb4cb1d2914b20a6ecc7456c71 +size 48964 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en.png index f1ecac35c9..83bfb57c9d 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:259e72d177d2ecab0192cc5be7bdc48c697d4dfca83eb72a1487e83c9e3279a9 -size 28889 +oid sha256:50d8c9e3272a47de6994d64704c31a4b23840a7ef80b88de7df480b47a0f41d6 +size 32182 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en.png index 54530d68cf..bb0c85257e 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:09f49efb93873a6bffd389ff4edca77bbc00d622669ff25bc5b0c042d65e283f -size 27781 +oid sha256:08731d9bad63b2e9fd5e391df69ec8938887e994cd879ddcc0b2d33cd4c53b56 +size 31084 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_0_en.png index 8c838d0407..81446e80a5 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f12d9f5c181579e054fde00a32ce72cf032326b3c63f2424e5df696db0138368 -size 78667 +oid sha256:604ac96927bdd49547fd39d2c9313ecd87b59ed7c8bbf9f5e5973cdb3f948b5e +size 82277 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_10_en.png index 1e0fce8252..01655ac6c0 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3081dd73e3a33e786266b49de1267c401e55ac3c5b37d1b9c61749b0f7d55c29 -size 99240 +oid sha256:a2f1c32f860df198a084dc1570dde8f94cedf304d5f21036cfbc39c0dc43e603 +size 106169 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_1_en.png index 8c838d0407..81446e80a5 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f12d9f5c181579e054fde00a32ce72cf032326b3c63f2424e5df696db0138368 -size 78667 +oid sha256:604ac96927bdd49547fd39d2c9313ecd87b59ed7c8bbf9f5e5973cdb3f948b5e +size 82277 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_2_en.png index 07f4f2564a..bdff50ac76 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f2e8f7d0a8d384f7958165b113f67f637efd7ab4d4e2f3db321edfdf83e8e82 -size 79079 +oid sha256:3b73e6363372ac7e23fec07eaed7332cbd4436b57b58292fd02a1c6736470431 +size 82728 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_3_en.png index 5010844ada..81c3cd3b4c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:510c61d0970c692d6faff16f131d4186a2cbe7f3183e40399647b9e456965333 -size 22808 +oid sha256:ce301d1fa711ea2f00193cd808580da80cb3eb9024824aacb17e764e775986fa +size 61590 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_4_en.png index d953e66bf8..a8de70c034 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e812f3ed67b96b3809b268878d741f1e9471472e8d2272b5b96a34d9d19802e4 -size 22547 +oid sha256:611016bb64174fe744cc34b427c8ac6216a326cd3ffb514ab511615e4cffe4d2 +size 61343 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_5_en.png index d6b011dde3..522b5b859c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e268ec678b7cae5d0e58330334bdd196ae227b78da9326186355926b1fa5420f -size 20603 +oid sha256:c983fbda82828a169957e6d37b867e98eef6d2807707fd736b20b6a758317f21 +size 59593 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_6_en.png index d072cb92dd..bd0c3984d7 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c27b870ca1639a58efcd42dfc31bbb12a935e685ca1b87e891ec88abaf8033b -size 98148 +oid sha256:9b5e578b50b6e5b4e06bd45cb16064ff425736ab1e5db12db8ffac1fc19a2d9f +size 100683 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_7_en.png index 5f20f70675..95582e918b 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6476a55d5fbc17aaf3e72ff29e3e92430f845236615da48573ac526dd35f5ac5 -size 46519 +oid sha256:0762f1f181107fad31c233bb1013cf82c6de2b94500e592609fdcc43e53a5807 +size 46550 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_8_en.png index 5e5baa079a..a22032f937 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db8f6633ebc3f856dce986bbfb2fd5ab46037e1438e896a3d4a96404cb50571d -size 41236 +oid sha256:3fef8280fdce4608a30e24063699ab99bcb55865b2df51b070e2dc0eafce23a6 +size 41271 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_0_en.png index 20de12e5be..b80a127217 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eaf6ef4d088e706e5e37a33ae7897af0ffbfda7afac486f9de2cabd7e24ac0be -size 86150 +oid sha256:80702a8d97a3d178f8d4b8c7cd91f34e5b2d05da5babcdb148d347820633df72 +size 88608 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_10_en.png index 7cbf6930a2..d6fcf4c50b 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7eb4d87dd2844f81bf745f53cbe83253fe8b48471692a00dbe0f385de75c8c3a -size 106079 +oid sha256:212e99c8bf79e5465c73305b9941f5b99fe27feb0e98a0b436e72288e5d3f2f1 +size 111946 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_1_en.png index 20de12e5be..b80a127217 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eaf6ef4d088e706e5e37a33ae7897af0ffbfda7afac486f9de2cabd7e24ac0be -size 86150 +oid sha256:80702a8d97a3d178f8d4b8c7cd91f34e5b2d05da5babcdb148d347820633df72 +size 88608 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_2_en.png index f4fd4c8c85..d2b85d8dca 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1014ee5cdde8ea23b6655ee805761e824bcae40d353d4208af41c8b7801998a -size 86938 +oid sha256:dd8ab022ccecdff2350e6d2f58c7437dfe8a587f5cda6276a415b6c737538d6c +size 88589 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_3_en.png index 3a1bcb5d3d..0993af003d 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c297102e973faa502a6567e1b3b08310f788961e92c05009a84ae5505aa1f6c4 -size 20809 +oid sha256:6649a87c37110a6820802a6fddf51250bd05f3b6b1ad4cfcf79bbcb20058d74e +size 70000 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_4_en.png index 1557f197a5..f02609d7dd 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e5c32de6f9cd766480cf5dac9f49dd5ad6083d48dd1901cdcd137377cf98dc7c -size 20581 +oid sha256:be1919432e860b977e841ce4d2d0bb3452e680a3575433707f4f9cef39860815 +size 69758 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_5_en.png index 6bddcdb30e..ad1d7b2e48 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:609178fe7f051bbfadf1859737b6a6241bd1a45c2053c2d5f72856a07f2f63b2 -size 18736 +oid sha256:22220f563256a794ea94d7e594c06e36ca26a0427ebcb8d1e838089453217af4 +size 67952 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_6_en.png index 5f3b07d888..7cc568c6e6 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0550ab262835b885364b429000e0a887a6bfe594ba031fdbbb04fb4ec63ea475 -size 104884 +oid sha256:44520745df70fbcfb7a1e6cdc309dfc931e3afd55843431ba43b4e9aec21cbfc +size 106144 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_7_en.png index 731e9f8217..6ab0b8da39 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca4a0325bc6141db37d6e233421edaa69bc1a302d328f46e8f926fb7caf1a888 -size 53737 +oid sha256:2e339f50db6e8b06bdf5a26559827f92f5a9fd2f20bce4a45d4edf5d45e071b9 +size 53790 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_8_en.png index 45e247d568..fefb0a3731 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f1a530e4685b92a73aa1eaf15c6268c77692fff0cc1995db251d2c24cf5b77ce -size 47797 +oid sha256:9496a4dd417cb64c20a1d53fb27f9f567f0eefc8c0ad428a9c6c4041f66eddb0 +size 47845 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en.png index 3a77b883b0..7366550a77 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6fc66d1964b323e7a4215b3127fb5812545116b24c668da7b18e716438b5449 -size 55377 +oid sha256:2159965425d13676b20c72d1fb1578a708a79b6386ea4ccfc8b8773885f24d40 +size 66188 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en.png index dc9ccfe88f..7366550a77 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d94b5d9a083d20ddf3d67679fe903234adbf2529aed1d7474c58b22e5bf3d5a -size 42030 +oid sha256:2159965425d13676b20c72d1fb1578a708a79b6386ea4ccfc8b8773885f24d40 +size 66188 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en.png index 8ab7b82f80..13ea28e242 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a9646499a3ae2203a941d1b975157820bb96b80bb3107c223e1e4c1b1ceea3f -size 55957 +oid sha256:8fadd7226eeb9132a6848635011dd31057e5f9fafc959476500efa4fbe2e907b +size 66699 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en.png index 03b063f06f..e9ac09a773 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8abb26ff01db7ab458d77f8279f6edf213f6abb9fe0697e7c1984eb3b8602193 -size 27231 +oid sha256:8149ddbd84689c55b4ef0809a8abbc4686f3e4e15a67b0b1441b3c3ca2b1271d +size 39939 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en.png index f25d6a3c40..170a627919 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:32cdf16ec3d642b0734b8e7829f6733d3c420a4a5d0e86ce78373eac7262af03 -size 54102 +oid sha256:a9d9919426c607a37e2c42c9c1f11fca097c40accfe2e6b909eb583684aa973b +size 63709 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en.png index b2df4882f4..170a627919 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7a7875fa017a8fa65ec7e9509bbddba823ef76fb4c42e023a39e4fe1e1d3b63 -size 39056 +oid sha256:a9d9919426c607a37e2c42c9c1f11fca097c40accfe2e6b909eb583684aa973b +size 63709 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en.png index 033f906cdc..fa16bd9643 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56d99be26704d77d3eca53fda5a74a41e6d69232d73722dd8b6b30d20afee6e6 -size 54676 +oid sha256:1af579d0d2544971386704109c6332febdf4bc4e6fba79ebb17a7b8b3b0ebc83 +size 64253 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en.png index 9b9f2b5366..fac57f2d6b 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eb4fd397600880c37e0fa67cacb29e04e4026a239187d6c211509c0084b41103 -size 24750 +oid sha256:c4ca90b2a211c370707f5aa2f0a3eaa0783c633d595b25bcad658cf283efc1df +size 37321 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Day_0_en.png deleted file mode 100644 index deeffba555..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Day_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:94acaee1daea87be3ccbbc7372a5473c1554c364323fe487fc8a92a8d5118d22 -size 12697 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Day_1_en.png deleted file mode 100644 index c1a7cc6009..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Day_1_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3c39f29b5435dfd402a046617032f35fd3e8c7ce13337755c67f55b548e19ae8 -size 13270 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Day_2_en.png deleted file mode 100644 index 8b21b5dae6..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Day_2_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aec83feacbffef1df6fb3bf3bcf6c937fc95c9e1f8974fdf193868b6fbc4d464 -size 18222 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Night_0_en.png deleted file mode 100644 index 43f170fa9f..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Night_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:14f1c3d40841097e78385edeb78fba3942545bb52fba33aeac4d5290f636ac05 -size 12229 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Night_1_en.png deleted file mode 100644 index 6b8418834f..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Night_1_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:57e525f8aefe4fa6aba18f46cf67e5ce2c46963ec0820e9f9d575be605b7c95f -size 12771 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Night_2_en.png deleted file mode 100644 index fcac8ce953..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enable_SecureBackupEnableView_Night_2_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c750e2f4038d335de92f98dcdaf4f7a7ca1425ea27266c2fa7867392d9dc9218 -size 16449 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en.png index 90c80083bc..4c10c82ea7 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:030e9ba7d127d48ff3f8c88a1cdac4aec19cc802f18fa0c5e732f20e9833c14e -size 37433 +oid sha256:e4733b4eb3cc0ee05a5a6c3ed3e7225b02652c749f960dc8ff5b6a935096b6aa +size 37556 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en.png index e55d6604d5..4c3e2fb0f7 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d088e7919f0cdfef4f6ea3dbac0dec13f81d991c4d2a479c57856283e94a81d -size 34676 +oid sha256:491522ac006703f66cf714ac8e878ba441f3f882e40b4d017acd8df2833cafd1 +size 34892 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_0_en.png index 30af0ac691..23bc54a2ae 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d97fb4f902a45f03f60c5fbcb9dbcbb1e0699c89832a21a65c940dabf762e167 -size 32427 +oid sha256:db4b223d8acc7445932515400461ff8c38bf7617f077d96c12291323e1ba7ea9 +size 34930 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_10_en.png index 30af0ac691..23bc54a2ae 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d97fb4f902a45f03f60c5fbcb9dbcbb1e0699c89832a21a65c940dabf762e167 -size 32427 +oid sha256:db4b223d8acc7445932515400461ff8c38bf7617f077d96c12291323e1ba7ea9 +size 34930 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_11_en.png index be1a78c99f..09b55d6cf7 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9aaff674eb4d0079c4c66f07657341c311e2d6ff20809b3f3a31f756d496a1f1 -size 36852 +oid sha256:d7bbbfe116694102f28a0922586496713a80dcbbdaef327e1249f260d74328a9 +size 35760 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_12_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_12_en.png index e1e2a85a6d..09b55d6cf7 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:40bdc9bd677b6d4ca459d0aebc66c875450e62138731040bc34c29405bef5d80 -size 51432 +oid sha256:d7bbbfe116694102f28a0922586496713a80dcbbdaef327e1249f260d74328a9 +size 35760 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_13_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_13_en.png index 5726f61609..c940251903 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f8aeea3ba0a1aed53900f6451e91b71eab90b991527b1888e1a28e289854730 -size 42049 +oid sha256:769305f9a2068a58391e9900f0a01f3bf7e38ffaffb452946ad78b1d2d22cf28 +size 55732 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_14_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_14_en.png new file mode 100644 index 0000000000..ad1c86a184 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_14_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3aefccbfe014fcf17673055d2411c3eee6ec6d52075d9cb3e5c217af6b319750 +size 54158 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_15_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_15_en.png new file mode 100644 index 0000000000..77fdf4913e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_15_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:95e8b843df09802ec4be046aee4de6453f8e7c1095249a1918f89635508f5cb1 +size 45465 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_16_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_16_en.png new file mode 100644 index 0000000000..a73970baa5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_16_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d2104b99e3ae361dc169c46b0afa00225f8f36dc34d53ac9e2b04136578c6650 +size 54655 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_17_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_17_en.png new file mode 100644 index 0000000000..7da2293634 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_17_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc2628645e4618bf72a8f7d8c2ef33f2150cdae0e606765aa869b42058298493 +size 41199 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_1_en.png index 5841c728a4..09b55d6cf7 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cf205ea97690e47b25e9b0edb22e004b0d0348d0ca9ddfb677813ee6378e0f9 -size 33574 +oid sha256:d7bbbfe116694102f28a0922586496713a80dcbbdaef327e1249f260d74328a9 +size 35760 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_2_en.png index f4e2028eca..b8d73ca9df 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a32c172f73d9b9d71f9b134ff79833d7c768d20324c0fda7d1a33c815914b05 -size 33484 +oid sha256:aa0372c637b7f8c1e83d65a9b037ddcbaf95c940a7974a38bd020489d36245dc +size 36173 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_3_en.png index 5d6f49dfe7..5129b34c5a 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c144327544cad0a8795205d197bd9c6fb4b735aec0a667a066c48dcc369e8afa -size 38937 +oid sha256:5481f4227f3f1cfcca8ff9653c13d43599b60408d6ddaaef854646fca856964c +size 35153 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_4_en.png index 4e4a82b2b7..23bc54a2ae 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9e11e12f7a6b5d91a175280d5ba9e34670856889488e1eb4deed6be39d2e47e5 -size 30816 +oid sha256:db4b223d8acc7445932515400461ff8c38bf7617f077d96c12291323e1ba7ea9 +size 34930 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_5_en.png index 5841c728a4..09b55d6cf7 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cf205ea97690e47b25e9b0edb22e004b0d0348d0ca9ddfb677813ee6378e0f9 -size 33574 +oid sha256:d7bbbfe116694102f28a0922586496713a80dcbbdaef327e1249f260d74328a9 +size 35760 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_6_en.png index 5841c728a4..63391deeb1 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cf205ea97690e47b25e9b0edb22e004b0d0348d0ca9ddfb677813ee6378e0f9 -size 33574 +oid sha256:01580b201933b02c99b486f8db6eb039dd41e4cc11e998b4a0dd5dff343b79a0 +size 35030 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_7_en.png index 5841c728a4..09b55d6cf7 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cf205ea97690e47b25e9b0edb22e004b0d0348d0ca9ddfb677813ee6378e0f9 -size 33574 +oid sha256:d7bbbfe116694102f28a0922586496713a80dcbbdaef327e1249f260d74328a9 +size 35760 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_8_en.png index bb90b5f586..09b55d6cf7 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8266c3af58782b0d4d9c6fe9801474f3cfa4c5131d0922a32815c497a2ff8876 -size 32425 +oid sha256:d7bbbfe116694102f28a0922586496713a80dcbbdaef327e1249f260d74328a9 +size 35760 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_9_en.png index 5841c728a4..09b55d6cf7 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cf205ea97690e47b25e9b0edb22e004b0d0348d0ca9ddfb677813ee6378e0f9 -size 33574 +oid sha256:d7bbbfe116694102f28a0922586496713a80dcbbdaef327e1249f260d74328a9 +size 35760 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_0_en.png index c75b2423f4..c348dbf3f1 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f925391dfd47629c15b31fe644eb66e43029064aebf73bc7136aa29763cef0a -size 31836 +oid sha256:97bafddc5171cf429509e5b16c830460618ef098f78d305658b9f6570fdaec7a +size 34352 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_10_en.png index c75b2423f4..c348dbf3f1 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f925391dfd47629c15b31fe644eb66e43029064aebf73bc7136aa29763cef0a -size 31836 +oid sha256:97bafddc5171cf429509e5b16c830460618ef098f78d305658b9f6570fdaec7a +size 34352 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_11_en.png index 121bf9f0b9..4229af3c83 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:53f3893f337b42d43d85357bc0d779a8bcd7ad51444bd318e885fe01f91392ee -size 36089 +oid sha256:6dfa61ab6f72bd468584dfe25f6aa292269911ef5e31ce6df5e408928be763e9 +size 35025 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_12_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_12_en.png index 1d95060de3..4229af3c83 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:441bab6f8b50f584cc21d3478872b317995f8547e6078527b69fe3efaff3066e -size 50596 +oid sha256:6dfa61ab6f72bd468584dfe25f6aa292269911ef5e31ce6df5e408928be763e9 +size 35025 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_13_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_13_en.png index 720481971d..becf761678 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5e6a4cba7bd3131c04814ab35ca28c1229ff3d3f6dca66073ac73ee29b00300 -size 41197 +oid sha256:96e4fc0848f1e59b0146e0f16287f095155216f22112c0aa856d96bee652146a +size 54887 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_14_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_14_en.png new file mode 100644 index 0000000000..19677cca49 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_14_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a87d5a93849b1395168b8d9f9f3478b62c3219ef8baf6064ce0e321db60823c5 +size 53363 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_15_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_15_en.png new file mode 100644 index 0000000000..4260a1d3b8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_15_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:650ace49ddbd467c3051b29a847f37285c1b011faa24e5280106e4c626ad645a +size 44484 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_16_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_16_en.png new file mode 100644 index 0000000000..1160437cf7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_16_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cfa63bbf8f36cffee2950bbc2c71f0d9a0d599ae21cf41a20fa0e6f9b3183af3 +size 53824 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_17_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_17_en.png new file mode 100644 index 0000000000..f1b4a6cb20 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_17_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:352ded021ffca52ccf5cb947d002da44e2d9d06c0a68f33784c65f35be1d5a4f +size 38744 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_1_en.png index 05465833c4..4229af3c83 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad59cb4b2f8d4acb93f71d882ddac39db538bce882e19fc711331550429a2692 -size 32862 +oid sha256:6dfa61ab6f72bd468584dfe25f6aa292269911ef5e31ce6df5e408928be763e9 +size 35025 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_2_en.png index 7a52909e96..63d833fed9 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f03f68f065c52756b71e08fac668a263adaaf288bfdb8e4567848ce93540222 -size 32879 +oid sha256:104d3fe72dcb590e7d452e65b9558f20df3dfad898cfcbab042e6d075c656eb8 +size 35550 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_3_en.png index f6c05d64d5..b0dc4b044f 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1e1947a0bb9a7cc951e272952a7811867cda7a9b64aed0fd994ee14bd309f9e -size 38166 +oid sha256:807cb5c65c62b5a628ad8719930aecebb03bab9d4d2565aa016a8bb4d23cef58 +size 34593 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_4_en.png index 4521a91e16..c348dbf3f1 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0edb47639a0d1338fa5983ab31867a96532dcc174d4d1f6bf7cf646a135fca9f -size 30240 +oid sha256:97bafddc5171cf429509e5b16c830460618ef098f78d305658b9f6570fdaec7a +size 34352 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_5_en.png index 05465833c4..4229af3c83 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad59cb4b2f8d4acb93f71d882ddac39db538bce882e19fc711331550429a2692 -size 32862 +oid sha256:6dfa61ab6f72bd468584dfe25f6aa292269911ef5e31ce6df5e408928be763e9 +size 35025 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_6_en.png index 05465833c4..ea9eaf039d 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad59cb4b2f8d4acb93f71d882ddac39db538bce882e19fc711331550429a2692 -size 32862 +oid sha256:4e9940713041c9078087df7e7ceaa430d96ca1c199f4cdc22c705a73539c84a3 +size 32717 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_7_en.png index 05465833c4..4229af3c83 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad59cb4b2f8d4acb93f71d882ddac39db538bce882e19fc711331550429a2692 -size 32862 +oid sha256:6dfa61ab6f72bd468584dfe25f6aa292269911ef5e31ce6df5e408928be763e9 +size 35025 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_8_en.png index 00e952c6ff..4229af3c83 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8983c85f207fea3ff7d66eb9128dc0c6404d24feae1e102d981e0398414f663 -size 31842 +oid sha256:6dfa61ab6f72bd468584dfe25f6aa292269911ef5e31ce6df5e408928be763e9 +size 35025 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_9_en.png index 05465833c4..4229af3c83 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.root_SecureBackupRootView_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad59cb4b2f8d4acb93f71d882ddac39db538bce882e19fc711331550429a2692 -size 32862 +oid sha256:6dfa61ab6f72bd468584dfe25f6aa292269911ef5e31ce6df5e408928be763e9 +size 35025 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en.png index e847dc4380..b1ddc8db95 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3954184d0a03b4a4c2d396e108a4c939d2d07e54eaa59bd886c4b884f423be38 -size 16658 +oid sha256:76aa1a5ebc4d4700b9c17bd96a860ec66f0f0290174f60bbff421d0621548d43 +size 16369 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en.png index 1f09073860..8ac2360af9 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb810bb206a376f1ce0d5c8f13cbcfc6e160a27bbbba01cadb39e217683b47c7 -size 14426 +oid sha256:5bcd30abc12ac1febb2fd694617d9fe1bd68ff7a193cc05eacc15d1506f3db8e +size 14129 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en.png index b200bc313b..425c102017 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72ffcd02d6e56b3d84ddff56e67bf082cf6867d970e5c90e4c65d7214efc1055 -size 16850 +oid sha256:a05ec1dbaa4efe16276ef9e284e7eaac194d4ca060524f2fd116465d4f4a5b19 +size 16563 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en.png index 1f09073860..8ac2360af9 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb810bb206a376f1ce0d5c8f13cbcfc6e160a27bbbba01cadb39e217683b47c7 -size 14426 +oid sha256:5bcd30abc12ac1febb2fd694617d9fe1bd68ff7a193cc05eacc15d1506f3db8e +size 14129 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en.png index 3e4f3140b7..6d04cbba5b 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ec24e0c0c8ef348b79fca7c884fc04d2959ba3eb68cf86673050e6af5ef2513 -size 16157 +oid sha256:78fcd3ba4aca3e25a7d183cb83286bd0ba805f48796db034207e60a99dcb20c6 +size 15810 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en.png index d1041f1751..55df3a686a 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:319f3ca5dadb404427e6d87046321d7b38bd7b31a085e74d401ede564c083e0f -size 13980 +oid sha256:400700ea092e1182d9774f0f717df1085f129fd6c37765d09dff762ffeed9e66 +size 13626 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en.png index abab10af9b..4bf8a8087d 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df0826b4c857daef4fe641b2dbe52afce3c3e02c74c25d1adbaa7231525c6cae -size 16381 +oid sha256:4194ce2cc2259d89ebfaa7dd220bb6ecedf3c0e635a1610d99c2bb2302254ff5 +size 16029 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en.png index d1041f1751..55df3a686a 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:319f3ca5dadb404427e6d87046321d7b38bd7b31a085e74d401ede564c083e0f -size 13980 +oid sha256:400700ea092e1182d9774f0f717df1085f129fd6c37765d09dff762ffeed9e66 +size 13626 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en.png index 549e6de403..8cb4366256 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0837cd46f284fae8d3f6a929b52eb5d7a27f43af7b38ac002a5a36c454998934 -size 43133 +oid sha256:95f32675a4220b75fd09530db3f97dad1da0b6a5bbc437b740db1da36481de4b +size 42617 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en.png index 202ea9dfe4..da4b250916 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:63241cb82588d2d3fe27a19de265fb5b0cf5e7d812a783e931ac235df3c64412 -size 40885 +oid sha256:6901a2702035fc9cbb9e2047844c7644556bee72145f533c41f2cb2cbe166a35 +size 40334 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en.png index 67e0439241..9782068f8c 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:000430ba03935ea53fef3c76205d7ea73c72aecc3951974fa67c77235bfaf1a8 -size 41940 +oid sha256:3dfb18ea5f87f3e8d58c4555a42bbba10b7d4525131c8b40b135a65428a84f09 +size 41465 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en.png index db50cc02a0..a247b43508 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39adceef0bb8ff4ea6386c778f1888d1f1b8f5b3b1f845edab865702a11477de -size 39671 +oid sha256:41c4860deea7a74984b997c0f2489bb6aa9f0225f91ea97d3beda42f385e926b +size 39184 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en.png index c2e9b21537..81b37b2e72 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d890b06789242d9794b168722907c26370a088162d9e435e1a0d3b21a57b01df -size 44451 +oid sha256:46554d24e2ccb5a655be40bc79909d2bb3fd04d9fcf914fb8839d915587fff47 +size 44091 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en.png index 89b4854c63..231778f22f 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:199e7e41b64c3b16c9ed15de70482babeecffbfe048c75a4740844de7fce0284 -size 42273 +oid sha256:e925e2547d3d8cb7322897c0a242cc92e658e9dc1b5fa8e5bbc4c3fc3597e4a5 +size 41906 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en.png index 260ef0100b..d003e9a1da 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e53f63a92a5692f395615dba771b0a688a1693e7b715866667a6945d1e349f5d -size 43176 +oid sha256:e6e8254736a5a4cdd8e8c52c4b51906700f9c2bff5d656a6797ca3400008cf50 +size 42927 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en.png index 5a2ba55bee..de6c73827c 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4637c3b2e4c171389ceabd36a53af376cab9f5f4fe8f99fcc535dff34acf45f9 -size 41088 +oid sha256:634a2ed76e3b115bb6175fe2801cbef3fb29c284f139a8577c201d1cdea2fc03 +size 40820 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_0_en.png index 1463fe4516..7abc6e9d3f 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5641f60c900c49dfccadfff390fe2e3a72c80e7bb4bbcdecac9a8af97a01c3a -size 28709 +oid sha256:6312b78386b2fef7ba615af754688053c3ab9abcdb441fbb824307694e8af99f +size 28945 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_1_en.png index bac9b2aa0b..35fec3118e 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:049980e2859db4dd266e4162f3859f129d7552ab0894ee88e3a3cf628e6fc606 -size 28993 +oid sha256:3cc6276ebb7179bfd9c8fe56716f286d57967c1e4d75e7265bef04e990ad32fa +size 26945 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_6_en.png index d37f5c5432..4203c40a0b 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6fdfaa160bbee0b300422392fcd7f335db78864a03d92238eb6843fc2e50b7d1 -size 26417 +oid sha256:4ff5e674b594c87a4ac6c1d1f296a1225d251e8a5882521ec02880dced28981a +size 26545 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_7_en.png index e68b81fd36..6d59710436 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c33ceaeae591f4f00a1efc71b2fd469b0dabaef6449ef126acb72300b4ae8551 -size 29665 +oid sha256:cd5a1deaa6f393b9cced7404a4a6ead9ffc346e8a08428fb9ae76a1a3244d400 +size 29916 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_0_en.png index fd6f9cd4fa..20083453f5 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6bfd432edf40bc901959d8209dd500b5102827376fd0d47fe5090882476bf5d5 -size 27771 +oid sha256:da14f6bed942b8a6e464e07ec6d29891749ca8093f5cda20cc7e304205cfce26 +size 28022 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_1_en.png index 5793b79841..1306d4e00b 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08e46fd726c936a781d3ab049c4c60e080c673498ce54a3f956fce42d8ef7b90 -size 28135 +oid sha256:a066ee27ec789680aca81ae3e43a67e7d96d6320f59da9b5e131d07ccc7b3dd3 +size 26046 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_6_en.png index 00a087a240..467ddef32c 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fca592210633e752cccfbcac3373acdd0ca55277fce9473b0e1b7db32ea19a04 -size 24208 +oid sha256:66be60cf12dc3ee2f5eb8c7341759504ed890058d93d2085c9df87ae04cf5f20 +size 24357 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_7_en.png index 0cd496c1f1..5234341ca3 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6058bb4b845ba2a174c1d35baa0e44604ec14e1d618b9a66f6825a58da81e61a -size 28649 +oid sha256:138b17fb048934acb059487edc542ef007fbd4c80bcc958dddf1f933b66340b9 +size 28892 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en.png new file mode 100644 index 0000000000..06fb695f99 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f18cc4f6c04a59e1a5d51a091efe66bfa7525e2fe229302174fe40fefa00cc28 +size 14026 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en.png new file mode 100644 index 0000000000..eaa3b34f51 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d82fea268b5a9271497a3b6518a88c757e6ba6f62f4a8990491351509383858a +size 13974 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en.png new file mode 100644 index 0000000000..8928f95830 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f781e977784d8bfb1bd84947519f31e28106cdc036c6dedc406be92fdbbc0c54 +size 40077 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en.png new file mode 100644 index 0000000000..f55850de1d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bea78fb1bb813bedce30e5b13257892bcc23a7d6eb1a404d24e764337568d6cd +size 41596 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_2_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en.png new file mode 100644 index 0000000000..21f135beca --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5d8163be5e84aa851df9666822a4d9bc7a40d3a99d6fa8498243c256b729d58f +size 44724 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_6_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en.png new file mode 100644 index 0000000000..77fe855b76 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81c559a9661b3fdccc8deb444b897f9a45b33de0d4d58367ff021f92202a17b6 +size 21883 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en.png new file mode 100644 index 0000000000..1c316959ed --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d3340a2d29e6c1d86f5ec5664179cae9501fcc95381311174d7d6b45b15af326 +size 24123 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en.png new file mode 100644 index 0000000000..1c316959ed --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d3340a2d29e6c1d86f5ec5664179cae9501fcc95381311174d7d6b45b15af326 +size 24123 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en.png new file mode 100644 index 0000000000..22a0da4353 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:68b70ae5220e244acdfa3f1a2e36089c1994e8f05eeb6c344b4734858540e55c +size 38942 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en.png new file mode 100644 index 0000000000..4731f5a297 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:db8b048d11f766a8e3db28bac99f3e8dea34e6f37fba1eacd9e6f98f6127462b +size 40383 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_2_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en.png new file mode 100644 index 0000000000..4a519862f3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1098ef994ad9552aca2f9ad9efe7b1239133544e561e73eced6fa96d08eaa3b +size 43785 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_6_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en.png new file mode 100644 index 0000000000..77082fb4bd --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8565e90948aa18f04e1b868467a007d68fe3d18d534289d5c4d59d4b38915585 +size 21190 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en.png new file mode 100644 index 0000000000..1c46f8eb53 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6c7e8cdf40bdf018931635565ac649fed518140a047a71cf70cf63e9edf54d3 +size 23932 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en.png new file mode 100644 index 0000000000..1c46f8eb53 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6c7e8cdf40bdf018931635565ac649fed518140a047a71cf70cf63e9edf54d3 +size 23932 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_0_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_10_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_10_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_10_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_11_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_11_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_11_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_12_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_12_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_12_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_12_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en.png new file mode 100644 index 0000000000..c13d05d4b3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:104d1d386398aac789420babf33755e03d163f40ae596cd8d24f35da0363fac6 +size 30952 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png new file mode 100644 index 0000000000..2f77b586a2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:145c85570217621cc576f305ed72e27205381c438e434205e07b7363cfd67f04 +size 30069 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_en.png new file mode 100644 index 0000000000..3161e69ab7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c238637a0a3107cfcd98230ac52fad7779d8c260b0b97bf30d231cd5493a944a +size 46453 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_en.png new file mode 100644 index 0000000000..21f135beca --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5d8163be5e84aa851df9666822a4d9bc7a40d3a99d6fa8498243c256b729d58f +size 44724 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png new file mode 100644 index 0000000000..6a6ac768f0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:781d869dff205f99d5a9bc9986abbe489bef24551e6fb695280541741895a508 +size 24238 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_5_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_5_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_5_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_en.png new file mode 100644 index 0000000000..7e3f0d4fb7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a25f85925a38d02a65af4fb342949e9545d3e5e19012885f2c3aee4caa8b8f7d +size 31535 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_7_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_7_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_7_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_8_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_8_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_8_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_9_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_9_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_9_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_0_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_10_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_10_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_10_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_11_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_11_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_11_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_12_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_12_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_12_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_12_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en.png new file mode 100644 index 0000000000..36c7cdb859 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85b92d23ab2f690c3c15ded7f14c513371cd36482fbeda1478f62c82650f1829 +size 30274 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en.png new file mode 100644 index 0000000000..49a47a746b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b848d50320f1494c9d0edbc21bddcc773de0cf9b1c36ce034235fd3fbbd77cf1 +size 29210 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_2_en.png new file mode 100644 index 0000000000..21dbb7d45d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fc4cbb2bea7749bda9f6c5647ff178717711bad5b1ee25621155bccb1e5f2328 +size 45528 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_3_en.png new file mode 100644 index 0000000000..4a519862f3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1098ef994ad9552aca2f9ad9efe7b1239133544e561e73eced6fa96d08eaa3b +size 43785 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en.png new file mode 100644 index 0000000000..7ff37748ea --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5931dfac0337cf856282d9c62983aa185fe208f2926868097238d5b95e222fe6 +size 23953 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_5_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_5_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_5_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_6_en.png new file mode 100644 index 0000000000..9768e763ee --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9ff1487c8c63a476a570d271f7a0b00c8990e1f90d49a2cae5056a9cc7e51e0e +size 30765 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_7_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_7_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_7_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_8_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_8_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_8_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_9_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_9_en.png rename to tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_9_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_1_en.png deleted file mode 100644 index 130cc1d84a..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_1_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2cc647747c41f4c2cf96a3425e8b279b39fd4e09e1441e5b5afcd260f79afaa1 -size 22714 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_3_en.png deleted file mode 100644 index 528c702f24..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_3_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c5435926f4a54e99902b78881c59d9d816110e401bcb92baf3b2fca844338f31 -size 48182 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_4_en.png deleted file mode 100644 index 4e84bcb0c2..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_4_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a7fe45575eb8423161d355d9b99ecff4acc0f708e5b106d9d38aa2657942d736 -size 28219 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_1_en.png deleted file mode 100644 index 7e3a113610..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_1_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:786a63d0d824657270b44428a1a251d0d521e6a97a4239516ae5f2eff06fdc3b -size 22125 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_3_en.png deleted file mode 100644 index 79198e05f9..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_3_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a6c90e5be60738ded8312822f95279aa76d3ec8d86265164f4e3e31dbcce61c5 -size 47355 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_4_en.png deleted file mode 100644 index 76173e1b2d..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_4_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c29d3f5bbff739d23800345feb42e73fe9374c5cb5db27690278fb22c5546fe3 -size 27754 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Day_0_en.png index 5c013af5af..6dec0aaedc 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1ee4058e23cff90b2998a7d754382862ef52c8a54dba886fc5c2ac221082e41 -size 66839 +oid sha256:5066c1271b008a9243c824e69dab0a398215b24636e29b85205477c2d532e684 +size 75714 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Night_0_en.png index ce0f82bb64..238187cdf6 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:25a3994beb58067031dec92193f87abd95510ee2ee0eef26f95fb218cdd03b42 -size 45043 +oid sha256:c3244ca7403cf807923385c33c120d6019f2aba62ed47d518531e3be14275dbd +size 47733 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLarge_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLarge_Day_0_en.png index 02fe526810..3125e0ae83 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLarge_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLarge_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1095107fc5951560298f5ac5c9122492896bb4094e06e377562107df0e26cbd7 -size 67459 +oid sha256:e6605ba4a222e94256f3d075901722be8d387333982f9b2f6d1a2a5b47819ad2 +size 75607 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLarge_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLarge_Night_0_en.png index 039445fc38..0555b64e22 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLarge_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomLarge_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5616680d4d1983825f9cf627d33f8d41d2a4453a08fbab78e88a18a9f095816 -size 55128 +oid sha256:2a00e9f3fcc7ae2b0b6c10584672f4c8da105fab40e51ca04b5f1613465bacb1 +size 57363 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Day_0_en.png index ef4af78c12..201eb8487c 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8207f1e573df09d6b120dd414dc39e2f79c1b402bab0a40f0988e1bb08254cc3 -size 46109 +oid sha256:1efd58a346d4941edeff3195ca6f5ce4f623969139a2759a99767cabbe7a024d +size 53710 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Night_0_en.png index e6cfa2d18e..3c9c4bd2b5 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72157bdf8f567debd2a527e9a7af9bdf51a621b5d53450f0579fd5c787efe022 -size 30554 +oid sha256:f34056ac9cb7825fb60b988908e372c506fa9c82f08fcb8bb4df103a7a092a72 +size 32995 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Day_0_en.png index 450248e2c3..e72bf4687e 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b16119db717c4eb85e4235c80b99c701c4760c02974d28d1c19fae079d1bc16 -size 42876 +oid sha256:4850355731d07cfedd4a110f79c8ff638c4aa775a585460bc485e9d8fee7bac7 +size 50921 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Night_0_en.png index 688d3275c2..78d35b150a 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4a8790793249cbd9ff7deaf36cde13e5719b50138633ab779a0ac2855f6380c -size 31693 +oid sha256:c56f678c9150545f18135028359465cad77ccbac078feada0d91149ed7e93913 +size 34069 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorFailure_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorFailure_Day_0_en.png index 0f892372f3..2efaead7fd 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorFailure_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorFailure_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bad0a18d7f8ec666303ad73bb5bfb9f0a57b5c5811bfe96899510c5138ce0c75 -size 7583 +oid sha256:c198550294e9e70f48a73081cb49e7d4cedacf46416ff5241aece38456a2001b +size 7567 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorFailure_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorFailure_Night_0_en.png index 575875bc19..b631f8c18f 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorFailure_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorFailure_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ffbf1dd50c1e9dbf2a1e164d5b669d4adfca7ea443049eec6692b5394ca002a -size 6349 +oid sha256:855f301f12299f1e2b788fa140562f90afbc3e7950752b2a145664122cabf290 +size 6377 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorLoading_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorLoading_Day_0_en.png index b4e3d5ad80..505f216784 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorLoading_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorLoading_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c17faa23172593de79f46d7887eef968d26686ae531bb2069ecd37a8280ae13 -size 8066 +oid sha256:0575cab729880d73cc389fde5ee1a070f70aa1b529a3f94823cc46c5c34273f5 +size 8088 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorLoading_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorLoading_Night_0_en.png index 4f5875a6e6..bfa94be68b 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorLoading_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.async_AsyncIndicatorLoading_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bbb8c8f4f1e12d62eaa0a08ca7bc4be62f9711888a05f3c40fa1e50501d0afaf -size 6851 +oid sha256:c5774d045f36aa019cce3b75993a33520b35ddd0f24aeff2055b456d6ab7c117 +size 6864 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelectedTrailingContent_Multiple selection List item - selection in trailing content_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelectedTrailingContent_Multiple_selection_List_item_-_selection_in_trailing_content_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelectedTrailingContent_Multiple selection List item - selection in trailing content_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelectedTrailingContent_Multiple_selection_List_item_-_selection_in_trailing_content_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple selection List item - selection in supporting text_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple_selection_List_item_-_selection_in_supporting_text_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple selection List item - selection in supporting text_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple_selection_List_item_-_selection_in_supporting_text_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItem_Multiple selection List item - no selection_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItem_Multiple_selection_List_item_-_no_selection_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItem_Multiple selection List item - no selection_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItem_Multiple_selection_List_item_-_no_selection_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemCustomFormattert_Single selection List item - custom formatter_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemCustomFormattert_Single_selection_List_item_-_custom_formatter_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemCustomFormattert_Single selection List item - custom formatter_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemCustomFormattert_Single_selection_List_item_-_custom_formatter_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInSupportingText_Single selection List item - selection in supporting text_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInSupportingText_Single_selection_List_item_-_selection_in_supporting_text_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInSupportingText_Single selection List item - selection in supporting text_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInSupportingText_Single_selection_List_item_-_selection_in_supporting_text_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInTrailingContent_Single selection List item - selection in trailing content_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInTrailingContent_Single_selection_List_item_-_selection_in_trailing_content_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInTrailingContent_Single selection List item - selection in trailing content_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInTrailingContent_Single_selection_List_item_-_selection_in_trailing_content_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemUnselectedWithSupportingText_Single selection List item - no selection, supporting text_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemUnselectedWithSupportingText_Single_selection_List_item_-_no_selection,_supporting_text_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemUnselectedWithSupportingText_Single selection List item - no selection, supporting text_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemUnselectedWithSupportingText_Single_selection_List_item_-_no_selection,_supporting_text_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItem_Single selection List item - no selection_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItem_Single_selection_List_item_-_no_selection_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItem_Single selection List item - no selection_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItem_Single_selection_List_item_-_no_selection_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemEmpty_Text field List item - empty_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemEmpty_Text field List item - empty_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text field List item - textfieldvalue_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text_field_List_item_-_textfieldvalue_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text field List item - textfieldvalue_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text_field_List_item_-_textfieldvalue_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItem_Text field List item - text_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItem_Text_field_List_item_-_text_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItem_Text field List item - text_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItem_Text_field_List_item_-_text_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigCheckmark_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigCheckmark_Day_0_en.png index c8d644ca8f..abe90d96b7 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigCheckmark_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigCheckmark_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:63c257c76c3034a49214715dd1f7ee116fe0d5541e00e9dd13ba03bf0eb8f2e8 -size 8593 +oid sha256:203f7ee722f1dcc9b16d0bf242d7ffb68fbbd1e850e38d09c759dd45b1baeb71 +size 8565 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigCheckmark_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigCheckmark_Night_0_en.png index eae3696587..7c8925a175 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigCheckmark_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigCheckmark_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2ad1563cf11a4f125c1df77d69e43faf4c096e02754709e911bdf8274124750 -size 7225 +oid sha256:cd5f27868d2eb8740fc84e1be39ed511fb89278adfd6232789a8abfcd7d6f40c +size 7214 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Day_0_en.png index 4f8bcc5486..084aff9841 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b43889f07930a9dec3e2f08099963c9b59d3e9886380869f7b4f9bb8e5401a2e -size 65515 +oid sha256:77004b5d420cc5fe51f98b66033ed0d68cb9c308104fcdc281a0f06a6ece0fed +size 46964 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Night_0_en.png index 32ca554ddb..601c55c1b3 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64bfd3b1c905e3061e809a8e6a2f1bcf9ac0541f79e8a1e5ecdb10ec9aab8454 -size 59443 +oid sha256:3467dc3bb6ed59048297d12badde6ab1748c19ff755809d4a14cf47e6c81ed9b +size 50973 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerDark_DateTime pickers_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerDark_DateTime pickers_en.png deleted file mode 100644 index b066d061a0..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerDark_DateTime pickers_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1d26d3a7f998e497105102f860e29762795fd4dcf91172cea1eae23c19bcdb05 -size 30604 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en.png new file mode 100644 index 0000000000..37b234801f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea4f3ed733159e0413383d8994943b5b7b3100160728138b7014a9567f665021 +size 30632 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerLight_DateTime pickers_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerLight_DateTime pickers_en.png deleted file mode 100644 index 88435161f2..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerLight_DateTime pickers_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:59e4995458c53d5156003bfc2ded6c30f2eb545390a47c7a490f5dc617e2937a -size 31019 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en.png new file mode 100644 index 0000000000..f957752384 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:657c14f3e42751aefbac5fc0b7d94462d2b90da1707ab7ce7997f5db1b29c6d2 +size 31013 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime pickers_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime pickers_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime pickers_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime pickers_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime pickers_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime pickers_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_CircularProgressIndicator_Progress Indicators_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_CircularProgressIndicator_Progress_Indicators_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_CircularProgressIndicator_Progress Indicators_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_CircularProgressIndicator_Progress_Indicators_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithDestructiveButton_Dialog with destructive button_Dialogs_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithDestructiveButton_Dialog_with_destructive_button_Dialogs_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithDestructiveButton_Dialog with destructive button_Dialogs_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithDestructiveButton_Dialog_with_destructive_button_Dialogs_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithOnlyMessageAndOkButton_Dialog with only message and ok button_Dialogs_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithOnlyMessageAndOkButton_Dialog_with_only_message_and_ok_button_Dialogs_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithOnlyMessageAndOkButton_Dialog with only message and ok button_Dialogs_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithOnlyMessageAndOkButton_Dialog_with_only_message_and_ok_button_Dialogs_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithThirdButton_Dialog with third button_Dialogs_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithThirdButton_Dialog_with_third_button_Dialogs_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithThirdButton_Dialog with third button_Dialogs_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithThirdButton_Dialog_with_third_button_Dialogs_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleAndOkButton_Dialog with title and ok button_Dialogs_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleAndOkButton_Dialog_with_title_and_ok_button_Dialogs_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleAndOkButton_Dialog with title and ok button_Dialogs_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleAndOkButton_Dialog_with_title_and_ok_button_Dialogs_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleIconAndOkButton_Dialog with title, icon and ok button_Dialogs_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleIconAndOkButton_Dialog_with_title,_icon_and_ok_button_Dialogs_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleIconAndOkButton_Dialog with title, icon and ok button_Dialogs_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleIconAndOkButton_Dialog_with_title,_icon_and_ok_button_Dialogs_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_FloatingActionButton_Floating Action Buttons_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_FloatingActionButton_Floating Action Buttons_en.png deleted file mode 100644 index 2641a0c3ba..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_FloatingActionButton_Floating Action Buttons_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:29360502072e71b0b3673b5b76a7a5a3a1582eb0778bafe99bc6af9ef104946b -size 9822 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_FloatingActionButton_Floating_Action_Buttons_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_FloatingActionButton_Floating_Action_Buttons_en.png new file mode 100644 index 0000000000..6adb0ad3aa --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_FloatingActionButton_Floating_Action_Buttons_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c4e8b0d14760c7fa9308861a7050ba715e04b36d161f63d24713c3eb2e7100c +size 9798 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_LinearProgressIndicator_Progress Indicators_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_LinearProgressIndicator_Progress Indicators_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List item - Primary action & Icon_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List_item_-_Primary_action_&_Icon_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List item - Primary action & Icon_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List_item_-_Primary_action_&_Icon_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineBothIcons_List item (1 line) - Both Icons_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineBothIcons_List_item_(1_line)_-_Both_Icons_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineBothIcons_List item (1 line) - Both Icons_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineBothIcons_List_item_(1_line)_-_Both_Icons_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingCheckbox_List item (1 line) - Leading Checkbox_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingCheckbox_List_item_(1_line)_-_Leading_Checkbox_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingCheckbox_List item (1 line) - Leading Checkbox_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingCheckbox_List_item_(1_line)_-_Leading_Checkbox_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingIcon_List item (1 line) - Leading Icon_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingIcon_List_item_(1_line)_-_Leading_Icon_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingIcon_List item (1 line) - Leading Icon_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingIcon_List_item_(1_line)_-_Leading_Icon_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingRadioButton_List item (1 line) - Leading RadioButton_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingRadioButton_List_item_(1_line)_-_Leading_RadioButton_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingRadioButton_List item (1 line) - Leading RadioButton_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingRadioButton_List_item_(1_line)_-_Leading_RadioButton_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingSwitch_List item (1 line) - Leading Switch_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingSwitch_List_item_(1_line)_-_Leading_Switch_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingSwitch_List item (1 line) - Leading Switch_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingSwitch_List_item_(1_line)_-_Leading_Switch_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineSimple_List item (1 line) - Simple_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineSimple_List_item_(1_line)_-_Simple_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineSimple_List item (1 line) - Simple_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineSimple_List_item_(1_line)_-_Simple_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingCheckBox_List item (1 line) - Trailing Checkbox_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingCheckBox_List_item_(1_line)_-_Trailing_Checkbox_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingCheckBox_List item (1 line) - Trailing Checkbox_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingCheckBox_List_item_(1_line)_-_Trailing_Checkbox_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingIcon_List item (1 line) - Trailing Icon_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingIcon_List_item_(1_line)_-_Trailing_Icon_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingIcon_List item (1 line) - Trailing Icon_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingIcon_List_item_(1_line)_-_Trailing_Icon_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingRadioButton_List item (1 line) - Trailing RadioButton_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingRadioButton_List_item_(1_line)_-_Trailing_RadioButton_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingRadioButton_List item (1 line) - Trailing RadioButton_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingRadioButton_List_item_(1_line)_-_Trailing_RadioButton_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingSwitch_List item (1 line) - Trailing Switch_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingSwitch_List_item_(1_line)_-_Trailing_Switch_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingSwitch_List item (1 line) - Trailing Switch_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingSwitch_List_item_(1_line)_-_Trailing_Switch_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesBothIcons_List item (3 lines) - Both Icons_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesBothIcons_List_item_(3_lines)_-_Both_Icons_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesBothIcons_List item (3 lines) - Both Icons_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesBothIcons_List_item_(3_lines)_-_Both_Icons_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingCheckbox_List item (3 lines) - Leading Checkbox_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingCheckbox_List_item_(3_lines)_-_Leading_Checkbox_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingCheckbox_List item (3 lines) - Leading Checkbox_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingCheckbox_List_item_(3_lines)_-_Leading_Checkbox_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingIcon_List item (3 lines) - Leading Icon_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingIcon_List_item_(3_lines)_-_Leading_Icon_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingIcon_List item (3 lines) - Leading Icon_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingIcon_List_item_(3_lines)_-_Leading_Icon_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingRadioButton_List item (3 lines) - Leading RadioButton_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingRadioButton_List_item_(3_lines)_-_Leading_RadioButton_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingRadioButton_List item (3 lines) - Leading RadioButton_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingRadioButton_List_item_(3_lines)_-_Leading_RadioButton_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingSwitch_List item (3 lines) - Leading Switch_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingSwitch_List_item_(3_lines)_-_Leading_Switch_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingSwitch_List item (3 lines) - Leading Switch_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingSwitch_List_item_(3_lines)_-_Leading_Switch_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesSimple_List item (3 lines) - Simple_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesSimple_List_item_(3_lines)_-_Simple_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesSimple_List item (3 lines) - Simple_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesSimple_List_item_(3_lines)_-_Simple_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingCheckBox_List item (3 lines) - Trailing Checkbox_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingCheckBox_List_item_(3_lines)_-_Trailing_Checkbox_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingCheckBox_List item (3 lines) - Trailing Checkbox_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingCheckBox_List_item_(3_lines)_-_Trailing_Checkbox_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingIcon_List item (3 lines) - Trailing Icon_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingIcon_List_item_(3_lines)_-_Trailing_Icon_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingIcon_List item (3 lines) - Trailing Icon_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingIcon_List_item_(3_lines)_-_Trailing_Icon_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingRadioButton_List item (3 lines) - Trailing RadioButton_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingRadioButton_List_item_(3_lines)_-_Trailing_RadioButton_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingRadioButton_List item (3 lines) - Trailing RadioButton_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingRadioButton_List_item_(3_lines)_-_Trailing_RadioButton_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingSwitch_List item (3 lines) - Trailing Switch_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingSwitch_List_item_(3_lines)_-_Trailing_Switch_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingSwitch_List item (3 lines) - Trailing Switch_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingSwitch_List_item_(3_lines)_-_Trailing_Switch_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIconsError_List item (2 lines) - Both Icons - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIconsError_List_item_(2_lines)_-_Both_Icons_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIconsError_List item (2 lines) - Both Icons - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIconsError_List_item_(2_lines)_-_Both_Icons_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIcons_List item (2 lines) - Both Icons_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIcons_List_item_(2_lines)_-_Both_Icons_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIcons_List item (2 lines) - Both Icons_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIcons_List_item_(2_lines)_-_Both_Icons_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckboxError_List item (2 lines) - Leading Checkbox - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckboxError_List_item_(2_lines)_-_Leading_Checkbox_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckboxError_List item (2 lines) - Leading Checkbox - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckboxError_List_item_(2_lines)_-_Leading_Checkbox_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckbox_List item (2 lines) - Leading Checkbox_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckbox_List_item_(2_lines)_-_Leading_Checkbox_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckbox_List item (2 lines) - Leading Checkbox_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckbox_List_item_(2_lines)_-_Leading_Checkbox_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIconError_List item (2 lines) - Leading Icon - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIconError_List_item_(2_lines)_-_Leading_Icon_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIconError_List item (2 lines) - Leading Icon - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIconError_List_item_(2_lines)_-_Leading_Icon_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIcon_List item (2 lines) - Leading Icon_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIcon_List_item_(2_lines)_-_Leading_Icon_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIcon_List item (2 lines) - Leading Icon_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIcon_List_item_(2_lines)_-_Leading_Icon_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButtonError_List item (2 lines) - Leading RadioButton - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButtonError_List_item_(2_lines)_-_Leading_RadioButton_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButtonError_List item (2 lines) - Leading RadioButton - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButtonError_List_item_(2_lines)_-_Leading_RadioButton_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButton_List item (2 lines) - Leading RadioButton_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButton_List_item_(2_lines)_-_Leading_RadioButton_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButton_List item (2 lines) - Leading RadioButton_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButton_List_item_(2_lines)_-_Leading_RadioButton_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitchError_List item (2 lines) - Leading Switch - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitchError_List_item_(2_lines)_-_Leading_Switch_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitchError_List item (2 lines) - Leading Switch - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitchError_List_item_(2_lines)_-_Leading_Switch_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitch_List item (2 lines) - Leading Switch_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitch_List_item_(2_lines)_-_Leading_Switch_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitch_List item (2 lines) - Leading Switch_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitch_List_item_(2_lines)_-_Leading_Switch_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimpleError_List item (2 lines) - Simple - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimpleError_List_item_(2_lines)_-_Simple_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimpleError_List item (2 lines) - Simple - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimpleError_List_item_(2_lines)_-_Simple_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimple_List item (2 lines) - Simple_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimple_List_item_(2_lines)_-_Simple_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimple_List item (2 lines) - Simple_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimple_List_item_(2_lines)_-_Simple_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBoxError_List item (2 lines) - Trailing Checkbox - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBoxError_List_item_(2_lines)_-_Trailing_Checkbox_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBoxError_List item (2 lines) - Trailing Checkbox - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBoxError_List_item_(2_lines)_-_Trailing_Checkbox_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBox_List item (2 lines) - Trailing Checkbox_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBox_List_item_(2_lines)_-_Trailing_Checkbox_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBox_List item (2 lines) - Trailing Checkbox_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBox_List_item_(2_lines)_-_Trailing_Checkbox_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIconError_List item (2 lines) - Trailing Icon - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIconError_List_item_(2_lines)_-_Trailing_Icon_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIconError_List item (2 lines) - Trailing Icon - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIconError_List_item_(2_lines)_-_Trailing_Icon_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIcon_List item (2 lines) - Trailing Icon_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIcon_List_item_(2_lines)_-_Trailing_Icon_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIcon_List item (2 lines) - Trailing Icon_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIcon_List_item_(2_lines)_-_Trailing_Icon_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButtonError_List item (2 lines) - Trailing RadioButton - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButtonError_List_item_(2_lines)_-_Trailing_RadioButton_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButtonError_List item (2 lines) - Trailing RadioButton - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButtonError_List_item_(2_lines)_-_Trailing_RadioButton_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButton_List item (2 lines) - Trailing RadioButton_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButton_List_item_(2_lines)_-_Trailing_RadioButton_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButton_List item (2 lines) - Trailing RadioButton_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButton_List_item_(2_lines)_-_Trailing_RadioButton_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitchError_List item (2 lines) - Trailing Switch - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitchError_List_item_(2_lines)_-_Trailing_Switch_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitchError_List item (2 lines) - Trailing Switch - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitchError_List_item_(2_lines)_-_Trailing_Switch_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitch_List item (2 lines) - Trailing Switch_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitch_List_item_(2_lines)_-_Trailing_Switch_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitch_List item (2 lines) - Trailing Switch_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitch_List_item_(2_lines)_-_Trailing_Switch_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescriptionAndDivider_List section header with description and divider_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescriptionAndDivider_List_section_header_with_description_and_divider_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescriptionAndDivider_List section header with description and divider_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescriptionAndDivider_List_section_header_with_description_and_divider_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescription_List section header with description_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescription_List_section_header_with_description_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescription_List section header with description_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescription_List_section_header_with_description_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDivider_List section header with divider_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDivider_List_section_header_with_divider_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDivider_List section header with divider_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDivider_List_section_header_with_divider_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeader_List section header_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeader_List_section_header_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeader_List section header_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeader_List_section_header_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextCustomPadding_List supporting text - custom padding_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextCustomPadding_List_supporting_text_-_custom_padding_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextCustomPadding_List supporting text - custom padding_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextCustomPadding_List_supporting_text_-_custom_padding_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextDefaultPadding_List supporting text - default padding_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextDefaultPadding_List_supporting_text_-_default_padding_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextDefaultPadding_List supporting text - default padding_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextDefaultPadding_List_supporting_text_-_default_padding_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextLargePadding_List supporting text - large padding_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextLargePadding_List_supporting_text_-_large_padding_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextLargePadding_List supporting text - large padding_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextLargePadding_List_supporting_text_-_large_padding_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextNoPadding_List supporting text - no padding_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextNoPadding_List_supporting_text_-_no_padding_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextNoPadding_List supporting text - no padding_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextNoPadding_List_supporting_text_-_no_padding_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List supporting text - small padding_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List_supporting_text_-_small_padding_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List supporting text - small padding_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List_supporting_text_-_small_padding_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_MediumTopAppBar_App Bars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_MediumTopAppBar_App_Bars_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_MediumTopAppBar_App Bars_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_MediumTopAppBar_App_Bars_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom Sheets_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom Sheets_en.png deleted file mode 100644 index b7073fe2b5..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom Sheets_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:437177eaa4bdd53a403f0c742c4ccee8d90ac3aebe357b97c8308a02535d4ede -size 7416 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en.png new file mode 100644 index 0000000000..eba368322a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7dfe9390f76d961b136be0e8117c75b5617f292f92cbb06ec55075a081e2c97e +size 7143 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom Sheets_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom Sheets_en.png deleted file mode 100644 index b468064746..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom Sheets_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3f6e971231fb2aa0eb200880b01b66ef1de1c646cbf13a6c9887c5cff49f2472 -size 7738 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en.png new file mode 100644 index 0000000000..5b7d3bc913 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b51c31823790651708d61bf7653583c2b6bd2fadd5051d0a74c677f964fa9954 +size 7657 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveNoneQuery_Search views_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveNoneQuery_Search_views_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveNoneQuery_Search views_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveNoneQuery_Search_views_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithContent_Search views_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithContent_Search_views_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithContent_Search views_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithContent_Search_views_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search views_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search views_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search views_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search_views_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search views_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search_views_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search views_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search_views_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search views_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search_views_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarInactive_Search views_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarInactive_Search_views_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarInactive_Search views_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarInactive_Search_views_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar with action and close button_Snackbars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar with action and close button_Snackbars_en.png deleted file mode 100644 index 4b066be821..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar with action and close button_Snackbars_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0c5d090a3260e036a55d505aa77efea3f87eab44dfddc0b9921f0ff1e37894b2 -size 17488 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar_with_action_and_close_button_Snackbars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar_with_action_and_close_button_Snackbars_en.png new file mode 100644 index 0000000000..0d59e5edb1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar_with_action_and_close_button_Snackbars_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56add2018c57255c7ae2cde37a9fb9b2aaade95d540ce25058bb9cda59014d1d +size 17511 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar with action and close button on new line_Snackbars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar with action and close button on new line_Snackbars_en.png deleted file mode 100644 index 7f564c3c1e..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar with action and close button on new line_Snackbars_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9adda3be9f29bdd1cf0c861a1cec6cffe54355107a0ea1fa123c311d010c9066 -size 18004 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar_with_action_and_close_button_on_new_line_Snackbars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar_with_action_and_close_button_on_new_line_Snackbars_en.png new file mode 100644 index 0000000000..43b7bf8065 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar_with_action_and_close_button_on_new_line_Snackbars_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a32deb412a30ddf97dce8b46d714474bdc257cf6673c100e115c92d308d6d61d +size 18055 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar with action on new line_Snackbars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar with action on new line_Snackbars_en.png deleted file mode 100644 index 2cdaf526ad..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar with action on new line_Snackbars_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ac35075fe2575c76cd4a7c5569532672a519d9dfd43ccedac175a2b551dc6ce3 -size 17093 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar_with_action_on_new_line_Snackbars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar_with_action_on_new_line_Snackbars_en.png new file mode 100644 index 0000000000..24d6256ea5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar_with_action_on_new_line_Snackbars_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab5947e923ac546d5498b51ec7e2ddcaf46787ad5fa61fdacd7883a7552fea45 +size 17144 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithAction_Snackbar with action_Snackbars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithAction_Snackbar with action_Snackbars_en.png deleted file mode 100644 index 517cbdfb1d..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithAction_Snackbar with action_Snackbars_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6303379f0d3c01d2a7221d2d96f9549fc938217809e0285a4cc610f4bc26e322 -size 16709 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en.png new file mode 100644 index 0000000000..b615d62be3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33f8f9d907088058926fc1b9aa01f225cb564c0a6f47f83d9368a94c0dcd4775 +size 16732 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_Snackbar_Snackbar_Snackbars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_Snackbar_Snackbar_Snackbars_en.png index 81f1c43237..7de7c4d83e 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_Snackbar_Snackbar_Snackbars_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_Snackbar_Snackbar_Snackbars_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89fe35e3ae758e588aa20cb81c1a3ba88ab50e899e712dc5ccb2ca47945764bf -size 14128 +oid sha256:f14f1f61bfdb35d48e329e152b6591585c6e23eda37fe38594848de64bf08c31 +size 14155 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_TopAppBar_App Bars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_TopAppBar_App_Bars_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_TopAppBar_App Bars_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_TopAppBar_App_Bars_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en.png index 33edf8e59a..63108d9433 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f229349b73eb44932d3e5afd47cca8b456354b298272c0b20a67ce77fc1fe16 -size 13574 +oid sha256:1c3f8d8596a343d874f047bdad91e52206308066b6837271a3083b31f139ef49 +size 13526 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en.png index 68612c2a19..90de328262 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de9e050ea9073ded176f1202fcf2550251f2c42197549bccdd37a339227f0cbf -size 12377 +oid sha256:a86e5acab8589a935f1368134181edd47046096ed04a53c2d81e2ced733af0a9 +size 12116 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en.png index 0a1449cca7..4927ef3385 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1196ef582a13714f6f33a0dc1f04996cb55e38c247585d8aa1b3804eb39d030d -size 12325 +oid sha256:997f06a80f20ea48389fc05cf0abb7eb584ef02688f6eebb3e5dc0151afbaa04 +size 50460 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en.png index e8f36f6999..96a5311a1e 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5bf752831831664d3fa3caf4877ff606956e268c69066b8c4c66062771d12eef -size 11779 +oid sha256:f05e63ae7b68f0049c8c702c584ec276c0cca2d7680d9b401f652d155870717d +size 48946 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerCaption_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerCaption_Day_0_en.png new file mode 100644 index 0000000000..151c86e357 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerCaption_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:391d3741acfd768614a9bc70e948f7fc49b37d75e65591721b922e678d520bac +size 44773 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerCaption_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerCaption_Night_0_en.png new file mode 100644 index 0000000000..91dfb69902 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerCaption_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:713a314ecd36e4d95e5c287ab9a4b5968f5a5090dbb4910d9739e66969fdd424 +size 43417 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Day_0_en.png index 0a1449cca7..4927ef3385 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1196ef582a13714f6f33a0dc1f04996cb55e38c247585d8aa1b3804eb39d030d -size 12325 +oid sha256:997f06a80f20ea48389fc05cf0abb7eb584ef02688f6eebb3e5dc0151afbaa04 +size 50460 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Night_0_en.png index e8f36f6999..96a5311a1e 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5bf752831831664d3fa3caf4877ff606956e268c69066b8c4c66062771d12eef -size 11779 +oid sha256:f05e63ae7b68f0049c8c702c584ec276c0cca2d7680d9b401f652d155870717d +size 48946 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Day_0_en.png index 522dac8461..a075c67fc8 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c1096dd5654c5c6bcd274812f51fd7a278084f63cc2879c4217c3f1bd8fbaaa -size 41102 +oid sha256:8a2232abaacfc851133d6c30e6c88a83304beb7a3ba594793c70dad307330c36 +size 51848 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Night_0_en.png index 1411021e07..b5b2dd3d60 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:afc6b93d2e4ce6bcb02c38eccb115e61f0ebd682a7c0b8037e74c20952d8ae2b -size 38429 +oid sha256:c215ef11337a05564282b052d6a725f41d9e929c493fd74bc1e785f21293acbe +size 50191 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_0_en.png index 18c6848a23..a567a7d6af 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ba137e031bf871e7dfc4e02dde94543a2fdae300182e943f5e231a63767df21 -size 19952 +oid sha256:f515200dc9a53f034ad2476ede89f4c3c94cfd6fd316ee61e6fa13deb7ba6e02 +size 74514 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_10_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_10_en.png index 1238496c22..405eba9d83 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eec0c7c8a131d5e17833897ee777eebd7272e15ee8c664fe106bfb924617d6e4 -size 15465 +oid sha256:ee4c30220a64f8751bf2d51b9b09d75ab1e69bbbe4d34b63093740070e7c35c4 +size 57833 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_11_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_11_en.png index ec2f06f1f7..89c19bc86a 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0fe4485cd7a3194f9805e8ca3f83146ca4ef84bc8fe7b7d314908c27fc1f55e9 -size 19341 +oid sha256:ab63cc4ccf3082558b1571e7ee5399d3409ab8f6205f68b907c6359fdaea7be4 +size 72521 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_1_en.png index 9c21394fa3..b8407463e0 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:40061abbccaf9416b767383eb7c31aa213490dddae7d5ebf0a43e160fcfaa0d4 -size 22480 +oid sha256:c51b345180a86b2826430c121e54aabbfad2405f6d422b7c0e1112582fa9f755 +size 83966 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_2_en.png index a6f04755f2..a0875268d8 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b3b984b2c549b30f18bc7ea8afae134d10b223137c98461cc99c09d8f2c7a7b -size 16288 +oid sha256:f1c8e13e73696d0aa32cdf17915a6e488072db61af2349dba78e6add23c6a8e5 +size 60982 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_3_en.png index b522c20bff..69d175e03d 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:332c75d3dfd87165edfd3c7550864424f12048b90f8fe2204e5127f23607762c -size 15862 +oid sha256:0207816e0da278a25a7da9d456d664cf925ed419f8a5046a69465ffef985ff95 +size 59299 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_4_en.png index 0a78fb33c3..7c5bbc0f59 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4521f8d315cbdb95e5e3e1f34fe395e543aef52ee402b6ff0072a81cc170f37c -size 18253 +oid sha256:da1e22e5b3de270fba766ff027113ce7836ffd72b1cf4de2e6cac6544086fff4 +size 67786 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_5_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_5_en.png index 3ebec5c75b..691a386ea4 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7dbc2ec4e7fa64dbb8214457aac6c049a71cf438ddda9819400837e50e13fa79 -size 15580 +oid sha256:10510c2a67fc859d0564c6e34a7021e81f1119eaca448c5b5e928bc0a7d5a619 +size 58228 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_6_en.png index 7ee96cb375..e2351db809 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e956f8706aa28409214bd473953d5d21455bba28730098c8107a02ba9fe3cbc -size 15788 +oid sha256:b1b7e7050bc5135b0166a752f5d3a5276348e6fa4a35cab8721a2adb324360ed +size 59040 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_7_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_7_en.png index fdc18ba43f..204be1a21f 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:631320ef054296d5bd1cbe4d2efc93fc2cf4d502e6cde6092df9fd0a53ff0cc4 -size 16319 +oid sha256:d7e4015c9c11b473205c611694cf2b5d52d20d49bee6cd6f61474f09a87de696 +size 61191 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_8_en.png index 9cd314c654..7ca52ef726 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d13a2471950e1fa8a1e006e5f37ad19d2046ed5b894735b13dee153e22a317d6 -size 18349 +oid sha256:5b274f77b1b017d7385ae1dfffb8051bb95db6372a7f32ce6838fa692c3f2562 +size 68381 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_9_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_9_en.png index 42598fa896..a4495f3476 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2564c5807df45b8bf1ce9557bcb49121bd34fc831affe17abd440dc53b915467 -size 15666 +oid sha256:fc0952625f2a7c5901a3949135c1a0a343f40b33d4192425a97245dbe6b5a04a +size 58502 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_0_en.png index ad21a120e3..cff4bec3f7 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e5bf7c115d5c0e25f325d8e8e7b01f98466c4d8fbd752882db8e8b3e2e4ea70b -size 18846 +oid sha256:8b06693cde76b2ac00c31b1c767910e46dbac4f099abf17e66ceb2de87acb445 +size 72106 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_10_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_10_en.png index f1bdd6f14f..11c89aba8e 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82ce783813417d6da3db55ae1d4f8a6690b775c4e809c3480ca66494662921ab -size 14624 +oid sha256:1fff7343bff8fe781d50732fb4e1fd7e6caf228f9520b9f639cab367db4dfd5c +size 55852 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_11_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_11_en.png index f4592dbda9..8edecf2449 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2dfd6f04f64d1a6a750c67b10f55f74fa423c884b8788b5e5de520bdaa7b6564 -size 18446 +oid sha256:08d653cdcc4c65cc1b1dbd89789cf24594e00a8f7fa2941d56fb93fda7681e4e +size 70210 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_1_en.png index 66aeaddc68..46923d92a5 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4af010eaab0dedba3220446203abd237c7f9817df08e8458896e5e6edcef7f87 -size 21295 +oid sha256:01c2360b1d526713d82566794154743924fb15e1bb6e8ec250a2cc5629b5db02 +size 81247 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_2_en.png index d051afdd4d..60901ff09c 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b094fa4d99b60bec8131d02afb0b004929120a13e9b6f56fe5ec80899db23367 -size 15549 +oid sha256:447b98cc3293ed3eee779cb9ffca62191b5f2d8911bc6e5912c4b23a2115b514 +size 59195 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_3_en.png index cd36d31c39..9d761b243f 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e556677d17885e1daa1054f19fc91a9adb2c3cce6fb386aee95e0f7f5869218 -size 15137 +oid sha256:862065af54f5ceaba8304d3c15f24c7021146db4e9becec859705888cdde4bae +size 57523 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_4_en.png index 1b59db3513..d8774c39a0 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a4c6d53fac8158b00f0b3dbf82cd355da6416eb373b3bd57b58cf0a782bc0a6 -size 17374 +oid sha256:a61b8ca772feebb1ccb86949b4a63080d42f2d26a29ec3ea90e47404e9ff3912 +size 65950 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_5_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_5_en.png index dc6cb0c954..b561d79b42 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5273133c6205403ade60a42e415c74aa988fcf60cf3125aa50ae10e0d3257558 -size 14833 +oid sha256:e75e80ffc8761b0a91f0ee3137df1987104b9a0546a6edc05d55cbc29262c313 +size 56483 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_6_en.png index 8d90207152..807283d490 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb093750e294a6e271f743dd40e3a8acad1783c7d7b6ef3e715a78854ede40ae -size 14968 +oid sha256:1ccbf26f9c3cbbba607bcb1d1a4f3701717a8d39d7c4194a393a0fb1746114c1 +size 57164 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_7_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_7_en.png index 5ca572a992..24f085a0bf 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e99bd0a813c6130c77f549c3bda2743ef5cad1b442c789796a765737ce2a52e0 -size 15616 +oid sha256:224bc191f6bd8761d5301039f29bebf1b3a9773ee454820cb9c64ab2d9c7b612 +size 59458 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_8_en.png index dd11957d1a..8b5db4956e 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3840ff79977e18a589db993e62575d9a81484cb712e64eef0b5df740f3255fa4 -size 17459 +oid sha256:c491288bfd12be3e23272f35a49beafac768bac836b43cc1bde0507ed810094e +size 66436 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_9_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_9_en.png index d9e5e61da9..db33b596b2 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e366992f5cefcede77af4511e1f06464fa62352f341161cf0f0e5c320dffdfd -size 14869 +oid sha256:079cd865d0feb88d5746c3966f8ec0d72e40140626d5ffd6a918095e38f09fcd +size 56699 diff --git a/tools/localazy/config.json b/tools/localazy/config.json index fb05345687..f18744bae9 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -61,6 +61,7 @@ "name" : ":features:createroom:impl", "includeRegex" : [ "screen_create_room_.*", + "screen\\.create_room\\..*", "screen_start_chat_.*" ] },